2007-08-12 19:40:44 +00:00
|
|
|
//
|
|
|
|
// File: VLPListDocument.h - Convert document from and to Cocoa plist
|
|
|
|
//
|
|
|
|
// Author(s):
|
|
|
|
//
|
|
|
|
// (MN) Matthias Neeracher
|
|
|
|
//
|
|
|
|
// Copyright © 2007 Matthias Neeracher
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "VLPListDocument.h"
|
2007-08-12 20:45:25 +00:00
|
|
|
#import "VLModel.h"
|
2007-08-12 19:40:44 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// To convert from and to complex file formats, we use ruby scripts operating
|
|
|
|
// on the XML representation of a Cocoa property list. The property list
|
|
|
|
// representation is strictly intended as an intermediate representation,
|
|
|
|
// subject to change as necessary.
|
|
|
|
//
|
|
|
|
|
|
|
|
@implementation VLDocument (Plist)
|
|
|
|
|
|
|
|
class VLPlistVisitor : public VLSongVisitor {
|
|
|
|
public:
|
2007-08-12 20:45:25 +00:00
|
|
|
VLPlistVisitor(NSMutableDictionary * plist, bool performanceOrder)
|
2007-08-12 19:40:44 +00:00
|
|
|
: fPlist(plist), fPerfOrder(performanceOrder) {}
|
|
|
|
|
|
|
|
virtual void Visit(VLSong & song);
|
|
|
|
protected:
|
|
|
|
virtual void VisitMeasure(size_t m, VLProperties & p, VLMeasure & meas);
|
|
|
|
virtual void VisitNote(VLLyricsNote & n);
|
|
|
|
virtual void VisitChord(VLChord & c);
|
2007-08-12 20:45:25 +00:00
|
|
|
|
|
|
|
NSArray * EncodeProperties(const std::vector<VLProperties> & properties);
|
|
|
|
NSDictionary * EncodeProperties(const VLProperties & properties);
|
|
|
|
|
|
|
|
NSMutableDictionary * fPlist;
|
|
|
|
NSMutableArray * fMeasures;
|
|
|
|
NSMutableArray * fNotes;
|
|
|
|
NSMutableArray * fChords;
|
|
|
|
bool fPerfOrder;
|
|
|
|
const VLSong * fSong;
|
2007-08-12 19:40:44 +00:00
|
|
|
};
|
|
|
|
|
2007-08-12 20:45:25 +00:00
|
|
|
NSArray * VLPlistVisitor::EncodeProperties(const std::vector<VLProperties> & properties)
|
|
|
|
{
|
|
|
|
NSMutableArray * pa = [NSMutableArray arrayWithCapacity:properties.size()];
|
|
|
|
|
|
|
|
for (std::vector<VLProperties>::const_iterator i = properties.begin();
|
|
|
|
i != properties.end(); ++i)
|
|
|
|
[pa addObject:EncodeProperties(*i)];
|
|
|
|
|
|
|
|
return pa;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSDictionary * VLPlistVisitor::EncodeProperties(const VLProperties & properties)
|
|
|
|
{
|
|
|
|
return [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[NSNumber numberWithInt: properties.fTime.fNum], @"timeNum",
|
|
|
|
[NSNumber numberWithInt: properties.fTime.fDenom], @"timeDenom",
|
|
|
|
[NSNumber numberWithInt: properties.fKey], @"key",
|
|
|
|
[NSNumber numberWithInt: properties.fMode], @"mode",
|
2007-08-12 21:30:40 +00:00
|
|
|
[NSNumber numberWithInt: properties.fDivisions], @"divisions",
|
|
|
|
nil];
|
2007-08-12 20:45:25 +00:00
|
|
|
}
|
|
|
|
|
2007-08-12 19:40:44 +00:00
|
|
|
void VLPlistVisitor::Visit(VLSong & song)
|
|
|
|
{
|
|
|
|
fSong = &song;
|
|
|
|
fMeasures = [NSMutableArray arrayWithCapacity:32];
|
|
|
|
VisitMeasures(song, fPerfOrder);
|
|
|
|
|
|
|
|
[fPlist setObject:EncodeProperties(song.fProperties) forKey:@"properties"];
|
|
|
|
[fPlist setObject:fMeasures forKey:@"measures"];
|
|
|
|
}
|
|
|
|
|
|
|
|
void VLPlistVisitor::VisitMeasure(size_t m, VLProperties & p, VLMeasure & meas)
|
|
|
|
{
|
|
|
|
fNotes = [NSMutableArray arrayWithCapacity:1];
|
|
|
|
fChords= [NSMutableArray arrayWithCapacity:1];
|
|
|
|
|
|
|
|
VisitNotes(meas, p, true);
|
|
|
|
VisitChords(meas);
|
|
|
|
|
2007-08-23 12:16:38 +00:00
|
|
|
NSMutableDictionary * md =
|
|
|
|
[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
2007-08-12 19:40:44 +00:00
|
|
|
[NSNumber numberWithInt:m], @"measure",
|
|
|
|
[NSNumber numberWithInt:meas.fPropIdx], @"properties",
|
2007-08-12 21:30:40 +00:00
|
|
|
fNotes, @"melody", fChords, @"chords",
|
|
|
|
nil];
|
2007-08-23 12:16:38 +00:00
|
|
|
int times;
|
|
|
|
bool last;
|
|
|
|
size_t volta;
|
|
|
|
if (fSong->DoesBeginRepeat(m, ×))
|
|
|
|
[md setObject:
|
|
|
|
[NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[NSNumber numberWithInt:times], @"times", nil]
|
|
|
|
forKey: @"begin-repeat"];
|
|
|
|
if (fSong->DoesBeginEnding(m, &last, &volta))
|
|
|
|
[md setObject:
|
|
|
|
[NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[NSNumber numberWithBool:!last], @"last",
|
|
|
|
[NSNumber numberWithInt:volta], @"volta",
|
|
|
|
nil]
|
|
|
|
forKey: @"begin-ending"];
|
|
|
|
if (fSong->DoesEndRepeat(m+1, ×))
|
|
|
|
[md setObject:
|
|
|
|
[NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[NSNumber numberWithInt:times], @"times", nil]
|
|
|
|
forKey: @"end-repeat"];
|
|
|
|
if (fSong->DoesEndEnding(m, &last, &volta))
|
|
|
|
[md setObject:
|
|
|
|
[NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[NSNumber numberWithBool:!last], @"last",
|
|
|
|
[NSNumber numberWithInt:volta], @"volta",
|
|
|
|
nil]
|
|
|
|
forKey: @"end-ending"];
|
2007-08-12 19:40:44 +00:00
|
|
|
[fMeasures addObject:md];
|
|
|
|
}
|
|
|
|
|
|
|
|
void VLPlistVisitor::VisitNote(VLLyricsNote & n)
|
|
|
|
{
|
2007-08-18 23:17:27 +00:00
|
|
|
NSMutableArray * ly = [NSMutableArray arrayWithCapacity:0];
|
|
|
|
for (size_t i = 0; i<n.fLyrics.size(); ++i)
|
|
|
|
[ly addObject:n.fLyrics[i].fText.size()
|
|
|
|
? [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[NSString stringWithUTF8String:n.fLyrics[i].fText.c_str()], @"text",
|
|
|
|
[NSNumber numberWithInt:n.fLyrics[i].fKind], @"kind",
|
|
|
|
nil]
|
|
|
|
: [NSDictionary dictionary]];
|
|
|
|
|
2007-08-12 19:40:44 +00:00
|
|
|
NSDictionary * nd =
|
2007-08-12 20:45:25 +00:00
|
|
|
[NSDictionary dictionaryWithObjectsAndKeys:
|
2007-08-12 19:40:44 +00:00
|
|
|
[NSNumber numberWithInt:n.fDuration.fNum], @"durNum",
|
|
|
|
[NSNumber numberWithInt:n.fDuration.fDenom], @"durDenom",
|
|
|
|
[NSNumber numberWithInt:n.fPitch], @"pitch",
|
|
|
|
[NSNumber numberWithInt:n.fTied], @"tied",
|
2007-08-12 21:30:40 +00:00
|
|
|
[NSNumber numberWithInt:n.fVisual], @"visual",
|
2007-08-18 23:17:27 +00:00
|
|
|
ly, @"lyrics",
|
2007-08-12 21:30:40 +00:00
|
|
|
nil];
|
2007-08-12 19:40:44 +00:00
|
|
|
[fNotes addObject:nd];
|
|
|
|
}
|
|
|
|
|
|
|
|
void VLPlistVisitor::VisitChord(VLChord & c)
|
|
|
|
{
|
|
|
|
NSDictionary * cd =
|
2007-08-12 20:45:25 +00:00
|
|
|
[NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[NSNumber numberWithInt:c.fDuration.fNum], @"durNum",
|
|
|
|
[NSNumber numberWithInt:c.fDuration.fDenom], @"durDenom",
|
|
|
|
[NSNumber numberWithInt:c.fPitch], @"pitch",
|
|
|
|
[NSNumber numberWithInt:c.fSteps], @"steps",
|
2007-08-12 21:30:40 +00:00
|
|
|
[NSNumber numberWithInt:c.fRootPitch], @"root",
|
|
|
|
nil];
|
2007-08-12 19:40:44 +00:00
|
|
|
[fChords addObject: cd];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)plistInPerformanceOrder:(BOOL)performanceOrder
|
|
|
|
{
|
|
|
|
NSMutableDictionary * plist =
|
2007-08-23 12:16:38 +00:00
|
|
|
[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
songTitle, @"title",
|
|
|
|
songGroove, @"groove", songTempo, @"tempo",
|
|
|
|
songComposer, @"composer", songLyricist, @"lyricist",
|
|
|
|
[NSDate date], @"saved",
|
|
|
|
[NSString stringWithFormat:@"VocalEasel %@",
|
|
|
|
[[NSBundle mainBundle]
|
|
|
|
objectForInfoDictionaryKey:@"CFBundleVersion"]],
|
|
|
|
@"software",
|
|
|
|
nil];
|
2007-08-12 19:40:44 +00:00
|
|
|
|
|
|
|
VLPlistVisitor songWriter(plist, performanceOrder);
|
|
|
|
songWriter.Visit(*song);
|
|
|
|
|
|
|
|
return plist;
|
|
|
|
}
|
|
|
|
|
2007-08-12 21:30:40 +00:00
|
|
|
- (IBAction)dump:(id)sender
|
2007-08-18 23:17:27 +00:00
|
|
|
{
|
|
|
|
id plist = [self plistInPerformanceOrder:NO];
|
|
|
|
if ([sender tag])
|
|
|
|
plist = [[[NSString alloc] initWithData:
|
|
|
|
[NSPropertyListSerialization dataFromPropertyList:plist format:NSPropertyListXMLFormat_v1_0 errorDescription:nil]
|
|
|
|
encoding:NSUTF8StringEncoding] autorelease];
|
|
|
|
NSLog(@"%@\n", plist);
|
2007-08-12 21:30:40 +00:00
|
|
|
}
|
|
|
|
|
2007-08-12 19:40:44 +00:00
|
|
|
- (BOOL)readFromPlist:(id)plist error:(NSError **)outError
|
|
|
|
{
|
2007-08-12 20:45:25 +00:00
|
|
|
return NO;
|
2007-08-12 19:40:44 +00:00
|
|
|
}
|
|
|
|
|
2007-08-12 20:45:25 +00:00
|
|
|
- (NSData *)runFilter:(NSString *)filterName withContents:(NSData *)contents
|
2007-08-12 19:40:44 +00:00
|
|
|
{
|
2007-08-22 18:27:40 +00:00
|
|
|
NSString * filterPath = [[NSBundle mainBundle] pathForResource:filterName
|
|
|
|
ofType:nil
|
|
|
|
inDirectory:@"Filters"];
|
|
|
|
NSPipe * filterInput = [NSPipe pipe];
|
|
|
|
NSPipe * filterOutput = [NSPipe pipe];
|
|
|
|
NSPipe * filterError = [NSPipe pipe];
|
|
|
|
|
|
|
|
NSTask * filterTask = [[NSTask alloc] init];
|
|
|
|
[filterTask setLaunchPath:filterPath];
|
|
|
|
[filterTask setStandardInput:filterInput];
|
|
|
|
[filterTask setStandardOutput:filterOutput];
|
|
|
|
[filterTask setStandardError:filterError];
|
|
|
|
[filterTask launch];
|
|
|
|
|
|
|
|
NSFileHandle * inputHandle = [filterInput fileHandleForWriting];
|
|
|
|
[inputHandle writeData:contents];
|
|
|
|
[inputHandle closeFile];
|
|
|
|
|
|
|
|
NSFileHandle * outputHandle = [filterOutput fileHandleForReading];
|
|
|
|
NSData * output = [outputHandle readDataToEndOfFile];
|
|
|
|
|
|
|
|
NSFileHandle * errorHandle = [filterError fileHandleForReading];
|
|
|
|
NSData * error = [errorHandle readDataToEndOfFile];
|
|
|
|
|
|
|
|
[filterTask waitUntilExit];
|
|
|
|
[filterTask release];
|
|
|
|
|
|
|
|
if ([error length])
|
|
|
|
[NSException raise:NSInvalidArgumentException
|
|
|
|
format:@"Filter %@: %@", filterName, error];
|
|
|
|
|
|
|
|
return output;
|
2007-08-12 19:40:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSFileWrapper *)fileWrapperWithFilter:(NSString *)filterName
|
|
|
|
error:(NSError **)outError
|
|
|
|
{
|
|
|
|
NSBundle * mainBundle = [NSBundle mainBundle];
|
|
|
|
BOOL perfOrder = [mainBundle pathForResource:filterName
|
2007-08-12 20:45:25 +00:00
|
|
|
ofType:@"pwriter" inDirectory:@"Filters"] != nil;
|
2007-08-12 19:40:44 +00:00
|
|
|
filterName = [filterName stringByAppendingPathExtension:
|
|
|
|
perfOrder ? @"pwriter" : @"writer"];
|
2007-08-22 18:27:40 +00:00
|
|
|
id inPlist= [self plistInPerformanceOrder:perfOrder];
|
|
|
|
NSData * inData =
|
|
|
|
[NSPropertyListSerialization dataFromPropertyList:inPlist
|
|
|
|
format:NSPropertyListXMLFormat_v1_0
|
|
|
|
errorDescription:nil];
|
2007-08-12 19:40:44 +00:00
|
|
|
NSData * outData= [self runFilter:filterName withContents:inData];
|
|
|
|
|
|
|
|
return [[[NSFileWrapper alloc] initRegularFileWithContents:outData]
|
|
|
|
autorelease];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)readFromFileWrapper:(NSFileWrapper *)wrapper
|
|
|
|
withFilter:(NSString *)filterName
|
|
|
|
error:(NSError **)outError
|
|
|
|
{
|
2007-08-22 18:27:40 +00:00
|
|
|
filterName = [filterName stringByAppendingPathExtension:@"reader"];
|
|
|
|
|
|
|
|
NSData * inData = [wrapper regularFileContents];
|
|
|
|
NSData * outData = [self runFilter:filterName withContents:inData];
|
|
|
|
NSString*errString;
|
|
|
|
id outPlist =
|
|
|
|
[NSPropertyListSerialization propertyListFromData:outData
|
|
|
|
mutabilityOption:NSPropertyListImmutable
|
|
|
|
format:NULL errorDescription:&errString];
|
|
|
|
if (!outPlist)
|
|
|
|
return NO;
|
|
|
|
else
|
|
|
|
return [self readFromPlist:outPlist error:outError];
|
2007-08-12 19:40:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|