mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 19:23:59 +00:00
Low level lyrics support
This commit is contained in:
parent
fbf53052e3
commit
5ce21f2f78
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
@implementation NSMutableString (VLLilypond)
|
@implementation NSMutableString (VLLilypond)
|
||||||
|
|
||||||
- (void) substituteMacro:(NSString *)m withValue:(NSString *)value
|
- (void) substituteMacro:(NSString *)m withValue:(NSString *)value repeat:(BOOL)repeat
|
||||||
{
|
{
|
||||||
if ([value isEqual:@""])
|
if ([value isEqual:@""])
|
||||||
return;
|
return;
|
||||||
|
@ -26,10 +26,12 @@
|
||||||
[value rangeOfCharacterFromSet:
|
[value rangeOfCharacterFromSet:
|
||||||
[NSCharacterSet characterSetWithCharactersInString:@"\n"]];
|
[NSCharacterSet characterSetWithCharactersInString:@"\n"]];
|
||||||
BOOL hasEOL= range.location != NSNotFound;
|
BOOL hasEOL= range.location != NSNotFound;
|
||||||
|
unsigned from = 0;
|
||||||
|
|
||||||
for (range = [self rangeOfString:macro];
|
for (range = [self rangeOfString:macro];
|
||||||
range.location != NSNotFound;
|
range.location != NSNotFound;
|
||||||
range = [self rangeOfString:macro]
|
range = [self rangeOfString:macro options:0
|
||||||
|
range:NSMakeRange(from, [self length]-from)]
|
||||||
) {
|
) {
|
||||||
if (hasEOL) {
|
if (hasEOL) {
|
||||||
//
|
//
|
||||||
|
@ -49,27 +51,43 @@
|
||||||
[[self substringWithRange:suffix]
|
[[self substringWithRange:suffix]
|
||||||
stringByTrimmingCharactersInSet:
|
stringByTrimmingCharactersInSet:
|
||||||
[NSCharacterSet whitespaceCharacterSet]];
|
[NSCharacterSet whitespaceCharacterSet]];
|
||||||
|
NSString * nl;
|
||||||
if ([nonBlank length]) {
|
if ([nonBlank length]) {
|
||||||
NSRange nb = [pfxStr rangeOfString:nonBlank];
|
NSRange nb = [pfxStr rangeOfString:nonBlank];
|
||||||
prefix.length = nb.location - prefix.location;
|
prefix.length = nb.location;
|
||||||
pfxStr =
|
pfxStr =
|
||||||
[[self substringWithRange:prefix]
|
[[self substringWithRange:prefix]
|
||||||
stringByAppendingString:@" "];
|
stringByAppendingString:@" "];
|
||||||
sfxStr = [NSString stringWithFormat:@"\n%@", pfxStr];
|
sfxStr = [NSString stringWithFormat:@"\n%@", pfxStr];
|
||||||
|
nl = @"\n";
|
||||||
} else {
|
} else {
|
||||||
range = line;
|
range = line;
|
||||||
|
nl = @"";
|
||||||
}
|
}
|
||||||
NSArray * lines = [value componentsSeparatedByString:@"\n"];
|
NSArray * lines = [value componentsSeparatedByString:@"\n"];
|
||||||
value =
|
value =
|
||||||
[NSString stringWithFormat:@"%@%@%@", pfxStr,
|
[NSString stringWithFormat:@"%@%@%@%@", nl, pfxStr,
|
||||||
[lines componentsJoinedByString:
|
[lines componentsJoinedByString:
|
||||||
[@"\n" stringByAppendingString:pfxStr]],
|
[@"\n" stringByAppendingString:pfxStr]],
|
||||||
sfxStr];
|
sfxStr];
|
||||||
}
|
}
|
||||||
|
from = range.location + [value length];
|
||||||
|
if (repeat) {
|
||||||
|
NSRange line = [self lineRangeForRange:range];
|
||||||
|
[self insertString:[self substringWithRange:line]
|
||||||
|
atIndex:line.location+line.length];
|
||||||
|
from = line.location+2*line.length+[value length]-range.length;
|
||||||
|
}
|
||||||
[self replaceCharactersInRange:range withString:value];
|
[self replaceCharactersInRange:range withString:value];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (void)substituteMacro:(NSString*)macro withValue:(NSString*)value
|
||||||
|
{
|
||||||
|
[self substituteMacro:macro withValue:value repeat:NO];
|
||||||
|
}
|
||||||
|
|
||||||
- (void) purgeMacros
|
- (void) purgeMacros
|
||||||
{
|
{
|
||||||
for (NSRange range = [self rangeOfString:@"<{"];
|
for (NSRange range = [self rangeOfString:@"<{"];
|
||||||
|
@ -87,7 +105,7 @@
|
||||||
const int kMajorOffset = 6;
|
const int kMajorOffset = 6;
|
||||||
const int kMinorOffset = 9;
|
const int kMinorOffset = 9;
|
||||||
|
|
||||||
const char * sKeyNames[] = {
|
static const char * sKeyNames[] = {
|
||||||
"ges", "des", "as", "es", "bes", "f",
|
"ges", "des", "as", "es", "bes", "f",
|
||||||
"c", "g", "d", "a", "e", "b", "fis", "cis", "gis"
|
"c", "g", "d", "a", "e", "b", "fis", "cis", "gis"
|
||||||
};
|
};
|
||||||
|
@ -127,6 +145,13 @@ const char * sKeyNames[] = {
|
||||||
song->LilypondNotes(lys);
|
song->LilypondNotes(lys);
|
||||||
[ly substituteMacro:@"NOTES" withValue:
|
[ly substituteMacro:@"NOTES" withValue:
|
||||||
[NSString stringWithUTF8String:lys.c_str()]];
|
[NSString stringWithUTF8String:lys.c_str()]];
|
||||||
|
if (size_t stanzas = song->CountStanzas())
|
||||||
|
for (size_t s=0; s++<stanzas; ) {
|
||||||
|
song->LilypondStanza(lys, s);
|
||||||
|
[ly substituteMacro:@"LYRICS" withValue:
|
||||||
|
[NSString stringWithUTF8String:lys.c_str()]
|
||||||
|
repeat: s<stanzas];
|
||||||
|
}
|
||||||
[ly purgeMacros];
|
[ly purgeMacros];
|
||||||
return [ly dataUsingEncoding:enc];
|
return [ly dataUsingEncoding:enc];
|
||||||
}
|
}
|
||||||
|
|
|
@ -781,8 +781,8 @@ VLSong::VLSong()
|
||||||
fProperties.push_back(defaultProperties);
|
fProperties.push_back(defaultProperties);
|
||||||
fMeasures.resize(32); // Leadin, AABA
|
fMeasures.resize(32); // Leadin, AABA
|
||||||
|
|
||||||
VLNote rest = VLRest(1);
|
VLLyricsNote rest = VLLyricsNote(VLRest(1));
|
||||||
VLChord rchord;
|
VLChord rchord;
|
||||||
rchord.fDuration = 1;
|
rchord.fDuration = 1;
|
||||||
|
|
||||||
for (int i=0; i<32; ++i) {
|
for (int i=0; i<32; ++i) {
|
||||||
|
@ -874,7 +874,7 @@ uint8_t & LastTie(VLMeasure & measure)
|
||||||
//
|
//
|
||||||
// Dealing with notes is similar, but we also have to handle ties
|
// Dealing with notes is similar, but we also have to handle ties
|
||||||
//
|
//
|
||||||
void VLSong::AddNote(VLNote note, size_t measure, VLFraction at)
|
void VLSong::AddNote(VLLyricsNote note, size_t measure, VLFraction at)
|
||||||
{
|
{
|
||||||
VLNoteList::iterator i = fMeasures[measure].fMelody.begin();
|
VLNoteList::iterator i = fMeasures[measure].fMelody.begin();
|
||||||
VLFraction t(0);
|
VLFraction t(0);
|
||||||
|
@ -1017,6 +1017,25 @@ void VLSong::Transpose(int semi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t VLSong::CountStanzas() const
|
||||||
|
{
|
||||||
|
size_t stanzas = 0;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
for (; i!=e; ++i)
|
||||||
|
if (i->fLyrics.size() > stanzas)
|
||||||
|
for (size_t s = stanzas; s < i->fLyrics.size(); ++s)
|
||||||
|
if (i->fLyrics[s])
|
||||||
|
stanzas = s+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stanzas;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void VLSong::LilypondNotes(std::string & notes) const
|
void VLSong::LilypondNotes(std::string & notes) const
|
||||||
{
|
{
|
||||||
notes = "";
|
notes = "";
|
||||||
|
@ -1064,3 +1083,113 @@ void VLSong::LilypondChords(std::string & chords) const
|
||||||
chords += '\n';
|
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 & measure, VLFraction & at)
|
||||||
|
{
|
||||||
|
at += VLFraction(1,64);
|
||||||
|
|
||||||
|
return PrevWord(measure, at);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VLSong::PrevWord(size_t & measure, VLFraction & at)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
do {
|
||||||
|
VLMeasure & meas = fMeasures[measure];
|
||||||
|
VLNoteList::iterator note = fMeasures[measure].fMelody.begin();
|
||||||
|
VLNoteList::iterator end = fMeasures[measure].fMelody.end();
|
||||||
|
bool hasWord = false;
|
||||||
|
VLFraction word(0);
|
||||||
|
VLFraction now(0);
|
||||||
|
|
||||||
|
for (VLNoteList::iterator note = meas.fMelody.begin();
|
||||||
|
note != meas.fMelody.end() && now < at;
|
||||||
|
++note
|
||||||
|
) {
|
||||||
|
if (!(note->fTied & VLNote::kTiedWithPrev)
|
||||||
|
&& !(note->fHyphen & VLNote::kHyphenToPrev)
|
||||||
|
) {
|
||||||
|
word = now;
|
||||||
|
hasWord = true;
|
||||||
|
}
|
||||||
|
now += note->fDuration;
|
||||||
|
}
|
||||||
|
if (hasWord) {
|
||||||
|
at = word;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
at = meas.fProperties->fTime;
|
||||||
|
}
|
||||||
|
} while (measure-- > 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VLSong::NextWord(size_t & measure, VLFraction & at)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
bool firstMeasure = true;
|
||||||
|
do {
|
||||||
|
VLMeasure & meas = fMeasures[measure];
|
||||||
|
VLNoteList::iterator note = fMeasures[measure].fMelody.begin();
|
||||||
|
VLNoteList::iterator end = fMeasures[measure].fMelody.end();
|
||||||
|
bool hasWord = false;
|
||||||
|
VLFraction word(0);
|
||||||
|
VLFraction now(0);
|
||||||
|
|
||||||
|
for (VLNoteList::iterator note = meas.fMelody.begin();
|
||||||
|
note != meas.fMelody.end();
|
||||||
|
++note
|
||||||
|
) {
|
||||||
|
if (!(note->fTied & VLNote::kTiedWithPrev)
|
||||||
|
&& !(note->fHyphen & VLNote::kHyphenToPrev)
|
||||||
|
&& (!firstMeasure || now > at)
|
||||||
|
) {
|
||||||
|
word = now;
|
||||||
|
hasWord = true;
|
||||||
|
}
|
||||||
|
now += note->fDuration;
|
||||||
|
}
|
||||||
|
if (hasWord) {
|
||||||
|
at = word;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
firstMeasure = false;
|
||||||
|
}
|
||||||
|
} while (++measure < fMeasures.size());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -98,6 +98,21 @@ inline bool operator>=(VLFraction one, VLFraction other)
|
||||||
|
|
||||||
class VLProperties;
|
class VLProperties;
|
||||||
|
|
||||||
|
struct VLSyllable {
|
||||||
|
std::string fText; // Syllable text
|
||||||
|
uint8_t fKind; // Adjacency information
|
||||||
|
enum {
|
||||||
|
kSingle = 0,
|
||||||
|
kBegin = 1,
|
||||||
|
kEnd = 2,
|
||||||
|
kMiddle = 3,
|
||||||
|
kHasNext = 1,
|
||||||
|
kHasPrev = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
operator bool() const { return fText.size() > 0; }
|
||||||
|
};
|
||||||
|
|
||||||
struct VLNote {
|
struct VLNote {
|
||||||
VLFraction fDuration;
|
VLFraction fDuration;
|
||||||
int8_t fPitch; // Semitones
|
int8_t fPitch; // Semitones
|
||||||
|
@ -116,6 +131,7 @@ struct VLNote {
|
||||||
kTiedWithNext = 1,
|
kTiedWithNext = 1,
|
||||||
kTiedWithPrev = 2
|
kTiedWithPrev = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
VLNote() : fPitch(kNoPitch) {}
|
VLNote() : fPitch(kNoPitch) {}
|
||||||
VLNote(VLFraction dur, int8_t pitch)
|
VLNote(VLFraction dur, int8_t pitch)
|
||||||
: fDuration(dur), fPitch(pitch), fTied(kNotTied)
|
: fDuration(dur), fPitch(pitch), fTied(kNotTied)
|
||||||
|
@ -205,20 +221,21 @@ struct VLProperties {
|
||||||
void VisualNote(VLFraction at, VLFraction actualDur, VLFraction *visualDur, bool * triplet) const;
|
void VisualNote(VLFraction at, VLFraction actualDur, VLFraction *visualDur, bool * triplet) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VLSyllable {
|
struct VLLyricsNote : VLNote {
|
||||||
std::string fText;
|
VLLyricsNote() {}
|
||||||
bool fHyphen; // Followed by hyphen
|
explicit VLLyricsNote(const VLNote & note)
|
||||||
|
{ *static_cast<VLNote *>(this) = note; }
|
||||||
|
|
||||||
|
std::vector<VLSyllable> fLyrics;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::list<VLChord> VLChordList;
|
typedef std::list<VLChord> VLChordList;
|
||||||
typedef std::list<VLNote> VLNoteList;
|
typedef std::list<VLLyricsNote> VLNoteList;
|
||||||
typedef std::list<VLSyllable> VLSyllList;
|
|
||||||
|
|
||||||
struct VLMeasure {
|
struct VLMeasure {
|
||||||
VLProperties * fProperties;
|
VLProperties * fProperties;
|
||||||
VLChordList fChords;
|
VLChordList fChords;
|
||||||
VLNoteList fMelody;
|
VLNoteList fMelody;
|
||||||
VLSyllList fLyrics;
|
|
||||||
|
|
||||||
VLMeasure();
|
VLMeasure();
|
||||||
|
|
||||||
|
@ -233,14 +250,20 @@ struct VLSong {
|
||||||
std::vector<VLMeasure> fMeasures;
|
std::vector<VLMeasure> fMeasures;
|
||||||
|
|
||||||
void AddChord(VLChord chord, size_t measure, VLFraction at);
|
void AddChord(VLChord chord, size_t measure, VLFraction at);
|
||||||
void AddNote(VLNote note, size_t measure, VLFraction at);
|
void AddNote(VLLyricsNote note, size_t measure, VLFraction at);
|
||||||
void DelChord(size_t measure, VLFraction at);
|
void DelChord(size_t measure, VLFraction at);
|
||||||
void DelNote(size_t measure, VLFraction at);
|
void DelNote(size_t measure, VLFraction at);
|
||||||
void Transpose(int semitones);
|
void Transpose(int semitones);
|
||||||
|
|
||||||
|
bool FindWord(size_t & measure, VLFraction & at);
|
||||||
|
bool PrevWord(size_t & measure, VLFraction & at);
|
||||||
|
bool NextWord(size_t & measure, VLFraction & at);
|
||||||
|
|
||||||
size_t CountMeasures() const { return fMeasures.size(); }
|
size_t CountMeasures() const { return fMeasures.size(); }
|
||||||
|
size_t CountStanzas() const;
|
||||||
void LilypondNotes(std::string & notes) const;
|
void LilypondNotes(std::string & notes) const;
|
||||||
void LilypondChords(std::string & chords) const;
|
void LilypondChords(std::string & chords) const;
|
||||||
|
void LilypondStanza(std::string & lyrics, size_t stanza) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Local Variables:
|
// Local Variables:
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
if (fClickMode == 'k')
|
if (fClickMode == 'k')
|
||||||
[self song]->DelNote(fCursorMeasure, fCursorAt);
|
[self song]->DelNote(fCursorMeasure, fCursorAt);
|
||||||
else
|
else
|
||||||
[self song]->AddNote(newNote, fCursorMeasure, fCursorAt);
|
[self song]->AddNote(VLLyricsNote(newNote), fCursorMeasure, fCursorAt);
|
||||||
|
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
[[self document] updateChangeCount:NSChangeDone];
|
[[self document] updateChangeCount:NSChangeDone];
|
||||||
|
|
|
@ -146,6 +146,38 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB ";
|
||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSXMLElement *)syllable:(const VLSyllable *)syllable inStanza:(int)stanza
|
||||||
|
{
|
||||||
|
NSString * syll;
|
||||||
|
switch (syllable->fKind) {
|
||||||
|
default:
|
||||||
|
case VLSyllable::kSingle:
|
||||||
|
syll = @"single";
|
||||||
|
break;
|
||||||
|
case VLSyllable::kBegin:
|
||||||
|
syll = @"begin";
|
||||||
|
break;
|
||||||
|
case VLSyllable::kEnd:
|
||||||
|
syll = @"end";
|
||||||
|
break;
|
||||||
|
case VLSyllable::kMiddle:
|
||||||
|
syll = @"middle";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString * text = [NSString stringWithUTF8String:syllable->fText.c_str()];
|
||||||
|
NSXMLNode* num = [NSXMLNode attributeWithName:@"number"
|
||||||
|
stringValue:[NSString stringWithFormat:@"%d",
|
||||||
|
stanza]];
|
||||||
|
return [NSXMLNode
|
||||||
|
elementWithName:@"lyric"
|
||||||
|
children: [NSArray arrayWithObjects:
|
||||||
|
[NSXMLNode elementWithName:@"syllabic" stringValue:syll],
|
||||||
|
[NSXMLNode elementWithName:@"text" stringValue:text],
|
||||||
|
nil]
|
||||||
|
attributes: [NSArray arrayWithObject:num]];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)addNotes:(VLNoteList *)notes toMeasure:(NSXMLElement *)meas
|
- (void)addNotes:(VLNoteList *)notes toMeasure:(NSXMLElement *)meas
|
||||||
{
|
{
|
||||||
VLFraction resolution(1, song->fProperties.front().fDivisions*4);
|
VLFraction resolution(1, song->fProperties.front().fDivisions*4);
|
||||||
|
@ -156,7 +188,13 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB ";
|
||||||
) {
|
) {
|
||||||
VLFraction u = note->fDuration / resolution;
|
VLFraction u = note->fDuration / resolution;
|
||||||
int units = (u.fNum+u.fDenom/2)/u.fDenom;
|
int units = (u.fNum+u.fDenom/2)/u.fDenom;
|
||||||
[meas addChild:[self noteWithPitch:note->fPitch duration:units useSharps:useSharps]];
|
NSXMLElement*n =
|
||||||
|
[self noteWithPitch:note->fPitch duration:units useSharps:useSharps];
|
||||||
|
for (size_t i=0; i<note->fLyrics.size(); ++i)
|
||||||
|
if (note->fLyrics[i])
|
||||||
|
[n addChild:[self syllable:¬e->fLyrics[i] inStanza:i+1]];
|
||||||
|
|
||||||
|
[meas addChild:n];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,10 +363,10 @@ int8_t sStepToPitch[] = {
|
||||||
9, 11, 0, 2, 4, 5, 7
|
9, 11, 0, 2, 4, 5, 7
|
||||||
};
|
};
|
||||||
|
|
||||||
- (VLNote) readNote:(NSXMLElement*)note withUnit:(VLFraction)unit
|
- (VLLyricsNote) readNote:(NSXMLElement*)note withUnit:(VLFraction)unit
|
||||||
{
|
{
|
||||||
NSError * outError;
|
NSError * outError;
|
||||||
VLNote n;
|
VLLyricsNote n;
|
||||||
|
|
||||||
n.fTied = 0;
|
n.fTied = 0;
|
||||||
|
|
||||||
|
@ -343,6 +381,25 @@ int8_t sStepToPitch[] = {
|
||||||
n.fPitch += [[alter stringValue] intValue];
|
n.fPitch += [[alter stringValue] intValue];
|
||||||
}
|
}
|
||||||
n.fDuration = VLFraction([note intForXPath:@"./duration" error:&outError])*unit;
|
n.fDuration = VLFraction([note intForXPath:@"./duration" error:&outError])*unit;
|
||||||
|
NSEnumerator * e = [[note elementsForName:@"lyric"] objectEnumerator];
|
||||||
|
for (NSXMLElement * lyric; lyric = [e nextObject]; ) {
|
||||||
|
int stanza = [[[lyric attributeForName:@"number"]
|
||||||
|
stringValue] intValue];
|
||||||
|
if (stanza > n.fLyrics.size())
|
||||||
|
n.fLyrics.resize(stanza);
|
||||||
|
--stanza;
|
||||||
|
NSString * kind = [lyric stringForXPath:@"./syllabic" error:&outError];
|
||||||
|
if ([kind isEqual:@"begin"])
|
||||||
|
n.fLyrics[stanza].fKind = VLSyllable::kBegin;
|
||||||
|
else if ([kind isEqual:@"end"])
|
||||||
|
n.fLyrics[stanza].fKind = VLSyllable::kEnd;
|
||||||
|
else if ([kind isEqual:@"middle"])
|
||||||
|
n.fLyrics[stanza].fKind = VLSyllable::kMiddle;
|
||||||
|
else
|
||||||
|
n.fLyrics[stanza].fKind = VLSyllable::kSingle;
|
||||||
|
n.fLyrics[stanza].fText =
|
||||||
|
[[lyric stringForXPath:@"./text" error:&outError] UTF8String];
|
||||||
|
}
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -364,7 +421,7 @@ int8_t sStepToPitch[] = {
|
||||||
NSEnumerator * n = [[measure elementsForName:@"note"] objectEnumerator];
|
NSEnumerator * n = [[measure elementsForName:@"note"] objectEnumerator];
|
||||||
|
|
||||||
for (NSXMLElement * note; note = [n nextObject]; ) {
|
for (NSXMLElement * note; note = [n nextObject]; ) {
|
||||||
VLNote n = [self readNote:note withUnit:unit];
|
VLLyricsNote n = [self readNote:note withUnit:unit];
|
||||||
if (n.fPitch != VLNote::kNoPitch)
|
if (n.fPitch != VLNote::kNoPitch)
|
||||||
song->AddNote(n, m, at);
|
song->AddNote(n, m, at);
|
||||||
at += n.fDuration;
|
at += n.fDuration;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user