mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 19:23:59 +00:00
Load / save / translate repeats & endings
This commit is contained in:
parent
5df833b2bf
commit
638ba81948
|
@ -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;
|
||||||
|
|
|
@ -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, ×)) {
|
||||||
|
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, ×)) {
|
||||||
|
if (fStatus.size() && fStatus.back().fVolta == fStatus.back().fTimes)
|
||||||
|
fStatus.pop_back();
|
||||||
|
fStatus.push_back(Repeat(fMeasure, times));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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, ×)
|
(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)];
|
||||||
|
|
|
@ -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, ×)) {
|
||||||
|
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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user