Skip to content

Commit d17c0e9

Browse files
author
tlr
committed
Fadeout repeated notes option (default off)
1 parent cd828ab commit d17c0e9

4 files changed

Lines changed: 38 additions & 18 deletions

File tree

src/PluginEditor.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,7 @@ void RipplerXAudioProcessorEditor::showSettingsMenu()
966966
{
967967
bool stereoizer = (bool)audioProcessor.params.getRawParameterValue("stereoizer")->load();
968968
bool reuseVoices = (bool)audioProcessor.params.getRawParameterValue("reuse_voices")->load();
969+
bool fadeoutRepeats = (bool)audioProcessor.params.getRawParameterValue("fadeout_repeats")->load();
969970

970971
PopupMenu menu;
971972
PopupMenu scaleMenu;
@@ -977,6 +978,7 @@ void RipplerXAudioProcessorEditor::showSettingsMenu()
977978

978979
PopupMenu polyphonyMenu;
979980
polyphonyMenu.addItem(10, "Reuse voices on repeated notes", true, reuseVoices);
981+
polyphonyMenu.addItem(12, "Fadeout repeated notes", reuseVoices, fadeoutRepeats);
980982

981983
menu.addSubMenu("UI Scale", scaleMenu);
982984
menu.addSubMenu("Polyphony", polyphonyMenu);
@@ -985,7 +987,7 @@ void RipplerXAudioProcessorEditor::showSettingsMenu()
985987
auto menuPos = localPointToGlobal(settingsBtn.getBounds().getBottomRight());
986988
menu.showMenuAsync(PopupMenu::Options()
987989
.withTargetScreenArea({ menuPos.getX() - 125, menuPos.getY(), 1, 1 }),
988-
[this, stereoizer, reuseVoices](int result) {
990+
[this, stereoizer, reuseVoices, fadeoutRepeats](int result) {
989991
if (result == 0) return;
990992
if (result == 1) audioProcessor.setScale(1.f);
991993
if (result == 2) audioProcessor.setScale(1.25f);
@@ -1004,6 +1006,10 @@ void RipplerXAudioProcessorEditor::showSettingsMenu()
10041006
auto param = audioProcessor.params.getParameter("stereoizer");
10051007
param->setValueNotifyingHost(stereoizer ? 0.f : 1.f);
10061008
}
1009+
if (result == 12) {
1010+
auto param = audioProcessor.params.getParameter("fadeout_repeats");
1011+
param->setValueNotifyingHost(fadeoutRepeats ? 0.f : 1.f);
1012+
}
10071013
});
10081014
}
10091015

