Generate MMA through VLMMAWriter

This commit is contained in:
Matthias Neeracher 2007-05-27 04:35:45 +00:00
parent 6779d727e7
commit 3a5c3df5ec
6 changed files with 310 additions and 21 deletions

View File

@ -9,6 +9,7 @@
//
#import "VLMMADocument.h"
#import "VLMMAWriter.h"
@implementation VLDocument (MMA)
@ -16,6 +17,8 @@
{
char buf[32];
NSBundle * bndl = [NSBundle mainBundle];
VLMMAWriter writer;
writer.Visit(*song);
const VLProperties & prop = song->fProperties.front();
std::string mmaFile = std::string("// Generated by VocalEasel ")
@ -30,20 +33,7 @@
mmaFile += buf;
sprintf(buf, "KeySig %d%c\n", labs(prop.fKey), prop.fKey>=0 ? '#' : '&');
mmaFile += buf;
mmaFile += '\n';
std::string mmas;
size_t meas = 0;
VLSong::iterator end = song->end();
for (VLSong::iterator i=song->begin(); i!=end; ++i) {
size_t m = *i;
sprintf(buf, "%-5d", ++meas);
mmaFile += buf;
song->fMeasures[m].MMAChords(mmas, prop, i==song->begin());
mmaFile += mmas;
song->fMeasures[m].MMANotes(mmas, prop, song->TiedDuration(m+1));
mmaFile += "\t{ " + mmas + " }\n";
}
mmaFile += '\n'+writer.Measures();
return [[NSString stringWithUTF8String:mmaFile.c_str()]
dataUsingEncoding:NSUTF8StringEncoding];

256
Sources/VLMMAWriter.cpp Normal file
View File

@ -0,0 +1,256 @@
//
// File: VLMMAWriter.h
//
// Author(s):
//
// (MN) Matthias Neeracher
//
// Copyright © 2007 Matthias Neeracher
//
#include "VLMMAWriter.h"
void VLMMAWriter::Visit(VLSong & song)
{
fSong = &song;
fMeas = 0;
fInitial= true;
fMeasures.clear();
VisitMeasures(song, true);
}
void VLMMAWriter::VisitMeasure(size_t m, VLProperties & p, VLMeasure & meas)
{
char buf[8];
sprintf(buf, "%-3d", ++fMeas);
fUseSharps = p.fKey >= 0;
//
// Generate chords
//
fAccum.clear();
VisitChords(meas);
std::string chords = buf+fAccum;
//
// Generate melody and account for ties
//
bool setLastDur = false;
fAccum.clear();
fTied = meas.fMelody.front().fTied & VLNote::kTiedWithPrev;
if (fTied && meas.fMelody.size() == 1) {
VisitNotes(meas, p, true);
if (meas.fMelody.back().fTied & VLNote::kTiedWithNext) {
fAccum = "~<>~;";
} else {
fAccum = "~<>;";
}
} else {
VisitNotes(meas, p, true);
if (meas.fMelody.back().fTied & VLNote::kTiedWithNext) {
fAccum.replace(fAccum.find_last_of(';'), 0, "~", 1);
setLastDur = true;
}
}
std::string melody = fAccum;
fMeasures += chords+"\t{ " + melody + " }\n";
if (setLastDur)
fLastDur = fMeasures.find_last_of("123468");
}
static const char kScale[] = "c d ef g a b";
static std::string MMAPitchName(int8_t pitch, bool useSharps, bool showNatural=false)
{
if (pitch == VLNote::kNoPitch)
return "r";
char name[3];
name[2] = 0;
name[1] = showNatural?'n':0;
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] = '&';
}
if (!showNatural)
name[0] = toupper(name[0]);
return name;
}
static std::string MMAOctave(int8_t pitch)
{
std::string name;
if (pitch != VLNote::kNoPitch) {
for (int raise = (pitch-VLNote::kMiddleC)/VLNote::kOctave; raise>0; --raise)
name += '+';
for (int lower = (VLNote::kMiddleC+VLNote::kOctave-1-pitch)/VLNote::kOctave; lower>0; --lower)
name += '-';
}
return name;
}
void VLMMAWriter::VisitNote(VLLyricsNote & n)
{
char buf[4];
std::string dur;
if (n.fDuration.fNum == 1)
if (!(n.fDuration.fDenom & (n.fDuration.fDenom-1))) {
sprintf(buf, "%d", n.fDuration.fDenom);
dur = buf;
} else if (n.fDuration.fDenom == 3) {
dur = "23"; // Half note triplet
} else if (n.fDuration.fDenom == 6) {
//
// Quarter note triplet / swing 8th
//
dur = n.fVisual==VLNote::kEighth ? "81" : "43";
} else if (n.fDuration.fDenom == 12) {
//
// Eighth note triplet / swing 8th / swing 16th
//
dur = n.fVisual==VLNote::kEighth ? "82" : "3";
} else if (n.fDuration.fDenom == 24) {
dur = "6"; // 16th note triplet
}
if (n.fTied & VLNote::kTiedWithPrev) {
if (fTied) {
fMeasures.replace(fLastDur+1, 0, '+'+dur);
if (!(n.fTied & VLNote::kTiedWithNext))
fAccum += "~";
} else {
size_t d = fAccum.find_last_of("123468");
fAccum.replace(d+1, 0, '+'+dur);
}
return;
}
fTied = false;
if (fAccum.size() > 1)
fAccum += ' ';
fAccum += dur+MMAPitchName(n.fPitch, fUseSharps, true)+MMAOctave(n.fPitch)+';';
}
#define _ VLChord::
//
// 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 VLMMAWriter::VisitChord(VLChord & c)
{
int quarters = static_cast<int>(c.fDuration*4.0f+0.5f);
if (!quarters--)
return;
std::string name;
if (c.fPitch == VLNote::kNoPitch) {
name = fInitial ? "z" : "/";
} else {
fInitial = false;
std::string base, ext;
base = MMAPitchName(c.fPitch, fUseSharps);
size_t best = 0;
size_t bestBits = 32;
size_t bestScore= 0;
for (size_t i=0; kMMAModifiers[i].fName; ++i) {
uint32_t steps = (VLChord::kmUnison | VLChord::kmMaj3rd | VLChord::km5th)
| kMMAModifiers[i].fAddSteps
&~kMMAModifiers[i].fDelSteps;
if (c.fSteps == steps) {
//
// Exact match
//
best = i;
break;
}
steps ^= c.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 (c.fRootPitch != VLNote::kNoPitch)
name += '/' + MMAPitchName(c.fRootPitch, fUseSharps);
std::toupper(base[0]);
}
while (quarters--)
name += " /";
fAccum += ' '+name;
}

37
Sources/VLMMAWriter.h Normal file
View File

@ -0,0 +1,37 @@
//
// File: VLMMAWriter.h
//
// Author(s):
//
// (MN) Matthias Neeracher
//
// Copyright © 2007 Matthias Neeracher
//
#include "VLModel.h"
class VLMMAWriter: public VLSongVisitor {
public:
VLMMAWriter() {}
virtual void Visit(VLSong & song);
virtual void VisitMeasure(size_t m, VLProperties & p, VLMeasure & meas);
virtual void VisitNote(VLLyricsNote & n);
virtual void VisitChord(VLChord & c);
const std::string & Measures() const { return fMeasures; }
private:
std::string fMeasures;
VLSong * fSong;
bool fUseSharps;
bool fTied;
bool fInitial;
int fMeas;
size_t fLastDur;
std::string fAccum;
};
// Local Variables:
// mode:C++
// End:

View File

@ -237,7 +237,7 @@ void VLNote::MakeRepresentable()
if (fDuration >= part) {
fDuration = part;
return;
} else if (!nonTriplet && fDuration >= triplet) {
} else if (fVisual > kWhole && !nonTriplet && fDuration >= triplet) {
fDuration = triplet;
fVisual |= kTriplet;
return;
@ -269,12 +269,6 @@ VLLyricsNote::VLLyricsNote(VLFraction dur, int pitch)
{
}
struct VLChordModifier {
const char * fName;
uint32_t fAddSteps;
uint32_t fDelSteps;
};
#define _ VLChord::
static const VLChordModifier kModifiers[] = {

View File

@ -212,6 +212,12 @@ struct VLChord : VLNote {
bool MMAName(std::string & name, bool useSharps, bool initial) const;
};
struct VLChordModifier {
const char * fName;
uint32_t fAddSteps;
uint32_t fDelSteps;
};
struct VLProperties {
VLFraction fTime; // Time (non-normalized)
int8_t fKey; // Circle of fifths from C, >0 sharps, <0 flats

View File

@ -35,6 +35,7 @@
952DCD78096BBB11001C2316 /* VLSheetViewChords.mm in Sources */ = {isa = PBXBuildFile; fileRef = 952DCD77096BBB11001C2316 /* VLSheetViewChords.mm */; };
9530A7020BD9E16700635FEC /* display.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 9530A7010BD9E16700635FEC /* display.tiff */; };
953722670AE9F0E100B6E483 /* VLLilypondDocument.mm in Sources */ = {isa = PBXBuildFile; fileRef = 953722660AE9F0E100B6E483 /* VLLilypondDocument.mm */; };
9545C5C30C092F4600251547 /* VLMMAWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9545C5C10C092F4600251547 /* VLMMAWriter.cpp */; };
95498DBD0AE3812F006B5F81 /* VLSoundSched.mm in Sources */ = {isa = PBXBuildFile; fileRef = 95498DBC0AE3812F006B5F81 /* VLSoundSched.mm */; };
954BBD860AEDDE5300BBFD5F /* VLAppController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 954BBD850AEDDE5300BBFD5F /* VLAppController.mm */; };
954BBD9A0AEDE81500BBFD5F /* VLPitchTransformer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 954BBD990AEDE81500BBFD5F /* VLPitchTransformer.mm */; };
@ -166,6 +167,8 @@
9530A7010BD9E16700635FEC /* display.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = display.tiff; path = Resources/display.tiff; sourceTree = "<group>"; };
953722650AE9F0E100B6E483 /* VLLilypondDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLLilypondDocument.h; path = Sources/VLLilypondDocument.h; sourceTree = "<group>"; };
953722660AE9F0E100B6E483 /* VLLilypondDocument.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = VLLilypondDocument.mm; path = Sources/VLLilypondDocument.mm; sourceTree = "<group>"; };
9545C5C10C092F4600251547 /* VLMMAWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = VLMMAWriter.cpp; path = Sources/VLMMAWriter.cpp; sourceTree = "<group>"; };
9545C5C20C092F4600251547 /* VLMMAWriter.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = VLMMAWriter.h; path = Sources/VLMMAWriter.h; sourceTree = "<group>"; };
95498DBB0AE3812F006B5F81 /* VLSoundSched.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = VLSoundSched.h; path = Sources/VLSoundSched.h; sourceTree = "<group>"; };
95498DBC0AE3812F006B5F81 /* VLSoundSched.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; name = VLSoundSched.mm; path = Sources/VLSoundSched.mm; sourceTree = "<group>"; };
954BBD840AEDDE5300BBFD5F /* VLAppController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLAppController.h; path = Sources/VLAppController.h; sourceTree = "<group>"; };
@ -342,6 +345,8 @@
2A37F4ABFDCFA73011CA2CEA /* Classes */ = {
isa = PBXGroup;
children = (
9545C5C10C092F4600251547 /* VLMMAWriter.cpp */,
9545C5C20C092F4600251547 /* VLMMAWriter.h */,
955DA2940C0551EC008F73B8 /* VLLilypondWriter.cpp */,
955DA2950C0551EC008F73B8 /* VLLilypondWriter.h */,
95A55C520BD5E5760068A203 /* VLPDFDocument.h */,
@ -688,6 +693,7 @@
95A55C540BD5E5770068A203 /* VLPDFDocument.mm in Sources */,
95784D870BFAD795009ABEA4 /* VLMirrorWindow.mm in Sources */,
955DA2960C0551EC008F73B8 /* VLLilypondWriter.cpp in Sources */,
9545C5C30C092F4600251547 /* VLMMAWriter.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};