Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mono-Legato Envelope mode #6344

Merged
merged 1 commit into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 43 additions & 10 deletions src/common/SurgePatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1596,17 +1596,34 @@ void SurgePatch::load_xml(const void *data, int datasize, bool is_preset)
{
for (int sc = 0; sc < n_scenes; ++sc)
{
std::string mvname = "monoVoicePrority_" + std::to_string(sc);
auto *mv1 = TINYXML_SAFE_TO_ELEMENT(nonparamconfig->FirstChild(mvname.c_str()));
storage->getPatch().scene[sc].monoVoicePriorityMode = ALWAYS_LATEST;
if (mv1)
{
// Get value
int mvv;
if (mv1->QueryIntAttribute("v", &mvv) == TIXML_SUCCESS)
{
std::string mvname = "monoVoicePrority_" + std::to_string(sc);
auto *mv1 = TINYXML_SAFE_TO_ELEMENT(nonparamconfig->FirstChild(mvname.c_str()));
storage->getPatch().scene[sc].monoVoicePriorityMode = ALWAYS_LATEST;
if (mv1)
{
storage->getPatch().scene[sc].monoVoicePriorityMode =
(MonoVoicePriorityMode)mvv;
// Get value
int mvv;
if (mv1->QueryIntAttribute("v", &mvv) == TIXML_SUCCESS)
{
storage->getPatch().scene[sc].monoVoicePriorityMode =
(MonoVoicePriorityMode)mvv;
}
}
}
{
std::string mvname = "monoVoiceEnvelope_" + std::to_string(sc);
auto *mv1 = TINYXML_SAFE_TO_ELEMENT(nonparamconfig->FirstChild(mvname.c_str()));
storage->getPatch().scene[sc].monoVoiceEnvelopeMode = RESTART_FROM_ZERO;
if (mv1)
{
// Get value
int mvv;
if (mv1->QueryIntAttribute("v", &mvv) == TIXML_SUCCESS)
{
storage->getPatch().scene[sc].monoVoiceEnvelopeMode =
(MonoVoiceEnvelopeMode)mvv;
}
}
}
}
Expand Down Expand Up @@ -1883,6 +1900,14 @@ void SurgePatch::load_xml(const void *data, int datasize, bool is_preset)
polylimit.val.i = DEFAULT_POLYLIMIT;
}

if (revision < 20)
{
for (auto &sc : scene)
{
sc.monoVoiceEnvelopeMode = RESTART_FROM_ZERO;
}
}

// ensure that filtersubtype is a valid value
for (auto &sc : scene)
{
Expand Down Expand Up @@ -2621,6 +2646,14 @@ unsigned int SurgePatch::save_xml(void **data) // allocates mem, must be freed b
nonparamconfig.InsertEndChild(mvv);
}

for (int sc = 0; sc < n_scenes; ++sc)
{
std::string mvname = "monoVoiceEnvelope_" + std::to_string(sc);
TiXmlElement mvv(mvname.c_str());
mvv.SetAttribute("v", storage->getPatch().scene[sc].monoVoiceEnvelopeMode);
nonparamconfig.InsertEndChild(mvv);
}

TiXmlElement hcs("hardclipmodes");
hcs.SetAttribute("global", (int)(storage->hardclipMode));
for (int sc = 0; sc < n_scenes; ++sc)
Expand Down
12 changes: 10 additions & 2 deletions src/common/SurgeStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,12 @@ const int FIRoffsetI16 = FIRipolI16_N >> 1;
// added new Conditioner parameter (Side Low Cut)
// 17 -> 18 (XT 1.1 nightlies) added clipping options to Delay Feedback parameter (via deform)
// added Tone parameter to Phaser effect
// 18 -> 19 (XT 1.1 release) added String deform options (interpolation, bipolar Decay params, Stiffness options)
// 18 -> 19 (XT 1.1 nightlies) added String deform options (interpolation, bipolar Decay params, Stiffness options)
// added Extend to Delay Feedback parameter (allows negative delay)
// 19 -> 20 (XT 1.1 release) added voice envelope mode, but super late so don't break 19
// clang-format on

const int ff_revision = 19;
const int ff_revision = 20;

const int n_scene_params = 273;
const int n_global_params = 11 + n_fx_slots * (n_fx_params + 1); // each param plus a type
Expand Down Expand Up @@ -487,6 +488,12 @@ enum MonoVoicePriorityMode
ALWAYS_LOWEST,
};

enum MonoVoiceEnvelopeMode
{
RESTART_FROM_ZERO,
RESTART_FROM_LATEST
};

struct MidiKeyState
{
int keystate;
Expand Down Expand Up @@ -611,6 +618,7 @@ struct SurgeSceneStorage
bool modsource_doprocess[n_modsources];

MonoVoicePriorityMode monoVoicePriorityMode = ALWAYS_LATEST;
MonoVoiceEnvelopeMode monoVoiceEnvelopeMode = RESTART_FROM_ZERO;
};

const int n_stepseqsteps = 16;
Expand Down
30 changes: 27 additions & 3 deletions src/common/SurgeSynthesizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit
&storage, &storage.getPatch().scene[scene], storage.getPatch().scenedata[scene],
key, velocity, channel, scene, detune, &channelState[channel].keyState[key],
&channelState[mpeMainChannel], &channelState[channel], mpeEnabled, voiceCounter++,
host_noteid, host_originating_key, host_originating_channel);
host_noteid, host_originating_key, host_originating_channel, 0.f, 0.f);
}
break;
}
Expand Down Expand Up @@ -781,6 +781,7 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit
{
int32_t noteIdToReuse = -1;
int16_t channelToReuse, keyToReuse;
SurgeVoice *stealEnvelopesFrom{nullptr};
for (iter = voices[scene].begin(); iter != voices[scene].end(); iter++)
{
SurgeVoice *v = *iter;
Expand All @@ -792,6 +793,13 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit
noteIdToReuse = v->host_note_id;
channelToReuse = v->originating_host_channel;
keyToReuse = v->originating_host_key;
stealEnvelopesFrom = v;
}
else
{
// Non-gated voices only win if there's no gated voice
if (!stealEnvelopesFrom)
stealEnvelopesFrom = v;
}
v->uber_release();
}
Expand All @@ -806,6 +814,12 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit
}

SurgeVoice *nvoice = getUnusedVoice(scene);
float aegReuse{0.f}, fegReuse{0.f};
if (stealEnvelopesFrom &&
storage.getPatch().scene[scene].monoVoiceEnvelopeMode != RESTART_FROM_ZERO)
{
stealEnvelopesFrom->getAEGFEGLevel(aegReuse, fegReuse);
}
if (nvoice)
{
int mpeMainChannel = getMpeMainChannel(channel, key);
Expand All @@ -817,7 +831,8 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit
&storage, &storage.getPatch().scene[scene], storage.getPatch().scenedata[scene],
key, velocity, channel, scene, detune, &channelState[channel].keyState[key],
&channelState[mpeMainChannel], &channelState[channel], mpeEnabled,
voiceCounter++, host_noteid, host_originating_key, host_originating_channel);
voiceCounter++, host_noteid, host_originating_key, host_originating_channel,
aegReuse, fegReuse);
}
}
else
Expand All @@ -833,6 +848,7 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit
case pm_mono_st:
case pm_mono_st_fp:
{
std::cout << "PM MONO ST" << std::endl;
bool found_one = false;
int primode = storage.getPatch().scene[scene].monoVoicePriorityMode;
bool createVoice = true;
Expand Down Expand Up @@ -896,6 +912,8 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit
if (createVoice)
{
list<SurgeVoice *>::const_iterator iter;

float aegStart{0.}, fegStart{0.};
for (iter = voices[scene].begin(); iter != voices[scene].end(); iter++)
{
SurgeVoice *v = *iter;
Expand All @@ -920,7 +938,12 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit
else
{
if (v->state.scene_id == scene)
{
if (storage.getPatch().scene[scene].monoVoiceEnvelopeMode !=
RESTART_FROM_ZERO)
v->getAEGFEGLevel(aegStart, fegStart);
v->uber_release(); // make this optional for poly legato
}
}
}
if (!found_one)
Expand All @@ -930,13 +953,14 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit
SurgeVoice *nvoice = getUnusedVoice(scene);
if (nvoice)
{
std::cout << "SET UP FEG AEG" << std::endl;
voices[scene].push_back(nvoice);
new (nvoice) SurgeVoice(
&storage, &storage.getPatch().scene[scene],
storage.getPatch().scenedata[scene], key, velocity, channel, scene, detune,
&channelState[channel].keyState[key], &channelState[mpeMainChannel],
&channelState[channel], mpeEnabled, voiceCounter++, host_noteid,
host_originating_key, host_originating_channel);
host_originating_key, host_originating_channel, aegStart, fegStart);
}
}
else
Expand Down
7 changes: 4 additions & 3 deletions src/common/dsp/SurgeVoice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ SurgeVoice::SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *oscene, pdata *
int velocity, int channel, int scene_id, float detune,
MidiKeyState *keyState, MidiChannelState *mainChannelState,
MidiChannelState *voiceChannelState, bool mpeEnabled, int64_t voiceOrder,
int32_t host_nid, int16_t host_key, int16_t host_chan)
int32_t host_nid, int16_t host_key, int16_t host_chan, float aegStart,
float fegStart)
//: fb(storage,oscene)
{
// assign pointers
Expand Down Expand Up @@ -327,8 +328,8 @@ SurgeVoice::SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *oscene, pdata *

applyModulationToLocalcopy<true>();

ampEGSource.attack();
filterEGSource.attack();
ampEGSource.attackFrom(aegStart);
filterEGSource.attackFrom(fegStart);

for (int i = 0; i < n_lfos_voice; i++)
{
Expand Down
8 changes: 7 additions & 1 deletion src/common/dsp/SurgeVoice.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class alignas(16) SurgeVoice
int velocity, int channel, int scene_id, float detune, MidiKeyState *keyState,
MidiChannelState *mainChannelState, MidiChannelState *voiceChannelState,
bool mpeEnabled, int64_t voiceOrder, int32_t host_note_id,
int16_t originating_host_key, int16_t originating_host_channel);
int16_t originating_host_key, int16_t originating_host_channel, float aegStart,
float fegStart);
~SurgeVoice();

void release();
Expand Down Expand Up @@ -169,6 +170,11 @@ class alignas(16) SurgeVoice
}
}

void getAEGFEGLevel(float &aeg, float &feg)
{
aeg = ampEGSource.get_output(0);
feg = filterEGSource.get_output(0);
}
static float channelKeyEquvialent(float key, int channel, bool isMpeEnabled,
SurgeStorage *storage, bool remapKeyForTuning = true);

Expand Down
26 changes: 23 additions & 3 deletions src/common/dsp/modulators/ADSRModulationSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,36 @@ class ADSRModulationSource : public ModulationSource
attack();
}

virtual void attack() override
virtual void attack() override { attackFrom(0.f); }

virtual void attackFrom(float start)
{
phase = 0;
output = 0;
idlecount = 0;
scalestage = 1.f;

if (start > 0)
{
output = start;
switch (lc[a_s].i)
{
case 0:
// output = sqrt(phase);
phase = output * output;
break;
case 1:
phase = output;
break;
case 2:
// output = phase * phase;
phase = sqrt(output);
break;
};
}
// Reset the analog state machine too
_v_c1 = 0.f;
_v_c1_delayed = 0.f;
_v_c1 = start;
_v_c1_delayed = start;
_discharge = 0.f;

envstate = s_attack;
Expand Down
63 changes: 44 additions & 19 deletions src/surge-xt/gui/SurgeGUIEditorValueCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1673,29 +1673,54 @@ int32_t SurgeGUIEditor::controlModifierClicked(Surge::GUI::IComponentTagValue *c
(p->val.i == pm_mono || p->val.i == pm_mono_st ||
p->val.i == pm_mono_fp || p->val.i == pm_mono_st_fp))
{
std::vector<std::string> labels = {"Last", "High", "Low", "Legacy"};
std::vector<MonoVoicePriorityMode> vals = {
ALWAYS_LATEST, ALWAYS_HIGHEST, ALWAYS_LOWEST,
NOTE_ON_LATEST_RETRIGGER_HIGHEST};
{
std::vector<std::string> labels = {"Last", "High", "Low", "Legacy"};
std::vector<MonoVoicePriorityMode> vals = {
ALWAYS_LATEST, ALWAYS_HIGHEST, ALWAYS_LOWEST,
NOTE_ON_LATEST_RETRIGGER_HIGHEST};

contextMenu.addSectionHeader("NOTE PRIORITY");
contextMenu.addSectionHeader("NOTE PRIORITY");

for (int i = 0; i < 4; ++i)
{
bool isChecked = (vals[i] == synth->storage.getPatch()
.scene[current_scene]
.monoVoicePriorityMode);
contextMenu.addItem(Surge::GUI::toOSCase(labels[i]), true,
isChecked, [this, isChecked, vals, i]() {
synth->storage.getPatch()
.scene[current_scene]
.monoVoicePriorityMode = vals[i];
if (!isChecked)
synth->storage.getPatch().isDirty =
true;
});
for (int i = 0; i < 4; ++i)
{
bool isChecked = (vals[i] == synth->storage.getPatch()
.scene[current_scene]
.monoVoicePriorityMode);
contextMenu.addItem(Surge::GUI::toOSCase(labels[i]), true,
isChecked, [this, isChecked, vals, i]() {
synth->storage.getPatch()
.scene[current_scene]
.monoVoicePriorityMode = vals[i];
if (!isChecked)
synth->storage.getPatch().isDirty =
true;
});
}
}

{
std::vector<std::string> labels = {"Zero", "Current"};
std::vector<MonoVoiceEnvelopeMode> vals = {RESTART_FROM_ZERO,
RESTART_FROM_LATEST};

contextMenu.addSectionHeader("ENVELOPE RESTART FROM");

for (int i = 0; i < 2; ++i)
{
bool isChecked = (vals[i] == synth->storage.getPatch()
.scene[current_scene]
.monoVoiceEnvelopeMode);
contextMenu.addItem(Surge::GUI::toOSCase(labels[i]), true,
isChecked, [this, isChecked, vals, i]() {
synth->storage.getPatch()
.scene[current_scene]
.monoVoiceEnvelopeMode = vals[i];
if (!isChecked)
synth->storage.getPatch().isDirty =
true;
});
}
}
contextMenu.addSeparator();

contextMenu.addSubMenu(
Expand Down