src/PluginProcessor.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ RipplerXAudioProcessor::RipplerXAudioProcessor()
9393
std::make_unique<juce::AudioParameterInt>("bend_range", "PitchBend Range", 1, 24, 2),
9494
std::make_unique<juce::AudioParameterBool>("stereoizer", "Stereoizer", true),
9595
std::make_unique<juce::AudioParameterBool>("reuse_voices", "Reuse Voices", false),
96+
std::make_unique<juce::AudioParameterBool>("fadeout_repeats", "Fadeout Repeated Notes", false),
9697
}),
9798
mtsClientPtr{nullptr}
9899
#endif
@@ -383,19 +384,24 @@ bool RipplerXAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts)
383384
// Sort the array by release time if the note is not pressed, otherwise sort by press time.
384385
// The release time sort should take priority over press time sort.
385386
int RipplerXAudioProcessor::pickVoice(int note) {
386-
int pick = 0;
387387
bool reuseVoices = (bool)params.getRawParameterValue("reuse_voices")->load();
388388

389+
// Priority 1: note already playing in a voice
390+
if (reuseVoices) {
391+
for (int i = 0; i < polyphony; ++i) {
392+
if (voices[i]->note == note) {
393+
return i;
394+
}
395+
}
396+
}
397+
398+
int pick = 0;
389399
for (int i = 1; i < polyphony; ++i) {
390400
const auto& v1 = voices[i];
391401
const auto& v2 = voices[pick];
392402

393-
if (v1->note == note && reuseVoices) {
394-
pick = i;
395-
break;
396-
}
397403
// Priority 2: Released voices come before pressed ones
398-
else if (!v1->isPressed && v2->isPressed) {
404+
if (!v1->isPressed && v2->isPressed) {
399405
pick = i;
400406
}
401407
else if (v1->isPressed && !v2->isPressed) {
@@ -419,16 +425,19 @@ void RipplerXAudioProcessor::onNote(MIDIMsg msg)
419425
auto srate = getSampleRate();
420426

421427
int nvoice = pickVoice(msg.note);
422-
DBG("" << nvoice);
423428
Voice& voice = *voices[nvoice];
424429

430+
bool reuse_voices = (bool)params.getRawParameterValue("reuse_voices")->load();
431+
bool fadeout_repeats = (bool)params.getRawParameterValue("fadeout_repeats")->load();
432+
bool skip_fadeout = reuse_voices && !fadeout_repeats && voice.note == msg.note;
433+
425434
auto mallet_type = (MalletType)params.getRawParameterValue("mallet_type")->load();
426435
auto mallet_stiff = (double)params.getRawParameterValue("mallet_stiff")->load();
427436
auto mallet_ktrack = (double)params.getRawParameterValue("mallet_ktrack")->load();
428437
auto vel_mallet_stiff = (double)params.getRawParameterValue("vel_mallet_stiff")->load();
429438
auto malletFreq = fmax(100.0, fmin(5000.0, exp(log(mallet_stiff) + msg.vel / 127.0 * vel_mallet_stiff * 2.0 * (log(5000.0) - log(100.0)))));
430439

431-
voice.trigger(srate, msg.note, msg.vel / 127.0, mallet_type, malletFreq, mallet_ktrack, mtsClientPtr);
440+
voice.trigger(srate, msg.note, msg.vel / 127.0, mallet_type, malletFreq, mallet_ktrack, skip_fadeout, mtsClientPtr);
432441
}
433442

434443
void RipplerXAudioProcessor::offNote(MIDIMsg msg)

src/dsp/Voice.cpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ double Voice::note2freq(int _note, MTSClient *mts)
1515
}
1616

1717
// Triggers mallet and noise generator
18-
void Voice::trigger(double _srate, int _note, double _vel, MalletType _malletType, double _malletFreq, double _mallet_ktrack, MTSClient* mts)
18+
void Voice::trigger(double _srate, int _note, double _vel, MalletType _malletType, double _malletFreq, double _mallet_ktrack, bool skip_fadeout, MTSClient* mts)
1919
{
2020
srate = _srate;
2121
malletType = _malletType;
@@ -27,14 +27,17 @@ void Voice::trigger(double _srate, int _note, double _vel, MalletType _malletTyp
2727
malletKtrack = _mallet_ktrack;
2828

2929
// fade out active voice before re-triggering
30-
if (((resA.on && resA.active) || (resB.on && resB.active))) {
30+
if (skip_fadeout) {
31+
triggerStart(false);
32+
}
33+
else if (((resA.on && resA.active) || (resB.on && resB.active))) {
3134
isFading = true;
3235
fadeTotalSamples = (int)(globals::REPEAT_NOTE_FADE_MS * 0.001 * srate);
3336
fadeSamples = fadeTotalSamples;
3437
updateResonators();
3538
}
3639
else {
37-
triggerStart();
40+
triggerStart(true);
3841
}
3942
}
4043

@@ -43,15 +46,17 @@ double Voice::fadeOut()
4346
fadeSamples--;
4447
if (fadeSamples <= 0) {
4548
isFading = false;
46-
triggerStart();
49+
triggerStart(true);
4750
}
4851
return isFading ? (double)fadeSamples / (double)fadeTotalSamples : 1.0;
4952
}
5053

51-
void Voice::triggerStart()
54+
void Voice::triggerStart(bool reset)
5255
{
53-
resA.clear();
54-
resB.clear();
56+
if (reset) {
57+
resA.clear();
58+
resB.clear();
59+
}
5560
isRelease = false;
5661
isPressed = true;
5762
pressed_ts = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();

src/dsp/Voice.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ class Voice
2525
~Voice() {}
2626

2727
double note2freq(int _note, MTSClient *mts);
28-
void trigger(double srate, int _note, double vel, MalletType malletType, double malletFreq, double malletKTrack, MTSClient *mts);
29-
void triggerStart();
28+
void trigger(double srate, int _note, double vel, MalletType malletType, double malletFreq, double malletKTrack, bool skip_fade, MTSClient *mts);
29+
void triggerStart(bool reset);
3030
double fadeOut();
3131
void release();
3232
void clear();

0 commit comments

Comments
 (0)