mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 03:04:00 +00:00
Successfully round trip double accidentals
This commit is contained in:
parent
a2b1a1e991
commit
ffdc41e1d7
|
@ -348,6 +348,10 @@ class MusicXMLListener
|
||||||
@note['visual'] = VL::WantSharp
|
@note['visual'] = VL::WantSharp
|
||||||
when 'flat'
|
when 'flat'
|
||||||
@note['visual'] = VL::WantFlat
|
@note['visual'] = VL::WantFlat
|
||||||
|
when 'double-sharp'
|
||||||
|
@note['visual'] = VL::Want2Sharp
|
||||||
|
when 'flat-flat'
|
||||||
|
@note['visual'] = VL::Want2Flat
|
||||||
end
|
end
|
||||||
when 'root-step'
|
when 'root-step'
|
||||||
@note['pitch'] = PITCH[@text]
|
@note['pitch'] = PITCH[@text]
|
||||||
|
|
|
@ -9,7 +9,6 @@ require 'rexml/document'
|
||||||
|
|
||||||
INPUT = readPlist(INFILE)
|
INPUT = readPlist(INFILE)
|
||||||
|
|
||||||
$USE_FLATS = false
|
|
||||||
$DIVISIONS = 3
|
$DIVISIONS = 3
|
||||||
|
|
||||||
def newTextElement(name, text)
|
def newTextElement(name, text)
|
||||||
|
@ -59,7 +58,6 @@ def _part_list
|
||||||
end
|
end
|
||||||
|
|
||||||
def _attributes(prop)
|
def _attributes(prop)
|
||||||
$USE_FLATS = prop['key'] <= 0
|
|
||||||
$DIVISIONS = prop['divisions']
|
$DIVISIONS = prop['divisions']
|
||||||
attr = REXML::Element.new('attributes')
|
attr = REXML::Element.new('attributes')
|
||||||
attr.add_element newTextElement('divisions', prop['divisions'])
|
attr.add_element newTextElement('divisions', prop['divisions'])
|
||||||
|
@ -100,28 +98,23 @@ def _tempo(tempo)
|
||||||
return dir
|
return dir
|
||||||
end
|
end
|
||||||
|
|
||||||
STEPS = 'C DbD EbE F GbG AbA BbB '
|
STEPS = ' BC D EF G A B C '
|
||||||
|
|
||||||
def _pitch(name, pitch, preferFlats, prefix="")
|
def _pitch(name, pitch, accidental, prefix="")
|
||||||
oct = pitch/12 - 1
|
pitch-= accidental
|
||||||
stp = 2*(pitch%12)
|
oct = pitch/12 - 1
|
||||||
step = STEPS[stp]
|
stp = (pitch%12)+2
|
||||||
alt = STEPS[stp+1] == ?b
|
step = STEPS[stp]
|
||||||
if alt
|
if step == 0x20
|
||||||
if preferFlats
|
abort "Pitch #{pitch} not representable"
|
||||||
alt = -1
|
|
||||||
else
|
|
||||||
step = step == ?A ? ?G : step-1
|
|
||||||
alt = 1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
if prefix.length > 0
|
if prefix.length > 0
|
||||||
prefix += "-"
|
prefix += "-"
|
||||||
end
|
end
|
||||||
pitch= REXML::Element.new(name)
|
pitch= REXML::Element.new(name)
|
||||||
pitch.add_element newTextElement(prefix+'step', step.chr)
|
pitch.add_element newTextElement(prefix+'step', step.chr)
|
||||||
if alt
|
if accidental != 0
|
||||||
pitch.add_element newTextElement(prefix+'alter', alt)
|
pitch.add_element newTextElement(prefix+'alter', accidental)
|
||||||
end
|
end
|
||||||
if prefix.length == 0
|
if prefix.length == 0
|
||||||
pitch.add_element newTextElement('octave', oct)
|
pitch.add_element newTextElement('octave', oct)
|
||||||
|
@ -130,28 +123,28 @@ def _pitch(name, pitch, preferFlats, prefix="")
|
||||||
return pitch
|
return pitch
|
||||||
end
|
end
|
||||||
|
|
||||||
def _addAccidental(note, pitch, preferFlats)
|
|
||||||
stp = 2*(pitch%12)
|
|
||||||
alt = STEPS[stp+1] == ?b
|
|
||||||
if alt
|
|
||||||
note.add_element newTextElement('accidental',
|
|
||||||
preferFlats ? 'flat' : 'sharp')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
TYPE = %w[whole half quarter eighth 16th 32nd]
|
TYPE = %w[whole half quarter eighth 16th 32nd]
|
||||||
|
ACC = %w[flat-flat flat natural sharp double-sharp]
|
||||||
|
|
||||||
def _note(pitch, dur, visual, tied)
|
def _note(pitch, dur, visual, tied)
|
||||||
preferFlats = (visual & VL::WantSharp) == 0 &&
|
accidental = nil
|
||||||
((visual & VL::WantFlat) != 0 || $USE_FLATS)
|
case visual & VL::Accidentals
|
||||||
|
when VL::Want2Flat
|
||||||
|
accidental = -2
|
||||||
|
when VL::WantFlat
|
||||||
|
accidental = -1
|
||||||
|
when VL::WantNatural
|
||||||
|
accidental = 0
|
||||||
|
when VL::WantSharp
|
||||||
|
accidental = 1
|
||||||
|
when VL::Want2Sharp
|
||||||
|
accidental = 2
|
||||||
|
end
|
||||||
note = REXML::Element.new('note')
|
note = REXML::Element.new('note')
|
||||||
if pitch == VL::NoPitch
|
if pitch == VL::NoPitch
|
||||||
note.add_element(REXML::Element.new('rest'))
|
note.add_element(REXML::Element.new('rest'))
|
||||||
else
|
else
|
||||||
if (tied & VL::InChord) != 0
|
note.add_element(_pitch('pitch', pitch, accidental))
|
||||||
note.add_element 'chord'
|
|
||||||
end
|
|
||||||
note.add_element(_pitch('pitch', pitch, preferFlats))
|
|
||||||
end
|
end
|
||||||
note.add_element newTextElement('duration', dur)
|
note.add_element newTextElement('duration', dur)
|
||||||
if (tied & VL::TiedWithPrev) != 0
|
if (tied & VL::TiedWithPrev) != 0
|
||||||
|
@ -162,8 +155,8 @@ def _note(pitch, dur, visual, tied)
|
||||||
end
|
end
|
||||||
note.add_element newTextElement('voice', 1)
|
note.add_element newTextElement('voice', 1)
|
||||||
note.add_element newTextElement('type', TYPE[visual & 7])
|
note.add_element newTextElement('type', TYPE[visual & 7])
|
||||||
if (visual & (VL::WantSharp | VL::WantFlat)) != 0
|
if accidental
|
||||||
_addAccidental(note, pitch, preferFlats)
|
note.add_element newTextElement('accidental', ACC[accidental+2])
|
||||||
end
|
end
|
||||||
|
|
||||||
return note
|
return note
|
||||||
|
|
|
@ -7,9 +7,12 @@ class VL
|
||||||
|
|
||||||
TiedWithNext = 1
|
TiedWithNext = 1
|
||||||
TiedWithPrev = 2
|
TiedWithPrev = 2
|
||||||
WantSharp = 0x20
|
WantSharp = 0x10
|
||||||
|
Want2Sharp = 0x20
|
||||||
WantFlat = 0x40
|
WantFlat = 0x40
|
||||||
InChord = 4
|
Want2Flat = 0x80
|
||||||
|
WantNatural = 0x50
|
||||||
|
Accidentals = 0xF0
|
||||||
|
|
||||||
Unison = 1<<0
|
Unison = 1<<0
|
||||||
Min2nd = 1<<1
|
Min2nd = 1<<1
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "VLModel.h"
|
#include "VLModel.h"
|
||||||
#include "VLPitchName.h"
|
#include "VLPitchName.h"
|
||||||
|
#include "VLPitchGrid.h"
|
||||||
|
|
||||||
#pragma mark class VLFraction
|
#pragma mark class VLFraction
|
||||||
|
|
||||||
|
@ -548,6 +549,11 @@ uint8_t & LastTie(VLMeasure & measure)
|
||||||
//
|
//
|
||||||
void VLSong::AddNote(VLLyricsNote note, size_t measure, VLFraction at)
|
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);
|
||||||
//
|
//
|
||||||
// Always keep an empty measure in reserve
|
// Always keep an empty measure in reserve
|
||||||
//
|
//
|
||||||
|
@ -771,9 +777,17 @@ static void TransposePinned(int8_t & pitch, int semi)
|
||||||
pitch = octave+pitchInOctave;
|
pitch = octave+pitchInOctave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void FlipAccidentals(uint16_t & visual)
|
||||||
|
{
|
||||||
|
visual = (visual & ~VLNote::kAccidentalsMask)
|
||||||
|
| ((visual & VLNote::kPreferSharps) << 2)
|
||||||
|
| ((visual & VLNote::kPreferFlats) << 2);
|
||||||
|
}
|
||||||
|
|
||||||
void VLSong::ChangeKey(int section, int newKey, int newMode, bool transpose)
|
void VLSong::ChangeKey(int section, int newKey, int newMode, bool transpose)
|
||||||
{
|
{
|
||||||
VLProperties & prop = fProperties[section];
|
VLProperties & prop = fProperties[section];
|
||||||
|
bool flipAcc= newKey*prop.fKey < 0;
|
||||||
int semi = 7*(newKey-prop.fKey) % 12;
|
int semi = 7*(newKey-prop.fKey) % 12;
|
||||||
prop.fKey = newKey;
|
prop.fKey = newKey;
|
||||||
prop.fMode = newMode;
|
prop.fMode = newMode;
|
||||||
|
@ -790,6 +804,10 @@ void VLSong::ChangeKey(int section, int newKey, int newMode, bool transpose)
|
||||||
for (; i!=e; ++i) {
|
for (; i!=e; ++i) {
|
||||||
TransposePinned(i->fPitch, semi);
|
TransposePinned(i->fPitch, semi);
|
||||||
TransposePinned(i->fRootPitch, semi);
|
TransposePinned(i->fRootPitch, semi);
|
||||||
|
if (flipAcc) {
|
||||||
|
FlipAccidentals(i->fVisual);
|
||||||
|
FlipAccidentals(i->fRootAccidental);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int pass=0; pass<2 && semi;) {
|
for (int pass=0; pass<2 && semi;) {
|
||||||
|
@ -806,6 +824,8 @@ void VLSong::ChangeKey(int section, int newKey, int newMode, bool transpose)
|
||||||
if (i->fPitch == VLNote::kNoPitch)
|
if (i->fPitch == VLNote::kNoPitch)
|
||||||
continue;
|
continue;
|
||||||
i->fPitch += semi;
|
i->fPitch += semi;
|
||||||
|
if (flipAcc)
|
||||||
|
FlipAccidentals(i->fVisual);
|
||||||
low = std::min(low, i->fPitch);
|
low = std::min(low, i->fPitch);
|
||||||
high = std::max(high, i->fPitch);
|
high = std::max(high, i->fPitch);
|
||||||
}
|
}
|
||||||
|
@ -816,6 +836,7 @@ void VLSong::ChangeKey(int section, int newKey, int newMode, bool transpose)
|
||||||
semi = -12; // Transpose an Octave down
|
semi = -12; // Transpose an Octave down
|
||||||
else
|
else
|
||||||
break; // Looks like we're done
|
break; // Looks like we're done
|
||||||
|
flipAcc = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#import "VLPListDocument.h"
|
#import "VLPListDocument.h"
|
||||||
#import "VLModel.h"
|
#import "VLModel.h"
|
||||||
|
#import "VLPitchGrid.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
// To convert from and to complex file formats, we use ruby scripts operating
|
// To convert from and to complex file formats, we use ruby scripts operating
|
||||||
|
@ -40,6 +41,7 @@ protected:
|
||||||
NSMutableArray * fChords;
|
NSMutableArray * fChords;
|
||||||
bool fPerfOrder;
|
bool fPerfOrder;
|
||||||
const VLSong * fSong;
|
const VLSong * fSong;
|
||||||
|
VLVisualFilter fVisFilter;
|
||||||
};
|
};
|
||||||
|
|
||||||
NSArray * VLPlistVisitor::EncodeProperties(const std::vector<VLProperties> & properties)
|
NSArray * VLPlistVisitor::EncodeProperties(const std::vector<VLProperties> & properties)
|
||||||
|
@ -79,7 +81,8 @@ void VLPlistVisitor::VisitMeasure(size_t m, VLProperties & p, VLMeasure & meas)
|
||||||
{
|
{
|
||||||
fNotes = [NSMutableArray arrayWithCapacity:1];
|
fNotes = [NSMutableArray arrayWithCapacity:1];
|
||||||
fChords= [NSMutableArray arrayWithCapacity:1];
|
fChords= [NSMutableArray arrayWithCapacity:1];
|
||||||
|
|
||||||
|
fVisFilter.ResetWithKey(p.fKey);
|
||||||
VisitNotes(meas, p, true);
|
VisitNotes(meas, p, true);
|
||||||
VisitChords(meas);
|
VisitChords(meas);
|
||||||
|
|
||||||
|
@ -137,14 +140,15 @@ void VLPlistVisitor::VisitNote(VLLyricsNote & n)
|
||||||
[NSNumber numberWithInt:n.fLyrics[i].fKind], @"kind",
|
[NSNumber numberWithInt:n.fLyrics[i].fKind], @"kind",
|
||||||
nil]
|
nil]
|
||||||
: [NSDictionary dictionary]];
|
: [NSDictionary dictionary]];
|
||||||
|
|
||||||
|
int grid = VLPitchToGrid(n.fPitch, n.fVisual, 0);
|
||||||
NSDictionary * nd =
|
NSDictionary * nd =
|
||||||
[NSDictionary dictionaryWithObjectsAndKeys:
|
[NSDictionary dictionaryWithObjectsAndKeys:
|
||||||
[NSNumber numberWithInt:n.fDuration.fNum], @"durNum",
|
[NSNumber numberWithInt:n.fDuration.fNum], @"durNum",
|
||||||
[NSNumber numberWithInt:n.fDuration.fDenom], @"durDenom",
|
[NSNumber numberWithInt:n.fDuration.fDenom], @"durDenom",
|
||||||
[NSNumber numberWithInt:n.fPitch], @"pitch",
|
[NSNumber numberWithInt:n.fPitch], @"pitch",
|
||||||
[NSNumber numberWithInt:n.fTied], @"tied",
|
[NSNumber numberWithInt:n.fTied], @"tied",
|
||||||
[NSNumber numberWithInt:n.fVisual], @"visual",
|
[NSNumber numberWithInt:fVisFilter(grid, n.fVisual)], @"visual",
|
||||||
ly, @"lyrics",
|
ly, @"lyrics",
|
||||||
nil];
|
nil];
|
||||||
[fNotes addObject:nd];
|
[fNotes addObject:nd];
|
||||||
|
|
|
@ -80,10 +80,9 @@ static inline int8_t StepToSemi(int step)
|
||||||
return sSemi[step];
|
return sSemi[step];
|
||||||
}
|
}
|
||||||
|
|
||||||
int VLPitchToGrid(int8_t pitch, uint16_t & visual, int key)
|
uint16_t VLPitchAccidental(int8_t pitch, uint16_t visual, int key)
|
||||||
{
|
{
|
||||||
int semi = pitch % 12;
|
int semi = pitch % 12;
|
||||||
int octave = (pitch/12)-5;
|
|
||||||
|
|
||||||
if ((visual &= VLNote::kAccidentalsMask)) {
|
if ((visual &= VLNote::kAccidentalsMask)) {
|
||||||
//
|
//
|
||||||
|
@ -91,62 +90,73 @@ int VLPitchToGrid(int8_t pitch, uint16_t & visual, int key)
|
||||||
//
|
//
|
||||||
switch (visual) {
|
switch (visual) {
|
||||||
case VLNote::kWantNatural:
|
case VLNote::kWantNatural:
|
||||||
if (!IsBasicNote(semi))
|
if (IsBasicNote(semi))
|
||||||
break;
|
return visual;
|
||||||
visual = 0; // Don't draw naturals unless needed
|
break;
|
||||||
goto computePosition;
|
|
||||||
case VLNote::kWant2Flat:
|
case VLNote::kWant2Flat:
|
||||||
if (IsBasicNote(semi+2)) {
|
if (IsBasicNote(semi+2))
|
||||||
semi += 2;
|
return visual;
|
||||||
goto computePosition;
|
else
|
||||||
}
|
return VLNote::kWantFlat;
|
||||||
visual = VLNote::kWantFlat;
|
|
||||||
goto flatIsPossible;
|
|
||||||
case VLNote::kWantFlat:
|
case VLNote::kWantFlat:
|
||||||
if (!IsBasicNote(semi+1))
|
if (IsBasicNote(semi+1))
|
||||||
break;
|
return visual;
|
||||||
flatIsPossible:
|
break;
|
||||||
semi += 1;
|
|
||||||
if (key < 0 && HasFlat(semi, key))
|
|
||||||
visual = 0;
|
|
||||||
goto computePosition;
|
|
||||||
case VLNote::kWant2Sharp:
|
case VLNote::kWant2Sharp:
|
||||||
if (IsBasicNote(semi-2)) {
|
if (IsBasicNote(semi-2))
|
||||||
semi -= 2;
|
return visual;
|
||||||
goto computePosition;
|
else
|
||||||
}
|
return VLNote::kWantSharp;
|
||||||
visual = VLNote::kWantSharp;
|
|
||||||
goto sharpIsPossible;
|
|
||||||
case VLNote::kWantSharp:
|
case VLNote::kWantSharp:
|
||||||
if (!IsBasicNote(semi-1))
|
if (IsBasicNote(semi-1))
|
||||||
break;
|
return visual;
|
||||||
sharpIsPossible:
|
break;
|
||||||
semi -= 1;
|
default:
|
||||||
if (key > 0 && HasSharp(semi, key))
|
break;
|
||||||
visual = 0;
|
|
||||||
goto computePosition;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// No visuals, or no match
|
// No visuals, or no match
|
||||||
//
|
//
|
||||||
visual = 0;
|
|
||||||
if (IsBasicNote(semi)) {
|
if (IsBasicNote(semi)) {
|
||||||
if (key < 0 ? HasFlat(semi, key) : key > 0 && HasSharp(semi, key))
|
return VLNote::kWantNatural;
|
||||||
visual = VLNote::kWantNatural;
|
} else if (key <= 0) {
|
||||||
} else if (key < 0) {
|
return VLNote::kWantFlat;
|
||||||
semi += 1;
|
|
||||||
if (!HasFlat(semi, key))
|
|
||||||
visual = VLNote::kWantFlat;
|
|
||||||
} else if (key > 0) {
|
|
||||||
semi -= 1;
|
|
||||||
if (!HasSharp(semi, key))
|
|
||||||
visual = VLNote::kWantSharp;
|
|
||||||
} else {
|
} else {
|
||||||
semi += 1;
|
return VLNote::kWantSharp;
|
||||||
visual = VLNote::kWantFlat;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int VLPitchToGrid(int8_t pitch, uint16_t visual, int key)
|
||||||
|
{
|
||||||
|
int semi = pitch % 12;
|
||||||
|
int octave = (pitch/12)-5;
|
||||||
|
|
||||||
|
switch (visual & VLNote::kAccidentalsMask) {
|
||||||
|
case VLNote::kWantNatural:
|
||||||
|
break;
|
||||||
|
case VLNote::kWant2Flat:
|
||||||
|
semi += 2;
|
||||||
|
break;
|
||||||
|
case VLNote::kWantFlat:
|
||||||
|
semi += 1;
|
||||||
|
break;
|
||||||
|
case VLNote::kWant2Sharp:
|
||||||
|
semi -= 2;
|
||||||
|
break;
|
||||||
|
case VLNote::kWantSharp:
|
||||||
|
semi -= 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (IsBasicNote(semi)) {
|
||||||
|
;
|
||||||
|
} else if (key <= 0) {
|
||||||
|
semi += 1;
|
||||||
|
} else {
|
||||||
|
semi -= 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
computePosition:
|
|
||||||
return SemiToStep(semi)+7*octave;
|
return SemiToStep(semi)+7*octave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,56 +202,52 @@ int8_t VLGridToPitch(int gridPos, uint16_t visual, int key)
|
||||||
return octave+semi+accidental;
|
return octave+semi+accidental;
|
||||||
}
|
}
|
||||||
|
|
||||||
VLVisualFilter::VLVisualFilter(int key)
|
void VLVisualFilter::ResetWithKey(int key)
|
||||||
{
|
{
|
||||||
memset(&fKeyState[0], 0, 7*sizeof(fKeyState[0]));
|
memset(&fState[0], 0, 7*sizeof(fState[0]));
|
||||||
switch (key) { // Almost every state falls through
|
switch (key) { // Almost every state falls through
|
||||||
case -6:
|
case -6:
|
||||||
fKeyState[0] = VLNote::kWantFlat;
|
fState[0] = VLNote::kWantFlat;
|
||||||
case -5:
|
case -5:
|
||||||
fKeyState[5] = VLNote::kWantFlat;
|
fState[5] = VLNote::kWantFlat;
|
||||||
case -4:
|
case -4:
|
||||||
fKeyState[1] = VLNote::kWantFlat;
|
fState[1] = VLNote::kWantFlat;
|
||||||
case -3:
|
case -3:
|
||||||
fKeyState[4] = VLNote::kWantFlat;
|
fState[4] = VLNote::kWantFlat;
|
||||||
case -2:
|
case -2:
|
||||||
fKeyState[2] = VLNote::kWantFlat;
|
fState[2] = VLNote::kWantFlat;
|
||||||
case -1:
|
case -1:
|
||||||
fKeyState[6] = VLNote::kWantFlat;
|
fState[6] = VLNote::kWantFlat;
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
fKeyState[2] = VLNote::kWantSharp;
|
fState[2] = VLNote::kWantSharp;
|
||||||
case 5:
|
case 5:
|
||||||
fKeyState[5] = VLNote::kWantSharp;
|
fState[5] = VLNote::kWantSharp;
|
||||||
case 4:
|
case 4:
|
||||||
fKeyState[1] = VLNote::kWantSharp;
|
fState[1] = VLNote::kWantSharp;
|
||||||
case 3:
|
case 3:
|
||||||
fKeyState[4] = VLNote::kWantSharp;
|
fState[4] = VLNote::kWantSharp;
|
||||||
case 2:
|
case 2:
|
||||||
fKeyState[0] = VLNote::kWantSharp;
|
fState[0] = VLNote::kWantSharp;
|
||||||
case 1:
|
case 1:
|
||||||
fKeyState[3] = VLNote::kWantSharp;
|
fState[3] = VLNote::kWantSharp;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
memcpy(fState, fKeyState, 7*sizeof(fKeyState[0]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t VLVisualFilter::operator()(int gridPos, uint16_t visual)
|
uint16_t VLVisualFilter::operator()(int gridPos, uint16_t visual)
|
||||||
{
|
{
|
||||||
gridPos %= 12;
|
gridPos = (gridPos+60) % 12;
|
||||||
if (!visual)
|
if (visual == VLNote::kWantNatural)
|
||||||
visual = fKeyState[gridPos];
|
|
||||||
if (visual != fState[gridPos])
|
|
||||||
if (!fState[gridPos] && visual == VLNote::kWantNatural) {
|
|
||||||
visual = 0;
|
|
||||||
} else {
|
|
||||||
if (!visual)
|
|
||||||
visual = VLNote::kWantNatural;
|
|
||||||
fState[gridPos] = visual==VLNote::kWantNatural ? 0 : visual;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
visual = 0;
|
visual = 0;
|
||||||
|
if (visual != fState[gridPos]) {
|
||||||
|
fState[gridPos] = visual;
|
||||||
|
if (!visual)
|
||||||
|
visual = VLNote::kWantNatural;
|
||||||
|
} else {
|
||||||
|
visual = 0;
|
||||||
|
}
|
||||||
return visual;
|
return visual;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,19 @@ uint16_t VLVisualInKey(int8_t pitch, int key);
|
||||||
//
|
//
|
||||||
// Grid position is defined from middle C
|
// Grid position is defined from middle C
|
||||||
//
|
//
|
||||||
int VLPitchToGrid(int8_t pitch, uint16_t & visual, int key);
|
uint16_t VLPitchAccidental(int8_t pitch, uint16_t visual, int key);
|
||||||
int8_t VLGridToPitch(int gridPos, uint16_t visual, int key);
|
int VLPitchToGrid(int8_t pitch, uint16_t visual, int key);
|
||||||
|
int8_t VLGridToPitch(int gridPos, uint16_t visual, int key);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Avoid repeating accidentals
|
// Avoid repeating accidentals
|
||||||
//
|
//
|
||||||
class VLVisualFilter {
|
class VLVisualFilter {
|
||||||
public:
|
public:
|
||||||
VLVisualFilter(int key);
|
VLVisualFilter(int key=0) { ResetWithKey(key); }
|
||||||
|
|
||||||
|
void ResetWithKey(int key);
|
||||||
uint16_t operator()(int gridPos, uint16_t visual);
|
uint16_t operator()(int gridPos, uint16_t visual);
|
||||||
private:
|
private:
|
||||||
uint16_t fState[7];
|
uint16_t fState[7];
|
||||||
uint16_t fKeyState[7];
|
|
||||||
};
|
};
|
|
@ -353,7 +353,7 @@
|
||||||
visual:note->fVisual
|
visual:note->fVisual
|
||||||
at:NSMakePoint([self noteXInMeasure:measIdx at:at],
|
at:NSMakePoint([self noteXInMeasure:measIdx at:at],
|
||||||
kSystemY)];
|
kSystemY)];
|
||||||
uint16_t visual = note->fVisual;
|
uint16_t visual = VLPitchAccidental(note->fPitch, note->fVisual, kProp.fKey);
|
||||||
pos =
|
pos =
|
||||||
NSMakePoint([self noteXInMeasure:measIdx at:at],
|
NSMakePoint([self noteXInMeasure:measIdx at:at],
|
||||||
kSystemY+[self noteYInSection:measure.fPropIdx
|
kSystemY+[self noteYInSection:measure.fPropIdx
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TestPitchToGrid(grid,visualOut,pitch,visualIn,key) \
|
#define TestPitchToGrid(grid,visualOut,pitch,visualIn,key) \
|
||||||
do { uint16_t vis = visualIn; \
|
do { uint16_t vis = VLPitchAccidental(pitch, visualIn, key); \
|
||||||
STAssertEquals(grid, VLPitchToGrid(pitch,vis,key),\
|
STAssertEquals(grid, VLPitchToGrid(pitch,vis,key),\
|
||||||
@"VLPitchToGrid(%d,%02x,%d)", pitch, visualIn, key);\
|
@"VLPitchToGrid(%d,%02x,%d)", pitch, visualIn, key);\
|
||||||
STAssertEquals((uint16_t)visualOut, vis,\
|
STAssertEquals((uint16_t)visualOut, vis,\
|
||||||
|
@ -32,47 +32,47 @@
|
||||||
|
|
||||||
- (void)testPitchToGrid
|
- (void)testPitchToGrid
|
||||||
{
|
{
|
||||||
TestPitchToGrid( 0, 0, 60, 0, 0); // Middle C, C Major
|
TestPitchToGrid( 0, VLNote::kWantNatural, 60, 0, 0); // Middle C, C Major
|
||||||
TestPitchToGrid( 0, VLNote::kWantNatural, 60, 0, 2); // Middle C, D Major
|
TestPitchToGrid( 0, VLNote::kWantNatural, 60, 0, 2); // Middle C, D Major
|
||||||
TestPitchToGrid( 0, 0, 60, 0, -5); // Middle C, Db Major
|
TestPitchToGrid( 0, VLNote::kWantNatural, 60, 0, -5); // Middle C, Db Major
|
||||||
TestPitchToGrid( 0, VLNote::kWantNatural, 60, 0, -6); // Middle C, Gb Major
|
TestPitchToGrid( 0, VLNote::kWantNatural, 60, 0, -6); // Middle C, Gb Major
|
||||||
TestPitchToGrid( 0, 0, 60, VLNote::kWantNatural, 0);
|
TestPitchToGrid( 0, VLNote::kWantNatural, 60, VLNote::kWantNatural, 0);
|
||||||
TestPitchToGrid( -1, VLNote::kWantSharp, 60, VLNote::kWantSharp, 0);
|
TestPitchToGrid( -1, VLNote::kWantSharp, 60, VLNote::kWantSharp, 0);
|
||||||
TestPitchToGrid( 0, 0, 60, VLNote::kWantFlat, 0);
|
TestPitchToGrid( 0, VLNote::kWantNatural, 60, VLNote::kWantFlat, 0);
|
||||||
TestPitchToGrid( 0, VLNote::kWantNatural, 60, VLNote::kWantFlat, 2);
|
TestPitchToGrid( 0, VLNote::kWantNatural, 60, VLNote::kWantFlat, 2);
|
||||||
TestPitchToGrid( -1, VLNote::kWantSharp, 60, VLNote::kWant2Sharp, 0);
|
TestPitchToGrid( -1, VLNote::kWantSharp, 60, VLNote::kWant2Sharp, 0);
|
||||||
TestPitchToGrid( 1, VLNote::kWant2Flat, 60, VLNote::kWant2Flat, 0);
|
TestPitchToGrid( 1, VLNote::kWant2Flat, 60, VLNote::kWant2Flat, 0);
|
||||||
|
|
||||||
TestPitchToGrid( 1, VLNote::kWantFlat, 61, 0, 0); // D flat, C Major
|
TestPitchToGrid( 1, VLNote::kWantFlat, 61, 0, 0); // D flat, C Major
|
||||||
TestPitchToGrid( 1, VLNote::kWantFlat, 61, 0, -1); // D flat, F Major
|
TestPitchToGrid( 1, VLNote::kWantFlat, 61, 0, -1); // D flat, F Major
|
||||||
TestPitchToGrid( 1, 0, 61, 0, -4); // D flat, Ab Major
|
TestPitchToGrid( 1, VLNote::kWantFlat, 61, 0, -4); // D flat, Ab Major
|
||||||
TestPitchToGrid( 0, VLNote::kWantSharp, 61, 0, 1); // D flat, G Major
|
TestPitchToGrid( 0, VLNote::kWantSharp, 61, 0, 1); // D flat, G Major
|
||||||
TestPitchToGrid( 0, 0, 61, 0, 2); // D flat, D Major
|
TestPitchToGrid( 0, VLNote::kWantSharp, 61, 0, 2); // D flat, D Major
|
||||||
TestPitchToGrid( 1, VLNote::kWantFlat, 61, VLNote::kWantNatural, 0);
|
TestPitchToGrid( 1, VLNote::kWantFlat, 61, VLNote::kWantNatural, 0);
|
||||||
TestPitchToGrid( 0, VLNote::kWantSharp, 61, VLNote::kWantSharp, 0);
|
TestPitchToGrid( 0, VLNote::kWantSharp, 61, VLNote::kWantSharp, 0);
|
||||||
TestPitchToGrid( 0, 0, 61, VLNote::kWantSharp, 2);
|
TestPitchToGrid( 0, VLNote::kWantSharp, 61, VLNote::kWantSharp, 2);
|
||||||
TestPitchToGrid( 1, VLNote::kWantFlat, 61, VLNote::kWantFlat, 0);
|
TestPitchToGrid( 1, VLNote::kWantFlat, 61, VLNote::kWantFlat, 0);
|
||||||
TestPitchToGrid( 1, 0, 61, VLNote::kWantFlat, -4);
|
TestPitchToGrid( 1, VLNote::kWantFlat, 61, VLNote::kWantFlat, -4);
|
||||||
TestPitchToGrid( -1, VLNote::kWant2Sharp, 61, VLNote::kWant2Sharp, 0);
|
TestPitchToGrid( -1, VLNote::kWant2Sharp, 61, VLNote::kWant2Sharp, 0);
|
||||||
TestPitchToGrid( 1, VLNote::kWantFlat, 61, VLNote::kWant2Flat, 0);
|
TestPitchToGrid( 1, VLNote::kWantFlat, 61, VLNote::kWant2Flat, 0);
|
||||||
|
|
||||||
TestPitchToGrid( 1, 0, 62, 0, 0); // D, C Major
|
TestPitchToGrid( 1, VLNote::kWantNatural, 62, 0, 0); // D, C Major
|
||||||
TestPitchToGrid( 1, 0, 62, VLNote::kWantNatural, 0);
|
TestPitchToGrid( 1, VLNote::kWantNatural, 62, VLNote::kWantNatural, 0);
|
||||||
TestPitchToGrid( 1, 0, 62, VLNote::kWantSharp, 0);
|
TestPitchToGrid( 1, VLNote::kWantNatural, 62, VLNote::kWantSharp, 0);
|
||||||
TestPitchToGrid( 1, 0, 62, VLNote::kWantFlat, 0);
|
TestPitchToGrid( 1, VLNote::kWantNatural, 62, VLNote::kWantFlat, 0);
|
||||||
TestPitchToGrid( 0, VLNote::kWant2Sharp, 62, VLNote::kWant2Sharp, 0);
|
TestPitchToGrid( 0, VLNote::kWant2Sharp, 62, VLNote::kWant2Sharp, 0);
|
||||||
TestPitchToGrid( 2, VLNote::kWant2Flat, 62, VLNote::kWant2Flat, 0);
|
TestPitchToGrid( 2, VLNote::kWant2Flat, 62, VLNote::kWant2Flat, 0);
|
||||||
|
|
||||||
TestPitchToGrid( 6, 0, 71, 0, 0); // B, C Major
|
TestPitchToGrid( 6, VLNote::kWantNatural, 71, 0, 0); // B, C Major
|
||||||
TestPitchToGrid( 6, 0, 71, VLNote::kWantNatural, 0);
|
TestPitchToGrid( 6, VLNote::kWantNatural, 71, VLNote::kWantNatural, 0);
|
||||||
TestPitchToGrid( 6, 0, 71, VLNote::kWantSharp, 0);
|
TestPitchToGrid( 6, VLNote::kWantNatural, 71, VLNote::kWantSharp, 0);
|
||||||
TestPitchToGrid( 7, VLNote::kWantFlat, 71, VLNote::kWantFlat, 0);
|
TestPitchToGrid( 7, VLNote::kWantFlat, 71, VLNote::kWantFlat, 0);
|
||||||
TestPitchToGrid( 5, VLNote::kWant2Sharp, 71, VLNote::kWant2Sharp, 0);
|
TestPitchToGrid( 5, VLNote::kWant2Sharp, 71, VLNote::kWant2Sharp, 0);
|
||||||
TestPitchToGrid( 7, VLNote::kWantFlat, 71, VLNote::kWant2Flat, 0);
|
TestPitchToGrid( 7, VLNote::kWantFlat, 71, VLNote::kWant2Flat, 0);
|
||||||
|
|
||||||
TestPitchToGrid( 7, 0, 72, 0, 0); // Octaves
|
TestPitchToGrid( 7, VLNote::kWantNatural, 72, 0, 0); // Octaves
|
||||||
TestPitchToGrid( 14, 0, 84, 0, 0);
|
TestPitchToGrid( 14, VLNote::kWantNatural, 84, 0, 0);
|
||||||
TestPitchToGrid( -7, 0, 48, 0, 0);
|
TestPitchToGrid( -7, VLNote::kWantNatural, 48, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TestGridToPitch(pitch,grid,visualIn,key) \
|
#define TestGridToPitch(pitch,grid,visualIn,key) \
|
||||||
|
@ -105,12 +105,12 @@
|
||||||
{
|
{
|
||||||
VLVisualFilter filterBbMajor(-2);
|
VLVisualFilter filterBbMajor(-2);
|
||||||
|
|
||||||
TestVisualFilter(0, 0, 0, filterBbMajor);
|
TestVisualFilter(0, 0, VLNote::kWantNatural, filterBbMajor);
|
||||||
TestVisualFilter(VLNote::kWantFlat, 0, VLNote::kWantFlat, filterBbMajor);
|
TestVisualFilter(VLNote::kWantFlat, 0, VLNote::kWantFlat, filterBbMajor);
|
||||||
TestVisualFilter(VLNote::kWantNatural, 0, 0, filterBbMajor);
|
TestVisualFilter(VLNote::kWantNatural, 0, VLNote::kWantNatural, filterBbMajor);
|
||||||
TestVisualFilter(VLNote::kWantSharp, 0, VLNote::kWantSharp, filterBbMajor);
|
TestVisualFilter(VLNote::kWantSharp, 0, VLNote::kWantSharp, filterBbMajor);
|
||||||
TestVisualFilter(0, 2, VLNote::kWantFlat, filterBbMajor);
|
TestVisualFilter(0, 2, VLNote::kWantFlat, filterBbMajor);
|
||||||
TestVisualFilter(VLNote::kWantSharp, 2, VLNote::kWantSharp, filterBbMajor);
|
TestVisualFilter(VLNote::kWantSharp, 2, VLNote::kWantSharp, filterBbMajor);
|
||||||
TestVisualFilter(VLNote::kWantFlat, 2, 0, filterBbMajor);
|
TestVisualFilter(VLNote::kWantNatural, 2, VLNote::kWantNatural, filterBbMajor);
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user