VLSongVisitor; generate Lilypond through VLLilypondWriter

This commit is contained in:
Matthias Neeracher 2007-05-24 08:52:14 +00:00
parent 559781ce3d
commit 6779d727e7
6 changed files with 427 additions and 320 deletions

View File

@ -9,6 +9,7 @@
// //
#import "VLLilypondDocument.h" #import "VLLilypondDocument.h"
#import "VLLilypondWriter.h"
@interface NSMutableString (VLLilypond) @interface NSMutableString (VLLilypond)
@ -115,6 +116,8 @@ static const char * sKeyNames[] = {
- (NSData *)lilypondDataWithError:(NSError **)outError - (NSData *)lilypondDataWithError:(NSError **)outError
{ {
const VLProperties & prop = song->fProperties.front(); const VLProperties & prop = song->fProperties.front();
VLLilypondWriter writer;
writer.Visit(*song);
NSBundle * bndl = [NSBundle mainBundle]; NSBundle * bndl = [NSBundle mainBundle];
NSString * tmpl = NSString * tmpl =
[bndl pathForResource:lilypondTemplate [bndl pathForResource:lilypondTemplate
@ -130,12 +133,10 @@ static const char * sKeyNames[] = {
[bndl objectForInfoDictionaryKey:@"CFBundleVersion"]]; [bndl objectForInfoDictionaryKey:@"CFBundleVersion"]];
[ly substituteMacro:@"PAPERSIZE" withValue:@"letter"]; [ly substituteMacro:@"PAPERSIZE" withValue:@"letter"];
[ly substituteMacro:@"FORMATTING" withValue:@"ragged-last-bottom = ##f"]; [ly substituteMacro:@"FORMATTING" withValue:@"ragged-last-bottom = ##f"];
std::string lys;
song->LilypondChords(lys);
[ly substituteMacro:@"VLVERSION" withValue: [ly substituteMacro:@"VLVERSION" withValue:
[bndl objectForInfoDictionaryKey:@"CFBundleVersion"]]; [bndl objectForInfoDictionaryKey:@"CFBundleVersion"]];
[ly substituteMacro:@"CHORDS" withValue: [ly substituteMacro:@"CHORDS" withValue:
[NSString stringWithUTF8String:lys.c_str()]]; [NSString stringWithUTF8String:writer.Chords().c_str()]];
[ly substituteMacro:@"TIME" withValue: [ly substituteMacro:@"TIME" withValue:
[NSString stringWithFormat:@"%d/%d", [NSString stringWithFormat:@"%d/%d",
prop.fTime.fNum, prop.fTime.fDenom]]; prop.fTime.fNum, prop.fTime.fDenom]];
@ -144,14 +145,12 @@ static const char * sKeyNames[] = {
sKeyNames[prop.fKey+kMajorOffset]] sKeyNames[prop.fKey+kMajorOffset]]
: [NSString stringWithFormat:@"%s \\minor", : [NSString stringWithFormat:@"%s \\minor",
sKeyNames[prop.fKey+kMinorOffset]]]; sKeyNames[prop.fKey+kMinorOffset]]];
song->LilypondNotes(lys);
[ly substituteMacro:@"NOTES" withValue: [ly substituteMacro:@"NOTES" withValue:
[NSString stringWithUTF8String:lys.c_str()]]; [NSString stringWithUTF8String:writer.Melody().c_str()]];
if (size_t stanzas = song->CountStanzas()) if (size_t stanzas = song->CountStanzas())
for (size_t s=0; s++<stanzas; ) { for (size_t s=0; s<stanzas; ++s) {
song->LilypondStanza(lys, s);
[ly substituteMacro:@"LYRICS" withValue: [ly substituteMacro:@"LYRICS" withValue:
[NSString stringWithUTF8String:lys.c_str()] [NSString stringWithUTF8String:writer.Lyrics(s).c_str()]
repeat: s<stanzas]; repeat: s<stanzas];
} }
[ly purgeMacros]; [ly purgeMacros];

View File

@ -0,0 +1,297 @@
//
// File: VLLilypondWriter.h
//
// Author(s):
//
// (MN) Matthias Neeracher
//
// Copyright © 2007 Matthias Neeracher
//
#include "VLLilypondWriter.h"
void VLLilypondWriter::Visit(VLSong & song)
{
fSong = &song;
fChords.clear();
fMelody.clear();
fLyrics.clear();
fLyrics.resize(song.CountStanzas());
fL = fLyrics;
fInPickup = true;
fIndent.clear();
fSeenEnding = 0;
fNumEndings = 0;
VisitMeasures(song, false);
//
// Terminate melody
//
if (fIndent.size())
fMelody += fSeenEnding ? "}}\n" : "}\n";
}
void VLLilypondWriter::VisitMeasure(size_t m, VLProperties & p, VLMeasure & meas)
{
char measNo[8];
if (!(m % 4))
sprintf(measNo, " %% %d", m+1);
else
measNo[0] = 0;
fUseSharps = p.fKey >= 0;
fInPickup = fInPickup && !m && meas.NoChords();
//
// Generate chords
//
fAccum.clear();
VisitChords(meas);
fAccum += measNo;
fChords+= fAccum + '\n';
//
// Generate structure elements
//
int times;
size_t volta;
bool repeat;
fAccum.clear();
if (fSong->DoesEndRepeat(m)) {
fAccum += "}\n";
fIndent = "";
}
if (fSong->DoesBeginEnding(m, &repeat, &volta)) {
fAccum += fSeenEnding ? "}{\n" : "} \\alternative {{\n";
fAccum += " \\set Score.repeatCommands = #'((volta \"";
const char * comma = "";
for (int r=0; r<8; ++r)
if (volta & (1<<r)) {
char volta[8];
sprintf(volta, "%s%d.", comma, r+1);
comma = ", ";
fAccum += volta;
}
fAccum += "\")" + std::string(repeat ? "" : " end-repeat") + ")\n";
fSeenEnding |= volta;
++fNumEndings;
} else if (fSong->DoesEndEnding(m)) {
fAccum += "}}\n";
fIndent = "";
}
if (fSong->DoesBeginRepeat(m, &times)) {
char volta[8];
sprintf(volta, "%d", times);
fAccum = fAccum + "\\repeat volta "+volta+" {\n";
fIndent = " ";
fSeenEnding = 0;
fNumEndings = 0;
}
fAccum += fIndent;
if (fSong->fCoda == m)
fAccum += "\\break \\mark \\markup { \\musicglyph #\"scripts.coda\" }\n"
+ fIndent;
fMelody += fAccum;
//
// Generate melody & lyrics
//
fAccum.clear();
for (size_t stanza=0; stanza<fL.size(); ++stanza)
fL[stanza].clear();
fPrevNote.fPitch = VLNote::kNoPitch;
VisitNotes(meas, p, true);
//
// Consolidate triplets and dots
//
size_t trip;
while ((trip = fAccum.find("} \\times 2/3 { ")) != std::string::npos)
fAccum.erase(trip, 15);
while ((trip = fAccum.find(" ~ } \\times 2/3 { ")) != std::string::npos)
fAccum.erase(trip+2, 17);
while ((trip = fAccum.find(" ~.")) != std::string::npos)
fAccum.erase(trip, 2);
if (fSong->fGoToCoda == m+1)
fAccum += "\n"
+ fIndent
+ "\\mark \\markup { \\musicglyph #\"scripts.coda\" } |";
else
fAccum += " |";
fMelody += fAccum + measNo + '\n';
//
// Accumulate lyrics
//
const char * nuline = m%4 ? "" : "\n";
for (size_t stanza=0; stanza<fLyrics.size(); ++stanza)
fLyrics[stanza] += fL[stanza] + nuline;
}
static const char kScale[] = "c d ef g a b";
static const char kValue[] = {
1, 2, 4, 8, 16, 32
};
static std::string LilypondPitchName(int8_t pitch, bool useSharps)
{
if (pitch == VLNote::kNoPitch)
return "r";
pitch %= 12;
if (kScale[pitch] != ' ')
return kScale[pitch] + std::string();
else if (useSharps)
return kScale[pitch-1] + std::string("is");
else
return kScale[pitch+1] + std::string("es");
}
void VLLilypondWriter::VisitNote(VLLyricsNote & n)
{
std::string nm = LilypondPitchName(n.fPitch, fUseSharps);
if (n.fPitch != VLNote::kNoPitch) {
int ref = VLNote::kMiddleC-VLNote::kOctave;
for (int ticks = (n.fPitch-ref)/VLNote::kOctave; ticks>0; --ticks)
nm += '\'';
for (int commas = (ref-n.fPitch)/VLNote::kOctave; commas>0; --commas)
nm += ',';
fInPickup = false;
} else if (fInPickup) {
nm = "s";
}
const char * space = fAccum.size() ? " " : "";
const char * tie = n.fTied & VLNote::kTiedWithNext ? " ~" : "";
char duration[32];
if (n.fTied == VLNote::kTiedWithPrev && n.fVisual == fPrevNote.fVisual+1
&& n.fPitch == fPrevNote.fPitch
)
strcpy(duration, ".");
else if (n.fVisual & VLNote::kTriplet)
sprintf(duration, "%s\\times 2/3 { %s%d%s }",
space, nm.c_str(), kValue[n.fVisual], tie);
else
sprintf(duration, "%s%s%d%s", space, nm.c_str(), kValue[n.fVisual], tie);
fAccum += duration;
fPrevNote= n;
if (n.fPitch != VLNote::kNoPitch && !(n.fTied & VLNote::kTiedWithPrev))
for (size_t i=0; i<fL.size(); ++i)
if (n.fLyrics.size() <= i || !n.fLyrics[i]) {
fL[i] += " \\skip1";
} else {
fL[i] += ' ' + n.fLyrics[i].fText;
if (n.fLyrics[i].fKind & VLSyllable::kHasNext)
fL[i] += " --";
}
}
static const char * kLilypondStepNames[] = {
"", "", "sus2", "", "", "sus", "5-", "", "5+", "6", "7", "7+", "",
"9-", "9", "9+", "", "11", "11+", "", "13-", "13"
};
void VLLilypondWriter::VisitChord(VLChord & c)
{
std::string name = LilypondPitchName(c.fPitch, fUseSharps);
char duration[16];
if (c.fDuration.fNum == 1 && !(c.fDuration.fDenom & (c.fDuration.fDenom-1))) // Power of two
sprintf(duration, "%d", c.fDuration.fDenom);
else
sprintf(duration, "1*%d/%d", c.fDuration.fNum, c.fDuration.fDenom);
name += std::string(duration);
std::string ext;
uint32_t steps = c.fSteps;
if (c.fPitch == VLNote::kNoPitch)
goto done;
//
// m / dim
//
if (steps & VLChord::kmMin3rd)
if (steps & (VLChord::kmDim5th|VLChord::kmDim7th)
&& !(steps & (VLChord::km5th|VLChord::kmMin7th|VLChord::kmMaj7th|VLChord::kmMin9th|VLChord::kmMaj9th|VLChord::km11th|VLChord::kmAug11th|VLChord::kmMin13th|VLChord::kmMaj13th))
) {
ext = "dim";
steps|= (steps & VLChord::kmDim7th) << 1;
steps&= ~(VLChord::kmMin3rd|VLChord::kmDim5th|VLChord::kmDim7th);
} else {
ext = "m";
steps&= ~VLChord::kmMin3rd;
}
steps &= ~(VLChord::kmUnison | VLChord::km5th);
//
// Maj
//
if (steps & VLChord::kmMaj7th) {
if (ext.size())
ext += '.';
ext += "maj";
if (steps & VLChord::kmMaj9th) {
ext += "9";
steps &= ~VLChord::kmMaj9th;
} else
ext += "7";
steps&= ~VLChord::kmMaj7th;
}
//
// Sus
//
if (steps & (VLChord::kmMaj2nd|VLChord::km4th)) {
if (ext.size())
ext += '.';
ext += "sus";
if (steps & VLChord::kmMaj2nd)
ext += "2";
else
ext += "4";
steps&= ~(VLChord::kmMaj2nd|VLChord::km4th);
}
//
// 6/9
//
if ((steps & (VLChord::kmDim7th|VLChord::kmMaj9th)) == (VLChord::kmDim7th|VLChord::kmMaj9th)) {
if (ext.size() && !isalpha(ext[ext.size()-1]))
ext += '.';
ext += "6.9";
steps&= ~(VLChord::kmDim7th|VLChord::kmMaj9th);
}
//
// Other extensions. Only the highest unaltered extension is listed.
//
if (uint32_t unaltered = steps & (VLChord::kmMin7th|VLChord::kmMaj9th|VLChord::km11th|VLChord::kmMaj13th)) {
steps &= ~unaltered;
for (int step = VLChord::kMaj13th; step > VLChord::kDim7th; --step)
if (unaltered & (1 << step)) {
std::string sn = kLilypondStepNames[step];
if (ext.size() && !isalpha(ext[ext.size()-1]) && sn.size())
ext += '.';
ext += sn;
break;
}
}
for (int step = VLChord::kMin2nd; steps; ++step)
if (steps & (1 << step)) {
std::string sn = kLilypondStepNames[step];
if (ext.size() && !isalpha(ext[ext.size()-1]) && sn.size())
ext += '.';
ext += sn;
steps &= ~(1 << step);
}
if (ext.size())
name += ':' + ext;
//
// Root
//
if (c.fRootPitch != VLNote::kNoPitch)
name += "/+" + LilypondPitchName(c.fRootPitch, fUseSharps);
done:
if (fAccum.size())
fAccum += ' ';
fAccum += name;
}

View File

@ -0,0 +1,43 @@
//
// File: VLLilypondWriter.h
//
// Author(s):
//
// (MN) Matthias Neeracher
//
// Copyright © 2007 Matthias Neeracher
//
#include "VLModel.h"
class VLLilypondWriter: public VLSongVisitor {
public:
VLLilypondWriter() {}
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 & Chords() const { return fChords; }
const std::string & Melody() const { return fMelody; }
const std::string & Lyrics(size_t stanza) const { return fLyrics[stanza]; }
private:
std::string fChords;
std::string fMelody;
std::vector<std::string> fLyrics;
VLSong * fSong;
bool fUseSharps;
bool fInPickup;
size_t fSeenEnding;
int fNumEndings;
VLNote fPrevNote;
std::string fAccum;
std::string fIndent;
std::vector<std::string> fL;
};
// Local Variables:
// mode:C++
// End:

View File

@ -92,19 +92,6 @@ static std::string PitchName(int8_t pitch, bool useSharps)
+ std::string(kVLFlatStr); + std::string(kVLFlatStr);
} }
static std::string LilypondPitchName(int8_t pitch, bool useSharps)
{
if (pitch == VLNote::kNoPitch)
return "r";
pitch %= 12;
if (kScale[pitch] != ' ')
return kScale[pitch] + std::string();
else if (useSharps)
return kScale[pitch-1] + std::string("is");
else
return kScale[pitch+1] + std::string("es");
}
static std::string MMAPitchName(int8_t pitch, bool useSharps) static std::string MMAPitchName(int8_t pitch, bool useSharps)
{ {
if (pitch == VLNote::kNoPitch) if (pitch == VLNote::kNoPitch)
@ -164,50 +151,6 @@ void VLNote::Name(std::string & name, bool useSharps) const
name = PitchName(fPitch, useSharps); name = PitchName(fPitch, useSharps);
} }
void VLNote::LilypondName(std::string & name, VLFraction at, VLFraction prevDur, VLFraction nextDur, bool & triplet, bool & pickup, const VLProperties & prop) const
{
std::string n = LilypondPitchName(fPitch, prop.fKey >= 0);
if (fPitch != kNoPitch) {
for (int ticks = (fPitch-kMiddleC+kOctave)/kOctave; ticks>0; --ticks)
n += '\'';
for (int commas = (kMiddleC-kOctave-fPitch)/kOctave; commas>0; --commas)
n += ',';
pickup = false;
} else if (pickup) {
n = "s";
}
std::vector<std::string> durations;
VLFraction prevPart(0);
for (VLFraction dur = fDuration; dur.fNum; ) {
char duration[32];
VLFraction part, visual;
bool grouped = dur==nextDur ||
(prevPart!=0 ? dur==prevPart : dur==prevDur);
prop.PartialNote(at, dur, grouped, &part);
prop.VisualNote(at, part, triplet, &visual, &triplet);
if (!triplet && fPitch != kNoPitch && part == dur && 2*visual == prevPart) {
durations.pop_back();
sprintf(duration, "%s%d.", n.c_str(), visual.fDenom/2);
} else if (triplet) {
sprintf(duration, "\\times 2/3 { %s%d }", n.c_str(), visual.fDenom);
} else {
sprintf(duration, "%s%d", n.c_str(), visual.fDenom);
}
durations.push_back(duration);
prevPart = part;
at += part;
dur -= part;
}
for (size_t i=0; i<durations.size(); ++i) {
if (i && fPitch != kNoPitch)
name += " ~ ";
name += durations[i];
}
if (fTied & kTiedWithNext)
name += " ~";
}
static struct { static struct {
VLFract fVal; VLFract fVal;
const char * fName; const char * fName;
@ -530,109 +473,6 @@ void VLChord::Name(std::string & base, std::string & ext, std::string & root, bo
root = PitchName(fRootPitch, useSharps); root = PitchName(fRootPitch, useSharps);
} }
static const char * kLilypondStepNames[] = {
"", "", "sus2", "", "", "sus", "5-", "", "5+", "6", "7", "7+", "",
"9-", "9", "9+", "", "11", "11+", "", "13-", "13"
};
void VLChord::LilypondName(std::string & name, bool useSharps) const
{
name = LilypondPitchName(fPitch, useSharps);
char duration[16];
if (fDuration.fNum == 1 && !(fDuration.fDenom & (fDuration.fDenom-1))) // Power of two
sprintf(duration, "%d", fDuration.fDenom);
else
sprintf(duration, "1*%d/%d", fDuration.fNum, fDuration.fDenom);
name += std::string(duration);
if (fPitch == kNoPitch)
return;
std::string ext;
uint32_t steps = fSteps;
//
// m / dim
//
if (steps & kmMin3rd)
if (steps & (kmDim5th|kmDim7th)
&& !(steps & (km5th|kmMin7th|kmMaj7th|kmMin9th|kmMaj9th|km11th|kmAug11th|kmMin13th|kmMaj13th))
) {
ext = "dim";
steps|= (steps & kmDim7th) << 1;
steps&= ~(kmMin3rd|kmDim5th|kmDim7th);
} else {
ext = "m";
steps&= ~kmMin3rd;
}
steps &= ~(kmUnison | km5th);
//
// Maj
//
if (steps & kmMaj7th) {
if (ext.size())
ext += '.';
ext += "maj";
if (steps & kmMaj9th) {
ext += "9";
steps &= ~kmMaj9th;
} else
ext += "7";
steps&= ~kmMaj7th;
}
//
// Sus
//
if (steps & (kmMaj2nd|km4th)) {
if (ext.size())
ext += '.';
ext += "sus";
if (steps & kmMaj2nd)
ext += "2";
else
ext += "4";
steps&= ~(kmMaj2nd|km4th);
}
//
// 6/9
//
if ((steps & (kmDim7th|kmMaj9th)) == (kmDim7th|kmMaj9th)) {
if (ext.size() && !isalpha(ext[ext.size()-1]))
ext += '.';
ext += "6.9";
steps&= ~(kmDim7th|kmMaj9th);
}
//
// Other extensions. Only the highest unaltered extension is listed.
//
if (uint32_t unaltered = steps & (kmMin7th | kmMaj9th | km11th | kmMaj13th)) {
steps &= ~unaltered;
for (int step = kMaj13th; step > kDim7th; --step)
if (unaltered & (1 << step)) {
std::string sn = kLilypondStepNames[step];
if (ext.size() && !isalpha(ext[ext.size()-1]) && sn.size())
ext += '.';
ext += sn;
break;
}
}
for (int step = kMin2nd; steps; ++step)
if (steps & (1 << step)) {
std::string sn = kLilypondStepNames[step];
if (ext.size() && !isalpha(ext[ext.size()-1]) && sn.size())
ext += '.';
ext += sn;
steps &= ~(1 << step);
}
if (ext.size())
name += ':' + ext;
//
// Root
//
if (fRootPitch != kNoPitch)
name += "/+" + LilypondPitchName(fRootPitch, useSharps);
}
// //
// MMA supports a large but finite list of chords // MMA supports a large but finite list of chords
// //
@ -1678,152 +1518,6 @@ size_t VLSong::CountBotLedgers() const
return 0; return 0;
} }
void VLSong::LilypondNotes(std::string & notes) const
{
notes = "";
std::string indent = "";
size_t seenEnding = 0;
int numEndings = 0;
size_t endMeasure = fMeasures.size()-EmptyEnding();
bool pickup = fMeasures[0].NoChords();
for (size_t measure=0; measure<endMeasure; ++measure) {
VLNoteList::const_iterator i = fMeasures[measure].fMelody.begin();
VLNoteList::const_iterator e = fMeasures[measure].fMelody.end();
VLFraction at(0);
int times;
size_t volta;
bool repeat;
if (DoesBeginRepeat(measure, &times)) {
char volta[8];
sprintf(volta, "%d", times);
notes = notes + "\\repeat volta "+volta+" {\n";
indent = " ";
seenEnding = 0;
numEndings = 0;
}
if (DoesEndRepeat(measure)) {
notes += "}\n";
indent = "";
}
if (DoesBeginEnding(measure, &repeat, &volta)) {
notes += seenEnding ? "}{\n" : "} \\alternative {{\n";
notes += " \\set Score.repeatCommands = #'((volta \"";
const char * comma = "";
for (int r=0; r<8; ++r)
if (volta & (1<<r)) {
char volta[8];
sprintf(volta, "%s%d.", comma, r+1);
comma = ", ";
notes += volta;
}
notes = notes + "\")" + (repeat ? "" : " end-repeat") + ")\n";
seenEnding |= volta;
++numEndings;
} else if (DoesEndEnding(measure)) {
notes += "}}\n";
indent = "";
}
notes += indent;
if (fCoda == measure)
notes += "\\break \\mark \\markup { \\musicglyph #\"scripts.coda\" }\n"
+ indent;
VLFraction prevDur(0);
bool triplet = false;
for (; i!=e; ++i) {
std::string note;
VLNoteList::const_iterator n = i;
VLFraction nextDur(0);
if (++n != e)
nextDur = n->fDuration;
i->LilypondName(note, at, prevDur, nextDur, triplet, pickup, fProperties[fMeasures[measure].fPropIdx]);
prevDur = i->fDuration;
at += i->fDuration;
notes += note+" ";
}
//
// Consolidate triplets
//
size_t trip;
while ((trip = notes.find("} \\times 2/3 { ")) != std::string::npos)
notes.erase(trip, 15);
while ((trip = notes.find("} ~ \\times 2/3 { ")) != std::string::npos)
notes.replace(trip, 17, "~ ", 2);
//
// Swap ties into correct order
//
while ((trip = notes.find("} ~")) != std::string::npos)
notes.replace(trip, 3, "~ } ", 4);
if (fGoToCoda == measure+1)
notes += "\n"
+ indent
+ "\\mark \\markup { \\musicglyph #\"scripts.coda\" } |";
else
notes += '|';
if (!(measure % 4)) {
char measNo[8];
sprintf(measNo, " %% %d", measure+1);
notes += measNo;
}
if (measure < fMeasures.size()-1)
notes += '\n';
}
}
void VLSong::LilypondChords(std::string & chords) const
{
chords = "";
for (size_t measure=0; measure<fMeasures.size(); ++measure) {
bool useSharps = fProperties[fMeasures[measure].fPropIdx].fKey>=0;
VLChordList::const_iterator i = fMeasures[measure].fChords.begin();
VLChordList::const_iterator e = fMeasures[measure].fChords.end();
for (; i!=e; ++i) {
std::string chord;
i->LilypondName(chord, useSharps);
chords += chord+" ";
}
if (!(measure % 4)) {
char measNo[8];
sprintf(measNo, " %% %d", measure+1);
chords += measNo;
}
if (measure < fMeasures.size()-1)
chords += '\n';
}
}
void VLSong::LilypondStanza(std::string & lyrics, size_t stanza) const
{
lyrics = "";
std::string sep;
for (size_t measure=0; measure<fMeasures.size(); ++measure) {
VLNoteList::const_iterator i = fMeasures[measure].fMelody.begin();
VLNoteList::const_iterator e = fMeasures[measure].fMelody.end();
VLFraction at(0);
for (; i!=e; ++i) {
if (i->fPitch == VLNote::kNoPitch
|| (i->fTied & VLNote::kTiedWithPrev)
) {
continue; // Rest or continuation note, skip
} else if (i->fLyrics.size() < stanza || !i->fLyrics[stanza-1]) {
lyrics += sep + "\\skip1";
} else {
lyrics += sep + i->fLyrics[stanza-1].fText;
if (i->fLyrics[stanza-1].fKind & VLSyllable::kHasNext)
lyrics += " --";
}
sep = " ";
}
if ((measure % 4) == 3) {
sep = "\n";
}
}
}
bool VLSong::FindWord(size_t stanza, size_t & measure, VLFraction & at) bool VLSong::FindWord(size_t stanza, size_t & measure, VLFraction & at)
{ {
at += VLFraction(1,64); at += VLFraction(1,64);
@ -2534,3 +2228,58 @@ VLFract VLSong::TiedDuration(size_t measure)
} }
return total; return total;
} }
VLSongVisitor::~VLSongVisitor()
{
}
void VLSongVisitor::VisitMeasures(VLSong & song, bool performanceOrder)
{
if (performanceOrder) {
VLSong::iterator e = song.end();
for (VLSong::iterator m=song.begin(); m!=e; ++m) {
VLMeasure & meas = song.fMeasures[*m];
VLProperties& prop = song.fProperties[meas.fPropIdx];
VisitMeasure(*m, prop, meas);
}
} else {
size_t e = song.CountMeasures() - song.EmptyEnding();
for (size_t m=0; m!=e; ++m) {
VLMeasure & meas = song.fMeasures[m];
VLProperties& prop = song.fProperties[meas.fPropIdx];
VisitMeasure(m, prop, meas);
}
}
}
void VLSongVisitor::VisitNotes(VLMeasure & measure, const VLProperties & prop,
bool decomposed)
{
VLNoteList decomp;
VLNoteList::iterator n;
VLNoteList::iterator e;
if (decomposed) {
measure.DecomposeNotes(prop, decomp);
n = decomp.begin();
e = decomp.end();
} else {
n = measure.fMelody.begin();
e = measure.fMelody.end();
}
for (; n!=e; ++n)
VisitNote(*n);
}
void VLSongVisitor::VisitChords(VLMeasure & measure)
{
VLChordList::iterator c = measure.fChords.begin();
VLChordList::iterator e = measure.fChords.end();
for (; c!=e; ++c)
VisitChord(*c);
}

