From 85010dd497106f99fb0753015ed0b4e734ff3f17 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 2 Nov 2019 15:39:38 -0400 Subject: [PATCH] Support Release Velocity in the 16 vintage codebase (#1267) OK so I got a linnstrument, alright? Release velocity is super useful and I should have done this forever ago. Basically plumb it through as a modulation source in the DSP engine; hook it up properly in the VST3; and modify the GUI so the Velocity modulator is an either-or modulator. This results in the ability to modulate with release velocity just as you can with anything else, albeit in a bit of a clumsy fashion. Closes #811 --- src/common/ModulationSource.h | 13 +- src/common/SurgeSynthesizer.cpp | 10 + src/common/dsp/SurgeVoice.cpp | 12 +- src/common/dsp/SurgeVoice.h | 2 +- src/common/dsp/SurgeVoiceState.h | 6 +- src/common/gui/CModulationSourceButton.cpp | 10 +- src/common/gui/CModulationSourceButton.h | 14 + src/common/gui/SurgeGUIEditor.cpp | 283 +++++++++++++-------- src/vst3/SurgeVst3Processor.cpp | 6 +- 9 files changed, 238 insertions(+), 118 deletions(-) diff --git a/src/common/ModulationSource.h b/src/common/ModulationSource.h index 95c9880ded2..774078fc4c4 100644 --- a/src/common/ModulationSource.h +++ b/src/common/ModulationSource.h @@ -45,6 +45,7 @@ enum modsources ms_slfo6, // ms_arpeggiator, ms_timbre, + ms_releasevelocity, n_modsources, /*ms_stepseq1, ms_stepseq2, @@ -60,7 +61,7 @@ const char modsource_abberations_button[n_modsources][32] = { "Off", "Velocity", "Keytrack", "Poly AT", "Channel AT", "Pitchbend", "Modwheel", "Ctrl 1", "Ctrl 2", "Ctrl 3", "Ctrl 4", "Ctrl 5", "Ctrl 6", "Ctrl 7", "Ctrl 8", "Amp EG", "Filter EG", "LFO 1", "LFO 2", "LFO 3", "LFO 4", "LFO 5", "LFO 6", "SLFO 1", - "SLFO 2", "SLFO 3", "SLFO 4", "SLFO 5", "SLFO 6", "Timbre" /*,"Arpeggio"*/}; + "SLFO 2", "SLFO 3", "SLFO 4", "SLFO 5", "SLFO 6", "Timbre", "Rel. Velcty" /*,"Arpeggio"*/}; const char modsource_abberations[n_modsources][32] = {"Off", "Velocity", @@ -91,13 +92,15 @@ const char modsource_abberations[n_modsources][32] = {"Off", "Scene LFO 4", "Scene LFO 5", "Scene LFO 6", - "Timbre" /*,"Arpeggio"*/}; + "Timbre", + "Release Velocity" + /*,"Arpeggio"*/}; const char modsource_abberations_short[n_modsources][32] = { "off", "velocity", "keytrack", "Poly AT", "Ch. AT", "Pitch Bend", "Modwheel", "CTRL1", "CTRL2", "CTRL3", "CTRL4", "CTRL5", "CTRL6", "CTRL7", "CTRL8", "AEG", "FEG", "LFO1", "LFO2", "LFO3", "LFO4", "LFO5", "LFO6", "SLFO1", - "SLFO2", "SLFO3", "SLFO4", "SLFO5", "SLFO6", "TIMBR" /*,"Arpeggio"*/}; + "SLFO2", "SLFO3", "SLFO4", "SLFO5", "SLFO6", "TIMBR", "RELVEL" /*,"Arpeggio"*/}; const int modsource_grid_xy[n_modsources][2] = { {0, 0}, {0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, // vel -> mw @@ -105,13 +108,13 @@ const int modsource_grid_xy[n_modsources][2] = { {6, 2}, {6, 4}, // EGs {0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2}, {5, 2}, // LFO {0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}, {5, 4}, // SLFO - {6, 0} // Timbre + {6, 0}, {0, 0} // Timbre, relvel is special }; inline bool isScenelevel(modsources ms) { return ((ms <= ms_ctrl8) || ((ms >= ms_slfo1) && (ms <= ms_slfo6))) && (ms != ms_velocity) && - (ms != ms_keytrack) && (ms != ms_polyaftertouch) && (ms != ms_timbre); + (ms != ms_keytrack) && (ms != ms_polyaftertouch) && (ms != ms_timbre) && (ms != ms_releasevelocity); } inline bool canModulateMonophonicTarget(modsources ms) diff --git a/src/common/SurgeSynthesizer.cpp b/src/common/SurgeSynthesizer.cpp index 557ea047b2d..3d12b2068cb 100644 --- a/src/common/SurgeSynthesizer.cpp +++ b/src/common/SurgeSynthesizer.cpp @@ -527,6 +527,15 @@ void SurgeSynthesizer::releaseNote(char channel, char key, char velocity) int channelmask = ((channel == 0) ? 3 : 0) || ((channel == 1) ? 1 : 0) || ((channel == 2) ? 2 : 0); + for( int s=0; s<2; ++s ) + { + for( auto *v : voices[s] ) + { + if ((v->state.key == key) && (v->state.channel == channel)) + v->state.releasevelocity = velocity; + } + } + // if(channelmask&1) { if (!channelState[channel].hold) @@ -541,6 +550,7 @@ void SurgeSynthesizer::releaseNote(char channel, char key, char velocity) else holdbuffer[1].push_back(key); // hold pedal is down, add to bufffer } + } void SurgeSynthesizer::releaseNotePostHoldCheck(int scene, char channel, char key, char velocity) diff --git a/src/common/dsp/SurgeVoice.cpp b/src/common/dsp/SurgeVoice.cpp index dd2bf6660fe..2d40624f4ca 100644 --- a/src/common/dsp/SurgeVoice.cpp +++ b/src/common/dsp/SurgeVoice.cpp @@ -71,9 +71,14 @@ SurgeVoice::SurgeVoice(SurgeStorage* storage, age = 0; age_release = 0; state.key = key; - state.velocity = velocity; state.channel = channel; + + state.velocity = velocity; state.fvel = velocity / 127.f; + + state.releasevelocity = 0; + state.freleasevel = 0; + state.scene_id = scene_id; state.detune = detune; state.uberrelease = false; @@ -125,11 +130,15 @@ SurgeVoice::SurgeVoice(SurgeStorage* storage, modsources[ms_lfo1 + i] = &lfo[i]; } modsources[ms_velocity] = &velocitySource; + modsources[ms_releasevelocity] = &releaseVelocitySource; modsources[ms_keytrack] = &keytrackSource; modsources[ms_polyaftertouch] = &polyAftertouchSource; polyAftertouchSource.init(storage->poly_aftertouch[state.scene_id & 1][state.key & 127]); + velocitySource.output = state.fvel; + releaseVelocitySource.output = state.freleasevel; keytrackSource.output = 0; + ampEGSource.init(storage, &scene->adsr[0], localcopy, &state); filterEGSource.init(storage, &scene->adsr[1], localcopy, &state); modsources[ms_ampeg] = &EGSource; @@ -302,6 +311,7 @@ void SurgeVoice::release() lfo[i].release(); state.gate = false; + releaseVelocitySource.output = state.releasevelocity / 127.0f; } void SurgeVoice::uber_release() diff --git a/src/common/dsp/SurgeVoice.h b/src/common/dsp/SurgeVoice.h index aaa44acec24..e88f2229f0c 100644 --- a/src/common/dsp/SurgeVoice.h +++ b/src/common/dsp/SurgeVoice.h @@ -164,7 +164,7 @@ class alignas(16) SurgeVoice std::array modsources; - ModulationSource velocitySource; + ModulationSource velocitySource, releaseVelocitySource; ModulationSource keytrackSource; ControllerModulationSource polyAftertouchSource; ModulationSource monoAftertouchSource; diff --git a/src/common/dsp/SurgeVoiceState.h b/src/common/dsp/SurgeVoiceState.h index 71fcd22a81d..3d3a9f9e65a 100644 --- a/src/common/dsp/SurgeVoiceState.h +++ b/src/common/dsp/SurgeVoiceState.h @@ -7,12 +7,12 @@ struct SurgeVoiceState { bool gate; bool keep_playing, uberrelease; - float pitch, fvel, pkey, detune; + float pitch, fvel, pkey, detune, freleasevel; MidiKeyState* keyState; MidiChannelState* mainChannelState; MidiChannelState* voiceChannelState; - int key, velocity, channel, scene_id; + int key, velocity, channel, scene_id, releasevelocity; float portasrc_key, portaphase; float getPitch(); -}; \ No newline at end of file +}; diff --git a/src/common/gui/CModulationSourceButton.cpp b/src/common/gui/CModulationSourceButton.cpp index 9f33566087f..c5b4261e0b4 100644 --- a/src/common/gui/CModulationSourceButton.cpp +++ b/src/common/gui/CModulationSourceButton.cpp @@ -173,7 +173,15 @@ void CModulationSourceButton::draw(CDrawContext* dc) dc->drawRect(framer, kDrawFilled); dc->setFillColor(FillCol); dc->drawRect(fillr, kDrawFilled); - dc->drawString(label, txtbox, kCenterText, true); + + if( hasAlternate && useAlternate ) + { + dc->drawString(alternateLabel.c_str(), txtbox, kCenterText, true); + } + else + { + dc->drawString(label, txtbox, kCenterText, true); + } if (is_metacontroller) { diff --git a/src/common/gui/CModulationSourceButton.h b/src/common/gui/CModulationSourceButton.h index 8691a2c3a21..d47f41b3656 100644 --- a/src/common/gui/CModulationSourceButton.h +++ b/src/common/gui/CModulationSourceButton.h @@ -4,6 +4,8 @@ #pragma once #include "vstcontrols.h" #include "SurgeBitmaps.h" +#include + class CModulationSourceButton : public CCursorHidingControl { @@ -52,6 +54,18 @@ class CModulationSourceButton : public CCursorHidingControl { return state; } + + bool hasAlternate = false; + int alternateId; + std::string alternateLabel; + virtual void setAlternate( int alt, const std::string &altLabel ) { + hasAlternate = true; + this->alternateId = alt; + this->alternateLabel = altLabel; + } + bool useAlternate = false; + void setUseAlternate( bool f ) { useAlternate = f; if( hasAlternate ) { invalid(); setDirty(); } } + virtual void draw(VSTGUI::CDrawContext* dc); // virtual void mouse (VSTGUI::CDrawContext *pContext, VSTGUI::CPoint &where, long button = -1); virtual VSTGUI::CMouseEventResult onMouseDown(VSTGUI::CPoint& where, const VSTGUI::CButtonState& buttons); diff --git a/src/common/gui/SurgeGUIEditor.cpp b/src/common/gui/SurgeGUIEditor.cpp index 2478c162b0d..bf3c07adbd6 100644 --- a/src/common/gui/SurgeGUIEditor.cpp +++ b/src/common/gui/SurgeGUIEditor.cpp @@ -262,9 +262,10 @@ void SurgeGUIEditor::idle() for (int i = 1; i < n_modsources; i++) { - ((CModulationSourceButton*)gui_modsrc[i]) - ->update_rt_vals(synth->isActiveModulation(ptag, (modsources)i), 0, - synth->isModsourceUsed((modsources)i)); + if( gui_modsrc[i] ) + ((CModulationSourceButton*)gui_modsrc[i]) + ->update_rt_vals(synth->isActiveModulation(ptag, (modsources)i), 0, + synth->isModsourceUsed((modsources)i)); } synth->storage.CS_ModRouting.leave(); } @@ -274,8 +275,9 @@ void SurgeGUIEditor::idle() synth->storage.CS_ModRouting.enter(); for (int i = 1; i < n_modsources; i++) { - ((CModulationSourceButton*)gui_modsrc[i]) - ->update_rt_vals(false, 0, synth->isModsourceUsed((modsources)i)); + if( gui_modsrc[i] ) + ((CModulationSourceButton*)gui_modsrc[i]) + ->update_rt_vals(false, 0, synth->isModsourceUsed((modsources)i)); } synth->storage.CS_ModRouting.leave(); } @@ -298,7 +300,8 @@ void SurgeGUIEditor::idle() #endif for (int i = 1; i < n_modsources; i++) { - ((CModulationSourceButton*)gui_modsrc[i])->setblink(blinkstate); + if( gui_modsrc[i] ) + ((CModulationSourceButton*)gui_modsrc[i])->setblink(blinkstate); } blinkstate = !blinkstate; } @@ -467,10 +470,10 @@ void SurgeGUIEditor::idle() { int cc = j - metaparam_offset; gui_modsrc[ms_ctrl1 + cc]->setValue( - ((ControllerModulationSource*)synth->storage.getPatch() - .scene[0] - .modsources[ms_ctrl1 + i]) - ->get_target01()); + ((ControllerModulationSource*)synth->storage.getPatch() + .scene[0] + .modsources[ms_ctrl1 + i]) + ->get_target01()); } else if((j < n_total_params) && nonmod_param[j]) { @@ -534,6 +537,12 @@ void SurgeGUIEditor::toggle_mod_editing() void SurgeGUIEditor::refresh_mod() { + CModulationSourceButton *cms = (CModulationSourceButton *)gui_modsrc[modsource]; + + modsources thisms = modsource; + if( cms->hasAlternate && cms->useAlternate ) + thisms = (modsources)cms->alternateId; + synth->storage.CS_ModRouting.enter(); for (int i = 0; i < 512; i++) { @@ -544,10 +553,10 @@ void SurgeGUIEditor::refresh_mod() { s->setModMode(mod_editor ? 1 : 0); s->setModPresent(synth->isModDestUsed(i)); - s->setModCurrent(synth->isActiveModulation(i, modsource)); + s->setModCurrent(synth->isActiveModulation(i, thisms)); } // s->setDirty(); - s->setModValue(synth->getModulation(i, modsource)); + s->setModValue(synth->getModulation(i, thisms)); s->invalid(); } } @@ -555,7 +564,7 @@ void SurgeGUIEditor::refresh_mod() if (oscdisplay) { ((COscillatorDisplay*)oscdisplay)->setIsMod(mod_editor); - ((COscillatorDisplay*)oscdisplay)->setModSource(modsource); + ((COscillatorDisplay*)oscdisplay)->setModSource(thisms); oscdisplay->invalid(); oscdisplay->setDirty(true); } @@ -569,8 +578,11 @@ void SurgeGUIEditor::refresh_mod() state = mod_editor ? 2 : 1; if (i == modsource_editor) state |= 4; - ((CModulationSourceButton*)gui_modsrc[i])->state = state; - ((CModulationSourceButton*)gui_modsrc[i])->invalid(); + if( gui_modsrc[i] ) + { + ((CModulationSourceButton*)gui_modsrc[i])->state = state; + ((CModulationSourceButton*)gui_modsrc[i])->invalid(); + } } // ctnvg frame->redraw(); @@ -697,9 +709,11 @@ void SurgeGUIEditor::openOrRecreateEditor() } int rws = 15; - for (int k = 1; k < n_modsources; k++) + /* This loop bound is 1.6.* valid ONLY */ + for (int k = 1; k < /* n_modsources */ ms_releasevelocity; k++) { modsources ms = (modsources)k; + CRect r = positionForModulationGrid(ms); int state = 0; @@ -726,6 +740,14 @@ void SurgeGUIEditor::openOrRecreateEditor() else { ((CModulationSourceButton*)gui_modsrc[ms])->setlabel(modsource_abberations_button[ms]); + /* + ** Velocity special case for 1.6.* vintage + */ + if( ms == ms_velocity ) + { + ((CModulationSourceButton*)gui_modsrc[ms])->setAlternate(ms_releasevelocity, + modsource_abberations_button[ms_releasevelocity]); + } } frame->addView(gui_modsrc[ms]); } @@ -1708,6 +1730,7 @@ int32_t SurgeGUIEditor::controlModifierClicked(CControl* control, CButtonState b if (button & kRButton) { + CModulationSourceButton *cms = (CModulationSourceButton *)control; CRect menuRect; CPoint where; frame->getCurrentMouseLocation(where); @@ -1720,112 +1743,152 @@ int32_t SurgeGUIEditor::controlModifierClicked(CControl* control, CButtonState b int eid = 0; int id_clearallmr = -1, id_learnctrl = -1, id_clearctrl = -1, id_bipolar = -1, id_copy = -1, id_paste = -1, id_rename = -1; - contextMenu->addEntry((char*)modsource_abberations[modsource], eid++); - int n_md = 0; + if( cms->hasAlternate ) + { + int idOn = modsource; + int idOff = cms->alternateId; + if( cms->useAlternate ) + { + auto t = idOn; + idOn = idOff; + idOff = t; + } + + contextMenu->addEntry((char*)modsource_abberations[idOn], eid++); + std::string offLab = "Switch to "; + offLab += modsource_abberations[idOff]; + bool activeMod = (cms->state & 3) == 2; + + auto *mi = addCallbackMenu( + contextMenu, offLab, [cms]() { + cms->setUseAlternate( ! cms->useAlternate ); + } + ); + if( activeMod ) + mi->setEnabled(false); + eid++; + } + else + { + contextMenu->addEntry((char*)modsource_abberations[modsource], eid++); + } + int n_total_md = synth->storage.getPatch().param_ptr.size(); const int max_md = 4096; assert(max_md >= n_total_md); - bool first_destination = true; bool cancellearn = false; int ccid = 0; // should start at 0, but started at 1 before.. might be a reason but don't remember why... - for (int md = 0; md < n_total_md; md++) + std::vector possibleSources; + possibleSources.push_back(modsource); + if( cms->hasAlternate ) { - auto activeScene = synth->storage.getPatch().scene_active.val.i; - Parameter* parameter = synth->storage.getPatch().param_ptr[md]; + possibleSources.push_back((modsources)(cms->alternateId)); + } - if (((md < n_global_params) || ((parameter->scene - 1) == activeScene)) && - synth->isActiveModulation(md, modsource)) + for( auto thisms : possibleSources ) + { + bool first_destination = true; + int n_md = 0; + for (int md = 0; md < n_total_md; md++) { - char tmptxt[256]; - sprintf(tmptxt, "Clear %s -> %s [%.2f]", (char*)modsource_abberations[modsource], - synth->storage.getPatch().param_ptr[md]->get_full_name(), - synth->getModDepth(md, modsource)); - - auto clearOp = [this, first_destination, md, n_total_md, modsource, control]() { - bool resetName = false; // Should I reset the name? - std::string newName = ""; // And to what? - int ccid = modsource - ms_ctrl1; - + auto activeScene = synth->storage.getPatch().scene_active.val.i; + Parameter* parameter = synth->storage.getPatch().param_ptr[md]; + + if (((md < n_global_params) || ((parameter->scene - 1) == activeScene)) && + synth->isActiveModulation(md, thisms)) + { + char tmptxt[256]; + sprintf(tmptxt, "Clear %s -> %s [%.2f]", (char*)modsource_abberations[thisms], + synth->storage.getPatch().param_ptr[md]->get_full_name(), + synth->getModDepth(md, thisms)); + + auto clearOp = [this, first_destination, md, n_total_md, thisms, control]() { + bool resetName = false; // Should I reset the name? + std::string newName = ""; // And to what? + int ccid = thisms - ms_ctrl1; + + if (first_destination) + { + if (strncmp(synth->storage.getPatch().CustomControllerLabel[ccid], + synth->storage.getPatch().param_ptr[md]->get_name(), 15) == 0) + { + // So my modulator is named after my short name. I haven't been renamed. So + // I want to reset at least to "-" unless someone is after me + resetName = true; + newName = "-"; + + // Now we have to find if there's another modulation below me + int nextmd = md + 1; + while (nextmd < n_total_md && !synth->isActiveModulation(nextmd, thisms)) + nextmd++; + if (nextmd < n_total_md && + strlen(synth->storage.getPatch().param_ptr[nextmd]->get_name()) > 1) + newName = synth->storage.getPatch().param_ptr[nextmd]->get_name(); + } + } + + synth->clearModulation(md, thisms); + refresh_mod(); + + if (resetName) + { + // And this is where we apply the name refresh, of course. + strncpy(synth->storage.getPatch().CustomControllerLabel[ccid], newName.c_str(), + 15); + synth->storage.getPatch().CustomControllerLabel[ccid][15] = 0; + ((CModulationSourceButton*)control) + ->setlabel(synth->storage.getPatch().CustomControllerLabel[ccid]); + control->setDirty(); + control->invalid(); + synth->updateDisplay(); + } + }; + if (first_destination) { - if (strncmp(synth->storage.getPatch().CustomControllerLabel[ccid], - synth->storage.getPatch().param_ptr[md]->get_name(), 15) == 0) - { - // So my modulator is named after my short name. I haven't been renamed. So - // I want to reset at least to "-" unless someone is after me - resetName = true; - newName = "-"; - - // Now we have to find if there's another modulation below me - int nextmd = md + 1; - while (nextmd < n_total_md && !synth->isActiveModulation(nextmd, modsource)) - nextmd++; - if (nextmd < n_total_md && - strlen(synth->storage.getPatch().param_ptr[nextmd]->get_name()) > 1) - newName = synth->storage.getPatch().param_ptr[nextmd]->get_name(); - } - } - - synth->clearModulation(md, modsource); - refresh_mod(); - - if (resetName) - { - // And this is where we apply the name refresh, of course. - strncpy(synth->storage.getPatch().CustomControllerLabel[ccid], newName.c_str(), - 15); - synth->storage.getPatch().CustomControllerLabel[ccid][15] = 0; - ((CModulationSourceButton*)control) - ->setlabel(synth->storage.getPatch().CustomControllerLabel[ccid]); - control->setDirty(); - control->invalid(); - synth->updateDisplay(); + contextMenu->addEntry("-", eid++); + first_destination = false; } - }; + + addCallbackMenu(contextMenu, tmptxt, clearOp); + eid++; - if (first_destination) - { - contextMenu->addEntry("-", eid++); - first_destination = false; + n_md++; } - - addCallbackMenu(contextMenu, tmptxt, clearOp); + } + if (n_md) + { + char clearLab[256]; + sprintf( clearLab, "Clear all %s routings", modsource_abberations[thisms] ); + addCallbackMenu( + contextMenu, clearLab, [this, n_total_md, thisms, control]() { + for (int md = 1; md < n_total_md; md++) + synth->clearModulation(md, thisms); + refresh_mod(); + + // Also blank out the name and rebuild the UI + if (within_range(ms_ctrl1, thisms, ms_ctrl1 + n_customcontrollers - 1)) + { + int ccid = thisms - ms_ctrl1; + + synth->storage.getPatch().CustomControllerLabel[ccid][0] = '-'; + synth->storage.getPatch().CustomControllerLabel[ccid][1] = 0; + ((CModulationSourceButton*)control) + ->setlabel(synth->storage.getPatch().CustomControllerLabel[ccid]); + control->setDirty(); + control->invalid(); + + synth->updateDisplay(); + } + }); eid++; - - n_md++; } } - - if (n_md) - { - addCallbackMenu( - contextMenu, "Clear all routings", [this, n_total_md, modsource, control]() { - for (int md = 1; md < n_total_md; md++) - synth->clearModulation(md, modsource); - refresh_mod(); - - // Also blank out the name and rebuild the UI - if (within_range(ms_ctrl1, modsource, ms_ctrl1 + n_customcontrollers - 1)) - { - int ccid = modsource - ms_ctrl1; - - synth->storage.getPatch().CustomControllerLabel[ccid][0] = '-'; - synth->storage.getPatch().CustomControllerLabel[ccid][1] = 0; - ((CModulationSourceButton*)control) - ->setlabel(synth->storage.getPatch().CustomControllerLabel[ccid]); - control->setDirty(); - control->invalid(); - - synth->updateDisplay(); - } - }); - eid++; - } int sc = limit_range(synth->storage.getPatch().scene_active.val.i, 0, 1); if (within_range(ms_ctrl1, modsource, ms_ctrl1 + n_customcontrollers - 1)) { @@ -2151,7 +2214,8 @@ void SurgeGUIEditor::valueChanged(CControl* control) } else { - int state = ((CModulationSourceButton*)control)->get_state(); + CModulationSourceButton *cms = (CModulationSourceButton *)control; + int state = cms->get_state(); modsources newsource = (modsources)(tag - tag_mod_source0); long buttons = 0; // context->getMouseButtons(); // temp fix vstgui 3.5 bool ciep = @@ -2391,13 +2455,20 @@ void SurgeGUIEditor::valueChanged(CControl* control) if (modsource && mod_editor && synth->isValidModulation(p->id, modsource) && dynamic_cast(control) != nullptr) { - synth->setModulation(ptag, modsource, ((CSurgeSlider*)control)->getModValue()); + modsources thisms = modsource; + if( gui_modsrc[modsource] ) + { + CModulationSourceButton *cms = (CModulationSourceButton *)gui_modsrc[modsource]; + if( cms->hasAlternate && cms->useAlternate ) + thisms = (modsources) cms->alternateId; + } + synth->setModulation(ptag, thisms, ((CSurgeSlider*)control)->getModValue()); ((CSurgeSlider*)control)->setModPresent(synth->isModDestUsed(p->id)); - ((CSurgeSlider*)control)->setModCurrent(synth->isActiveModulation(p->id, modsource)); + ((CSurgeSlider*)control)->setModCurrent(synth->isActiveModulation(p->id, thisms)); synth->getParameterName(ptag, txt); - sprintf(pname, "%s -> %s", modsource_abberations_short[modsource], txt); - sprintf(pdisp, "%f", synth->getModDepth(ptag, modsource)); + sprintf(pname, "%s -> %s", modsource_abberations_short[thisms], txt); + sprintf(pdisp, "%f", synth->getModDepth(ptag, thisms)); ((CParameterTooltip*)infowindow)->setLabel(pname, pdisp); modulate = true; diff --git a/src/vst3/SurgeVst3Processor.cpp b/src/vst3/SurgeVst3Processor.cpp index d9fac3cf950..90460c80c36 100644 --- a/src/vst3/SurgeVst3Processor.cpp +++ b/src/vst3/SurgeVst3Processor.cpp @@ -237,6 +237,7 @@ void SurgeVst3Processor::processEvent(const Event& e) case Event::kNoteOnEvent: if (e.noteOn.velocity == 0.f) { + std::cout << "NoteOff with 0 v " << e.noteOff.velocity << std::endl; getSurge()->releaseNote(e.noteOn.channel, e.noteOn.pitch, e.noteOn.velocity); } else @@ -248,8 +249,11 @@ void SurgeVst3Processor::processEvent(const Event& e) break; case Event::kNoteOffEvent: - getSurge()->releaseNote(e.noteOff.channel, e.noteOff.pitch, e.noteOff.velocity); + { + char cVel = value01ToMidi7Bit(e.noteOff.velocity); + getSurge()->releaseNote(e.noteOff.channel, e.noteOff.pitch, cVel); break; + } case Event::kPolyPressureEvent: getSurge()->polyAftertouch(e.polyPressure.channel, e.polyPressure.pitch,