2011-08-28 02:57:49 +00:00
|
|
|
//
|
|
|
|
// File: VLPitchGrid.h - Translate between (MIDI) pitches and their vertical position
|
|
|
|
//
|
|
|
|
// Author(s):
|
|
|
|
//
|
|
|
|
// (MN) Matthias Neeracher
|
|
|
|
//
|
|
|
|
// Copyright © 2011 Matthias Neeracher
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "VLPitchGrid.h"
|
|
|
|
#include "VLModel.h"
|
|
|
|
|
|
|
|
#define P(x) (1<<x)
|
|
|
|
|
|
|
|
static inline bool IsBasicNote(int semi)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Bitmap of basic notes, padded left and right by two positions
|
|
|
|
// B C D E F G A B C
|
|
|
|
const uint16_t kBasicNotes = P( 1)|P( 2)|P( 4)|P( 6)|P( 7)|P( 9)|P(11)|P(13)|P(14);
|
|
|
|
|
|
|
|
return kBasicNotes & (1<<(semi+2));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool HasFlat(int semi, int key)
|
|
|
|
{
|
|
|
|
const uint16_t kFMajor = P(1) | P(13); // Bb
|
|
|
|
const uint16_t kBbMajor = kFMajor | P(6); // Eb
|
|
|
|
const uint16_t kEbMajor = kBbMajor | P(11); // Ab
|
|
|
|
const uint16_t kAbMajor = kEbMajor | P(4); // Db
|
|
|
|
const uint16_t kDbMajor = kAbMajor | P(9); // Gb
|
|
|
|
const uint16_t kGbMajor = kDbMajor | P(2) | P(14); // Cb
|
|
|
|
|
|
|
|
static const uint16_t sFlats[] =
|
|
|
|
{kFMajor, kBbMajor, kEbMajor, kAbMajor, kDbMajor, kGbMajor};
|
|
|
|
|
|
|
|
return sFlats[-1-key] & (1<<(semi+2));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool HasSharp(int semi, int key)
|
|
|
|
{
|
|
|
|
const uint16_t kGMajor = P(7); // F#
|
|
|
|
const uint16_t kDMajor = kGMajor | P(2) | P(14); // C#
|
|
|
|
const uint16_t kAMajor = kDMajor | P(9); // G#
|
|
|
|
const uint16_t kEMajor = kAMajor | P(4); // D#
|
|
|
|
const uint16_t kBMajor = kEMajor | P(11); // A#
|
|
|
|
const uint16_t kFsMajor = kBMajor | P(6); // E#
|
|
|
|
|
|
|
|
static const uint16_t sSharps[] =
|
|
|
|
{kGMajor, kDMajor, kAMajor, kEMajor, kBMajor, kFsMajor};
|
|
|
|
|
|
|
|
return sSharps[key-1] & (1<<(semi+2));
|
|
|
|
}
|
|
|
|
|
2011-08-28 04:12:44 +00:00
|
|
|
uint16_t VLVisualInKey(int8_t pitch, int key)
|
|
|
|
{
|
|
|
|
if (key < 0 && HasFlat(pitch % 12, key))
|
|
|
|
return VLNote::kWantFlat;
|
|
|
|
else if (key > 0 && HasSharp(pitch % 12, key))
|
|
|
|
return VLNote::kWantSharp;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-08-28 02:57:49 +00:00
|
|
|
static inline int8_t SemiToStep(int semi)
|
|
|
|
{
|
|
|
|
static const int8_t sSteps[] =
|
|
|
|
// Bb B C D E F G A B C
|
|
|
|
{-1,-1,0,0,1,1,2,3,3,4,4,5,5,6,7,7};
|
|
|
|
|
|
|
|
return sSteps[semi+2];
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int8_t StepToSemi(int step)
|
|
|
|
{
|
|
|
|
// C D E F G A B
|
|
|
|
static const int8_t sSemi[] = {0,2,4,5,7,9,11};
|
|
|
|
|
|
|
|
return sSemi[step];
|
|
|
|
}
|
|
|
|
|
2011-08-28 18:47:25 +00:00
|
|
|
uint16_t VLPitchAccidental(int8_t pitch, uint16_t visual, int key)
|
2011-08-28 02:57:49 +00:00
|
|
|
{
|
2011-08-31 02:02:10 +00:00
|
|
|
int semi = pitch % 12;
|
2011-09-05 22:20:48 +00:00
|
|
|
//
|
|
|
|
// The user expressed a preference, try to match it
|
|
|
|
//
|
|
|
|
switch (visual & VLNote::kAccidentalsMask) {
|
|
|
|
case VLNote::kWantNatural:
|
|
|
|
if (IsBasicNote(semi))
|
|
|
|
return visual;
|
|
|
|
break;
|
|
|
|
case VLNote::kWant2Flat:
|
|
|
|
if (IsBasicNote(semi+2))
|
|
|
|
return visual;
|
|
|
|
else
|
|
|
|
return VLNote::kWantFlat;
|
|
|
|
case VLNote::kWantFlat:
|
|
|
|
if (IsBasicNote(semi+1))
|
|
|
|
return visual;
|
|
|
|
break;
|
|
|
|
case VLNote::kWant2Sharp:
|
|
|
|
if (IsBasicNote(semi-2))
|
|
|
|
return visual;
|
|
|
|
else
|
|
|
|
return VLNote::kWantSharp;
|
|
|
|
case VLNote::kWantSharp:
|
|
|
|
if (IsBasicNote(semi-1))
|
|
|
|
return visual;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2011-08-28 02:57:49 +00:00
|
|
|
}
|
|
|
|
//
|
|
|
|
// No visuals, or no match
|
|
|
|
//
|
2011-08-31 02:02:10 +00:00
|
|
|
visual &= ~VLNote::kAccidentalsMask;
|
|
|
|
if (IsBasicNote(semi))
|
|
|
|
return visual | VLNote::kWantNatural;
|
|
|
|
else if (key <= 0)
|
|
|
|
return visual | VLNote::kWantFlat;
|
|
|
|
else
|
|
|
|
return visual | VLNote::kWantSharp;
|
2011-08-28 18:47:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2011-08-28 02:57:49 +00:00
|
|
|
}
|
|
|
|
return SemiToStep(semi)+7*octave;
|
|
|
|
}
|
|
|
|
|
|
|
|
int8_t VLGridToPitch(int gridPos, uint16_t visual, int key)
|
|
|
|
{
|
|
|
|
int octave = VLNote::kMiddleC;
|
|
|
|
|
|
|
|
while (gridPos > 6) {
|
|
|
|
octave += 12;
|
|
|
|
gridPos -= 7;
|
|
|
|
}
|
|
|
|
while (gridPos < 0) {
|
|
|
|
octave -= 12;
|
|
|
|
gridPos += 7;
|
|
|
|
}
|
|
|
|
int semi = StepToSemi(gridPos);
|
|
|
|
int accidental;
|
|
|
|
switch (visual) {
|
|
|
|
case VLNote::kWantFlat:
|
|
|
|
accidental = -1;
|
|
|
|
break;
|
|
|
|
case VLNote::kWant2Flat:
|
|
|
|
accidental = -2;
|
|
|
|
break;
|
|
|
|
case VLNote::kWantSharp:
|
|
|
|
accidental = 1;
|
|
|
|
break;
|
|
|
|
case VLNote::kWant2Sharp:
|
|
|
|
accidental = 2;
|
|
|
|
break;
|
|
|
|
case VLNote::kWantNatural:
|
|
|
|
accidental = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (key > 0 && HasSharp(semi, key))
|
|
|
|
accidental = 1;
|
|
|
|
else if (key < 0 && HasFlat(semi, key))
|
|
|
|
accidental = -1;
|
|
|
|
else
|
|
|
|
accidental = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return octave+semi+accidental;
|
|
|
|
}
|
|
|
|
|
2011-08-28 18:47:25 +00:00
|
|
|
void VLVisualFilter::ResetWithKey(int key)
|
2011-08-28 02:57:49 +00:00
|
|
|
{
|
2011-08-28 18:47:25 +00:00
|
|
|
memset(&fState[0], 0, 7*sizeof(fState[0]));
|
2011-08-28 02:57:49 +00:00
|
|
|
switch (key) { // Almost every state falls through
|
|
|
|
case -6:
|
2011-08-28 18:47:25 +00:00
|
|
|
fState[0] = VLNote::kWantFlat;
|
2011-08-28 02:57:49 +00:00
|
|
|
case -5:
|
2011-09-05 22:21:13 +00:00
|
|
|
fState[4] = VLNote::kWantFlat;
|
2011-08-28 02:57:49 +00:00
|
|
|
case -4:
|
2011-08-28 18:47:25 +00:00
|
|
|
fState[1] = VLNote::kWantFlat;
|
2011-08-28 02:57:49 +00:00
|
|
|
case -3:
|
2011-09-05 22:21:13 +00:00
|
|
|
fState[5] = VLNote::kWantFlat;
|
2011-08-28 02:57:49 +00:00
|
|
|
case -2:
|
2011-08-28 18:47:25 +00:00
|
|
|
fState[2] = VLNote::kWantFlat;
|
2011-08-28 02:57:49 +00:00
|
|
|
case -1:
|
2011-08-28 18:47:25 +00:00
|
|
|
fState[6] = VLNote::kWantFlat;
|
2011-08-28 02:57:49 +00:00
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case 6:
|
2011-08-28 18:47:25 +00:00
|
|
|
fState[2] = VLNote::kWantSharp;
|
2011-08-28 02:57:49 +00:00
|
|
|
case 5:
|
2011-08-28 18:47:25 +00:00
|
|
|
fState[5] = VLNote::kWantSharp;
|
2011-08-28 02:57:49 +00:00
|
|
|
case 4:
|
2011-08-28 18:47:25 +00:00
|
|
|
fState[1] = VLNote::kWantSharp;
|
2011-08-28 02:57:49 +00:00
|
|
|
case 3:
|
2011-08-28 18:47:25 +00:00
|
|
|
fState[4] = VLNote::kWantSharp;
|
2011-08-28 02:57:49 +00:00
|
|
|
case 2:
|
2011-08-28 18:47:25 +00:00
|
|
|
fState[0] = VLNote::kWantSharp;
|
2011-08-28 02:57:49 +00:00
|
|
|
case 1:
|
2011-08-28 18:47:25 +00:00
|
|
|
fState[3] = VLNote::kWantSharp;
|
2011-08-28 02:57:49 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t VLVisualFilter::operator()(int gridPos, uint16_t visual)
|
|
|
|
{
|
2011-08-31 02:02:10 +00:00
|
|
|
uint16_t acc = visual & VLNote::kAccidentalsMask;
|
|
|
|
gridPos = (gridPos+700) % 7;
|
|
|
|
if (acc == VLNote::kWantNatural)
|
|
|
|
acc = 0;
|
|
|
|
if (acc != fState[gridPos]) {
|
|
|
|
fState[gridPos] = acc;
|
|
|
|
if (!acc)
|
|
|
|
acc = VLNote::kWantNatural;
|
2011-08-28 18:47:25 +00:00
|
|
|
} else {
|
2011-08-31 02:02:10 +00:00
|
|
|
acc = 0;
|
2011-08-28 18:47:25 +00:00
|
|
|
}
|
2011-08-31 02:02:10 +00:00
|
|
|
return (visual & ~VLNote::kAccidentalsMask) | acc;
|
2011-08-28 02:57:49 +00:00
|
|
|
}
|