Revamp tuplet model

This commit is contained in:
Matthias Neeracher 2011-08-31 04:02:37 +02:00
parent 424a40bd07
commit 9d198f5cec
6 changed files with 65 additions and 41 deletions

View File

@ -261,9 +261,10 @@ void VLLilypondWriter::VisitNote(VLLyricsNote & n)
&& n.fPitch == fPrevNote.fPitch && n.fPitch == fPrevNote.fPitch
) )
strcpy(duration, "."); strcpy(duration, ".");
else if (n.fVisual & VLNote::kTriplet) else if (n.fVisual & VLNote::kTupletMask)
sprintf(duration, "%s\\times 2/3 { %s%d%s }", sprintf(duration, "%s\\times %d/%d { %s%d%s }",
space, nm.c_str(), kValue[n.fVisual & VLNote::kNoteHeadMask], tie); space, VLNote::TupletDenom(n.fVisual), VLNote::TupletNum(n.fVisual),
nm.c_str(), kValue[n.fVisual & VLNote::kNoteHeadMask], tie);
else else
sprintf(duration, "%s%s%d%s", sprintf(duration, "%s%s%d%s",
space, nm.c_str(), kValue[n.fVisual & VLNote::kNoteHeadMask], tie); space, nm.c_str(), kValue[n.fVisual & VLNote::kNoteHeadMask], tie);

View File

@ -314,7 +314,7 @@ void VLMeasure::DecomposeNotes(const VLProperties & prop, VLNoteList & decompose
p.AlignToGrid(inBeat, kQuarterDur); // Align all others p.AlignToGrid(inBeat, kQuarterDur); // Align all others
} }
checkTriplets: checkTriplets:
if (p.fVisual & VLNote::kTriplet) { if ((p.fVisual & VLNote::kTupletMask) == VLNote::kTriplet) {
// //
// Distinguish swing 8ths/16ths from triplets // Distinguish swing 8ths/16ths from triplets
// //
@ -335,7 +335,7 @@ void VLMeasure::DecomposeNotes(const VLProperties & prop, VLNoteList & decompose
// //
// First swing note (4th triplet -> 8th) // First swing note (4th triplet -> 8th)
// //
p.fVisual = (p.fVisual+1) & ~VLNote::kTriplet; p.fVisual = (p.fVisual+1) & ~VLNote::kTupletMask;
} }
} else if ((p.fDuration == sw12 && ((at+p.fDuration) % grid4 == 0)) } else if ((p.fDuration == sw12 && ((at+p.fDuration) % grid4 == 0))
|| (swing16 && p.fDuration == sw24 && ((at+p.fDuration) % grid8 == 0)) || (swing16 && p.fDuration == sw24 && ((at+p.fDuration) % grid8 == 0))
@ -344,7 +344,7 @@ void VLMeasure::DecomposeNotes(const VLProperties & prop, VLNoteList & decompose
// Second swing note (8th triplet -> 8th) // Second swing note (8th triplet -> 8th)
// //
if (!prevTriplets) if (!prevTriplets)
p.fVisual &= ~VLNote::kTriplet; p.fVisual &= ~VLNote::kTupletMask;
} else if ((p.fDuration > kMinDuration) && } else if ((p.fDuration > kMinDuration) &&
((at % p.fDuration != 0) ((at % p.fDuration != 0)
|| (p.fDuration != c.fDuration || (p.fDuration != c.fDuration
@ -357,11 +357,11 @@ void VLMeasure::DecomposeNotes(const VLProperties & prop, VLNoteList & decompose
// Get rid of awkward triplets // Get rid of awkward triplets
// //
p.fDuration *= VLFraction(3,4); p.fDuration *= VLFraction(3,4);
p.fVisual = (p.fVisual+1) & ~VLNote::kTriplet; p.fVisual = (p.fVisual+1) & ~VLNote::kTupletMask;
} }
} }
haveDuration: haveDuration:
if (p.fVisual & VLNote::kTriplet) if ((p.fVisual & VLNote::kTupletMask) == VLNote::kTriplet)
if ((prevTriplets = (prevTriplets+1)%3)) { if ((prevTriplets = (prevTriplets+1)%3)) {
prevTripDur = p.fDuration; prevTripDur = p.fDuration;
prevVisual = p.fVisual; prevVisual = p.fVisual;
@ -554,8 +554,7 @@ void VLSong::AddNote(VLLyricsNote note, size_t measure, VLFraction at)
// //
// Sanity check on accidentals // Sanity check on accidentals
// //
note.fVisual = (note.fVisual & ~VLNote::kAccidentalsMask) note.fVisual = VLPitchAccidental(note.fPitch, note.fVisual, Properties(measure).fKey);
| VLPitchAccidental(note.fPitch, note.fVisual, Properties(measure).fKey);
// //
// Always keep an empty measure in reserve // Always keep an empty measure in reserve
// //
@ -2080,6 +2079,9 @@ std::string VLSong::PrimaryGroove() const
return bestGroove; return bestGroove;
} }
#pragma mark -
#pragma mark class VLSongVisitor
//////////////////////// VLSongVisitor //////////////////////////////// //////////////////////// VLSongVisitor ////////////////////////////////
VLSongVisitor::~VLSongVisitor() VLSongVisitor::~VLSongVisitor()

View File

@ -142,10 +142,12 @@ struct VLNote {
kAccidentalsMask= 0x00F0, kAccidentalsMask= 0x00F0,
kTriplet = 0x300, kTriplet = 0x3200,
kTupletMask = 0x0F00 kTupletMask = 0xFF00
}; };
static int TupletNum(uint16_t visual) { return visual >> 12; }
static int TupletDenom(uint16_t visual) { return (visual >> 8) & 0x0F; }
VLNote(VLFraction dur=0, int pitch=kNoPitch, uint16_t visual=0); VLNote(VLFraction dur=0, int pitch=kNoPitch, uint16_t visual=0);
VLNote(std::string name); VLNote(std::string name);
std::string Name(uint16_t accidentals=0) const; std::string Name(uint16_t accidentals=0) const;

View File

@ -542,6 +542,8 @@ advanceAt:
[filterTask release]; [filterTask release];
if ([error length]) { if ([error length]) {
[contents writeToFile:@"/var/tmp/VocalEaselFilterInput" atomically:NO];
[error writeToFile:@"/var/tmp/VocalEaselFilterError" atomically:NO];
NSString * errStr = [[[NSString alloc] initWithData:error NSString * errStr = [[[NSString alloc] initWithData:error
encoding:NSUTF8StringEncoding] autorelease]; encoding:NSUTF8StringEncoding] autorelease];
[NSException raise:NSInvalidArgumentException [NSException raise:NSInvalidArgumentException

View File

@ -10,7 +10,7 @@
const float kLineX = 5.0; const float kLineX = 5.0;
const float kLineH = 10.0; const float kLineH = 10.0;
const float kTripletH = 5.0; const float kTupletH = 5.0;
#define kSystemBaseline ((fNumBotLedgers+1)*kLineH+fNumStanzas*kLyricsH) #define kSystemBaseline ((fNumBotLedgers+1)*kLineH+fNumStanzas*kLyricsH)
#define kSystemAscent ((fNumTopLedgers+7)*kLineH+kChordH) #define kSystemAscent ((fNumTopLedgers+7)*kLineH+kChordH)
#define kSystemH (kSystemBaseline+kSystemAscent) #define kSystemH (kSystemBaseline+kSystemAscent)

View File

@ -296,7 +296,7 @@
operation: NSCompositeSourceOver]; operation: NSCompositeSourceOver];
} }
- (void) drawTripletBracketFrom:(int)startX to:(int)endX atY:(int)y - (void) drawTuplet:(uint16_t)tuplet bracketFrom:(int)startX to:(int)endX atY:(int)y
{ {
static NSDictionary * sTripletFont = nil; static NSDictionary * sTripletFont = nil;
if (!sTripletFont) if (!sTripletFont)
@ -308,13 +308,15 @@
NSBezierPath * bz = [NSBezierPath bezierPath]; NSBezierPath * bz = [NSBezierPath bezierPath];
[bz moveToPoint: NSMakePoint(startX, y-kTripletH)]; [bz moveToPoint: NSMakePoint(startX, y-kTupletH)];
[bz lineToPoint: NSMakePoint(startX, y)]; [bz lineToPoint: NSMakePoint(startX, y)];
[bz lineToPoint: NSMakePoint(endX, y)]; [bz lineToPoint: NSMakePoint(endX, y)];
[bz lineToPoint: NSMakePoint(endX, y-kTripletH)]; [bz lineToPoint: NSMakePoint(endX, y-kTupletH)];
[bz stroke]; [bz stroke];
[@"3" drawAtPoint: NSMakePoint((startX+endX)*0.5f, y+kTripletH) NSString * tupletText = tuplet == VLNote::kTriplet ? @"3"
: [NSString stringWithFormat:@"%d:%d", VLNote::TupletNum(tuplet), VLNote::TupletDenom(tuplet)];
[tupletText drawAtPoint: NSMakePoint((startX+endX)*0.5f, y+kTupletH)
withAttributes: sTripletFont]; withAttributes: sTripletFont];
} }
@ -326,11 +328,6 @@
const VLSystemLayout & kLayout = (*fLayout)[system]; const VLSystemLayout & kLayout = (*fLayout)[system];
const CGFloat kSystemY = [self systemY:system]; const CGFloat kSystemY = [self systemY:system];
float tripletStartX;
float tripletEndX;
CGFloat tripletY;
bool hasTriplets = false;
for (int m = 0; m<kLayout.NumMeasures(); ++m) { for (int m = 0; m<kLayout.NumMeasures(); ++m) {
VLVisualFilter filterVisuals(kProp.fKey); VLVisualFilter filterVisuals(kProp.fKey);
int measIdx = m+kFirstMeas; int measIdx = m+kFirstMeas;
@ -340,6 +337,13 @@
VLNoteList melody; VLNoteList melody;
measure.DecomposeNotes(kProp, melody); measure.DecomposeNotes(kProp, melody);
VLFraction at(0); VLFraction at(0);
float tupletStartX;
float tupletEndX;
CGFloat tupletY;
int inTuplet = 0;
uint16_t tuplet;
VLFraction tupletDur;
for (VLNoteList::const_iterator note = melody.begin(); for (VLNoteList::const_iterator note = melody.begin();
note != melody.end(); note != melody.end();
++note ++note
@ -369,25 +373,38 @@
kSystemY+[self noteYInSection:measure.fPropIdx withPitch:65]); kSystemY+[self noteYInSection:measure.fPropIdx withPitch:65]);
[self drawRest:note->fVisual & VLNote::kNoteHeadMask at: pos]; [self drawRest:note->fVisual & VLNote::kNoteHeadMask at: pos];
} }
if (note->fVisual & VLNote::kTriplet) { if (uint16_t newTuplet = note->fVisual & VLNote::kTupletMask) {
tripletEndX = pos.x+kNoteW*0.5f; tupletEndX = pos.x+kNoteW*0.5f;
if (hasTriplets) { if (inTuplet && newTuplet == tuplet) {
tripletY = std::max(tripletY, pos.y+kLineH); tupletY = std::max(tupletY, pos.y+kLineH);
} else { } else {
tripletY = std::max(kSystemY+5.0f*kLineH, pos.y+kLineH); if (inTuplet)
tripletStartX = pos.x-kNoteW*0.5f; [self drawTuplet:tuplet bracketFrom:tupletStartX to:tupletEndX atY:tupletY];
hasTriplets = true; tupletY = std::max(kSystemY+5.0f*kLineH, pos.y+kLineH);
tupletStartX = pos.x-kNoteW*0.5f;
tuplet = newTuplet;
tupletDur = 0;
} }
} else if (hasTriplets) { ++inTuplet;
[self drawTripletBracketFrom:tripletStartX to:tripletEndX atY:tripletY]; tupletDur += note->fDuration / VLNote::TupletDenom(tuplet);
hasTriplets = false; if (tuplet == VLNote::kTriplet ? (tupletDur.fNum == 1 && !(tupletDur.fDenom & (tupletDur.fDenom-1)))
: inTuplet == VLNote::TupletNum(tuplet)
) {
//
// Tuplet adds up to power of two fraction
//
[self drawTuplet:tuplet bracketFrom:tupletStartX to:tupletEndX atY:tupletY];
inTuplet = 0;
}
} else if (++inTuplet) {
[self drawTuplet:tuplet bracketFrom:tupletStartX to:tupletEndX atY:tupletY];
inTuplet = 0;
} }
at += note->fDuration; at += note->fDuration;
} }
} if (inTuplet)
if (hasTriplets) { [self drawTuplet:tuplet bracketFrom:tupletStartX to:tupletEndX atY:tupletY];
[self drawTripletBracketFrom:tripletStartX to:tripletEndX atY:tripletY];
} }
if (fCursorRegion == kRegionNote && fLayout->SystemForMeasure(fCursorMeasure) == system) if (fCursorRegion == kRegionNote && fLayout->SystemForMeasure(fCursorMeasure) == system)
[self drawNoteCursor]; [self drawNoteCursor];