Fix triplet handling

This commit is contained in:
Matthias Neeracher 2007-01-01 05:02:17 +00:00
parent 9206a311b6
commit 3d880853b3
3 changed files with 69 additions and 28 deletions

View File

@ -158,7 +158,7 @@ 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, const VLProperties & prop) const void VLNote::LilypondName(std::string & name, VLFraction at, VLFraction prevDur, VLFraction nextDur, bool & triplet, const VLProperties & prop) const
{ {
std::string n = LilypondPitchName(fPitch, prop.fKey >= 0); std::string n = LilypondPitchName(fPitch, prop.fKey >= 0);
if (fPitch != kNoPitch) { if (fPitch != kNoPitch) {
@ -173,9 +173,10 @@ void VLNote::LilypondName(std::string & name, VLFraction at, const VLProperties
for (VLFraction dur = fDuration; dur.fNum; ) { for (VLFraction dur = fDuration; dur.fNum; ) {
char duration[32]; char duration[32];
VLFraction part, visual; VLFraction part, visual;
bool triplet; bool grouped = dur==nextDur ||
prop.PartialNote(at, dur, &part); (prevPart!=0 ? dur==prevPart : dur==prevDur);
prop.VisualNote(at, part, &visual, &triplet); prop.PartialNote(at, dur, grouped, &part);
prop.VisualNote(at, part, triplet, &visual, &triplet);
if (!triplet && fPitch != kNoPitch && part == dur && 2*visual == prevPart) { if (!triplet && fPitch != kNoPitch && part == dur && 2*visual == prevPart) {
durations.pop_back(); durations.pop_back();
sprintf(duration, "%s%d.", n.c_str(), visual.fDenom/2); sprintf(duration, "%s%d.", n.c_str(), visual.fDenom/2);
@ -214,22 +215,26 @@ static struct {
{{0,0}, 0} {{0,0}, 0}
}; };
void VLNote::MMAName(std::string & name, VLFraction at, const VLProperties & prop) const void VLNote::MMAName(std::string & name, VLFraction at, VLFraction prevDur, VLFraction nextDur, const VLProperties & prop) const
{ {
bool useSharps = prop.fKey >= 0; bool useSharps = prop.fKey >= 0;
name.clear(); name.clear();
VLFraction prevPart(0);
for (VLFraction dur = fDuration; dur.fNum; ) { for (VLFraction dur = fDuration; dur.fNum; ) {
VLFraction part; VLFraction part;
prop.PartialNote(at, dur, &part); bool grouped = dur==nextDur ||
(prevPart!=0 ? dur==prevPart : dur==prevDur);
prop.PartialNote(at, dur, grouped, &part);
for (int d=0; sMMADur[d].fName; ++d) for (int d=0; sMMADur[d].fName; ++d)
if (part == sMMADur[d].fVal) { if (part == sMMADur[d].fVal) {
if (name.size()) if (name.size())
name += '+'; name += '+';
name += sMMADur[d].fName; name += sMMADur[d].fName;
} }
dur -= part; prevPart = part;
at += part; dur -= part;
at += part;
} }
name += MMAPitchName(fPitch, useSharps); name += MMAPitchName(fPitch, useSharps);
if (fPitch != kNoPitch) { if (fPitch != kNoPitch) {
@ -448,7 +453,7 @@ void VLChord::LilypondName(std::string & name, bool useSharps) const
{ {
name = LilypondPitchName(fPitch, useSharps); name = LilypondPitchName(fPitch, useSharps);
char duration[16]; char duration[16];
if (fDuration.fNum == 1 && !(fDuration.fDenom & (fDuration.fDenom-1))) if (fDuration.fNum == 1 && !(fDuration.fDenom & (fDuration.fDenom-1))) // Power of two
sprintf(duration, "%d", fDuration.fDenom); sprintf(duration, "%d", fDuration.fDenom);
else else
sprintf(duration, "1*%d/%d", fDuration.fNum, fDuration.fDenom); sprintf(duration, "1*%d/%d", fDuration.fNum, fDuration.fDenom);
@ -687,7 +692,7 @@ static void TrimNote(VLFraction at, VLFraction & d, VLFraction grid)
} }
void VLProperties::PartialNote(VLFraction at, VLFraction totalDuration, void VLProperties::PartialNote(VLFraction at, VLFraction totalDuration,
VLFraction * noteDuration) const bool grouped, VLFraction * noteDuration) const
{ {
const VLFraction kBeat(1, fTime.fDenom); const VLFraction kBeat(1, fTime.fDenom);
@ -706,11 +711,20 @@ void VLProperties::PartialNote(VLFraction at, VLFraction totalDuration,
else else
TrimNote(at, *noteDuration, kBeat);// Don't let other notes span beats TrimNote(at, *noteDuration, kBeat);// Don't let other notes span beats
} }
if (!(noteDuration->fDenom % 3) && *noteDuration != totalDuration && ((at+*noteDuration)%kBeat) > 0) if (!(noteDuration->fDenom % 3))
*noteDuration *= VLFraction(3,4); // avoid frivolous triplets if (*noteDuration != totalDuration) {
if (((at+*noteDuration)%kBeat) > 0)
*noteDuration *= VLFraction(3,4); // avoid frivolous triplets
} else if (*noteDuration > VLFraction(1,4*fDivisions) && !grouped) {
if (*noteDuration == VLFraction(1,2*fDivisions))
if (at % VLFraction(1, 4*fDivisions/3) == 0)
return; // Permit larger swing notes
*noteDuration *= VLFraction(3,4); // avoid other isolated triplets
}
} }
void VLProperties::VisualNote(VLFraction at, VLFraction actualDur, void VLProperties::VisualNote(VLFraction at, VLFraction actualDur,
bool prevTriplet,
VLFraction *visualDur, bool * triplet) const VLFraction *visualDur, bool * triplet) const
{ {
bool swing= !(fDivisions % 3); // In swing mode? bool swing= !(fDivisions % 3); // In swing mode?
@ -722,14 +736,14 @@ void VLProperties::VisualNote(VLFraction at, VLFraction actualDur,
if (actualDur == 4*swung/3 && (at % swingGrid) == 0) { if (actualDur == 4*swung/3 && (at % swingGrid) == 0) {
*visualDur = swung; *visualDur = swung;
*triplet = false; *triplet = false;
} else if (actualDur == 2*swung/3 && ((at+actualDur) % swingGrid) == 0) { } else if (actualDur == 2*swung/3 && ((at+actualDur) % swingGrid) == 0 && !prevTriplet) {
*visualDur = swung; *visualDur = swung;
*triplet = false; *triplet = false;
} else { } else {
*visualDur = 4*actualDur/3; *visualDur = 3*actualDur/2;
} }
} else { } else {
*visualDur = 4*actualDur/3; *visualDur = 3*actualDur/2;
} }
} else { } else {
*visualDur = actualDur; *visualDur = actualDur;
@ -748,9 +762,14 @@ void VLMeasure::MMANotes(std::string & notes, const VLProperties & prop) const
VLNoteList::const_iterator e = fMelody.end(); VLNoteList::const_iterator e = fMelody.end();
notes.clear(); notes.clear();
VLFraction prevDur(0);
for (; i!=e; ++i) { for (; i!=e; ++i) {
std::string note; std::string note;
i->MMAName(note, at, prop); VLFraction nextDur(0);
VLNoteList::const_iterator n=i;
if (++n != e)
nextDur = n->fDuration;
i->MMAName(note, at, prevDur, nextDur, prop);
if (notes.size()) if (notes.size())
notes += ' '; notes += ' ';
notes += note+';'; notes += note+';';
@ -1087,12 +1106,28 @@ void VLSong::LilypondNotes(std::string & notes) const
indent = ""; indent = "";
} }
notes += indent; notes += indent;
VLFraction prevDur(0);
bool triplet = false;
for (; i!=e; ++i) { for (; i!=e; ++i) {
std::string note; std::string note;
i->LilypondName(note, at, fProperties[fMeasures[measure].fPropIdx]); VLNoteList::const_iterator n = i;
at += i->fDuration; VLFraction nextDur(0);
notes += note+" "; if (++n != e)
nextDur = n->fDuration;
i->LilypondName(note, at, prevDur, nextDur, triplet, 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);
notes += '|'; notes += '|';
if (!(measure % 4)) { if (!(measure % 4)) {
char measNo[8]; char measNo[8];

View File

@ -139,8 +139,8 @@ struct VLNote {
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, const VLProperties & prop) const; void LilypondName(std::string & name, VLFraction at, VLFraction prevDur, VLFraction nextDur, bool & triplet, const VLProperties & prop) const;
void MMAName(std::string & name, VLFraction at, const VLProperties & prop) const; void MMAName(std::string & name, VLFraction at, VLFraction prevDur, VLFraction nextDur, const VLProperties & prop) const;
}; };
struct VLRest : VLNote { struct VLRest : VLNote {
@ -214,11 +214,11 @@ struct VLProperties {
// //
// Subdivide a note and adjust for swing // Subdivide a note and adjust for swing
// //
void PartialNote(VLFraction at, VLFraction totalDuration, VLFraction * noteDuration) const; void PartialNote(VLFraction at, VLFraction totalDuration, bool grouped, VLFraction * noteDuration) const;
// //
// Determine visual representation of note head // Determine visual representation of note head
// //
void VisualNote(VLFraction at, VLFraction actualDur, VLFraction *visualDur, bool * triplet) const; void VisualNote(VLFraction at, VLFraction actualDur, bool prevTriplet, VLFraction *visualDur, bool * triplet) const;
}; };
struct VLLyricsNote : VLNote { struct VLLyricsNote : VLNote {

View File

@ -258,20 +258,26 @@
const VLMeasure measure = song->fMeasures[measIdx]; const VLMeasure measure = song->fMeasures[measIdx];
const VLNoteList & melody = measure.fMelody; const VLNoteList & melody = measure.fMelody;
VLFraction at(0); VLFraction at(0);
VLFraction prevDur(0);
bool triplet = false;
for (VLNoteList::const_iterator note = melody.begin(); for (VLNoteList::const_iterator note = melody.begin();
note != melody.end(); note != melody.end();
++note ++note
) { ) {
VLFraction dur = note->fDuration; VLFraction dur = note->fDuration;
VLFraction nextDur(0);
VLNoteList::const_iterator next = note;
if (++next != melody.end())
nextDur = next->fDuration;
BOOL first = !m || !note->fTied; BOOL first = !m || !note->fTied;
int pitch = note->fPitch; int pitch = note->fPitch;
while (dur > 0) { while (dur > 0) {
VLFraction partialDur; // Actual value of note drawn VLFraction partialDur; // Actual value of note drawn
VLFraction noteDur; // Visual value of note VLFraction noteDur; // Visual value of note
bool triplet; prop.PartialNote(at, dur, dur==prevDur || dur==nextDur,
prop.PartialNote(at, dur, &partialDur); &partialDur);
prop.VisualNote(at, partialDur, &noteDur, &triplet); prop.VisualNote(at, partialDur, triplet, &noteDur, &triplet);
prevDur = partialDur;
if (pitch != VLNote::kNoPitch) { if (pitch != VLNote::kNoPitch) {
VLMusicElement accidental; VLMusicElement accidental;
NSPoint pos = NSPoint pos =