diff --git a/plugins/common/plugin/InstrumentDescription.cpp b/plugins/common/plugin/InstrumentDescription.cpp index 7378fd03..071655b1 100644 --- a/plugins/common/plugin/InstrumentDescription.cpp +++ b/plugins/common/plugin/InstrumentDescription.cpp @@ -103,6 +103,7 @@ std::string getDescriptionBlob(sfizz_synth_t* handle) synth.sendMessage(*client, 0, "/key/slots", "", nullptr); synth.sendMessage(*client, 0, "/sw/last/slots", "", nullptr); synth.sendMessage(*client, 0, "/cc/slots", "", nullptr); + synth.sendMessage(*client, 0, "/sustain_or_sostenuto/slots", "", nullptr); blob.shrink_to_fit(); return blob; @@ -152,6 +153,8 @@ InstrumentDescription parseDescriptionBlob(absl::string_view blob) copyArgToBitSpan(args[0], desc.keyswitchUsed.span()); else if (Messages::matchOSC("/cc/slots", path, indices) && !strcmp(sig, "b")) copyArgToBitSpan(args[0], desc.ccUsed.span()); + else if (Messages::matchOSC("/sustain_or_sostenuto/slots", path, indices) && !strcmp(sig, "b")) + copyArgToBitSpan(args[0], desc.sustainOrSostenuto.span()); else if (Messages::matchOSC("/key&/label", path, indices) && !strcmp(sig, "s")) desc.keyLabel[indices[0]] = args[0].s; else if (Messages::matchOSC("/sw/last/&/label", path, indices) && !strcmp(sig, "s")) diff --git a/plugins/common/plugin/InstrumentDescription.h b/plugins/common/plugin/InstrumentDescription.h index b943dcba..094d06b3 100644 --- a/plugins/common/plugin/InstrumentDescription.h +++ b/plugins/common/plugin/InstrumentDescription.h @@ -26,6 +26,7 @@ struct InstrumentDescription { std::string image; BitArray<128> keyUsed {}; BitArray<128> keyswitchUsed {}; + BitArray<128> sustainOrSostenuto {}; BitArray ccUsed {}; std::array keyLabel {}; std::array keyswitchLabel {}; diff --git a/plugins/lv2/sfizz.cpp b/plugins/lv2/sfizz.cpp index d5157739..9bc3c4a1 100644 --- a/plugins/lv2/sfizz.cpp +++ b/plugins/lv2/sfizz.cpp @@ -683,6 +683,12 @@ sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, int delay, const LV2_Atom_Obj } } +static bool +sfizz_is_sustain_or_sostenuto(sfizz_plugin_t* self, unsigned cc) +{ + return (self->sustain_or_sostenuto[cc / 8] & (1 << (cc % 8))); +} + static void sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev) { @@ -704,14 +710,23 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev) (int)msg[1], msg[2]); break; - // Note(jpc) CC must be mapped by host, not handled here. - // See LV2 midi:binding. -#if defined(SFIZZ_LV2_PSA) case LV2_MIDI_MSG_CONTROLLER: { unsigned cc = msg[1]; float value = float(msg[2]) * (1.0f / 127.0f); + // Send + if (sfizz_is_sustain_or_sostenuto(self, cc)) { + sfizz_automate_hdcc(self->synth, + (int)ev->time.frames, + (int)cc, + value); + break; + } + +// Note(jpc) CC must be mapped by host, not handled here. +// See LV2 midi:binding. +#if defined(SFIZZ_LV2_PSA) switch (cc) { default: @@ -733,9 +748,9 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev) sfizz_all_sound_off(self->synth); break; } +#endif } break; -#endif case LV2_MIDI_MSG_CHANNEL_PRESSURE: sfizz_send_channel_aftertouch(self->synth, (int)ev->time.frames, @@ -1218,7 +1233,7 @@ sfizz_lv2_update_sfz_info(sfizz_plugin_t *self) // const InstrumentDescription desc = parseDescriptionBlob(blob); for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { - if (desc.ccUsed.test(cc)) { + if (desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc)) { // Mark all the used CCs for automation with default values self->ccauto[cc] = desc.ccDefault[cc]; self->have_ccauto = true; @@ -1226,6 +1241,9 @@ sfizz_lv2_update_sfz_info(sfizz_plugin_t *self) self->cc_current[cc] = desc.ccDefault[cc]; } } + + memcpy(self->sustain_or_sostenuto, desc.sustainOrSostenuto.data(), + sizeof(self->sustain_or_sostenuto)); } static bool @@ -1457,7 +1475,7 @@ save(LV2_Handle instance, self->sfz_blob_mutex->unlock(); for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { - if (desc.ccUsed.test(cc)) { + if (desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc)) { LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc)); store(handle, urid, diff --git a/plugins/lv2/sfizz_lv2_plugin.h b/plugins/lv2/sfizz_lv2_plugin.h index 90387684..9c1918f9 100644 --- a/plugins/lv2/sfizz_lv2_plugin.h +++ b/plugins/lv2/sfizz_lv2_plugin.h @@ -127,6 +127,9 @@ struct sfizz_plugin_t const uint8_t *volatile sfz_blob_data {}; volatile uint32_t sfz_blob_size {}; + // Sostenuto or sustain + char sustain_or_sostenuto[16] {}; + // Current CC values in the synth (synchronized by `synth_mutex`) // updated by hdcc or file load float *cc_current {}; diff --git a/plugins/lv2/sfizz_ui.cpp b/plugins/lv2/sfizz_ui.cpp index 3756c7f8..7e831989 100644 --- a/plugins/lv2/sfizz_ui.cpp +++ b/plugins/lv2/sfizz_ui.cpp @@ -447,7 +447,7 @@ sfizz_ui_update_description(sfizz_ui_t *self, const InstrumentDescription& desc) } for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { - bool ccUsed = desc.ccUsed.test(cc); + bool ccUsed = desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc); self->uiReceiveValue(editIdForCCUsed(cc), float(ccUsed)); if (ccUsed) { self->uiReceiveValue(editIdForCCDefault(cc), desc.ccDefault[cc]); diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index 9f9e614c..5bb96725 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -246,7 +246,7 @@ bool SfizzVstEditor::processUpdate(FUnknown* changedUnknown, int32 message) } for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { - bool ccUsed = desc.ccUsed.test(cc); + bool ccUsed = desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc); uiReceiveValue(editIdForCCUsed(int(cc)), float(ccUsed)); if (ccUsed) { uiReceiveValue(editIdForCCDefault(int(cc)), desc.ccDefault[cc]); diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 5a388357..c7893385 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -271,6 +271,7 @@ void Synth::Impl::clear() filePool.setRamLoading(config::loadInRam); clearCCLabels(); currentUsedCCs_.clear(); + sustainOrSostenuto_.clear(); changedCCsThisCycle_.clear(); changedCCsLastCycle_.clear(); clearKeyLabels(); @@ -2111,8 +2112,11 @@ void Synth::Impl::collectUsedCCsFromModulations(BitArray& usedCC BitArray Synth::Impl::collectAllUsedCCs() { BitArray used; - for (const LayerPtr& layerPtr : layers_) + for (const LayerPtr& layerPtr : layers_) { collectUsedCCsFromRegion(used, layerPtr->getRegion()); + sustainOrSostenuto_.set(layerPtr->region_.sustainCC); + sustainOrSostenuto_.set(layerPtr->region_.sostenutoCC); + } collectUsedCCsFromModulations(used, resources_.getModMatrix()); return used; } diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 166e10f5..9b00799f 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -180,6 +180,13 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'b'>(delay, path, &blob); } break; + MATCH("/sustain_or_sostenuto/slots", "") { + const BitArray<128>& sustainOrSostenuto = impl.sustainOrSostenuto_; + sfizz_blob_t blob { sustainOrSostenuto.data(), + static_cast(sustainOrSostenuto.byte_size()) }; + client.receive<'b'>(delay, path, &blob); + } break; + //---------------------------------------------------------------------- MATCH("/mem/buffers", "") { diff --git a/src/sfizz/SynthPrivate.h b/src/sfizz/SynthPrivate.h index 2597f417..44fd3b54 100644 --- a/src/sfizz/SynthPrivate.h +++ b/src/sfizz/SynthPrivate.h @@ -248,6 +248,7 @@ struct Synth::Impl final: public Parser::Listener { std::map keyLabelsMap_; BitArray<128> keySlots_; BitArray<128> swLastSlots_; + BitArray<128> sustainOrSostenuto_; std::vector keyswitchLabels_; std::map keyswitchLabelsMap_;