mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 11:14:00 +00:00
Generate MMA through VLMMAWriter
This commit is contained in:
parent
6779d727e7
commit
3a5c3df5ec
|
@ -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
256
Sources/VLMMAWriter.cpp
Normal 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
37
Sources/VLMMAWriter.h
Normal 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:
|
|
@ -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[] = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user