diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings index 9c519cb..10e1e21 100644 Binary files a/English.lproj/InfoPlist.strings and b/English.lproj/InfoPlist.strings differ diff --git a/Resources/Info.plist b/Resources/Info.plist index b94cd0c..1665603 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -30,6 +30,7 @@ VLLilypondType VLMusicXMLType VLMMAType + VLMIDIType NSPersistentStoreTypeKey XML @@ -80,6 +81,28 @@ NSPersistentStoreTypeKey Binary + + CFBundleTypeExtensions + + mid + + CFBundleTypeMIMETypes + + audio/midi + + CFBundleTypeName + VLMIDIType + CFBundleTypeOSTypes + + Midi + + CFBundleTypeRole + Editor + LSTypeIsPackage + + NSPersistentStoreTypeKey + XML + CFBundleExecutable VocalEasel diff --git a/Sources/VLDocument.h b/Sources/VLDocument.h index 48e87b6..698a86c 100644 --- a/Sources/VLDocument.h +++ b/Sources/VLDocument.h @@ -46,7 +46,8 @@ - (NSString *) tmpPath; - (NSString *) workPath; - (NSString *) baseName; -- (NSURL *) fileURLWithExtension:(NSString*)extension; +- (NSURL *) fileURLWithExtension:(NSString*)extension; +- (NSTask *) taskWithLaunchPath:(NSString *)path arguments:(NSArray *)args; @end diff --git a/Sources/VLDocument.mm b/Sources/VLDocument.mm index d52fed4..0ddef31 100644 --- a/Sources/VLDocument.mm +++ b/Sources/VLDocument.mm @@ -10,6 +10,7 @@ #import "VLXMLDocument.h" #import "VLLilypondDocument.h" #import "VLMMADocument.h" +#import "VLMIDIDocument.h" #import "VLPDFWindow.h" #import "VLLogWindow.h" #import "VLSheetWindow.h" @@ -198,6 +199,8 @@ return [self lilypondFileWrapperWithError:outError]; } else if ([typeName isEqual:@"VLMMAType"]) { return [self mmaFileWrapperWithError:outError]; + } else if ([typeName isEqual:@"VLMIDIType"]) { + return [self midiFileWrapperWithError:outError]; } else { if (outError) *outError = [NSError errorWithDomain:NSCocoaErrorDomain @@ -220,10 +223,28 @@ } } +- (NSTask *) taskWithLaunchPath:(NSString *)launch arguments:(NSArray *)args; +{ + NSTask * task = [[NSTask alloc] init]; + NSString * path = [self workPath]; + NSPipe * pipe = [NSPipe pipe]; + + [task setCurrentDirectoryPath: path]; + [task setStandardOutput: pipe]; + [task setStandardError: pipe]; + [task setArguments: args]; + [task setLaunchPath: launch]; + + [[self logWin] showWindow: self]; + + [NSThread detachNewThreadSelector:@selector(logFromFileHandle:) toTarget:logWin + withObject:[pipe fileHandleForReading]]; + + return task; +} + - (IBAction) engrave:(id)sender { - NSTask * lilypondTask = [[NSTask alloc] init]; - NSString * path = [self workPath]; NSString * base = [self baseName]; NSBundle * mainBundle = [NSBundle mainBundle]; @@ -233,29 +254,20 @@ NSError * err; [self writeToURL:[self fileURLWithExtension:@"ly"] ofType:@"VLLilypondType" error:&err]; - NSPipe * pipe = [NSPipe pipe]; - NSString * tool = + NSString * launch = + [mainBundle pathForResource:@"lilyWrapper" ofType:@"" + inDirectory:@"bin"]; + NSString * tool = [[NSUserDefaults standardUserDefaults] stringForKey:@"VLLilypondPath"]; - NSArray * arguments = [NSArray arrayWithObjects:tool, base, nil]; + NSArray * args = [NSArray arrayWithObjects:tool, base, nil]; + NSTask * task = [self taskWithLaunchPath:launch arguments:args]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(engraveDone:) - name:NSTaskDidTerminateNotification object:lilypondTask]; + name:NSTaskDidTerminateNotification object:task]; - [lilypondTask setCurrentDirectoryPath: path]; - [lilypondTask setStandardOutput: pipe]; - [lilypondTask setStandardError: pipe]; - [lilypondTask setArguments: arguments]; - [lilypondTask setLaunchPath: - [mainBundle pathForResource:@"lilyWrapper" ofType:@"" - inDirectory:@"bin"]]; - [lilypondTask launch]; - - [[self logWin] showWindow: self]; - - [NSThread detachNewThreadSelector:@selector(logFromFileHandle:) toTarget:logWin - withObject:[pipe fileHandleForReading]]; + [task launch]; } - (void)engraveDone:(NSNotification *)notification { diff --git a/Sources/VLMIDIDocument.h b/Sources/VLMIDIDocument.h new file mode 100644 index 0000000..892f9d3 --- /dev/null +++ b/Sources/VLMIDIDocument.h @@ -0,0 +1,16 @@ +// +// VLMMADocument.h +// Vocalese +// +// Created by Matthias Neeracher on 10/20/06. +// Copyright 2006 __MyCompanyName__. All rights reserved. +// + +#import +#import "VLDocument.h" + +@interface VLDocument (MIDI) + +- (NSFileWrapper *)midiFileWrapperWithError:(NSError **)outError; + +@end diff --git a/Sources/VLMIDIDocument.mm b/Sources/VLMIDIDocument.mm new file mode 100644 index 0000000..1c694fb --- /dev/null +++ b/Sources/VLMIDIDocument.mm @@ -0,0 +1,53 @@ +// +// VLLilypondDocument.mm +// Vocalese +// +// Created by Matthias Neeracher on 10/20/06. +// Copyright 2006 __MyCompanyName__. All rights reserved. +// + +#import "VLMMADocument.h" + +@implementation VLDocument (MIDI) + +- (NSFileWrapper *)midiFileWrapperWithError:(NSError **)outError +{ + NSBundle * mainBundle = [NSBundle mainBundle]; + + // + // Convert to MMA format + // + NSError * err; + NSURL * mmaURL = [self fileURLWithExtension:@"mma"]; + [self writeToURL: mmaURL ofType:@"VLMMAType" error:&err]; + + NSString * launch = + [mainBundle pathForResource:@"mmaWrapper" ofType:@"" + inDirectory:@"bin"]; + NSArray * args = [NSArray arrayWithObject: [mmaURL path]]; + NSTask * task = [self taskWithLaunchPath:launch arguments:args]; + + [[NSNotificationCenter defaultCenter] + addObserver:self selector:@selector(mmaDone:) + name:NSTaskDidTerminateNotification object:task]; + + [task launch]; + [task waitUntilExit]; + int status = [task terminationStatus]; + if (!status) { + return [[[NSFileWrapper alloc] + initWithPath:[[self fileURLWithExtension:@"mid"] path]] + autorelease]; + } else { + NSBeep(); + + if (outError) + *outError = [NSError errorWithDomain:NSCocoaErrorDomain + code:NSPersistentStoreSaveError + userInfo:nil]; + + return nil; + } +} + +@end diff --git a/Sources/VLMMADocument.mm b/Sources/VLMMADocument.mm index 5a3f0fe..05c725e 100644 --- a/Sources/VLMMADocument.mm +++ b/Sources/VLMMADocument.mm @@ -45,7 +45,6 @@ const char * sKeyNames[] = { song->fMeasures[m].MMANotes(mmas); mmaFile += "\t{ " + mmas + " }\n"; } - mmaFile += mmas; return [[NSString stringWithUTF8String:mmaFile.c_str()] dataUsingEncoding:NSUTF8StringEncoding]; diff --git a/Sources/VLModel.cpp b/Sources/VLModel.cpp index 8531714..c634b6b 100644 --- a/Sources/VLModel.cpp +++ b/Sources/VLModel.cpp @@ -246,34 +246,36 @@ struct VLChordModifier { uint32_t fDelSteps; }; +#define _ VLChord:: + static const VLChordModifier kModifiers[] = { - {"b13", VLChord::kmMin13th, 0}, - {"add13", VLChord::kmMaj13th, 0}, - {"13", VLChord::kmMin7th | VLChord::kmMaj9th | VLChord::km11th | VLChord::kmMaj13th, 0}, - {"#11", VLChord::kmAug11th, VLChord::km11th}, - {"add11", VLChord::km11th, 0}, - {"11", VLChord::kmMin7th | VLChord::kmMaj9th | VLChord::km11th, 0}, - {"#9", VLChord::kmAug9th, VLChord::kmMaj9th}, - {"+9", VLChord::kmAug9th, VLChord::kmMaj9th}, - {"b9", VLChord::kmMin9th, VLChord::kmMaj9th}, - {"-9", VLChord::kmMin9th, VLChord::kmMaj9th}, - {"69", VLChord::kmDim7th | VLChord::kmMaj9th, 0}, - {"add9", VLChord::kmMaj9th, 0}, - {"9", VLChord::kmMin7th | VLChord::kmMaj9th, 0}, - {"7", VLChord::kmMin7th, 0}, - {"maj", VLChord::kmMaj7th, VLChord::kmMin7th}, - {"6", VLChord::kmDim7th, 0}, - {"#5", VLChord::kmAug5th, VLChord::km5th}, - {"+5", VLChord::kmAug5th, VLChord::km5th}, - {"aug", VLChord::kmAug5th, VLChord::km5th}, - {"+", VLChord::kmAug5th, VLChord::km5th}, - {"b5", VLChord::kmDim5th, VLChord::km5th}, - {"-5", VLChord::kmDim5th, VLChord::km5th}, - {"sus4", VLChord::km4th, VLChord::kmMaj3rd}, - {"sus2", VLChord::kmMaj2nd, VLChord::kmMaj3rd}, - {"sus", VLChord::km4th, VLChord::kmMaj3rd}, - {"4", VLChord::km4th, VLChord::kmMaj3rd}, - {"2", VLChord::kmMaj2nd, VLChord::kmMaj3rd}, + {"b13", _ kmMin13th, 0}, + {"add13", _ kmMaj13th, 0}, + {"13", _ kmMin7th | _ kmMaj9th | _ km11th | _ kmMaj13th, 0}, + {"#11", _ kmAug11th, 0}, + {"add11", _ km11th, 0}, + {"11", _ kmMin7th | _ kmMaj9th | _ km11th, 0}, + {"#9", _ kmAug9th, _ kmMaj9th}, + {"+9", _ kmAug9th, _ kmMaj9th}, + {"b9", _ kmMin9th, _ kmMaj9th}, + {"-9", _ kmMin9th, _ kmMaj9th}, + {"69", _ kmDim7th | _ kmMaj9th, 0}, + {"add9", _ kmMaj9th, 0}, + {"9", _ kmMin7th | _ kmMaj9th, 0}, + {"7", _ kmMin7th, 0}, + {"maj", _ kmMaj7th, _ kmMin7th}, + {"6", _ kmDim7th, 0}, + {"#5", _ kmAug5th, _ km5th}, + {"+5", _ kmAug5th, _ km5th}, + {"aug", _ kmAug5th, _ km5th}, + {"+", _ kmAug5th, _ km5th}, + {"b5", _ kmDim5th, _ km5th}, + {"-5", _ kmDim5th, _ km5th}, + {"sus4", _ km4th, _ kmMaj3rd}, + {"sus2", _ kmMaj2nd, _ kmMaj3rd}, + {"sus", _ km4th, _ kmMaj3rd}, + {"4", _ km4th, _ kmMaj3rd}, + {"2", _ kmMaj2nd, _ kmMaj3rd}, {NULL, 0, 0} }; @@ -540,6 +542,65 @@ void VLChord::LilypondName(std::string & name, bool useSharps) const name += "/+" + LilypondPitchName(fRootPitch, useSharps); } +// +// MMA supports a large but finite list of chords +// +static const VLChordModifier kMMAModifiers[] = { + {"", 0, 0}, + {"+", _ kmAug5th, _ km5th}, + {"11", _ kmMin7th | _ kmMaj9th | _ km11th, 0}, + {"11b9", _ kmMin7th | _ kmMin9th | _ km11th, 0}, + {"13", _ kmMin7th | _ kmMaj9th | _ km11th | _ kmMaj13th, 0}, + {"5", 0, _ kmMaj3rd}, + {"6", _ kmDim7th, 0}, + {"69", _ kmDim7th | _ kmMaj9th, 0}, + {"7", _ kmMin7th, 0}, + {"7#11", _ kmMin7th | _ kmAug11th, 0}, + {"7#5", _ kmMin7th | _ kmAug5th, _ km5th}, + {"7#5#9", _ kmMin7th | _ kmAug5th | _ kmAug9th, _ km5th}, + {"7#5b9", _ kmMin7th | _ kmAug5th | _ kmMin9th, _ km5th}, + {"7#9", _ kmMin7th | _ kmAug9th, 0}, + {"7#9#11", _ kmMin7th | _ kmAug9th | _ kmAug11th, 0}, + {"7b5", _ kmMin7th | _ kmDim5th, _ km5th}, + {"7b5#9", _ kmMin7th | _ kmDim5th | _ kmAug9th, _ km5th}, + {"7b5b9", _ kmMin7th | _ kmDim5th | _ kmMin9th, _ km5th}, + {"7b9", _ kmMin7th | _ kmMin9th, 0}, + {"7sus", _ kmMin7th | _ km4th, _ kmMaj3rd}, + {"7sus2", _ kmMin7th | _ kmMaj2nd, _ kmMaj3rd}, + {"9", _ kmMin7th | _ kmMaj9th, 0}, + {"9#11", _ kmMin7th | _ kmMaj9th | _ kmAug11th, 0}, + {"9#5", _ kmMin7th | _ kmMaj9th | _ kmAug5th, _ km5th}, + {"9b5", _ kmMin7th | _ kmMaj9th | _ kmDim5th, _ km5th}, + {"9sus", _ kmMaj9th, 0}, + {"M13", _ kmMaj7th | _ kmMaj13th, 0}, + {"M7", _ kmMaj7th, 0}, + {"M7#11", _ kmMaj7th | _ kmMaj9th | _ kmAug11th, 0}, + {"M7#5", _ kmMaj7th | _ kmAug5th, _ km5th}, + {"M7b5", _ kmMaj7th | _ kmDim5th, _ km5th}, + {"M9", _ kmMaj7th | _ kmMaj9th, 0}, + {"aug9", _ kmMin7th | _ kmMaj9th | _ kmAug5th, _ km5th}, + {"dim3", _ kmMin3rd | _ kmDim5th, _ kmMaj3rd | _ km5th}, + {"dim7", _ kmMin3rd | _ kmDim5th | _ kmDim7th, _ kmMaj3rd | _ km5th}, + {"m", _ kmMin3rd, _ kmMaj3rd}, + {"m#5", _ kmMin3rd | _ kmAug5th, _ kmMaj3rd | _ km5th}, + {"m(maj7)", _ kmMin3rd | _ kmMaj7th, _ kmMaj3rd}, + {"m(sus9)", _ kmMin3rd | _ kmMaj9th, _ kmMaj3rd}, + {"m11", _ kmMin3rd | _ kmMin7th | _ kmMaj9th | _ km11th, _ kmMaj3rd}, + {"m6", _ kmMin3rd | _ kmDim7th, _ kmMaj3rd}, + {"m69", _ kmMin3rd | _ kmDim7th | _ kmMaj9th, _ kmMaj3rd}, + {"m7", _ kmMin3rd | _ kmMin7th, _ kmMaj3rd}, + {"m7b5", _ kmMin3rd | _ kmMin7th | _ kmDim5th, _ kmMaj3rd | _ km5th}, + {"m7b9", _ kmMin3rd | _ kmMin7th | _ kmMin9th, _ kmMaj3rd}, + {"m9", _ kmMin3rd | _ kmMin7th | _ kmMaj9th, _ kmMaj3rd}, + {"m9b5", _ kmMin3rd | _ kmMin7th | _ kmMaj9th | _ kmDim5th, _ kmMaj3rd | _ km5th}, + {"mM7", _ kmMin3rd | _ kmMaj7th, _ kmMaj3rd}, + {"mb5", _ kmMin3rd | _ kmDim5th, _ kmMaj3rd | _ km5th}, + {"sus", _ km4th, _ kmMaj3rd}, + {"sus2", _ kmMaj2nd, _ kmMaj3rd}, + {"sus9", _ kmMaj9th, 0}, + {NULL, 0, 0} +}; + void VLChord::MMAName(std::string & name, bool useSharps) const { VLFraction dur = fDuration; @@ -550,20 +611,49 @@ void VLChord::MMAName(std::string & name, bool useSharps) const if (fPitch == kNoPitch) { name = '/'; } else { - std::string base, ext, root; - Name(base, ext, root, useSharps); + std::string base, ext; + VLNote::Name(base, useSharps); + size_t best = 0; + size_t bestBits = 32; + size_t bestScore= 0; + for (size_t i=0; kMMAModifiers[i].fName; ++i) { + uint32_t steps = (kmUnison | kmMaj3rd | km5th) + | kMMAModifiers[i].fAddSteps + &~kMMAModifiers[i].fDelSteps; + if (fSteps == steps) { + // + // Exact match + // + best = i; + break; + } + steps ^= fSteps; + size_t bits=0; + size_t score=0; + for (uint32_t b=steps; b; b &= (b-1)) + ++bits; + for (size_t b=0; b<32; ++b) + if (steps & (1<MMAName(note, at, *fProperties); if (notes.size()) notes += ' '; - notes += note; + notes += note+';'; at += i->fDuration; } } diff --git a/Tools/mmaWrapper b/Tools/mmaWrapper index f90f041..3747937 100755 --- a/Tools/mmaWrapper +++ b/Tools/mmaWrapper @@ -1,4 +1,4 @@ #!/bin/sh cd ${0%/*/*}/share/mma -exec $0.py $* +exec ${0%/*}/mma.py $* diff --git a/Vocalese.xcodeproj/project.pbxproj b/Vocalese.xcodeproj/project.pbxproj index b131108..da7fd95 100644 --- a/Vocalese.xcodeproj/project.pbxproj +++ b/Vocalese.xcodeproj/project.pbxproj @@ -52,6 +52,8 @@ 95E04DAB0AEB4886006F30A0 /* VLXMLDocument.mm in Sources */ = {isa = PBXBuildFile; fileRef = 95F5F50E0ADCC433003980B2 /* VLXMLDocument.mm */; }; 95E04DC70AEB4B57006F30A0 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */; }; 95E04DCE0AEB4D9B006F30A0 /* Templates in Resources */ = {isa = PBXBuildFile; fileRef = 95E04DCA0AEB4D9B006F30A0 /* Templates */; }; + 95EDA5AA0B06DE46004D8D6E /* VLMIDIDocument.h in Copy MMA Library */ = {isa = PBXBuildFile; fileRef = 95EDA5A80B06DE46004D8D6E /* VLMIDIDocument.h */; }; + 95EDA5AB0B06DE47004D8D6E /* VLMIDIDocument.mm in Sources */ = {isa = PBXBuildFile; fileRef = 95EDA5A90B06DE46004D8D6E /* VLMIDIDocument.mm */; }; 95F5F50F0ADCC433003980B2 /* VLXMLDocument.mm in Sources */ = {isa = PBXBuildFile; fileRef = 95F5F50E0ADCC433003980B2 /* VLXMLDocument.mm */; }; 95F5F5340ADCCFBB003980B2 /* DTD in Resources */ = {isa = PBXBuildFile; fileRef = 95F5F51E0ADCCFBB003980B2 /* DTD */; }; 95F820AB0AF884A30010963D /* VLMMADocument.mm in Sources */ = {isa = PBXBuildFile; fileRef = 95F820AA0AF884A30010963D /* VLMMADocument.mm */; }; @@ -79,6 +81,7 @@ 95C461FE0B04432700649F92 /* MMA in Copy MMA Library */, 95C461C40B043E8900649F92 /* includes in Copy MMA Library */, 95C461C50B043E8900649F92 /* lib in Copy MMA Library */, + 95EDA5AA0B06DE46004D8D6E /* VLMIDIDocument.h in Copy MMA Library */, ); name = "Copy MMA Library"; runOnlyForDeploymentPostprocessing = 0; @@ -155,6 +158,8 @@ 95E04DA00AEB4837006F30A0 /* TVLLilypond */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = TVLLilypond; sourceTree = BUILT_PRODUCTS_DIR; }; 95E04DA60AEB486E006F30A0 /* TVLLilypond.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; name = TVLLilypond.mm; path = Tests/TVLLilypond.mm; sourceTree = ""; }; 95E04DCA0AEB4D9B006F30A0 /* Templates */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Templates; path = Resources/Templates; sourceTree = ""; }; + 95EDA5A80B06DE46004D8D6E /* VLMIDIDocument.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = VLMIDIDocument.h; path = Sources/VLMIDIDocument.h; sourceTree = ""; }; + 95EDA5A90B06DE46004D8D6E /* VLMIDIDocument.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; name = VLMIDIDocument.mm; path = Sources/VLMIDIDocument.mm; sourceTree = ""; }; 95F5F50D0ADCC433003980B2 /* VLXMLDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLXMLDocument.h; path = Sources/VLXMLDocument.h; sourceTree = ""; }; 95F5F50E0ADCC433003980B2 /* VLXMLDocument.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = VLXMLDocument.mm; path = Sources/VLXMLDocument.mm; sourceTree = ""; }; 95F5F51E0ADCCFBB003980B2 /* DTD */ = {isa = PBXFileReference; lastKnownFileType = folder; name = DTD; path = Resources/DTD; sourceTree = ""; }; @@ -259,12 +264,12 @@ 2A37F4AAFDCFA73011CA2CEA /* Vocalese */ = { isa = PBXGroup; children = ( - 95C461DC0B0442EB00649F92 /* MMA */, - 95C461D40B04403600649F92 /* Tools */, 2A37F4ABFDCFA73011CA2CEA /* Classes */, 2A37F4AFFDCFA73011CA2CEA /* Other Sources */, - 955E59560957C0C50045FDA5 /* Tests */, + 95C461D40B04403600649F92 /* Tools */, + 95C461DC0B0442EB00649F92 /* MMA */, 2A37F4B8FDCFA73011CA2CEA /* Resources */, + 955E59560957C0C50045FDA5 /* Tests */, 2A37F4C3FDCFA73011CA2CEA /* Frameworks */, 19C28FB0FE9D524F11CA2CBB /* Products */, ); @@ -274,6 +279,8 @@ 2A37F4ABFDCFA73011CA2CEA /* Classes */ = { isa = PBXGroup; children = ( + 95EDA5A80B06DE46004D8D6E /* VLMIDIDocument.h */, + 95EDA5A90B06DE46004D8D6E /* VLMIDIDocument.mm */, 95F820A90AF884A30010963D /* VLMMADocument.h */, 95F820AA0AF884A30010963D /* VLMMADocument.mm */, 95A1C3840AF2ACE20076597D /* VLSheetWindow.h */, @@ -562,6 +569,7 @@ 95FC66CE0AF0A591003D9C11 /* VLPDFView.mm in Sources */, 95A1C3860AF2ACE20076597D /* VLSheetWindow.mm in Sources */, 95F820AB0AF884A30010963D /* VLMMADocument.mm in Sources */, + 95EDA5AB0B06DE47004D8D6E /* VLMIDIDocument.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };