diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings
index a4d425a..9c519cb 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 1a6e1b4..b94cd0c 100644
--- a/Resources/Info.plist
+++ b/Resources/Info.plist
@@ -29,6 +29,7 @@
VLLilypondType
VLMusicXMLType
+ VLMMAType
NSPersistentStoreTypeKey
XML
@@ -65,6 +66,20 @@
NSPersistentStoreTypeKey
Binary
+
+ CFBundleTypeExtensions
+
+ mma
+
+ CFBundleTypeName
+ VLMMAType
+ CFBundleTypeRole
+ None
+ LSTypeIsPackage
+
+ NSPersistentStoreTypeKey
+ Binary
+
CFBundleExecutable
VocalEasel
diff --git a/Sources/VLDocument.h b/Sources/VLDocument.h
index 19d6197..40f3ef6 100644
--- a/Sources/VLDocument.h
+++ b/Sources/VLDocument.h
@@ -21,6 +21,8 @@
NSString * songLyricist;
NSString * songComposer;
NSString * songArranger;
+ NSString * songGroove;
+ NSNumber * songTempo;
VLSheetWindow * sheetWin;
VLLogWindow * logWin;
diff --git a/Sources/VLDocument.mm b/Sources/VLDocument.mm
index 16010a6..14b39e3 100644
--- a/Sources/VLDocument.mm
+++ b/Sources/VLDocument.mm
@@ -9,6 +9,7 @@
#import "VLDocument.h"
#import "VLXMLDocument.h"
#import "VLLilypondDocument.h"
+#import "VLMMADocument.h"
#import "VLPDFWindow.h"
#import "VLLogWindow.h"
#import "VLSheetWindow.h"
@@ -25,6 +26,8 @@
songLyricist = @"";
songComposer = @"";
songArranger = @"";
+ songGroove = @"Swing";
+ songTempo = [[NSNumber numberWithInt:120] retain];
sheetWin = nil;
pdfWin = nil;
logWin = nil;
@@ -157,6 +160,8 @@
return [self XMLFileWrapperWithError:outError flat:YES];
} else if ([typeName isEqual:@"VLLilypondType"]) {
return [self lilypondFileWrapperWithError:outError];
+ } else if ([typeName isEqual:@"VLMMAType"]) {
+ return [self mmaFileWrapperWithError:outError];
} else {
if (outError)
*outError = [NSError errorWithDomain:NSCocoaErrorDomain
diff --git a/Sources/VLMMADocument.h b/Sources/VLMMADocument.h
new file mode 100644
index 0000000..463fbaf
--- /dev/null
+++ b/Sources/VLMMADocument.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 (MMA)
+
+- (NSFileWrapper *)mmaFileWrapperWithError:(NSError **)outError;
+
+@end
diff --git a/Sources/VLMMADocument.mm b/Sources/VLMMADocument.mm
new file mode 100644
index 0000000..5a3f0fe
--- /dev/null
+++ b/Sources/VLMMADocument.mm
@@ -0,0 +1,66 @@
+//
+// VLLilypondDocument.mm
+// Vocalese
+//
+// Created by Matthias Neeracher on 10/20/06.
+// Copyright 2006 __MyCompanyName__. All rights reserved.
+//
+
+#import "VLMMADocument.h"
+
+@implementation VLDocument (MMA)
+
+const int kMajorOffset = 6;
+const int kMinorOffset = 9;
+
+const char * sKeyNames[] = {
+ "ges", "des", "as", "es", "bes", "f",
+ "c", "g", "d", "a", "e", "b", "fis", "cis", "gis"
+};
+
+- (NSData *)mmaDataWithError:(NSError **)outError
+{
+ char buf[32];
+ NSBundle * bndl = [NSBundle mainBundle];
+ const VLProperties & prop = song->fProperties.front();
+
+ std::string mmaFile = std::string("// Generated by VocalEasel ")
+ + (const char *)[[bndl objectForInfoDictionaryKey:@"CFBundleVersion"]
+ UTF8String]
+ + "\n\n";
+ sprintf(buf, "Tempo %d\n", [songTempo intValue]);
+ mmaFile += buf;
+ sprintf(buf, "Groove %s\n", [songGroove UTF8String]);
+ mmaFile += buf;
+ sprintf(buf, "KeySig %d%c\n", labs(prop.fKey), prop.fKey>=0 ? '#' : '&');
+ mmaFile += buf;
+ mmaFile += '\n';
+
+ std::string mmas;
+ for (size_t m=0; mCountMeasures(); ++m) {
+ sprintf(buf, "%-5d", m+1);
+ mmaFile += buf;
+ song->fMeasures[m].MMAChords(mmas);
+ mmaFile += mmas;
+ song->fMeasures[m].MMANotes(mmas);
+ mmaFile += "\t{ " + mmas + " }\n";
+ }
+ mmaFile += mmas;
+
+ return [[NSString stringWithUTF8String:mmaFile.c_str()]
+ dataUsingEncoding:NSUTF8StringEncoding];
+}
+
+- (NSFileWrapper *)mmaFileWrapperWithError:(NSError **)outError
+{
+ NSData * data = [self mmaDataWithError:outError];
+
+ if (!data)
+ return nil;
+ else
+ return [[[NSFileWrapper alloc]
+ initRegularFileWithContents:data]
+ autorelease];
+}
+
+@end
diff --git a/Sources/VLModel.cpp b/Sources/VLModel.cpp
index d56b551..493101e 100644
--- a/Sources/VLModel.cpp
+++ b/Sources/VLModel.cpp
@@ -104,6 +104,27 @@ static std::string LilypondPitchName(int8_t pitch, bool useSharps)
return kScale[pitch+1] + std::string("es");
}
+static std::string MMAPitchName(int8_t pitch, bool useSharps)
+{
+ if (pitch == VLNote::kNoPitch)
+ return "r";
+ char name[3];
+ name[2] = 0;
+ name[1] = 'n';
+ pitch %= 12;
+ if (kScale[pitch] != ' ') {
+ name[0] = kScale[pitch];
+ } else if (useSharps) {
+ name[0] = kScale[pitch-1];
+ name[1] = '#';
+ } else {
+ name[0] = kScale[pitch+1];
+ name[1] = '&';
+ }
+
+ return name;
+}
+
VLNote::VLNote(std::string name)
{
//
@@ -175,6 +196,50 @@ void VLNote::LilypondName(std::string & name, VLFraction at, const VLProperties
}
}
+static struct {
+ VLFract fVal;
+ const char * fName;
+} sMMADur [] = {
+ {{1,1}, "1"},
+ {{1,2}, "2"},
+ {{1,3}, "23"},
+ {{1,4}, "4"},
+ {{1,6}, "81"},
+ {{1,8}, "8"},
+ {{1,12}, "82"},
+ {{1,16}, "16"},
+ {{1,24}, "6"},
+ {{1,32}, "32"},
+ {{1,64}, "64"},
+ {{0,0}, 0}
+};
+
+void VLNote::MMAName(std::string & name, VLFraction at, const VLProperties & prop) const
+{
+ bool useSharps = prop.fKey >= 0;
+
+ name.clear();
+ for (VLFraction dur = fDuration; dur.fNum; ) {
+ VLFraction part;
+ prop.PartialNote(at, dur, &part);
+ for (int d=0; sMMADur[d].fName; ++d)
+ if (part == sMMADur[d].fVal) {
+ if (name.size())
+ name += '+';
+ name += sMMADur[d].fName;
+ }
+ dur -= part;
+ at += part;
+ }
+ name += MMAPitchName(fPitch, useSharps);
+ if (fPitch != kNoPitch) {
+ for (int raise = (fPitch-kMiddleC)/kOctave; raise>0; --raise)
+ name += '+';
+ for (int lower = (kMiddleC-fPitch)/kOctave; lower>0; --lower)
+ name += '-';
+ }
+}
+
struct VLChordModifier {
const char * fName;
uint32_t fAddSteps;
@@ -315,6 +380,7 @@ void VLChord::Name(std::string & base, std::string & ext, std::string & root, bo
if (steps & kmMaj7th) {
ext += "Maj";
steps&= ~kmMaj7th;
+ steps|= kmMin7th; // Write out the 7 for clarification
}
//
// 6/9
@@ -431,12 +497,42 @@ void VLChord::LilypondName(std::string & name, bool useSharps) const
name += "/+" + LilypondPitchName(fRootPitch, useSharps);
}
+void VLChord::MMAName(std::string & name, bool useSharps) const
+{
+ VLFraction dur = fDuration;
+ int quarters = static_cast(dur*4.0f+0.5f);
+ name = "";
+ if (!quarters--)
+ return;
+ if (fPitch == kNoPitch) {
+ name = '/';
+ } else {
+ std::string base, ext, root;
+ Name(base, ext, root, useSharps);
+
+ name = base+ext;
+ if (root.size())
+ name += '/'+root;
+ 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 (quarters--)
+ name += " /";
+}
+
static VLFraction MaxNote(VLFraction d)
{
if (d >= 1)
return 1;
if (d.fNum == 1 && !(d.fDenom & (d.fDenom-1))) // Power of 2
return d;
+
VLFraction note(1,2);
VLFraction triplet(1,3);
@@ -512,6 +608,38 @@ VLMeasure::VLMeasure()
{
}
+void VLMeasure::MMANotes(std::string & notes) const
+{
+ VLFraction at(0);
+ VLNoteList::const_iterator i = fMelody.begin();
+ VLNoteList::const_iterator e = fMelody.end();
+
+ notes.clear();
+ for (; i!=e; ++i) {
+ std::string note;
+ i->MMAName(note, at, *fProperties);
+ if (notes.size())
+ notes += ' ';
+ notes += note;
+ at += i->fDuration;
+ }
+}
+
+void VLMeasure::MMAChords(std::string & chords) const
+{
+ VLChordList::const_iterator i = fChords.begin();
+ VLChordList::const_iterator e = fChords.end();
+
+ chords.clear();
+ for (; i!=e; ++i) {
+ std::string chord;
+ i->MMAName(chord, fProperties->fKey >= 0);
+ if (chords.size())
+ chords += ' ';
+ chords += chord;
+ }
+}
+
VLSong::VLSong()
{
const VLFraction fourFour(4,4);
diff --git a/Sources/VLModel.h b/Sources/VLModel.h
index f9a15f8..dee8f51 100644
--- a/Sources/VLModel.h
+++ b/Sources/VLModel.h
@@ -124,6 +124,7 @@ struct VLNote {
void Name(std::string & name, bool useSharps = false) const;
void LilypondName(std::string & name, VLFraction at, const VLProperties & prop) const;
+ void MMAName(std::string & name, VLFraction at, const VLProperties & prop) const;
};
struct VLRest : VLNote {
@@ -185,6 +186,7 @@ struct VLChord : VLNote {
VLChord(std::string name);
void Name(std::string & base, std::string & ext, std::string & root, bool useSharps = false) const;
void LilypondName(std::string & name, bool useSharps = false) const;
+ void MMAName(std::string & name, bool useSharps) const;
};
struct VLProperties {
@@ -219,6 +221,9 @@ struct VLMeasure {
VLSyllList fLyrics;
VLMeasure();
+
+ void MMANotes(std::string & notes) const;
+ void MMAChords(std::string & chords) const;
};
struct VLSong {
diff --git a/Vocalese.xcodeproj/project.pbxproj b/Vocalese.xcodeproj/project.pbxproj
index ddd314b..d5b78f6 100644
--- a/Vocalese.xcodeproj/project.pbxproj
+++ b/Vocalese.xcodeproj/project.pbxproj
@@ -49,6 +49,7 @@
95ECE6590AF3324300FE3E98 /* lilyWrapper in Resources */ = {isa = PBXBuildFile; fileRef = 95ECE6580AF3324300FE3E98 /* lilyWrapper */; };
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 */; };
95FC668F0AF0A08C003D9C11 /* VLLogWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 95FC668E0AF0A08C003D9C11 /* VLLogWindow.mm */; };
95FC66960AF0A112003D9C11 /* VLLogWindow.nib in Resources */ = {isa = PBXBuildFile; fileRef = 95FC66950AF0A112003D9C11 /* VLLogWindow.nib */; };
95FC66A60AF0A24C003D9C11 /* VLPDFWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 95FC66A50AF0A24C003D9C11 /* VLPDFWindow.mm */; };
@@ -118,6 +119,8 @@
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 = ""; };
+ 95F820A90AF884A30010963D /* VLMMADocument.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = VLMMADocument.h; path = Sources/VLMMADocument.h; sourceTree = ""; };
+ 95F820AA0AF884A30010963D /* VLMMADocument.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; name = VLMMADocument.mm; path = Sources/VLMMADocument.mm; sourceTree = ""; };
95FC668D0AF0A08C003D9C11 /* VLLogWindow.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = VLLogWindow.h; path = Sources/VLLogWindow.h; sourceTree = ""; };
95FC668E0AF0A08C003D9C11 /* VLLogWindow.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; name = VLLogWindow.mm; path = Sources/VLLogWindow.mm; sourceTree = ""; };
95FC66950AF0A112003D9C11 /* VLLogWindow.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = VLLogWindow.nib; path = English.lproj/VLLogWindow.nib; sourceTree = ""; };
@@ -230,6 +233,8 @@
2A37F4ABFDCFA73011CA2CEA /* Classes */ = {
isa = PBXGroup;
children = (
+ 95F820A90AF884A30010963D /* VLMMADocument.h */,
+ 95F820AA0AF884A30010963D /* VLMMADocument.mm */,
95A1C3840AF2ACE20076597D /* VLSheetWindow.h */,
95A1C3850AF2ACE20076597D /* VLSheetWindow.mm */,
95FC66CC0AF0A591003D9C11 /* VLPDFView.mm */,
@@ -478,6 +483,7 @@
95FC66A60AF0A24C003D9C11 /* VLPDFWindow.mm in Sources */,
95FC66CE0AF0A591003D9C11 /* VLPDFView.mm in Sources */,
95A1C3860AF2ACE20076597D /* VLSheetWindow.mm in Sources */,
+ 95F820AB0AF884A30010963D /* VLMMADocument.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};