// // 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< 0 && HasSharp(pitch % 12, key)) return VLNote::kWantSharp; else return 0; } 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]; } uint16_t VLPitchAccidental(int8_t pitch, uint16_t visual, int key) { int semi = pitch % 12; // // 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; } // // No visuals, or no match // visual &= ~VLNote::kAccidentalsMask; if (IsBasicNote(semi)) return visual | VLNote::kWantNatural; else if (key <= 0) return visual | VLNote::kWantFlat; else return visual | VLNote::kWantSharp; } 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; } 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; } void VLVisualFilter::ResetWithKey(int key) { memset(&fState[0], 0, 7*sizeof(fState[0])); switch (key) { // Almost every state falls through case -6: fState[0] = VLNote::kWantFlat; case -5: fState[4] = VLNote::kWantFlat; case -4: fState[1] = VLNote::kWantFlat; case -3: fState[5] = VLNote::kWantFlat; case -2: fState[2] = VLNote::kWantFlat; case -1: fState[6] = VLNote::kWantFlat; case 0: break; case 6: fState[2] = VLNote::kWantSharp; case 5: fState[5] = VLNote::kWantSharp; case 4: fState[1] = VLNote::kWantSharp; case 3: fState[4] = VLNote::kWantSharp; case 2: fState[0] = VLNote::kWantSharp; case 1: fState[3] = VLNote::kWantSharp; default: break; } } uint16_t VLVisualFilter::operator()(int gridPos, uint16_t visual) { 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; } else { acc = 0; } return (visual & ~VLNote::kAccidentalsMask) | acc; }