From 9c2dcd582e95aeffbf6a3a06ae2568f0cd834b6f Mon Sep 17 00:00:00 2001 From: Matthias Neeracher Date: Sat, 4 Nov 2006 08:15:34 +0000 Subject: [PATCH] Added MMA export --- English.lproj/InfoPlist.strings | Bin 460 -> 570 bytes Resources/Info.plist | 15 ++++ Sources/VLDocument.h | 2 + Sources/VLDocument.mm | 5 ++ Sources/VLMMADocument.h | 16 ++++ Sources/VLMMADocument.mm | 66 +++++++++++++++ Sources/VLModel.cpp | 128 +++++++++++++++++++++++++++++ Sources/VLModel.h | 5 ++ Vocalese.xcodeproj/project.pbxproj | 6 ++ 9 files changed, 243 insertions(+) create mode 100644 Sources/VLMMADocument.h create mode 100644 Sources/VLMMADocument.mm diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings index a4d425ae4728baea54fc687cf80195ca60e8cac2..9c519cbb85f549f204948f366156eefcc57c0b0a 100644 GIT binary patch delta 95 zcmX@Zyo+VSY{p<;21kYvhDwG4hExUx23sIjV(zX40%8`xj+;07)ls4CpR$q004C`5*Ppg delta 11 ScmdnRa)x=sY{to)Oojj&hy$qr 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; };