View File

@ -150,7 +150,6 @@ struct VLNote {
VLNote(VLFraction dur=0, int pitch=kNoPitch); VLNote(VLFraction dur=0, int pitch=kNoPitch);
VLNote(std::string name); VLNote(std::string name);
void Name(std::string & name, bool useSharps = false) const; void Name(std::string & name, bool useSharps = false) const;
void LilypondName(std::string & name, VLFraction at, VLFraction prevDur, VLFraction nextDur, bool & triplet, bool & pickup, const VLProperties & prop) const;
void MMAName(std::string & name, VLFraction at, VLFraction dur, VLFraction prevDur, VLFraction nextDur, const VLProperties & prop) const; void MMAName(std::string & name, VLFraction at, VLFraction dur, VLFraction prevDur, VLFraction nextDur, const VLProperties & prop) const;
void MakeRepresentable(); void MakeRepresentable();
void AlignToGrid(VLFraction at, VLFraction grid); void AlignToGrid(VLFraction at, VLFraction grid);
@ -210,7 +209,6 @@ struct VLChord : VLNote {
VLChord(VLFraction dur=0, int pitch=kNoPitch, int rootPitch=kNoPitch); VLChord(VLFraction dur=0, int pitch=kNoPitch, int rootPitch=kNoPitch);
VLChord(std::string name); VLChord(std::string name);
void Name(std::string & base, std::string & ext, std::string & root, bool useSharps = false) const; void Name(std::string & base, std::string & ext, std::string & root, bool useSharps = false) const;
void LilypondName(std::string & name, bool useSharps = false) const;
bool MMAName(std::string & name, bool useSharps, bool initial) const; bool MMAName(std::string & name, bool useSharps, bool initial) const;
}; };
@ -274,7 +272,8 @@ struct VLRepeat {
std::vector<Ending> fEndings; std::vector<Ending> fEndings;
}; };
struct VLSong { class VLSong {
public:
VLSong(bool initialize = true); VLSong(bool initialize = true);
void swap(VLSong & other); void swap(VLSong & other);
void clear(); void clear();
@ -368,14 +367,28 @@ struct VLSong {
size_t CountStanzas() const; size_t CountStanzas() const;
size_t CountTopLedgers() const; size_t CountTopLedgers() const;
size_t CountBotLedgers() const; size_t CountBotLedgers() const;
void LilypondNotes(std::string & notes) const;
void LilypondChords(std::string & chords) const;
void LilypondStanza(std::string & lyrics, size_t stanza) const;
VLFract TiedDuration(size_t measure); VLFract TiedDuration(size_t measure);
private: private:
void AddMeasure(); void AddMeasure();
}; };
class VLSongVisitor {
public:
virtual ~VLSongVisitor();
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) {}
protected:
VLSongVisitor() {}
void VisitMeasures(VLSong & song, bool performanceOrder);
void VisitNotes(VLMeasure & measure, const VLProperties & prop,
bool decomposed);
void VisitChords(VLMeasure & measure);
};
// Local Variables: // Local Variables:
// mode:C++ // mode:C++
// End: // End:

