mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 11:14: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
|
&& 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);
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -132,20 +132,22 @@ struct VLNote {
|
||||||
|
|
||||||
kWantSharp = 0x10,
|
kWantSharp = 0x10,
|
||||||
kWant2Sharp = 0x20,
|
kWant2Sharp = 0x20,
|
||||||
kPreferSharps = 0x30, // kWantSharp | kWant2Sharp
|
kPreferSharps = 0x30, // kWantSharp | kWant2Sharp
|
||||||
kWantFlat = 0x40,
|
kWantFlat = 0x40,
|
||||||
kWant2Flat = 0x80,
|
kWant2Flat = 0x80,
|
||||||
kPreferFlats = 0xC0, // kWantFlat | kWant2Flat
|
kPreferFlats = 0xC0, // kWantFlat | kWant2Flat
|
||||||
kWantNatural = 0x50, // kWantSharp | kWantFlat
|
kWantNatural = 0x50, // kWantSharp | kWantFlat
|
||||||
kNaturalOrSharp = 0x70, // kPreferSharps| kWantFlat
|
kNaturalOrSharp = 0x70, // kPreferSharps| kWantFlat
|
||||||
kNaturalOrFlat = 0xD0, // kPreferFlats | kWantSharp
|
kNaturalOrFlat = 0xD0, // kPreferFlats | kWantSharp
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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];
|
||||||
|
|
Loading…
Reference in New Issue
Block a user