Skip to content

Commit

Permalink
Fix another octave-per-channel mode thing (surge-synthesizer#5472)
Browse files Browse the repository at this point in the history
In the case where e RETUNE_AT_MIDI with a long scale the
voice stealing compairson used different indices than the voice
souding algorithm, so we compared in untuned space accidentally.

Closes surge-synthesizer#5469
  • Loading branch information
baconpaul authored Nov 24, 2021
1 parent 7734015 commit a5a41fd
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 46 deletions.
15 changes: 13 additions & 2 deletions src/common/dsp/SurgeVoice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,27 @@ float SurgeVoiceState::getPitch(SurgeStorage *storage)
res = (1.f - frac) * b0 + frac * b1;
}

res = SurgeVoice::channelKeyEquvialent(res, channel, storage);
res = SurgeVoice::channelKeyEquvialent(res, channel, storage, false);

return res;
}

float SurgeVoice::channelKeyEquvialent(float key, int channel, SurgeStorage *storage)
float SurgeVoice::channelKeyEquvialent(float key, int channel, SurgeStorage *storage,
bool remapKeyForTuning)
{
float res = key;
if (storage->mapChannelToOctave)
{
if (remapKeyForTuning && !storage->isStandardTuning &&
storage->tuningApplicationMode == SurgeStorage::RETUNE_MIDI_ONLY)
{
auto idx = (int)floor(res);
float frac = res - idx; // frac is 0 means use idx; frac is 1 means use idx+1
float b0 = storage->currentTuning.logScaledFrequencyForMidiNote(idx) * 12;
float b1 = storage->currentTuning.logScaledFrequencyForMidiNote(idx + 1) * 12;
res = (1.f - frac) * b0 + frac * b1;
}

float shift;
if (channel > 7)
{
Expand Down
3 changes: 2 additions & 1 deletion src/common/dsp/SurgeVoice.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ class alignas(16) SurgeVoice
}
}

static float channelKeyEquvialent(float key, int channel, SurgeStorage *storage);
static float channelKeyEquvialent(float key, int channel, SurgeStorage *storage,
bool remapKeyForTuning = true);

private:
template <bool first> void calc_ctrldata(QuadFilterChainState *, int);
Expand Down
99 changes: 56 additions & 43 deletions src/surge-testrunner/UnitTestsTUN.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1422,64 +1422,77 @@ TEST_CASE("Octave Per Channel and Porta", "[tun]")
{{1, 3, 1}}, 31},
{"Note on off 1 ED2-31", ALWAYS_HIGHEST, 559.863, true,
{{1, 3, 1}, {10, 3, 1}, {10, 3, 0 }}, 31},

{ "Then in conjunction ED2-31", ALWAYS_HIGHEST, 611.6505, true,
{{26, 2, 1}, {5, 3, 1}}, 31},
{ "ED2-31 10/3 to 23/2", ALWAYS_HIGHEST, 684.203, true,
{{10, 3, 1}, {23, 2, 1}}, 31},
{ "ED2-31 10/3 to 18/2", ALWAYS_HIGHEST, 684.203, true,
{{10, 3, 1}, {18, 2, 1}}, 31},

};
// clang-format on

for (auto mode : {pm_mono, pm_mono_fp, pm_mono_st, pm_mono_st_fp})
for (auto tuningstyle : {SurgeStorage::RETUNE_ALL, SurgeStorage::RETUNE_MIDI_ONLY})
{
for (const auto &c : cases)
for (auto mode : {pm_mono, pm_mono_fp, pm_mono_st, pm_mono_st_fp})
{
DYNAMIC_SECTION("Mode " << mode << " : Pri : " << c.pri << " : Case : " << c.name
<< " : Scale : ED2-" << c.scaleLen
<< (c.should ? "" : " - SKIPPING"))
for (const auto &c : cases)
{
auto surge = surgeOnSine();
surge->storage.getPatch().scene[0].monoVoicePriorityMode = c.pri;
surge->storage.getPatch().scene[0].polymode.val.i = mode;
surge->storage.mapChannelToOctave = true;
surge->storage.setTuningApplicationMode(SurgeStorage::RETUNE_MIDI_ONLY);
surge->storage.retuneToScale(Tunings::evenDivisionOfSpanByM(2, c.scaleLen));

int len = BLOCK_SIZE * 20;
int idx = 0;
auto events = hs::playerEvents_t();
for (const auto nt : c.notes)
DYNAMIC_SECTION("Mode " << mode << " : Pri : " << c.pri << " : Case : " << c.name
<< " : Scale : ED2-" << c.scaleLen
<< " TuningApplication : " << tuningstyle
<< (c.should ? "" : " - SKIPPING"))
{
auto surge = surgeOnSine();
surge->storage.getPatch().scene[0].monoVoicePriorityMode = c.pri;
surge->storage.getPatch().scene[0].polymode.val.i = mode;
surge->storage.mapChannelToOctave = true;
surge->storage.tuningApplicationMode = tuningstyle;
surge->storage.setTuningApplicationMode(SurgeStorage::RETUNE_MIDI_ONLY);
surge->storage.retuneToScale(Tunings::evenDivisionOfSpanByM(2, c.scaleLen));

int len = BLOCK_SIZE * 20;
int idx = 0;
auto events = hs::playerEvents_t();
for (const auto nt : c.notes)
{
auto on = hs::Event();
on.type = nt.on ? hs::Event::NOTE_ON : hs::Event::NOTE_OFF;
on.channel = nt.c;
on.data1 = nt.n;
on.data2 = 100;
on.atSample = len * idx;
events.push_back(on);
idx++;
}

auto on = hs::Event();
on.type = nt.on ? hs::Event::NOTE_ON : hs::Event::NOTE_OFF;
on.channel = nt.c;
on.data1 = nt.n;
on.data2 = 100;
on.type = hs::Event::NO_EVENT;
on.atSample = len * idx;
events.push_back(on);
idx++;
}

auto on = hs::Event();
on.type = hs::Event::NO_EVENT;
on.atSample = len * idx;
events.push_back(on);
float *buffer;
int nS, nC;
hs::playAsConfigured(surge, events, &buffer, &nS, &nC);
delete[] buffer;

float *buffer;
int nS, nC;
hs::playAsConfigured(surge, events, &buffer, &nS, &nC);
delete[] buffer;

events.clear();
on.atSample = len * 3;
events.push_back(on);
hs::playAsConfigured(surge, events, &buffer, &nS, &nC);
events.clear();
on.atSample = len * 3;
events.push_back(on);
hs::playAsConfigured(surge, events, &buffer, &nS, &nC);

int nSTrim = (int)(nS / 2 * 0.8);
int start = (int)(nS / 2 * 0.05);
auto freq = frequencyFromData(buffer, nS, nC, 0, start, nSTrim);
int nSTrim = (int)(nS / 2 * 0.8);
int start = (int)(nS / 2 * 0.05);
auto freq = frequencyFromData(buffer, nS, nC, 0, start, nSTrim);

delete[] buffer;
delete[] buffer;

if (c.should)
REQUIRE(freq == Approx(c.res).margin(1));
else
REQUIRE_FALSE(freq == Approx(c.res).margin(1));
if (c.should)
REQUIRE(freq == Approx(c.res).margin(1));
else
REQUIRE_FALSE(freq == Approx(c.res).margin(1));
}
}
}
}
Expand Down

0 comments on commit a5a41fd

Please sign in to comment.