Implement MMA/MIDI output

This commit is contained in:
Matthias Neeracher 2006-11-12 11:37:36 +00:00
parent 8f2f268bdd
commit f4c1d7b591
10 changed files with 263 additions and 61 deletions

Binary file not shown.

View File

@ -30,6 +30,7 @@
<string>VLLilypondType</string>
<string>VLMusicXMLType</string>
<string>VLMMAType</string>
<string>VLMIDIType</string>
</array>
<key>NSPersistentStoreTypeKey</key>
<string>XML</string>
@ -80,6 +81,28 @@
<key>NSPersistentStoreTypeKey</key>
<string>Binary</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>mid</string>
</array>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>audio/midi</string>
</array>
<key>CFBundleTypeName</key>
<string>VLMIDIType</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>Midi</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>NSPersistentStoreTypeKey</key>
<string>XML</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>VocalEasel</string>

View File

@ -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

View File

@ -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 {

16
Sources/VLMIDIDocument.h Normal file
View File

@ -0,0 +1,16 @@
//
// VLMMADocument.h
// Vocalese
//
// Created by Matthias Neeracher on 10/20/06.
// Copyright 2006 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "VLDocument.h"
@interface VLDocument (MIDI)
- (NSFileWrapper *)midiFileWrapperWithError:(NSError **)outError;
@end

53
Sources/VLMIDIDocument.mm Normal file
View File

@ -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

View File

@ -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];

View File

@ -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<<b))
score += 32-b;
if (bits < bestBits || (bits==bestBits && score < bestScore)) {
best = i;
bestBits = bits;
bestScore = score;
}
}
ext = kMMAModifiers[best].fName;
name = base+ext;
if (root.size())
name += '/'+root;
if (fRootPitch != kNoPitch)
name += '/' + PitchName(fRootPitch, useSharps);
std::toupper(base[0]);
size_t mod;
while ((mod = name.find("Maj")) != std::string::npos)
name.erase(mod+1, 2);
while ((mod = name.find(kVLSharpStr, 3)) != std::string::npos)
name.replace(mod, 3, '#', 1);
while ((mod = name.find(kVLFlatStr, 3)) != std::string::npos)
name.replace(mod, 3, '&', 1);
while ((mod = name.find(kVLSharpStr)) != std::string::npos)
name.replace(mod, 3, "#", 1);
while ((mod = name.find(kVLFlatStr)) != std::string::npos)
name.replace(mod, 3, "&", 1);
}
while (quarters--)
name += " /";
@ -663,7 +753,7 @@ void VLMeasure::MMANotes(std::string & notes) const
i->MMAName(note, at, *fProperties);
if (notes.size())
notes += ' ';
notes += note;
notes += note+';';
at += i->fDuration;
}
}

View File

@ -1,4 +1,4 @@
#!/bin/sh
cd ${0%/*/*}/share/mma
exec $0.py $*
exec ${0%/*}/mma.py $*

View File

@ -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 = "<group>"; };
95E04DCA0AEB4D9B006F30A0 /* Templates */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Templates; path = Resources/Templates; sourceTree = "<group>"; };
95EDA5A80B06DE46004D8D6E /* VLMIDIDocument.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = VLMIDIDocument.h; path = Sources/VLMIDIDocument.h; sourceTree = "<group>"; };
95EDA5A90B06DE46004D8D6E /* VLMIDIDocument.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; name = VLMIDIDocument.mm; path = Sources/VLMIDIDocument.mm; sourceTree = "<group>"; };
95F5F50D0ADCC433003980B2 /* VLXMLDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLXMLDocument.h; path = Sources/VLXMLDocument.h; sourceTree = "<group>"; };
95F5F50E0ADCC433003980B2 /* VLXMLDocument.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = VLXMLDocument.mm; path = Sources/VLXMLDocument.mm; sourceTree = "<group>"; };
95F5F51E0ADCCFBB003980B2 /* DTD */ = {isa = PBXFileReference; lastKnownFileType = folder; name = DTD; path = Resources/DTD; sourceTree = "<group>"; };
@ -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;
};