Skip to content

Commit

Permalink
Starting note in Porta for MIDI_RETUNE correct
Browse files Browse the repository at this point in the history
Stops the repeated note retune issue with MTS and AT_MIDI

Closes surge-synthesizer#5458
  • Loading branch information
baconpaul committed Nov 25, 2021
1 parent a5a41fd commit 3fbe802
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 21 deletions.
13 changes: 13 additions & 0 deletions src/common/SurgeStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2342,6 +2342,19 @@ void SurgeStorage::reportError(const std::string &msg, const std::string &title)
l->onSurgeError(msg, title);
}

float SurgeStorage::remapKeyInMidiOnlyMode(float res)
{
if (!isStandardTuning && tuningApplicationMode == 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 = currentTuning.logScaledFrequencyForMidiNote(idx) * 12;
float b1 = currentTuning.logScaledFrequencyForMidiNote(idx + 1) * 12;
res = (1.f - frac) * b0 + frac * b1;
}
return res;
}

namespace Surge
{
namespace Storage
Expand Down
2 changes: 2 additions & 0 deletions src/common/SurgeStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -1266,6 +1266,8 @@ class alignas(16) SurgeStorage
return tuningPitchInv;
} // Obviously that's the inverse of the above

float remapKeyInMidiOnlyMode(float inKey);

void setTuningApplicationMode(const TuningApplicationMode m);

void initialize_oddsound();
Expand Down
20 changes: 6 additions & 14 deletions src/common/dsp/SurgeVoice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,7 @@ float SurgeVoiceState::getPitch(SurgeStorage *storage)
else if (!storage->isStandardTuning &&
storage->tuningApplicationMode == SurgeStorage::RETUNE_MIDI_ONLY)
{
// Then we tune here
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;
res = storage->remapKeyInMidiOnlyMode(res);
}

res = SurgeVoice::channelKeyEquvialent(res, channel, storage, false);
Expand All @@ -82,14 +77,9 @@ float SurgeVoice::channelKeyEquvialent(float key, int channel, SurgeStorage *sto
float res = key;
if (storage->mapChannelToOctave)
{
if (remapKeyForTuning && !storage->isStandardTuning &&
storage->tuningApplicationMode == SurgeStorage::RETUNE_MIDI_ONLY)
if (remapKeyForTuning)
{
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;
res = storage->remapKeyInMidiOnlyMode(res);
}

float shift;
Expand Down Expand Up @@ -160,7 +150,9 @@ SurgeVoice::SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *oscene, pdata *
(scene->portamento.val.f == scene->portamento.val_min.f))
state.portasrc_key = state.getPitch(storage);
else
state.portasrc_key = storage->last_key[scene_id];
{
state.portasrc_key = storage->remapKeyInMidiOnlyMode(storage->last_key[scene_id]);
}
state.priorpkey = state.portasrc_key;

storage->last_key[scene_id] = key;
Expand Down
110 changes: 103 additions & 7 deletions src/surge-testrunner/UnitTestsTUN.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1433,23 +1433,24 @@ TEST_CASE("Octave Per Channel and Porta", "[tun]")
};
// clang-format on

for (auto tuningstyle : {SurgeStorage::RETUNE_ALL, SurgeStorage::RETUNE_MIDI_ONLY})
// for (auto tuningstyle : {SurgeStorage::RETUNE_ALL, SurgeStorage::RETUNE_MIDI_ONLY})
auto tuningstyle = SurgeStorage::RETUNE_MIDI_ONLY;
{
for (auto mode : {pm_mono, pm_mono_fp, pm_mono_st, pm_mono_st_fp})
{
for (const auto &c : cases)
{
DYNAMIC_SECTION("Mode " << mode << " : Pri : " << c.pri << " : Case : " << c.name
<< " : Scale : ED2-" << c.scaleLen
<< " TuningApplication : " << tuningstyle
<< (c.should ? "" : " - SKIPPING"))
DYNAMIC_SECTION("Mode "
<< mode << " : Pri : " << c.pri << " : Case : " << c.name
<< " : Scale : ED2-" << c.scaleLen << " TuningApplication : "
<< (tuningstyle == SurgeStorage::RETUNE_ALL ? "All" : "MIDI")
<< (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.setTuningApplicationMode(tuningstyle);
surge->storage.retuneToScale(Tunings::evenDivisionOfSpanByM(2, c.scaleLen));

int len = BLOCK_SIZE * 20;
Expand Down Expand Up @@ -1497,3 +1498,98 @@ TEST_CASE("Octave Per Channel and Porta", "[tun]")
}
}
}

TEST_CASE("Portamento With Repeated Notes", "[tun]")
{
for (auto tuningstyle : {SurgeStorage::RETUNE_ALL, SurgeStorage::RETUNE_MIDI_ONLY})
{
DYNAMIC_SECTION("Repeated note with porta in "
<< (tuningstyle == SurgeStorage::RETUNE_ALL ? "All" : "MIDI"))
{
auto surge = surgeOnSine();
surge->storage.setTuningApplicationMode(tuningstyle);
surge->storage.retuneToScale(Tunings::evenDivisionOfSpanByM(2, 31));
surge->storage.getPatch().scene[0].portamento.val.f = 1;

namespace hs = Surge::Headless;
auto events = hs::playerEvents_t();
{
auto on = hs::Event();
on.type = hs::Event::NOTE_ON;
on.channel = 0;
on.data1 = 90;
on.data2 = 100;
on.atSample = 0;
events.push_back(on);
}

{
auto on = hs::Event();
on.type = hs::Event::NOTE_OFF;
on.channel = 0;
on.data1 = 90;
on.data2 = 100;
on.atSample = 44100 * 3;
events.push_back(on);
}

{
auto on = hs::Event();
on.type = hs::Event::NO_EVENT;
on.channel = 0;
on.data1 = 90;
on.data2 = 100;
on.atSample = 44100 * 3 + BLOCK_SIZE * 10;
events.push_back(on);
}

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

for (int i = 0; i < 10; ++i)
{
events.clear();
{
auto on = hs::Event();
on.type = hs::Event::NOTE_ON;
on.channel = 0;
on.data1 = 90;
on.data2 = 100;
on.atSample = 0;
events.push_back(on);
}

{
auto on = hs::Event();
on.type = hs::Event::NOTE_OFF;
on.channel = 0;
on.data1 = 90;
on.data2 = 100;
on.atSample = BLOCK_SIZE * 80;
events.push_back(on);
}
{
auto on = hs::Event();
on.type = hs::Event::NO_EVENT;
on.channel = 0;
on.data1 = 90;
on.data2 = 100;
on.atSample = BLOCK_SIZE * 90;
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);

delete[] buffer;

REQUIRE(freq ==
Approx(surge->storage.currentTuning.frequencyForMidiNote(90)).margin(2));
}
}
}
}

0 comments on commit 3fbe802

Please sign in to comment.