From d483836259103e369b3a6057c1bfe3a917803179 Mon Sep 17 00:00:00 2001 From: Paul Walker Date: Mon, 7 Oct 2024 10:02:52 -0400 Subject: [PATCH] Fix a problem where mappings didn't span tuning center If a partial mapping existed and the mapping didnt span the tuning center (so like you mapped 69-81 with a tuning center of 60) we were off by an octave. This fix pushes the mapping to span the tuning center. Closes #72 Addresses https://github.com/surge-synthesizer/surge/issues/7822 --- include/TuningsImpl.h | 30 ++++++++++++++-- tests/alltests.cpp | 23 ++++++++++++ .../kbm-wrapping-7822/31edo2-subset-57.kbm | 29 +++++++++++++++ .../data/kbm-wrapping-7822/31edo2-subset.kbm | 29 +++++++++++++++ tests/data/kbm-wrapping-7822/31edo2.scl | 36 +++++++++++++++++++ 5 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 tests/data/kbm-wrapping-7822/31edo2-subset-57.kbm create mode 100644 tests/data/kbm-wrapping-7822/31edo2-subset.kbm create mode 100644 tests/data/kbm-wrapping-7822/31edo2.scl diff --git a/include/TuningsImpl.h b/include/TuningsImpl.h index 9dafaa1..5473e05 100644 --- a/include/TuningsImpl.h +++ b/include/TuningsImpl.h @@ -466,6 +466,28 @@ inline Tuning::Tuning(const Scale &s_, const KeyboardMapping &k_, bool allowTuni throw TuningError("Unable to tune to a scale with no notes. Your scale provided " + std::to_string(s.count) + " notes."); + int useMiddleNote{k.middleNote}; + if (k.count > 0) + { + // Is the KBM not spanning the tuning note + auto mapStart = useMiddleNote; + auto mapEnd = useMiddleNote + k.count; + while (mapStart > k.tuningConstantNote) + { + useMiddleNote -= k.count; + mapStart = useMiddleNote; + mapEnd = useMiddleNote + k.count; + // throw std::logic_error("Blah"); + } + while (mapEnd < k.tuningConstantNote) + { + useMiddleNote += k.count; + mapStart = useMiddleNote; + mapEnd = useMiddleNote + k.count; + // throw std::logic_error("Blah"); + } + } + int kbmRotations{1}; for (const auto &kv : k.keys) { @@ -512,11 +534,11 @@ inline Tuning::Tuning(const Scale &s_, const KeyboardMapping &k_, bool allowTuni double pitches[N]; int posPitch0 = 256 + k.tuningConstantNote; - int posScale0 = 256 + k.middleNote; + int posScale0 = 256 + useMiddleNote; double pitchMod = log(k.tuningPitch) / log(2) - 1; - int scalePositionOfTuningNote = k.tuningConstantNote - k.middleNote; + int scalePositionOfTuningNote = k.tuningConstantNote - useMiddleNote; if (k.count > 0) { while (scalePositionOfTuningNote >= k.count) @@ -676,10 +698,12 @@ inline Tuning::Tuning(const Scale &s_, const KeyboardMapping &k_, bool allowTuni ** If we mod that by the mapping size we know which note we are on */ int mappingKey = distanceFromScale0 % k.count; + int rotations = 0; if (mappingKey < 0) + { mappingKey += k.count; + } // Now have we gone off the end - int rotations = 0; int dt = distanceFromScale0; if (dt > 0) { diff --git a/tests/alltests.cpp b/tests/alltests.cpp index d71ed81..111d548 100644 --- a/tests/alltests.cpp +++ b/tests/alltests.cpp @@ -1150,6 +1150,7 @@ TEST_CASE("Skipped Note and Root") REQUIRE(t.isMidiNoteMapped(60)); REQUIRE(t.isMidiNoteMapped(69)); REQUIRE(t.frequencyForMidiNote(69) == Approx(440.0).margin(0.01)); + REQUIRE(t.frequencyForMidiNote(60) == Approx(246.9416506281).margin(0.01)); } SECTION("Tuning from 59 throws") @@ -1185,6 +1186,7 @@ TEST_CASE("Skipped Note and Root") auto t = Tunings::Tuning(s, k); REQUIRE(t.isMidiNoteMapped(60)); REQUIRE(t.isMidiNoteMapped(69)); + REQUIRE(t.frequencyForMidiNote(60) == Approx(246.9416506281).margin(0.01)); REQUIRE(t.frequencyForMidiNote(69) == Approx(440.0).margin(0.01)); } @@ -1196,6 +1198,7 @@ TEST_CASE("Skipped Note and Root") t = t.withSkippedNotesInterpolated(); REQUIRE(t.isMidiNoteMapped(60)); REQUIRE(t.isMidiNoteMapped(69)); + REQUIRE(t.frequencyForMidiNote(60) == Approx(246.9416506281).margin(0.01)); REQUIRE(t.frequencyForMidiNote(69) == Approx(440.0).margin(0.01)); } } @@ -1338,3 +1341,23 @@ TEST_CASE("Retuning API") } } } + +TEST_CASE("Surge 7822 non uniform mapping misses scale center") +{ + SECTION("At Note 57 - no wrapping") + { + auto scale = Tunings::readSCLFile(testFile("kbm-wrapping-7822/31edo2.scl")); + auto map = Tunings::readKBMFile(testFile("kbm-wrapping-7822/31edo2-subset-57.kbm")); + auto t = Tunings::Tuning(scale, map); + REQUIRE(t.frequencyForMidiNote(60) == Approx(400.0)); + REQUIRE(t.frequencyForMidiNote(61) == Approx(418.2936581199)); + } + SECTION("At Note 69 - wrapping") + { + auto scale = Tunings::readSCLFile(testFile("kbm-wrapping-7822/31edo2.scl")); + auto map = Tunings::readKBMFile(testFile("kbm-wrapping-7822/31edo2-subset.kbm")); + auto t = Tunings::Tuning(scale, map); + REQUIRE(t.frequencyForMidiNote(60) == Approx(400.0)); + REQUIRE(t.frequencyForMidiNote(61) == Approx(418.2936581199)); + } +} \ No newline at end of file diff --git a/tests/data/kbm-wrapping-7822/31edo2-subset-57.kbm b/tests/data/kbm-wrapping-7822/31edo2-subset-57.kbm new file mode 100644 index 0000000..fc0aa99 --- /dev/null +++ b/tests/data/kbm-wrapping-7822/31edo2-subset-57.kbm @@ -0,0 +1,29 @@ +! foo.kbm +! +! Size of map: +12 +! First MIDI note number to retune: +0 +! Last MIDI note number to retune: +127 +! Middle note where the first entry in the mapping is mapped to: +57 +! Reference note for which frequency is given: +60 +! Frequency to tune the above note to (floating point e.g. 440.0): +400 +! Scale degree to consider as formal octave: +31 +! Mapping. +0 +3 +5 +8 +10 +13 +16 +18 +21 +23 +26 +28 diff --git a/tests/data/kbm-wrapping-7822/31edo2-subset.kbm b/tests/data/kbm-wrapping-7822/31edo2-subset.kbm new file mode 100644 index 0000000..a96f090 --- /dev/null +++ b/tests/data/kbm-wrapping-7822/31edo2-subset.kbm @@ -0,0 +1,29 @@ +! foo.kbm +! +! Size of map: +12 +! First MIDI note number to retune: +0 +! Last MIDI note number to retune: +127 +! Middle note where the first entry in the mapping is mapped to: +69 +! Reference note for which frequency is given: +60 +! Frequency to tune the above note to (floating point e.g. 440.0): +400 +! Scale degree to consider as formal octave: +31 +! Mapping. +0 +3 +5 +8 +10 +13 +16 +18 +21 +23 +26 +28 diff --git a/tests/data/kbm-wrapping-7822/31edo2.scl b/tests/data/kbm-wrapping-7822/31edo2.scl new file mode 100644 index 0000000..3a08d79 --- /dev/null +++ b/tests/data/kbm-wrapping-7822/31edo2.scl @@ -0,0 +1,36 @@ +! foo.scl +! + + 31 +! + 38.70968 + 77.41935 + 116.12903 + 154.83871 + 193.54839 + 232.25806 + 270.96774 + 309.67742 + 348.38710 + 387.09677 + 425.80645 + 464.51613 + 503.22581 + 541.93548 + 580.64516 + 619.35484 + 658.06452 + 696.77419 + 735.48387 + 774.19355 + 812.90323 + 851.61290 + 890.32258 + 929.03226 + 967.74194 + 1006.45161 + 1045.16129 + 1083.87097 + 1122.58065 + 1161.29032 + 2/1