Load / save / translate repeats & endings

This commit is contained in:
Matthias Neeracher 2006-12-31 10:31:29 +00:00
parent 5df833b2bf
commit 638ba81948
5 changed files with 350 additions and 31 deletions

View File

@ -29,8 +29,11 @@
mmaFile += '\n'; mmaFile += '\n';
std::string mmas; std::string mmas;
for (size_t m=0; m<song->CountMeasures(); ++m) { size_t meas = 0;
sprintf(buf, "%-5d", m+1); VLSong::iterator end = song->end();
for (VLSong::iterator i=song->begin(); i!=end; ++i) {
size_t m = *i;
sprintf(buf, "%-5d", ++meas);
mmaFile += buf; mmaFile += buf;
song->fMeasures[m].MMAChords(mmas, prop); song->fMeasures[m].MMAChords(mmas, prop);
mmaFile += mmas; mmaFile += mmas;

View File

@ -1043,12 +1043,50 @@ size_t VLSong::CountStanzas() const
void VLSong::LilypondNotes(std::string & notes) const void VLSong::LilypondNotes(std::string & notes) const
{ {
notes = ""; notes = "";
std::string indent = "";
size_t seenEnding = 0;
int numEndings = 0;
for (size_t measure=0; measure<fMeasures.size(); ++measure) { for (size_t measure=0; measure<fMeasures.size(); ++measure) {
VLNoteList::const_iterator i = fMeasures[measure].fMelody.begin(); VLNoteList::const_iterator i = fMeasures[measure].fMelody.begin();
VLNoteList::const_iterator e = fMeasures[measure].fMelody.end(); VLNoteList::const_iterator e = fMeasures[measure].fMelody.end();
VLFraction at(0); 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;
for (; i!=e; ++i) { for (; i!=e; ++i) {
std::string note; std::string note;
i->LilypondName(note, at, fProperties[fMeasures[measure].fPropIdx]); i->LilypondName(note, at, fProperties[fMeasures[measure].fPropIdx]);
@ -1452,11 +1490,15 @@ bool VLSong::CanBeEnding(size_t beginMeasure, size_t endMeasure,
return false; return false;
} }
bool VLSong::DoesBeginRepeat(size_t measure) const bool VLSong::DoesBeginRepeat(size_t measure, int * times) const
{ {
for (size_t r=0; r<fRepeats.size(); ++r) for (size_t r=0; r<fRepeats.size(); ++r)
if (fRepeats[r].fEndings[0].fBegin == measure) if (fRepeats[r].fEndings[0].fBegin == measure) {
if (times)
*times = fRepeats[r].fTimes;
return true; return true;
}
return false; return false;
} }
@ -1466,64 +1508,141 @@ bool VLSong::DoesEndRepeat(size_t measure, int * times) const
if (fRepeats[r].fEndings[0].fEnd == measure if (fRepeats[r].fEndings[0].fEnd == measure
&& fRepeats[r].fEndings.size() == 1 && fRepeats[r].fEndings.size() == 1
) { ) {
if (times)
*times = fRepeats[r].fTimes; *times = fRepeats[r].fTimes;
return true; return true;
} }
return false; return false;
} }
bool VLSong::DoesBeginEnding(size_t measure, size_t * volta) const bool VLSong::DoesBeginEnding(size_t measure, bool * repeat, size_t * volta) const
{ {
for (size_t r=0; r<fRepeats.size(); ++r) for (size_t r=0; r<fRepeats.size(); ++r)
if (fRepeats[r].fEndings[0].fEnd >= measure if (fRepeats[r].fEndings[0].fEnd >= measure
&& fRepeats[r].fEndings.size() > 1 && fRepeats[r].fEndings.size() > 1
) { ) {
*volta = (1<<fRepeats[r].fTimes)-1; size_t v = (1<<fRepeats[r].fTimes)-1;
for (size_t e=1; e<fRepeats[r].fEndings.size(); ++e) for (size_t e=1; e<fRepeats[r].fEndings.size(); ++e)
if (fRepeats[r].fEndings[e].fBegin == measure) { if (fRepeats[r].fEndings[e].fBegin == measure) {
*volta = fRepeats[r].fEndings[e].fVolta; if (repeat)
if (e == fRepeats[r].fEndings.size()-1
&& fRepeats[r].fEndings[e].fVolta == v
)
*repeat = false; // Not after last alternative
else
*repeat = true;
if (volta)
*volta = fRepeats[r].fEndings[e].fVolta;
return true; return true;
} else } else
*volta&= ~fRepeats[r].fEndings[e].fVolta; v &= ~fRepeats[r].fEndings[e].fVolta;
if (*volta && fRepeats[r].fEndings[0].fEnd == measure) { if (v && fRepeats[r].fEndings[0].fEnd == measure) {
// //
// Implied ending for all not mentioned // Implied ending for all not mentioned
// //
if (repeat)
*repeat = false;
if (volta)
*volta = v;
return true; return true;
} }
} }
return false; return false;
} }
bool VLSong::DoesEndEnding(size_t measure, bool * repeat) const bool VLSong::DoesEndEnding(size_t measure, bool * repeat, size_t * volta) const
{ {
for (size_t r=0; r<fRepeats.size(); ++r) for (size_t r=0; r<fRepeats.size(); ++r)
if (fRepeats[r].fEndings[0].fEnd+1 >= measure if (fRepeats[r].fEndings[0].fEnd+1 >= measure
&& fRepeats[r].fEndings.size() > 1 && fRepeats[r].fEndings.size() > 1
) { ) {
size_t volta = (1<<fRepeats[r].fTimes)-1; size_t v = (1<<fRepeats[r].fTimes)-1;
for (size_t e=1; e<fRepeats[r].fEndings.size(); ++e) for (size_t e=1; e<fRepeats[r].fEndings.size(); ++e)
if (fRepeats[r].fEndings[e].fEnd == measure) { if (fRepeats[r].fEndings[e].fEnd == measure) {
if (e == fRepeats[r].fEndings.size()-1 if (repeat)
&& fRepeats[r].fEndings[e].fVolta == volta if (e == fRepeats[r].fEndings.size()-1
) && fRepeats[r].fEndings[e].fVolta == v
*repeat = false; // Don't repeat after last alternative )
else *repeat = false; // Not after last alternative
*repeat = true; else
*repeat = true;
if (volta)
*volta = fRepeats[r].fEndings[e].fVolta;
return true; return true;
} else } else
volta &= ~fRepeats[r].fEndings[e].fVolta; v &= ~fRepeats[r].fEndings[e].fVolta;
if (volta && fRepeats[r].fEndings[0].fEnd+1 == measure) { if (v && fRepeats[r].fEndings[0].fEnd+1 == measure) {
// //
// Implied ending for all not mentioned // Implied ending for all not mentioned
// //
*repeat = false; if (repeat)
*repeat = false;
if (volta)
*volta = v;
return true; return true;
} }
} }
return false; return false;
} }
VLSong::iterator::iterator(const VLSong & song, bool end)
: fSong(song)
{
fMeasure = end ? fSong.CountMeasures() : 0;
AdjustStatus();
}
VLSong::iterator & VLSong::iterator::operator++()
{
++fMeasure;
AdjustStatus();
return *this;
}
void VLSong::iterator::AdjustStatus()
{
int times;
size_t volta;
bool repeat;
if (fSong.DoesEndRepeat(fMeasure)
|| (fSong.DoesEndEnding(fMeasure, &repeat) && repeat)
) {
if (++fStatus.back().fVolta < fStatus.back().fTimes) {
//
// Repeat again
//
fMeasure = fStatus.back().fBegin;
return;
}
}
if (fMeasure == fSong.CountMeasures())
while (fStatus.size())
if (++fStatus.back().fVolta < fStatus.back().fTimes) {
fMeasure = fStatus.back().fBegin;
return;
} else
fStatus.pop_back();
while (fSong.DoesBeginEnding(fMeasure, 0, &volta)) {
if (!(volta & (1<<fStatus.back().fVolta))) {
//
// Skip this ending this time around
//
do {
++fMeasure;
} while (!fSong.DoesEndEnding(fMeasure));
} else
break;
}
if (fSong.DoesBeginRepeat(fMeasure, &times)) {
if (fStatus.size() && fStatus.back().fVolta == fStatus.back().fTimes)
fStatus.pop_back();
fStatus.push_back(Repeat(fMeasure, times));
}
}

View File

@ -264,6 +264,45 @@ struct VLSong {
std::vector<VLMeasure> fMeasures; std::vector<VLMeasure> fMeasures;
std::vector<VLRepeat> fRepeats; std::vector<VLRepeat> fRepeats;
//
// Iterate over measures in performance order
//
class iterator {
public:
size_t operator*() { return fMeasure; }
iterator & operator++();
bool operator==(const iterator & other) const {
return fMeasure==other.fMeasure && fStatus == other.fStatus;
}
bool operator!=(const iterator & other) const {
return fMeasure!=other.fMeasure || fStatus != other.fStatus;
}
protected:
friend class VLSong;
iterator(const VLSong & song, bool end);
private:
size_t fMeasure;
const VLSong & fSong;
struct Repeat {
Repeat(size_t begin, int times)
: fBegin(begin), fTimes(times), fVolta(0) {}
size_t fBegin;
int8_t fTimes;
int8_t fVolta;
bool operator==(const Repeat & other) const {
return fBegin==other.fBegin && fVolta == other.fVolta;
}
bool operator!=(const Repeat & other) const {
return fBegin!=other.fBegin || fVolta != other.fVolta;
}
};
std::vector<Repeat> fStatus;
void AdjustStatus();
};
iterator begin() { return iterator(*this, false); }
iterator end() { return iterator(*this, true); }
void AddChord(VLChord chord, size_t measure, VLFraction at); void AddChord(VLChord chord, size_t measure, VLFraction at);
void AddNote(VLLyricsNote note, size_t measure, VLFraction at); void AddNote(VLLyricsNote note, size_t measure, VLFraction at);
@ -276,10 +315,10 @@ struct VLSong {
bool CanBeRepeat(size_t beginMeasure, size_t endMeasure, int * times = 0); bool CanBeRepeat(size_t beginMeasure, size_t endMeasure, int * times = 0);
bool CanBeEnding(size_t beginMeasure, size_t endMeasure, bool CanBeEnding(size_t beginMeasure, size_t endMeasure,
size_t * volta = 0, size_t * voltaOK = 0); size_t * volta = 0, size_t * voltaOK = 0);
bool DoesBeginRepeat(size_t measure) const; bool DoesBeginRepeat(size_t measure, int * times = 0) const;
bool DoesEndRepeat(size_t measure, int * times) const; bool DoesEndRepeat(size_t measure, int * times = 0) const;
bool DoesBeginEnding(size_t measure, size_t * volta) const; bool DoesBeginEnding(size_t measure, bool * repeat = 0, size_t * volta = 0) const;
bool DoesEndEnding(size_t measure, bool * repeat) const; bool DoesEndEnding(size_t measure, bool * repeat = 0, size_t * volta = 0) const;
void Transpose(int semitones); void Transpose(int semitones);
bool FindWord(size_t stanza, size_t & measure, VLFraction & at); bool FindWord(size_t stanza, size_t & measure, VLFraction & at);

View File

@ -332,10 +332,9 @@ VLMusicElement sSemi2Accidental[12][12] = {
const float x = fClefKeyW+measure*fMeasureW; const float x = fClefKeyW+measure*fMeasureW;
const float yy = kSystemY+4.0f*kLineH; const float yy = kSystemY+4.0f*kLineH;
bool repeat; bool repeat;
int times;
size_t volta; size_t volta;
bool dotsPrecede= measure != 0 && bool dotsPrecede= measure != 0 &&
(song->DoesEndRepeat(m, &times) (song->DoesEndRepeat(m)
|| (song->DoesEndEnding(m, &repeat) && repeat)); || (song->DoesEndEnding(m, &repeat) && repeat));
bool dotsFollow = measure<fMeasPerSystem && song->DoesBeginRepeat(m); bool dotsFollow = measure<fMeasPerSystem && song->DoesBeginRepeat(m);
if (!dotsPrecede && !dotsFollow) { if (!dotsPrecede && !dotsFollow) {
@ -384,7 +383,7 @@ VLMusicElement sSemi2Accidental[12][12] = {
[bz removeAllPoints]; [bz removeAllPoints];
} }
if (measure<fMeasPerSystem) { if (measure<fMeasPerSystem) {
if (song->DoesBeginEnding(m, &volta)) { if (song->DoesBeginEnding(m, 0, &volta)) {
[bz setLineWidth:kThin]; [bz setLineWidth:kThin];
[bz moveToPoint: NSMakePoint(x+kDblLineOff, yy+0.5f*kLineH)]; [bz moveToPoint: NSMakePoint(x+kDblLineOff, yy+0.5f*kLineH)];
[bz lineToPoint: NSMakePoint(x+kDblLineOff, yy+2.0f*kLineH)]; [bz lineToPoint: NSMakePoint(x+kDblLineOff, yy+2.0f*kLineH)];

View File

@ -295,6 +295,98 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB ";
NSXMLElement * harMeas = [melMeas copy]; NSXMLElement * harMeas = [melMeas copy];
size_t volta;
bool repeat;
int times;
if (song->DoesBeginRepeat(measure)) {
NSXMLElement * barline = [NSXMLNode elementWithName:@"barline"];
[barline addAttribute: [NSXMLNode attributeWithName:@"location"
stringValue:@"left"]];
NSString * style = @"heavy-light";
if (song->DoesEndRepeat(measure)
|| (song->DoesEndEnding(measure, &repeat) && repeat)
)
style = @"heavy-heavy";
[barline addChild: [NSXMLNode elementWithName:@"bar-style"
stringValue:style]];
NSXMLElement * repeat = [NSXMLNode elementWithName:@"repeat"];
[repeat addAttribute: [NSXMLNode attributeWithName:@"direction"
stringValue:@"forward"]];
[barline addChild:repeat];
[melMeas addChild:barline];
}
if (song->DoesBeginEnding(measure, 0, &volta)) {
NSXMLElement * barline = [NSXMLNode elementWithName:@"barline"];
[barline addAttribute: [NSXMLNode attributeWithName:@"location"
stringValue:@"left"]];
NSXMLElement * ending = [NSXMLNode elementWithName:@"ending"];
[ending addAttribute: [NSXMLNode attributeWithName:@"type"
stringValue:@"start"]];
NSString * number = nil;
for (size_t i = 0; i<8; ++i)
if (volta & (1<<i))
if (number)
number = [NSString stringWithFormat:@"%@,%d",
number, i+1];
else
number = [NSString stringWithFormat:@"%d", i+1];
[ending addAttribute: [NSXMLNode attributeWithName:@"number"
stringValue:number]];
[barline addChild:ending];
[melMeas addChild:barline];
}
if (song->DoesEndRepeat(measure+1, &times)) {
NSXMLElement * barline = [NSXMLNode elementWithName:@"barline"];
[barline addAttribute: [NSXMLNode attributeWithName:@"location"
stringValue:@"right"]];
NSString * style = @"light-heavy";
if (song->DoesBeginRepeat(measure+1))
style = @"heavy-heavy";
[barline addChild: [NSXMLNode elementWithName:@"bar-style"
stringValue:style]];
NSXMLElement * repeat = [NSXMLNode elementWithName:@"repeat"];
[repeat addAttribute: [NSXMLNode attributeWithName:@"direction"
stringValue:@"backward"]];
[repeat addAttribute:
[NSXMLNode attributeWithName:@"times"
stringValue:[NSString stringWithFormat:@"%d", times]]];
[barline addChild:repeat];
[melMeas addChild:barline];
}
if (song->DoesEndEnding(measure+1, &repeat, &volta)) {
NSXMLElement * barline = [NSXMLNode elementWithName:@"barline"];
[barline addAttribute: [NSXMLNode attributeWithName:@"location"
stringValue:@"right"]];
if (repeat) {
NSString * style = @"light-heavy";
if (song->DoesBeginRepeat(measure+1))
style = @"heavy-heavy";
[barline addChild: [NSXMLNode elementWithName:@"bar-style"
stringValue:style]];
NSXMLElement * repeat = [NSXMLNode elementWithName:@"repeat"];
[repeat addAttribute: [NSXMLNode attributeWithName:@"direction"
stringValue:@"backward"]];
[barline addChild:repeat];
}
NSXMLElement * ending = [NSXMLNode elementWithName:@"ending"];
[ending addAttribute:
[NSXMLNode attributeWithName:@"type"
stringValue:repeat ? @"stop" : @"discontinue"]];
NSString * number = nil;
for (size_t i = 0; i<8; ++i)
if (volta & (1<<i))
if (number)
number = [NSString stringWithFormat:@"%@,%d",
number, i+1];
else
number = [NSString stringWithFormat:@"%d", i+1];
[ending addAttribute: [NSXMLNode attributeWithName:@"number"
stringValue:number]];
[barline addChild:ending];
[melMeas addChild:barline];
}
[self addNotes:&song->fMeasures[measure].fMelody toMeasure:melMeas]; [self addNotes:&song->fMeasures[measure].fMelody toMeasure:melMeas];
[self addChords:&song->fMeasures[measure].fChords toMeasure:harMeas]; [self addChords:&song->fMeasures[measure].fChords toMeasure:harMeas];
@ -404,10 +496,72 @@ int8_t sStepToPitch[] = {
return n; return n;
} }
- (void) addRepeat:(VLRepeat *)repeat
{
size_t end = repeat->fEndings.size() > 1
? repeat->fEndings[1].fBegin : repeat->fEndings[0].fEnd;
song->AddRepeat(repeat->fEndings[0].fBegin, end, repeat->fTimes);
for (size_t e = 1; e<repeat->fEndings.size(); ++e)
song->AddEnding(repeat->fEndings[e].fBegin, repeat->fEndings[e].fEnd,
repeat->fEndings[e].fVolta);
}
- (void) readBarlines:(NSArray *)barlines measure:(int)m
repeat:(VLRepeat *)repeat inRepeat:(bool *)inRepeat
error:(NSError **)outError
{
NSEnumerator * e = [barlines objectEnumerator];
for (NSXMLElement * barline; barline = [e nextObject]; ) {
NSXMLElement * rep = [barline nodeForXPath:@"./repeat" error:outError];
NSXMLElement * ending = [barline nodeForXPath:@"./ending" error:outError];
NSString * direction = nil;
if (rep)
direction = [rep stringForXPath:@"./@direction" error:outError];
NSString * endingType = nil;
size_t volta = 0;
int maxEnding = 0;
if (ending) {
endingType = [ending stringForXPath:@"./@type" error:outError];
NSEnumerator * n=
[[[ending stringForXPath:@"./@number" error:outError]
componentsSeparatedByString:@","] objectEnumerator];
for (NSString * num; num = [n nextObject]; ) {
int n = [num intValue];
maxEnding = n;
volta |= 1<<(n-1);
}
}
if ([direction isEqual:@"forward"]) {
//
// New repeat, add old one if there was one
//
if (*inRepeat)
[self addRepeat:repeat];
*inRepeat = true;
repeat->fTimes = 0;
repeat->fEndings.clear();
repeat->fEndings.push_back(VLRepeat::Ending(m, 0, 0));
} else if ([endingType isEqual:@"start"]) {
repeat->fTimes = std::max<int8_t>(repeat->fTimes, maxEnding);
repeat->fEndings.push_back(VLRepeat::Ending(m, 0, volta));
} else if (endingType) {
repeat->fEndings.back().fEnd = m+1;
repeat->fEndings[0].fEnd = m+1;
} else if (direction) {
repeat->fTimes = [rep intForXPath:@"./@times" error:outError];
repeat->fEndings[0].fEnd = m+1;
}
}
}
- (void) readMelody:(NSArray *)measures error:(NSError **)outError - (void) readMelody:(NSArray *)measures error:(NSError **)outError
{ {
NSEnumerator * e = [measures objectEnumerator]; NSEnumerator * e = [measures objectEnumerator];
VLRepeat repeat;
bool inRepeat = false;
for (NSXMLElement * measure; measure = [e nextObject]; ) { for (NSXMLElement * measure; measure = [e nextObject]; ) {
VLProperties & prop = song->fProperties.front(); VLProperties & prop = song->fProperties.front();
VLFraction unit(1, 4*prop.fDivisions); VLFraction unit(1, 4*prop.fDivisions);
@ -418,6 +572,9 @@ int8_t sStepToPitch[] = {
if (m >= song->CountMeasures()) if (m >= song->CountMeasures())
song->fMeasures.resize(m); song->fMeasures.resize(m);
[self readBarlines:[measure elementsForName:@"barline"] measure:m
repeat:&repeat inRepeat:&inRepeat error:outError];
NSEnumerator * n = [[measure elementsForName:@"note"] objectEnumerator]; NSEnumerator * n = [[measure elementsForName:@"note"] objectEnumerator];
for (NSXMLElement * note; note = [n nextObject]; ) { for (NSXMLElement * note; note = [n nextObject]; ) {
@ -427,6 +584,8 @@ int8_t sStepToPitch[] = {
at += n.fDuration; at += n.fDuration;
} }
} }
if (inRepeat)
[self addRepeat:&repeat];
} }
- (void) readChords:(NSArray *)measures error:(NSError **)outError - (void) readChords:(NSArray *)measures error:(NSError **)outError