mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 03:04:00 +00:00
Revamp tuplet model
This commit is contained in:
parent
424a40bd07
commit
9d198f5cec
|
@ -261,9 +261,10 @@ void VLLilypondWriter::VisitNote(VLLyricsNote & n)
|
|||
&& n.fPitch == fPrevNote.fPitch
|
||||
)
|
||||
strcpy(duration, ".");
|
||||
else if (n.fVisual & VLNote::kTriplet)
|
||||
sprintf(duration, "%s\\times 2/3 { %s%d%s }",
|
||||
space, nm.c_str(), kValue[n.fVisual & VLNote::kNoteHeadMask], tie);
|
||||
else if (n.fVisual & VLNote::kTupletMask)
|
||||
sprintf(duration, "%s\\times %d/%d { %s%d%s }",
|
||||
space, VLNote::TupletDenom(n.fVisual), VLNote::TupletNum(n.fVisual),
|
||||
nm.c_str(), kValue[n.fVisual & VLNote::kNoteHeadMask], tie);
|
||||
else
|
||||
sprintf(duration, "%s%s%d%s",
|
||||
space, nm.c_str(), kValue[n.fVisual & VLNote::kNoteHeadMask], tie);
|
||||
|
|
|
@ -314,7 +314,7 @@ void VLMeasure::DecomposeNotes(const VLProperties & prop, VLNoteList & decompose
|
|||
p.AlignToGrid(inBeat, kQuarterDur); // Align all others
|
||||
}
|
||||
checkTriplets:
|
||||
if (p.fVisual & VLNote::kTriplet) {
|
||||
if ((p.fVisual & VLNote::kTupletMask) == VLNote::kTriplet) {
|
||||
//
|
||||
// Distinguish swing 8ths/16ths from triplets
|
||||
//
|
||||
|
@ -335,7 +335,7 @@ void VLMeasure::DecomposeNotes(const VLProperties & prop, VLNoteList & decompose
|
|||
//
|
||||
// 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))
|
||||
|| (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)
|
||||
//
|
||||
if (!prevTriplets)
|
||||
p.fVisual &= ~VLNote::kTriplet;
|
||||
p.fVisual &= ~VLNote::kTupletMask;
|
||||
} else if ((p.fDuration > kMinDuration) &&
|
||||
((at % p.fDuration != 0)
|
||||
|| (p.fDuration != c.fDuration
|
||||
|
@ -357,11 +357,11 @@ void VLMeasure::DecomposeNotes(const VLProperties & prop, VLNoteList & decompose
|
|||
// Get rid of awkward triplets
|
||||
//
|
||||
p.fDuration *= VLFraction(3,4);
|
||||
p.fVisual = (p.fVisual+1) & ~VLNote::kTriplet;
|
||||
p.fVisual = (p.fVisual+1) & ~VLNote::kTupletMask;
|
||||
}
|
||||
}
|
||||
haveDuration:
|
||||
if (p.fVisual & VLNote::kTriplet)
|
||||
if ((p.fVisual & VLNote::kTupletMask) == VLNote::kTriplet)
|
||||
if ((prevTriplets = (prevTriplets+1)%3)) {
|
||||
prevTripDur = p.fDuration;
|
||||
prevVisual = p.fVisual;
|
||||
|
@ -554,8 +554,7 @@ void VLSong::AddNote(VLLyricsNote note, size_t measure, VLFraction at)
|
|||
//
|
||||
// Sanity check on accidentals
|
||||
//
|
||||
note.fVisual = (note.fVisual & ~VLNote::kAccidentalsMask)
|
||||
| VLPitchAccidental(note.fPitch, note.fVisual, Properties(measure).fKey);
|
||||
note.fVisual = VLPitchAccidental(note.fPitch, note.fVisual, Properties(measure).fKey);
|
||||
//
|
||||
// Always keep an empty measure in reserve
|
||||
//
|
||||
|
@ -2080,6 +2079,9 @@ std::string VLSong::PrimaryGroove() const
|
|||
return bestGroove;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark class VLSongVisitor
|
||||
|
||||
//////////////////////// VLSongVisitor ////////////////////////////////
|
||||
|
||||
VLSongVisitor::~VLSongVisitor()
|
||||
|
|
|
@ -142,10 +142,12 @@ struct VLNote {
|
|||
|
||||
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(std::string name);
|
||||
std::string Name(uint16_t accidentals=0) const;
|
||||
|
|
|
@ -542,6 +542,8 @@ advanceAt:
|
|||
[filterTask release];
|
||||
|
||||
if ([error length]) {
|
||||
[contents writeToFile:@"/var/tmp/VocalEaselFilterInput" atomically:NO];
|
||||
[error writeToFile:@"/var/tmp/VocalEaselFilterError" atomically:NO];
|
||||
NSString * errStr = [[[NSString alloc] initWithData:error
|
||||
encoding:NSUTF8StringEncoding] autorelease];
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
const float kLineX = 5.0;
|
||||
const float kLineH = 10.0;
|
||||
const float kTripletH = 5.0;
|
||||
const float kTupletH = 5.0;
|
||||
#define kSystemBaseline ((fNumBotLedgers+1)*kLineH+fNumStanzas*kLyricsH)
|
||||
#define kSystemAscent ((fNumTopLedgers+7)*kLineH+kChordH)
|
||||
#define kSystemH (kSystemBaseline+kSystemAscent)
|
||||
|
|
|
@ -296,7 +296,7 @@
|
|||
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;
|
||||
if (!sTripletFont)
|
||||
|
@ -308,13 +308,15 @@
|
|||
|
||||
NSBezierPath * bz = [NSBezierPath bezierPath];
|
||||
|
||||
[bz moveToPoint: NSMakePoint(startX, y-kTripletH)];
|
||||
[bz moveToPoint: NSMakePoint(startX, y-kTupletH)];
|
||||
[bz lineToPoint: NSMakePoint(startX, y)];
|
||||
[bz lineToPoint: NSMakePoint(endX, y)];
|
||||
[bz lineToPoint: NSMakePoint(endX, y-kTripletH)];
|
||||
[bz lineToPoint: NSMakePoint(endX, y-kTupletH)];
|
||||
[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];
|
||||
}
|
||||
|
||||
|
@ -326,11 +328,6 @@
|
|||
const VLSystemLayout & kLayout = (*fLayout)[system];
|
||||
const CGFloat kSystemY = [self systemY:system];
|
||||
|
||||
float tripletStartX;
|
||||
float tripletEndX;
|
||||
CGFloat tripletY;
|
||||
bool hasTriplets = false;
|
||||
|
||||
for (int m = 0; m<kLayout.NumMeasures(); ++m) {
|
||||
VLVisualFilter filterVisuals(kProp.fKey);
|
||||
int measIdx = m+kFirstMeas;
|
||||
|
@ -340,6 +337,13 @@
|
|||
VLNoteList melody;
|
||||
measure.DecomposeNotes(kProp, melody);
|
||||
VLFraction at(0);
|
||||
float tupletStartX;
|
||||
float tupletEndX;
|
||||
CGFloat tupletY;
|
||||
int inTuplet = 0;
|
||||
uint16_t tuplet;
|
||||
VLFraction tupletDur;
|
||||
|
||||
for (VLNoteList::const_iterator note = melody.begin();
|
||||
note != melody.end();
|
||||
++note
|
||||
|
@ -369,25 +373,38 @@
|
|||
kSystemY+[self noteYInSection:measure.fPropIdx withPitch:65]);
|
||||
[self drawRest:note->fVisual & VLNote::kNoteHeadMask at: pos];
|
||||
}
|
||||
if (note->fVisual & VLNote::kTriplet) {
|
||||
tripletEndX = pos.x+kNoteW*0.5f;
|
||||
if (hasTriplets) {
|
||||
tripletY = std::max(tripletY, pos.y+kLineH);
|
||||
if (uint16_t newTuplet = note->fVisual & VLNote::kTupletMask) {
|
||||
tupletEndX = pos.x+kNoteW*0.5f;
|
||||
if (inTuplet && newTuplet == tuplet) {
|
||||
tupletY = std::max(tupletY, pos.y+kLineH);
|
||||
} else {
|
||||
tripletY = std::max(kSystemY+5.0f*kLineH, pos.y+kLineH);
|
||||
tripletStartX = pos.x-kNoteW*0.5f;
|
||||
hasTriplets = true;
|
||||
if (inTuplet)
|
||||
[self drawTuplet:tuplet bracketFrom:tupletStartX to:tupletEndX atY:tupletY];
|
||||
tupletY = std::max(kSystemY+5.0f*kLineH, pos.y+kLineH);
|
||||
tupletStartX = pos.x-kNoteW*0.5f;
|
||||
tuplet = newTuplet;
|
||||
tupletDur = 0;
|
||||
}
|
||||
} else if (hasTriplets) {
|
||||
[self drawTripletBracketFrom:tripletStartX to:tripletEndX atY:tripletY];
|
||||
hasTriplets = false;
|
||||
++inTuplet;
|
||||
tupletDur += note->fDuration / VLNote::TupletDenom(tuplet);
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (hasTriplets) {
|
||||
[self drawTripletBracketFrom:tripletStartX to:tripletEndX atY:tripletY];
|
||||
if (inTuplet)
|
||||
[self drawTuplet:tuplet bracketFrom:tupletStartX to:tupletEndX atY:tupletY];
|
||||
}
|
||||
if (fCursorRegion == kRegionNote && fLayout->SystemForMeasure(fCursorMeasure) == system)
|
||||
[self drawNoteCursor];
|
||||
|
|
Loading…
Reference in New Issue
Block a user