mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 03:04:00 +00:00
VLSongVisitor; generate Lilypond through VLLilypondWriter
This commit is contained in:
parent
559781ce3d
commit
6779d727e7
|
@ -9,6 +9,7 @@
|
|||
//
|
||||
|
||||
#import "VLLilypondDocument.h"
|
||||
#import "VLLilypondWriter.h"
|
||||
|
||||
@interface NSMutableString (VLLilypond)
|
||||
|
||||
|
@ -115,6 +116,8 @@ static const char * sKeyNames[] = {
|
|||
- (NSData *)lilypondDataWithError:(NSError **)outError
|
||||
{
|
||||
const VLProperties & prop = song->fProperties.front();
|
||||
VLLilypondWriter writer;
|
||||
writer.Visit(*song);
|
||||
NSBundle * bndl = [NSBundle mainBundle];
|
||||
NSString * tmpl =
|
||||
[bndl pathForResource:lilypondTemplate
|
||||
|
@ -130,12 +133,10 @@ static const char * sKeyNames[] = {
|
|||
[bndl objectForInfoDictionaryKey:@"CFBundleVersion"]];
|
||||
[ly substituteMacro:@"PAPERSIZE" withValue:@"letter"];
|
||||
[ly substituteMacro:@"FORMATTING" withValue:@"ragged-last-bottom = ##f"];
|
||||
std::string lys;
|
||||
song->LilypondChords(lys);
|
||||
[ly substituteMacro:@"VLVERSION" withValue:
|
||||
[bndl objectForInfoDictionaryKey:@"CFBundleVersion"]];
|
||||
[ly substituteMacro:@"CHORDS" withValue:
|
||||
[NSString stringWithUTF8String:lys.c_str()]];
|
||||
[NSString stringWithUTF8String:writer.Chords().c_str()]];
|
||||
[ly substituteMacro:@"TIME" withValue:
|
||||
[NSString stringWithFormat:@"%d/%d",
|
||||
prop.fTime.fNum, prop.fTime.fDenom]];
|
||||
|
@ -144,14 +145,12 @@ static const char * sKeyNames[] = {
|
|||
sKeyNames[prop.fKey+kMajorOffset]]
|
||||
: [NSString stringWithFormat:@"%s \\minor",
|
||||
sKeyNames[prop.fKey+kMinorOffset]]];
|
||||
song->LilypondNotes(lys);
|
||||
[ly substituteMacro:@"NOTES" withValue:
|
||||
[NSString stringWithUTF8String:lys.c_str()]];
|
||||
[NSString stringWithUTF8String:writer.Melody().c_str()]];
|
||||
if (size_t stanzas = song->CountStanzas())
|
||||
for (size_t s=0; s++<stanzas; ) {
|
||||
song->LilypondStanza(lys, s);
|
||||
for (size_t s=0; s<stanzas; ++s) {
|
||||
[ly substituteMacro:@"LYRICS" withValue:
|
||||
[NSString stringWithUTF8String:lys.c_str()]
|
||||
[NSString stringWithUTF8String:writer.Lyrics(s).c_str()]
|
||||
repeat: s<stanzas];
|
||||
}
|
||||
[ly purgeMacros];
|
||||
|
|
297
Sources/VLLilypondWriter.cpp
Normal file
297
Sources/VLLilypondWriter.cpp
Normal 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, ×)) {
|
||||
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;
|
||||
}
|
43
Sources/VLLilypondWriter.h
Normal file
43
Sources/VLLilypondWriter.h
Normal 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:
|
|
@ -92,19 +92,6 @@ static std::string PitchName(int8_t pitch, bool useSharps)
|
|||
+ 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)
|
||||
{
|
||||
if (pitch == VLNote::kNoPitch)
|
||||
|
@ -164,50 +151,6 @@ void VLNote::Name(std::string & name, bool useSharps) const
|
|||
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 {
|
||||
VLFract fVal;
|
||||
const char * fName;
|
||||
|
@ -530,109 +473,6 @@ void VLChord::Name(std::string & base, std::string & ext, std::string & root, bo
|
|||
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
|
||||
//
|
||||
|
@ -1678,152 +1518,6 @@ size_t VLSong::CountBotLedgers() const
|
|||
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, ×)) {
|
||||
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)
|
||||
{
|
||||
at += VLFraction(1,64);
|
||||
|
@ -2534,3 +2228,58 @@ VLFract VLSong::TiedDuration(size_t measure)
|
|||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,6 @@ struct VLNote {
|
|||
VLNote(VLFraction dur=0, int pitch=kNoPitch);
|
||||
VLNote(std::string name);
|
||||
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 MakeRepresentable();
|
||||
void AlignToGrid(VLFraction at, VLFraction grid);
|
||||
|
@ -210,7 +209,6 @@ struct VLChord : VLNote {
|
|||
VLChord(VLFraction dur=0, int pitch=kNoPitch, int rootPitch=kNoPitch);
|
||||
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;
|
||||
bool MMAName(std::string & name, bool useSharps, bool initial) const;
|
||||
};
|
||||
|
||||
|
@ -274,7 +272,8 @@ struct VLRepeat {
|
|||
std::vector<Ending> fEndings;
|
||||
};
|
||||
|
||||
struct VLSong {
|
||||
class VLSong {
|
||||
public:
|
||||
VLSong(bool initialize = true);
|
||||
void swap(VLSong & other);
|
||||
void clear();
|
||||
|
@ -368,14 +367,28 @@ struct VLSong {
|
|||
size_t CountStanzas() const;
|
||||
size_t CountTopLedgers() 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);
|
||||
private:
|
||||
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:
|
||||
// mode:C++
|
||||
// End:
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
954DD4E60B44E67F0056C504 /* VLSheetViewSelection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 954DD4E50B44E67F0056C504 /* VLSheetViewSelection.mm */; };
|
||||
954F20310BFABD96006CAE0E /* VLMirrorWindow.nib in Resources */ = {isa = PBXBuildFile; fileRef = 954F20300BFABD96006CAE0E /* VLMirrorWindow.nib */; };
|
||||
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 */; };
|
||||
955E59610957C1400045FDA5 /* TVLChord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955E59600957C1400045FDA5 /* TVLChord.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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; };
|
||||
|
@ -339,6 +342,8 @@
|
|||
2A37F4ABFDCFA73011CA2CEA /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
955DA2940C0551EC008F73B8 /* VLLilypondWriter.cpp */,
|
||||
955DA2950C0551EC008F73B8 /* VLLilypondWriter.h */,
|
||||
95A55C520BD5E5760068A203 /* VLPDFDocument.h */,
|
||||
95A55C530BD5E5770068A203 /* VLPDFDocument.mm */,
|
||||
95EDA5A80B06DE46004D8D6E /* VLMIDIDocument.h */,
|
||||
|
@ -682,6 +687,7 @@
|
|||
9599ED9D0B731CC500A6A2F7 /* VLGrooveController.mm in Sources */,
|
||||
95A55C540BD5E5770068A203 /* VLPDFDocument.mm in Sources */,
|
||||
95784D870BFAD795009ABEA4 /* VLMirrorWindow.mm in Sources */,
|
||||
955DA2960C0551EC008F73B8 /* VLLilypondWriter.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user