diff --git a/src/common/SurgeStorage.cpp b/src/common/SurgeStorage.cpp index 875bdd9ce0e..ceb7573bab7 100644 --- a/src/common/SurgeStorage.cpp +++ b/src/common/SurgeStorage.cpp @@ -1258,10 +1258,10 @@ int SurgeStorage::getAdjacentWaveTable(int id, bool nextPrev) const } } -void SurgeStorage::clipboard_copy(int type, int scene, int entry) +void SurgeStorage::clipboard_copy(int type, int scene, int entry, modsources ms) { bool includemod = false, includeall = false; - if (type == cp_oscmod) + if (type & cp_oscmod) { type = cp_osc; includemod = true; @@ -1271,9 +1271,8 @@ void SurgeStorage::clipboard_copy(int type, int scene, int entry) int id = -1; clipboard_type = type; - switch (type) + if (type & cp_osc) { - case cp_osc: cgroup = 2; cgroup_e = entry; id = getPatch().scene[scene].osc[entry].type.id; // first parameter id @@ -1285,8 +1284,9 @@ void SurgeStorage::clipboard_copy(int type, int scene, int entry) } memcpy(&clipboard_extraconfig[0], &getPatch().scene[scene].osc[entry].extraConfig, sizeof(OscillatorStorage::ExtraConfigurationData)); - break; - case cp_lfo: + } + if (type & cp_lfo) + { cgroup = 6; cgroup_e = entry + ms_lfo1; id = getPatch().scene[scene].lfo[entry].shape.id; @@ -1297,8 +1297,8 @@ void SurgeStorage::clipboard_copy(int type, int scene, int entry) clipboard_msegs[0] = getPatch().msegs[scene][entry]; if (getPatch().scene[scene].lfo[entry].shape.val.i == lt_formula) clipboard_formulae[0] = getPatch().formulamods[scene][entry]; - break; - case cp_scene: + } + if (type & cp_scene) { includemod = true; includeall = true; @@ -1320,17 +1320,13 @@ void SurgeStorage::clipboard_copy(int type, int scene, int entry) } clipboard_primode = getPatch().scene[scene].monoVoicePriorityMode; } - break; - default: - return; - } modRoutingMutex.lock(); { - clipboard_p.clear(); clipboard_modulation_scene.clear(); clipboard_modulation_voice.clear(); + clipboard_modulation_global.clear(); std::set used_entries; @@ -1377,14 +1373,62 @@ void SurgeStorage::clipboard_copy(int type, int scene, int entry) clipboard_modulation_scene.push_back(m); } } + + if (type & cp_modulator_target) + { + if (entry >= 0) + { + ms = (modsources)(entry + ms_lfo1); + } + n = getPatch().scene[scene].modulation_voice.size(); + for (int i = 0; i < n; i++) + { + if (getPatch().scene[scene].modulation_voice[i].source_id != ms) + continue; + ModulationRouting m; + m.source_id = getPatch().scene[scene].modulation_voice[i].source_id; + m.source_index = getPatch().scene[scene].modulation_voice[i].source_index; + m.depth = getPatch().scene[scene].modulation_voice[i].depth; + m.destination_id = getPatch().scene[scene].modulation_voice[i].destination_id; + clipboard_modulation_voice.push_back(m); + } + n = getPatch().scene[scene].modulation_scene.size(); + for (int i = 0; i < n; i++) + { + if (getPatch().scene[scene].modulation_scene[i].source_id != ms) + continue; + ModulationRouting m; + m.source_id = getPatch().scene[scene].modulation_scene[i].source_id; + m.source_index = getPatch().scene[scene].modulation_scene[i].source_index; + m.depth = getPatch().scene[scene].modulation_scene[i].depth; + m.destination_id = getPatch().scene[scene].modulation_scene[i].destination_id; + clipboard_modulation_scene.push_back(m); + } + n = getPatch().modulation_global.size(); + for (int i = 0; i < n; i++) + { + if (getPatch().modulation_global[i].source_id != ms || + getPatch().modulation_global[i].source_scene != scene) + continue; + ModulationRouting m; + m.source_id = getPatch().modulation_global[i].source_id; + m.source_index = getPatch().modulation_global[i].source_index; + m.source_scene = getPatch().modulation_global[i].source_scene; + m.depth = getPatch().modulation_global[i].depth; + m.destination_id = getPatch().modulation_global[i].destination_id; + clipboard_modulation_global.push_back(m); + } + } } modRoutingMutex.unlock(); } -void SurgeStorage::clipboard_paste(int type, int scene, int entry) +void SurgeStorage::clipboard_paste(int type, int scene, int entry, modsources ms, + std::function isValid) { assert(scene < n_scenes); - if (type != clipboard_type) + + if (!(type & clipboard_type)) return; int cgroup = -1; @@ -1393,12 +1437,12 @@ void SurgeStorage::clipboard_paste(int type, int scene, int entry) int n = clipboard_p.size(); int start = 0; - if (!n) - return; + // fixme make this one or the other + // if (!n) + // return; - switch (type) + if (type & cp_osc) { - case cp_osc: cgroup = 2; cgroup_e = entry; id = getPatch().scene[scene].osc[entry].type.id; // first parameter id @@ -1408,13 +1452,14 @@ void SurgeStorage::clipboard_paste(int type, int scene, int entry) memcpy(&getPatch().scene[scene].osc[entry].extraConfig, &clipboard_extraconfig[0], sizeof(OscillatorStorage::ExtraConfigurationData)); - break; - case cp_lfo: + } + if (type & cp_lfo) + { cgroup = 6; cgroup_e = entry + ms_lfo1; id = getPatch().scene[scene].lfo[entry].shape.id; - break; - case cp_scene: + } + if (type & cp_scene) { id = getPatch().scene[scene].octave.id; @@ -1437,14 +1482,16 @@ void SurgeStorage::clipboard_paste(int type, int scene, int entry) getPatch().scene[scene].monoVoicePriorityMode = clipboard_primode; } - break; - default: - return; + + if (type == cp_modulator_target) + { + // We use an == here rather than an & because in this case we want *only* the mod targets + // so disable the parameter copies + n = 0; } modRoutingMutex.lock(); { - for (int i = start; i < n; i++) { Parameter p = clipboard_p[i]; @@ -1460,9 +1507,7 @@ void SurgeStorage::clipboard_paste(int type, int scene, int entry) getPatch().param_ptr[pid]->deform_type = p.deform_type; } - switch (type) - { - case cp_osc: + if (type & cp_osc) { if (uses_wavetabledata(getPatch().scene[scene].osc[entry].type.val.i)) { @@ -1495,8 +1540,8 @@ void SurgeStorage::clipboard_paste(int type, int scene, int entry) getPatch().scene[scene].modulation_scene.push_back(m); } } - break; - case cp_lfo: + if (type & cp_lfo) + { if (getPatch().scene[scene].lfo[entry].shape.val.i == lt_stepseq) memcpy(&getPatch().stepsequences[scene][entry], &clipboard_stepsequences[0], sizeof(StepSequencerStorage)); @@ -1504,9 +1549,70 @@ void SurgeStorage::clipboard_paste(int type, int scene, int entry) getPatch().msegs[scene][entry] = clipboard_msegs[0]; if (getPatch().scene[scene].lfo[entry].shape.val.i == lt_formula) getPatch().formulamods[scene][entry] = clipboard_formulae[0]; - - break; - case cp_scene: + } + if (type & cp_modulator_target) + { + if (entry >= 0) + { + ms = (modsources)(entry + ms_lfo1); + } + // copy modroutings + n = clipboard_modulation_voice.size(); + for (int i = 0; i < n; i++) + { + ModulationRouting m; + m.source_id = ms; + m.source_index = clipboard_modulation_voice[i].source_index; + m.depth = clipboard_modulation_voice[i].depth; + m.destination_id = clipboard_modulation_voice[i].destination_id; + if (isValid(m.destination_id + getPatch().scene_start[scene], + (modsources)m.source_id)) + { + if (isScenelevel((modsources)m.source_id)) + { + getPatch().scene[scene].modulation_scene.push_back(m); + } + else + { + getPatch().scene[scene].modulation_voice.push_back(m); + } + } + } + n = clipboard_modulation_scene.size(); + for (int i = 0; i < n; i++) + { + ModulationRouting m; + m.source_id = ms; + m.source_index = clipboard_modulation_scene[i].source_index; + m.depth = clipboard_modulation_scene[i].depth; + m.destination_id = clipboard_modulation_scene[i].destination_id; + if (isValid(m.destination_id + getPatch().scene_start[scene], + (modsources)m.source_id)) + { + if (isScenelevel((modsources)m.source_id)) + { + getPatch().scene[scene].modulation_scene.push_back(m); + } + else + { + getPatch().scene[scene].modulation_voice.push_back(m); + } + } + } + n = clipboard_modulation_global.size(); + for (int i = 0; i < n; i++) + { + ModulationRouting m; + m.source_id = ms; + m.source_index = clipboard_modulation_global[i].source_index; + m.source_scene = scene; /* clipboard_modulation_global[i].source_scene; */ + m.depth = clipboard_modulation_global[i].depth; + m.destination_id = clipboard_modulation_global[i].destination_id; + if (isValid(m.destination_id, (modsources)m.source_id)) + getPatch().modulation_global.push_back(m); + } + } + if (type & cp_scene) { getPatch().scene[scene].modulation_voice.clear(); getPatch().scene[scene].modulation_scene.clear(); @@ -1533,7 +1639,6 @@ void SurgeStorage::clipboard_paste(int type, int scene, int entry) getPatch().scene[scene].modulation_scene.push_back(m); } } - } } modRoutingMutex.unlock(); diff --git a/src/common/SurgeStorage.h b/src/common/SurgeStorage.h index 588a01e1575..51a7744e7f0 100644 --- a/src/common/SurgeStorage.h +++ b/src/common/SurgeStorage.h @@ -880,12 +880,12 @@ struct PatchCategory enum surge_copysource { cp_off = 0, - cp_scene, - cp_osc, - cp_lfo, - cp_oscmod, - - n_copysources, + cp_scene = 1U << 1, + cp_osc = 1U << 2, + cp_oscmod = 1U << 3, + cp_lfo = 1U << 4, + cp_modulator_target = 1U << 5, + cp_lfomod = cp_lfo | cp_modulator_target }; class MTSClient; @@ -1014,8 +1014,15 @@ class alignas(16) SurgeStorage // void load_wt_wav(std::string filename, Wavetable* wt); bool load_wt_wav_portable(std::string filename, Wavetable *wt); std::string export_wt_wav_portable(std::string fbase, Wavetable *wt); - void clipboard_copy(int type, int scene, int entry); - void clipboard_paste(int type, int scene, int entry); + void clipboard_copy(int type, int scene, int entry, modsources ms = ms_original); + // this function is a bit of a hack to stop me having a reference to a surge synth + // here and also stop me having to move all of isValidModulation and its buddies onto + // storage + void clipboard_paste( + int type, int scene, int entry, modsources ms = ms_original, + std::function isValidModulation = [](auto a, auto b) { + return true; + }); int get_clipboard_type() const; int getAdjacentWaveTable(int id, bool nextPrev) const; @@ -1275,7 +1282,8 @@ class alignas(16) SurgeStorage MSEGStorage clipboard_msegs[n_lfos]; FormulaModulatorStorage clipboard_formulae[n_lfos]; OscillatorStorage::ExtraConfigurationData clipboard_extraconfig[n_oscs]; - std::vector clipboard_modulation_scene, clipboard_modulation_voice; + std::vector clipboard_modulation_scene, clipboard_modulation_voice, + clipboard_modulation_global; Wavetable clipboard_wt[n_oscs]; char clipboard_wt_names[n_oscs][256]; MonoVoicePriorityMode clipboard_primode = NOTE_ON_LATEST_RETRIGGER_HIGHEST; diff --git a/src/surge-xt/gui/SurgeGUIEditorValueCallbacks.cpp b/src/surge-xt/gui/SurgeGUIEditorValueCallbacks.cpp index a78c6dccff2..0ea6f53d8e9 100644 --- a/src/surge-xt/gui/SurgeGUIEditorValueCallbacks.cpp +++ b/src/surge-xt/gui/SurgeGUIEditorValueCallbacks.cpp @@ -831,10 +831,13 @@ int32_t SurgeGUIEditor::controlModifierClicked(Surge::GUI::IComponentTagValue *c contextMenu.addSeparator(); - auto hamSub = juce::PopupMenu(); - cms->buildHamburgerMenu(hamSub, false); - contextMenu.addSubMenu(Surge::GUI::toOSCaseForMenu("Switch To"), hamSub); - contextMenu.addSeparator(); + if (cms->needsHamburger()) + { + auto hamSub = juce::PopupMenu(); + cms->buildHamburgerMenu(hamSub, false); + contextMenu.addSubMenu(Surge::GUI::toOSCaseForMenu("Switch To"), hamSub); + contextMenu.addSeparator(); + } if (within_range(ms_ctrl1, modsource, ms_ctrl1 + n_customcontrollers - 1)) { @@ -929,21 +932,54 @@ int32_t SurgeGUIEditor::controlModifierClicked(Surge::GUI::IComponentTagValue *c contextMenu.addItem(Surge::GUI::toOSCaseForMenu("Copy Modulator"), [this, sc, lfo_id]() { - if (lfo_id >= 0) - { - synth->storage.clipboard_copy(cp_lfo, sc, lfo_id); - mostRecentCopiedMSEGState = msegEditState[sc][lfo_id]; - } + synth->storage.clipboard_copy(cp_lfo, sc, lfo_id); + mostRecentCopiedMSEGState = msegEditState[sc][lfo_id]; }); - if (synth->storage.get_clipboard_type() == cp_lfo) + contextMenu.addItem(Surge::GUI::toOSCaseForMenu("Copy Modulator with Targets"), + [this, sc, lfo_id]() { + synth->storage.clipboard_copy(cp_lfomod, sc, lfo_id); + mostRecentCopiedMSEGState = msegEditState[sc][lfo_id]; + }); + + contextMenu.addItem( + Surge::GUI::toOSCaseForMenu("Copy Targets"), [this, sc, lfo_id]() { + synth->storage.clipboard_copy(cp_modulator_target, sc, lfo_id); + }); + + if (synth->storage.get_clipboard_type() & cp_lfo || + synth->storage.get_clipboard_type() & cp_modulator_target) { - contextMenu.addItem(Surge::GUI::toOSCaseForMenu("Paste"), [this, sc, lfo_id]() { - if (lfo_id >= 0) - { - synth->storage.clipboard_paste(cp_lfo, sc, lfo_id); - msegEditState[sc][lfo_id] = mostRecentCopiedMSEGState; - } + auto t = synth->storage.get_clipboard_type(); + contextMenu.addItem( + Surge::GUI::toOSCaseForMenu("Paste"), [this, sc, t, lfo_id]() { + synth->storage.clipboard_paste( + t, sc, lfo_id, ms_original, [this](int p, modsources m) { + auto res = synth->isValidModulation(p, m); + return res; + }); + if (t & cp_lfo) + msegEditState[sc][lfo_id] = mostRecentCopiedMSEGState; + queue_refresh = true; + }); + } + } + else + { + contextMenu.addItem( + Surge::GUI::toOSCaseForMenu("Copy Targets"), [this, sc, modsource]() { + synth->storage.clipboard_copy(cp_modulator_target, sc, -1, modsource); + }); + + if (synth->storage.get_clipboard_type() & cp_modulator_target) + { + contextMenu.addItem(Surge::GUI::toOSCaseForMenu("Paste"), [this, sc, + modsource]() { + synth->storage.clipboard_paste( + cp_modulator_target, sc, -1, modsource, [this](int p, modsources m) { + auto res = synth->isValidModulation(p, m); + return res; + }); queue_refresh = true; }); }