View File

@ -41,6 +41,7 @@
954DD4E60B44E67F0056C504 /* VLSheetViewSelection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 954DD4E50B44E67F0056C504 /* VLSheetViewSelection.mm */; }; 954DD4E60B44E67F0056C504 /* VLSheetViewSelection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 954DD4E50B44E67F0056C504 /* VLSheetViewSelection.mm */; };
954F20310BFABD96006CAE0E /* VLMirrorWindow.nib in Resources */ = {isa = PBXBuildFile; fileRef = 954F20300BFABD96006CAE0E /* VLMirrorWindow.nib */; }; 954F20310BFABD96006CAE0E /* VLMirrorWindow.nib in Resources */ = {isa = PBXBuildFile; fileRef = 954F20300BFABD96006CAE0E /* VLMirrorWindow.nib */; };
955CBA4F0B2366DD001CF4A1 /* VLKeyValueUndo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 955CBA4D0B2366DD001CF4A1 /* VLKeyValueUndo.mm */; }; 955CBA4F0B2366DD001CF4A1 /* VLKeyValueUndo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 955CBA4D0B2366DD001CF4A1 /* VLKeyValueUndo.mm */; };
955DA2960C0551EC008F73B8 /* VLLilypondWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955DA2940C0551EC008F73B8 /* VLLilypondWriter.cpp */; };
955E58E5095658AB0045FDA5 /* VLModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955E58E4095658AB0045FDA5 /* VLModel.cpp */; }; 955E58E5095658AB0045FDA5 /* VLModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955E58E4095658AB0045FDA5 /* VLModel.cpp */; };
955E59610957C1400045FDA5 /* TVLChord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955E59600957C1400045FDA5 /* TVLChord.cpp */; }; 955E59610957C1400045FDA5 /* TVLChord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955E59600957C1400045FDA5 /* TVLChord.cpp */; };
955E59640957C15A0045FDA5 /* VLModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955E58E4095658AB0045FDA5 /* VLModel.cpp */; }; 955E59640957C15A0045FDA5 /* VLModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955E58E4095658AB0045FDA5 /* VLModel.cpp */; };
@ -178,6 +179,8 @@
954F20300BFABD96006CAE0E /* VLMirrorWindow.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = VLMirrorWindow.nib; path = English.lproj/VLMirrorWindow.nib; sourceTree = "<group>"; }; 954F20300BFABD96006CAE0E /* VLMirrorWindow.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = VLMirrorWindow.nib; path = English.lproj/VLMirrorWindow.nib; sourceTree = "<group>"; };
955CBA4C0B2366DD001CF4A1 /* VLKeyValueUndo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLKeyValueUndo.h; path = Sources/VLKeyValueUndo.h; sourceTree = "<group>"; }; 955CBA4C0B2366DD001CF4A1 /* VLKeyValueUndo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLKeyValueUndo.h; path = Sources/VLKeyValueUndo.h; sourceTree = "<group>"; };
955CBA4D0B2366DD001CF4A1 /* VLKeyValueUndo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = VLKeyValueUndo.mm; path = Sources/VLKeyValueUndo.mm; sourceTree = "<group>"; }; 955CBA4D0B2366DD001CF4A1 /* VLKeyValueUndo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = VLKeyValueUndo.mm; path = Sources/VLKeyValueUndo.mm; sourceTree = "<group>"; };
955DA2940C0551EC008F73B8 /* VLLilypondWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = VLLilypondWriter.cpp; path = Sources/VLLilypondWriter.cpp; sourceTree = "<group>"; };
955DA2950C0551EC008F73B8 /* VLLilypondWriter.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = VLLilypondWriter.h; path = Sources/VLLilypondWriter.h; sourceTree = "<group>"; };
955E58E3095658AB0045FDA5 /* VLModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLModel.h; path = Sources/VLModel.h; sourceTree = "<group>"; }; 955E58E3095658AB0045FDA5 /* VLModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLModel.h; path = Sources/VLModel.h; sourceTree = "<group>"; };
955E58E4095658AB0045FDA5 /* VLModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VLModel.cpp; path = Sources/VLModel.cpp; sourceTree = "<group>"; }; 955E58E4095658AB0045FDA5 /* VLModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VLModel.cpp; path = Sources/VLModel.cpp; sourceTree = "<group>"; };
955E595C0957C0FC0045FDA5 /* TVLChord */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = TVLChord; sourceTree = BUILT_PRODUCTS_DIR; }; 955E595C0957C0FC0045FDA5 /* TVLChord */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = TVLChord; sourceTree = BUILT_PRODUCTS_DIR; };
@ -339,6 +342,8 @@
2A37F4ABFDCFA73011CA2CEA /* Classes */ = { 2A37F4ABFDCFA73011CA2CEA /* Classes */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
955DA2940C0551EC008F73B8 /* VLLilypondWriter.cpp */,
955DA2950C0551EC008F73B8 /* VLLilypondWriter.h */,
95A55C520BD5E5760068A203 /* VLPDFDocument.h */, 95A55C520BD5E5760068A203 /* VLPDFDocument.h */,
95A55C530BD5E5770068A203 /* VLPDFDocument.mm */, 95A55C530BD5E5770068A203 /* VLPDFDocument.mm */,
95EDA5A80B06DE46004D8D6E /* VLMIDIDocument.h */, 95EDA5A80B06DE46004D8D6E /* VLMIDIDocument.h */,
@ -682,6 +687,7 @@
9599ED9D0B731CC500A6A2F7 /* VLGrooveController.mm in Sources */, 9599ED9D0B731CC500A6A2F7 /* VLGrooveController.mm in Sources */,
95A55C540BD5E5770068A203 /* VLPDFDocument.mm in Sources */, 95A55C540BD5E5770068A203 /* VLPDFDocument.mm in Sources */,
95784D870BFAD795009ABEA4 /* VLMirrorWindow.mm in Sources */, 95784D870BFAD795009ABEA4 /* VLMirrorWindow.mm in Sources */,
955DA2960C0551EC008F73B8 /* VLLilypondWriter.cpp in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };