Properly handle accidentals

This commit is contained in:
Matthias Neeracher 2006-10-21 09:23:37 +00:00
parent 601f15cb98
commit 707bbf2f82
3 changed files with 272 additions and 61 deletions

View File

@ -13,7 +13,8 @@
@class VLDocument; @class VLDocument;
enum VLMusicElement { enum VLMusicElement {
kMusicGClef, kMusicNothing = 0,
kMusicGClef = 0,
kMusicFlat, kMusicFlat,
kMusicSharp, kMusicSharp,
kMusicNatural, kMusicNatural,
@ -30,7 +31,11 @@ enum VLMusicElement {
kMusicSixteenthFlag, kMusicSixteenthFlag,
kMusicThirtysecondthFlag, kMusicThirtysecondthFlag,
kMusicNoteCursor, kMusicNoteCursor,
kMusicFlatCursor,
kMusicSharpCursor,
kMusicNaturalCursor,
kMusicRestCursor, kMusicRestCursor,
kMusicKillCursor,
kMusicElements kMusicElements
}; };
@ -64,6 +69,8 @@ enum VLRecalc {
int fCursorMeasure; int fCursorMeasure;
VLFract fCursorAt; VLFract fCursorAt;
int fCursorPitch; int fCursorPitch;
int fCursorActualPitch;
VLMusicElement fCursorAccidental;
IBOutlet id fFieldEditor; IBOutlet id fFieldEditor;
} }
@ -77,9 +84,10 @@ enum VLRecalc {
- (VLSong *) song; - (VLSong *) song;
- (NSImage *) musicElement:(VLMusicElement)elt; - (NSImage *) musicElement:(VLMusicElement)elt;
- (int) stepWithPitch:(int)pitch;
- (float) systemY:(int)system; - (float) systemY:(int)system;
- (float) noteYWithPitch:(int)pitch; - (float) noteYWithPitch:(int)pitch accidental:(VLMusicElement*)accidental;
- (float) noteYInMeasure:(int)measure withPitch:(int)pitch; - (float) noteYInMeasure:(int)measure withPitch:(int)pitch accidental:(VLMusicElement*)accidental;
- (float) noteXInMeasure:(int)measure at:(VLFraction)at; - (float) noteXInMeasure:(int)measure at:(VLFraction)at;
- (void) scrollMeasureToVisible:(int)measure; - (void) scrollMeasureToVisible:(int)measure;

View File

@ -38,7 +38,11 @@ static NSString * sElementNames[kMusicElements] = {
@"sixteenth-flag", @"sixteenth-flag",
@"thirtysecondth-flag", @"thirtysecondth-flag",
@"notecursor", @"notecursor",
@"restcursor" @"flatcursor",
@"sharpcursor",
@"naturalcursor",
@"restcursor",
@"killcursor"
}; };
static float sSharpPos[] = { static float sSharpPos[] = {
@ -115,47 +119,65 @@ static float sFlatPos[] = {
return kSystemY+b.origin.y+b.size.height-(system+1)*kSystemH; return kSystemY+b.origin.y+b.size.height-(system+1)*kSystemH;
} }
- (float) noteYWithPitch:(int)pitch int8_t sSemi2Pitch[2][12] = {{
// C Db D Eb E F Gb G Ab A Bb B
0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6,
},{
// C C# D D# E F F# G G# A A# B
0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6,
}};
#define S kMusicSharp,
#define F kMusicFlat,
#define N kMusicNatural,
#define _ kMusicNothing,
VLMusicElement sSemi2Accidental[12][12] = {
// C DbD EbE F GbG AbA BbB
{N _ N _ N _ _ N _ N _ N}, // Gb major - 6 flats
{_ _ N _ N _ _ N _ N _ N}, // Db major - 5 flats
{_ _ N _ N _ F _ _ N _ N}, // Ab major - 4 flats
{_ F _ _ N _ F _ _ N _ N}, // Eb major - 3 flats
{_ F _ _ N _ F _ F _ _ N}, // Bb major - 2 flats
{_ F _ F _ _ F _ F _ _ N}, // F major - 1 flat
// C C#D D#E F F#G G#A A#B
{_ S _ S _ _ S _ S _ S _}, // C major
{_ S _ S _ N _ _ S _ S _}, // G major - 1 sharp
{N _ _ S _ N _ _ S _ S _}, // D major - 2 sharps
{N _ _ S _ N _ N _ _ S _}, // A major - 3 sharps
{N _ N _ _ N _ N _ _ S _}, // E major - 4 sharps
{N _ N _ _ N _ N _ N _ _}, // B major - 5 sharps
};
#undef S
#undef F
#undef N
#undef _
- (int) stepWithPitch:(int)pitch
{
int semi = pitch % 12;
int key = [self song]->fProperties.front().fKey;
bool useSharps = key >= 0;
return sSemi2Pitch[useSharps][semi];
}
- (float) noteYWithPitch:(int)pitch accidental:(VLMusicElement*)accidental
{ {
int semi = pitch % 12; int semi = pitch % 12;
int octave = (pitch / 12) - 5; int octave = (pitch / 12) - 5;
bool useSharps = [self song]->fProperties.front().fKey >= 0; int key = [self song]->fProperties.front().fKey;
float y = octave*3.5f*kLineH; *accidental = sSemi2Accidental[key+6][semi];
float sharp = useSharps ? 0.0f : 0.5f*kLineH;
switch (semi) { return (octave*3.5f+[self stepWithPitch:pitch]*0.5f-1.0f)*kLineH;
case 0: // C
return y-1.0f*kLineH;
case 1: // C# / Db
return y-1.0f*kLineH+sharp;
case 2: // D
return y-0.5f*kLineH;
case 3: // D# / Eb
return y-0.5f*kLineH+sharp;
case 4: // E
return y;
case 5: // F
return y+0.5f*kLineH;
case 6: // F# / Gb
return y+0.5f*kLineH+sharp;
case 7: // G
return y+1.0f*kLineH;
case 8: // G# / Ab
return y+1.0f*kLineH+sharp;
case 9: // A
return y+1.5f*kLineH;
case 10: // A# / Bb
return y+1.5f*kLineH+sharp;
case 11: // B
default:
return y+2.0f*kLineH;
}
} }
- (float) noteYInMeasure:(int)measure withPitch:(int)pitch - (float) noteYInMeasure:(int)measure withPitch:(int)pitch accidental:(VLMusicElement*)accidental
{ {
return [self systemY:measure/fMeasPerSystem]+[self noteYWithPitch:pitch]; return [self systemY:measure/fMeasPerSystem]
+ [self noteYWithPitch:pitch accidental:accidental];
} }
- (float) noteXInMeasure:(int)measure at:(VLFraction)at - (float) noteXInMeasure:(int)measure at:(VLFraction)at
@ -420,7 +442,7 @@ static float sFlatPos[] = {
} }
const float kSemiFloor = -3.0f*kLineH; const float kSemiFloor = -3.0f*kLineH;
static int sSemiToPitch[] = { static int8_t sSemiToPitch[] = {
53, // F 53, // F
55, 57, // A 55, 57, // A
59, 60, // Middle C 59, 60, // Middle C
@ -434,6 +456,97 @@ static int sSemiToPitch[] = {
86, 88 // E 86, 88 // E
}; };
static int8_t sFlatAcc[] = {
6, // Cb
11,
4, // Db
9,
2, // Eb
7, // Fb
12,
5, // Gb
10,
3, // Ab
8,
1, // Bb
};
static int8_t sSharpAcc[] = {
2, // C# is the 2nd sharp
9,
4, // D#
11,
6, // E#
1, // F#
8,
3, // G#
10,
5, // A#
12,
7, // B#
};
- (void) accidentalFromEvent:(NSEvent *)event
{
const VLProperties & prop = [self song]->fProperties.front();
fCursorAccidental = (VLMusicElement)0;
if (prop.fKey >= 0) {
if (prop.fKey >= sSharpAcc[fCursorPitch % 12]) { // Sharp in Key
switch ([event modifierFlags] & (NSAlternateKeyMask|NSCommandKeyMask)) {
case NSAlternateKeyMask:
fCursorAccidental = kMusicFlatCursor; // G# -> Gb
fCursorActualPitch = fCursorPitch-1;
break;
default:
case NSCommandKeyMask:
fCursorActualPitch = fCursorPitch+1;
break; // G# -> G#
case NSAlternateKeyMask|NSCommandKeyMask:
fCursorAccidental = kMusicNaturalCursor; // G# -> G
fCursorActualPitch = fCursorPitch;
break;
}
return;
}
} else {
if (prop.fKey <= -sFlatAcc[fCursorPitch % 12]) { // Flat in Key
switch ([event modifierFlags] & (NSAlternateKeyMask|NSCommandKeyMask)) {
default:
case NSAlternateKeyMask:
fCursorActualPitch = fCursorPitch-1;
break; // Gb -> Gb
case NSCommandKeyMask:
fCursorAccidental = kMusicSharpCursor; // Gb -> G#
fCursorActualPitch = fCursorPitch+1;
break;
case NSAlternateKeyMask|NSCommandKeyMask:
fCursorAccidental = kMusicNaturalCursor; // Gb -> G
fCursorActualPitch = fCursorPitch;
break;
}
return;
}
}
//
// Natural
//
switch ([event modifierFlags] & (NSAlternateKeyMask|NSCommandKeyMask)) {
case NSAlternateKeyMask:
fCursorAccidental = kMusicFlatCursor; // G -> Gb
fCursorActualPitch = fCursorPitch-1;
break;
case NSCommandKeyMask:
fCursorAccidental = kMusicSharpCursor; // G -> G#
fCursorActualPitch = fCursorPitch+1;
break;
default:
case NSAlternateKeyMask|NSCommandKeyMask:
fCursorActualPitch = fCursorPitch;
break; // G -> G
}
}
- (VLRegion) findRegionForEvent:(NSEvent *) event - (VLRegion) findRegionForEvent:(NSEvent *) event
{ {
fCursorPitch = VLNote::kNoPitch; fCursorPitch = VLNote::kNoPitch;
@ -479,6 +592,8 @@ static int sSemiToPitch[] = {
int semi = static_cast<int>(roundf(loc.y / (0.5f*kLineH))); int semi = static_cast<int>(roundf(loc.y / (0.5f*kLineH)));
fCursorPitch = sSemiToPitch[semi]; fCursorPitch = sSemiToPitch[semi];
[self accidentalFromEvent:event];
return fCursorRegion = kRegionNote; return fCursorRegion = kRegionNote;
} }
@ -494,6 +609,14 @@ static int sSemiToPitch[] = {
[self setNeedsDisplay:(hadCursor || hasCursor)]; [self setNeedsDisplay:(hadCursor || hasCursor)];
} }
- (void)flagsChanged:(NSEvent *)event
{
if (fCursorPitch != VLNote::kNoPitch) {
[self accidentalFromEvent:event];
[self setNeedsDisplay:YES];
}
}
- (void) mouseEntered:(NSEvent *)event - (void) mouseEntered:(NSEvent *)event
{ {
[[self window] setAcceptsMouseMovedEvents:YES]; [[self window] setAcceptsMouseMovedEvents:YES];

View File

@ -18,7 +18,7 @@
- (void) addNoteAtCursor - (void) addNoteAtCursor
{ {
if (fCursorMeasure > -1) { if (fCursorMeasure > -1) {
VLNote newNote(1, fClickMode==' ' ? fCursorPitch : VLNote::kNoPitch); VLNote newNote(1, fClickMode==' ' ? fCursorActualPitch : VLNote::kNoPitch);
if (fClickMode == 'k') if (fClickMode == 'k')
[self song]->DelNote(fCursorMeasure, fCursorAt); [self song]->DelNote(fCursorMeasure, fCursorAt);
@ -40,6 +40,7 @@
if (fCursorMeasure < 0) { if (fCursorMeasure < 0) {
fCursorMeasure = 0; fCursorMeasure = 0;
fCursorPitch = VLNote::kMiddleC; fCursorPitch = VLNote::kMiddleC;
fCursorActualPitch = fCursorPitch;
fCursorAt = VLFraction(0); fCursorAt = VLFraction(0);
} }
} }
@ -48,24 +49,61 @@
{ {
int cursorX; int cursorX;
int cursorY; int cursorY;
VLMusicElement accidental;
VLMusicElement cursorElt; VLMusicElement cursorElt;
cursorX = [self noteXInMeasure:fCursorMeasure at:fCursorAt]-kNoteX; cursorX = [self noteXInMeasure:fCursorMeasure at:fCursorAt]-kNoteX;
if (fClickMode == ' ') { switch (fClickMode) {
default:
cursorY = cursorY =
[self noteYInMeasure:fCursorMeasure withPitch:fCursorPitch]-kNoteY; [self noteYInMeasure:fCursorMeasure
withPitch:fCursorPitch
accidental:&accidental]
-kNoteY;
cursorElt = kMusicNoteCursor; cursorElt = kMusicNoteCursor;
} else { break;
cursorY = [self noteYInMeasure:fCursorMeasure withPitch:65]; case 'r':
cursorY = [self noteYInMeasure:fCursorMeasure
withPitch:65
accidental:&accidental];
cursorElt = kMusicRestCursor; cursorElt = kMusicRestCursor;
break;
case 'k':
cursorY = [self noteYInMeasure:fCursorMeasure
withPitch:fCursorPitch
accidental:&accidental];
cursorElt = kMusicKillCursor;
break;
} }
NSPoint at = NSMakePoint(cursorX, cursorY);
[[self musicElement:cursorElt] [[self musicElement:cursorElt]
compositeToPoint:NSMakePoint(cursorX, cursorY) compositeToPoint:at
operation: NSCompositeSourceOver]; operation: NSCompositeSourceOver];
if (fCursorAccidental) {
at.y += kNoteY;
switch (cursorElt= fCursorAccidental) {
case kMusicFlatCursor:
at.x += kFlatW;
at.y += kFlatY;
break;
case kMusicSharpCursor:
at.x += kSharpW;
at.y += kSharpY;
break;
default:
at.x += kNaturalW;
at.y += kNaturalY;
break;
}
[[self musicElement:cursorElt]
compositeToPoint:at
operation: NSCompositeSourceOver];
}
} }
- (void) drawNote:(VLFraction)dur at:(NSPoint)p tied:(BOOL)tied - (void) drawNote:(VLFraction)dur at:(NSPoint)p
accidental:(VLMusicElement)accidental tied:(BOOL)tied
{ {
NSPoint s = p; NSPoint s = p;
NSPoint c = p; NSPoint c = p;
@ -93,6 +131,29 @@
[head compositeToPoint:p [head compositeToPoint:p
operation: NSCompositePlusDarker]; operation: NSCompositePlusDarker];
// //
// Draw accidental
//
if (accidental) {
NSPoint at = p;
at.y += kNoteY;
switch (accidental) {
case kMusicSharp:
at.x += kSharpW;
at.y += kSharpY;
break;
case kMusicFlat:
at.x += kFlatW;
at.y += kFlatY;
break;
case kMusicNatural:
at.x += kNaturalW;
at.y += kNaturalY;
break;
}
[[self musicElement:accidental]
compositeToPoint:at operation: NSCompositeSourceOver];
}
//
// Draw stem // Draw stem
// //
// //
@ -192,6 +253,8 @@
float kSystemY = [self systemY:system]; float kSystemY = [self systemY:system];
for (int m = 0; m<fMeasPerSystem; ++m) { for (int m = 0; m<fMeasPerSystem; ++m) {
VLMusicElement accidentals[7];
memset(accidentals, 0, 7*sizeof(VLMusicElement));
int measIdx = m+system*fMeasPerSystem; int measIdx = m+system*fMeasPerSystem;
if (measIdx >= song->CountMeasures()) if (measIdx >= song->CountMeasures())
break; break;
@ -229,17 +292,34 @@
} else { } else {
noteDur = partialDur; noteDur = partialDur;
} }
if (pitch != VLNote::kNoPitch) if (pitch != VLNote::kNoPitch) {
[self drawNote:noteDur VLMusicElement accidental;
at: NSMakePoint( NSPoint pos =
[self noteXInMeasure:m at:at], NSMakePoint([self noteXInMeasure:m at:at],
kSystemY+[self noteYWithPitch:pitch]) kSystemY+[self noteYWithPitch:pitch
tied:!first]; accidental:&accidental]);
VLMusicElement acc = accidental;
int step= [self stepWithPitch:pitch];
if (acc == accidentals[step])
acc = kMusicNothing; // Don't repeat accidentals
else if (acc == kMusicNothing)
if (accidentals[step] == kMusicNatural) // Resume signature
acc = prop.fKey < 0 ? kMusicFlat : kMusicSharp;
else else
[self drawRest:noteDur acc = kMusicNatural;
at: NSMakePoint( [self drawNote:noteDur
[self noteXInMeasure:m at:at], at: pos
kSystemY+[self noteYWithPitch:65])]; accidental: acc
tied:!first];
accidentals[step] = accidental;
} else {
VLMusicElement accidental;
NSPoint pos =
NSMakePoint([self noteXInMeasure:m at:at],
kSystemY+[self noteYWithPitch:65
accidental:&accidental]);
[self drawRest:noteDur at: pos];
}
dur -= partialDur; dur -= partialDur;
at += partialDur; at += partialDur;
first = NO; first = NO;