From 6fc69520c40dca13efae9b3d1430d461ed1192f9 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 2 Apr 2021 03:25:32 +0200 Subject: [PATCH 01/17] Fix an OSC pattern --- src/sfizz/SynthMessaging.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index c215d305..0b4f2922 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -45,7 +45,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'b'>(delay, path, &blob); } break; - MATCH("/key%d/label", "") { + MATCH("/key&/label", "") { if (indices[0] >= 128) break; const std::string* label = impl.getKeyLabel(indices[0]); From b272b1a3e373c0e232e82751235701c6e16a5dda Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 1 Apr 2021 21:48:31 +0200 Subject: [PATCH 02/17] Helper to extract a description of the instrument --- plugins/CMakeLists.txt | 7 +- .../common/plugin/InstrumentDescription.cpp | 183 ++++++++++++++++++ plugins/common/plugin/InstrumentDescription.h | 29 +++ 3 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 plugins/common/plugin/InstrumentDescription.cpp create mode 100644 plugins/common/plugin/InstrumentDescription.h diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index a1fe191b..ad2c4871 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,10 +1,13 @@ add_library(plugins-common STATIC EXCLUDE_FROM_ALL "common/plugin/MessageUtils.h" - "common/plugin/MessageUtils.cpp") + "common/plugin/MessageUtils.cpp" + "common/plugin/InstrumentDescription.h" + "common/plugin/InstrumentDescription.cpp") target_include_directories(plugins-common PUBLIC "common") target_link_libraries(plugins-common PUBLIC sfizz::spin_mutex - PUBLIC sfizz::filesystem absl::strings) + PUBLIC sfizz::filesystem absl::strings + PRIVATE sfizz::internal) add_library(sfizz::plugins-common ALIAS plugins-common) if((SFIZZ_LV2 AND SFIZZ_LV2_UI) OR SFIZZ_VST) diff --git a/plugins/common/plugin/InstrumentDescription.cpp b/plugins/common/plugin/InstrumentDescription.cpp new file mode 100644 index 00000000..98865bfb --- /dev/null +++ b/plugins/common/plugin/InstrumentDescription.cpp @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "InstrumentDescription.h" +#include "MessageUtils.h" +#include "sfizz.hpp" +#include +#include +#include +#include +#include +#include + +template +static void bufferedStrCat(std::string* buffer, const Args&... args) +{ + buffer->clear(); + absl::StrAppend(buffer, args...); +} + +std::string getDescriptionBlob(sfizz_synth_t* handle) +{ + std::string blob; + blob.reserve(128 * 1024); + + std::vector msgbuf; + msgbuf.resize(1024); + + std::string pathbuf; + pathbuf.reserve(256); + + struct ClientData { + sfz::Sfizz* synth = nullptr; + sfz::Client* client = nullptr; + std::string* blob = nullptr; + std::vector* msgbuf = nullptr; + std::string* pathbuf = nullptr; + }; + + sfz::Sfizz synth(handle); + ClientData cdata; + sfz::ClientPtr client = synth.createClient(&cdata); + cdata.synth = &synth; + cdata.client = client.get(); + cdata.blob = &blob; + cdata.msgbuf = &msgbuf; + cdata.pathbuf = &pathbuf; + + synth.setReceiveCallback(*client, [](void* data, int, const char* path, const char* sig, const sfizz_arg_t* args) { + ClientData& cdata = *reinterpret_cast(data); + unsigned indices[8]; + + /// + uint32_t msglen = sfizz_prepare_message(cdata.msgbuf->data(), cdata.msgbuf->size(), path, sig, args); + if (msglen > cdata.msgbuf->size()) { + cdata.msgbuf->resize(msglen); + sfizz_prepare_message(cdata.msgbuf->data(), cdata.msgbuf->size(), path, sig, args); + } + cdata.blob->append(cdata.msgbuf->data(), msglen); + + /// + if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b")) { + ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size); + for (unsigned key = 0; key < 128 && key < bits.bit_size(); ++key) { + if (bits.test(key)) { + bufferedStrCat(cdata.pathbuf, "/key", key, "/label"); + cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); + } + } + } + else if (Messages::matchOSC("/sw/last/slots", path, indices) && !strcmp(sig, "b")) { + ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size); + for (unsigned key = 0; key < 128 && key < bits.bit_size(); ++key) { + if (bits.test(key)) { + bufferedStrCat(cdata.pathbuf, "/sw/last/", key, "/label"); + cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); + } + } + } + else if (Messages::matchOSC("/cc/slots", path, indices) && !strcmp(sig, "b")) { + ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size); + for (unsigned cc = 0; cc < sfz::config::numCCs && cc < bits.bit_size(); ++cc) { + if (bits.test(cc)) { + bufferedStrCat(cdata.pathbuf, "/cc", cc, "/label"); + cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); + bufferedStrCat(cdata.pathbuf, "/cc", cc, "/default"); + cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); + } + } + } + }); + + synth.sendMessage(*client, 0, "/key/slots", "", nullptr); + synth.sendMessage(*client, 0, "/sw/last/slots", "", nullptr); + synth.sendMessage(*client, 0, "/cc/slots", "", nullptr); + + blob.shrink_to_fit(); + return blob; +} + +InstrumentDescription parseDescriptionBlob(absl::string_view blob) +{ + InstrumentDescription desc; + + const uint8_t* src = reinterpret_cast(blob.data()); + uint32_t srcSize = blob.size(); + char buffer[1024]; + + const char* path; + const char* sig; + const sfizz_arg_t* args; + + int32_t byteCount; + while ((byteCount = sfizz_extract_message(src, srcSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) { + unsigned indices[8]; + + /// + auto copyArgToBitSpan = [](const sfizz_arg_t& arg, BitSpan bits) + { + size_t size = std::min(bits.byte_size(), arg.b->size); + memcpy(bits.data(), arg.b->data, size); + }; + + // + if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b")) + copyArgToBitSpan(args[0], desc.keyUsed.span()); + else if (Messages::matchOSC("/sw/last/slots", path, indices) && !strcmp(sig, "b")) + 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("/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")) + desc.keyswitchLabel[indices[0]] = args[0].s; + else if (Messages::matchOSC("/cc&/label", path, indices) && !strcmp(sig, "s")) + desc.ccLabel[indices[0]] = args[0].s; + else if (Messages::matchOSC("/cc&/default", path, indices) && !strcmp(sig, "f")) + desc.ccDefault[indices[0]] = args[0].f; + + src += byteCount; + srcSize -= byteCount; + } + + return desc; +} + +std::ostream& operator<<(std::ostream& os, const InstrumentDescription& desc) +{ + os << "instrument:\n"; + + os << " keys:\n"; + for (unsigned i = 0; i < 128; ++i) { + if (desc.keyUsed.test(i)) { + os << " - number: " << i << "\n"; + if (!desc.keyLabel[i].empty()) + os << " label: " << desc.keyLabel[i].c_str() << "\n"; + } + } + + os << " keyswitches:\n"; + for (unsigned i = 0; i < 128; ++i) { + if (desc.keyswitchUsed.test(i)) { + os << " - number: " << i << "\n"; + if (!desc.keyswitchLabel[i].empty()) + os << " label: " << desc.keyswitchLabel[i].c_str() << "\n"; + } + } + + os << " cc:\n"; + for (unsigned i = 0; i < sfz::config::numCCs; ++i) { + if (desc.ccUsed.test(i)) { + os << " - number: " << i << "\n"; + os << " default: " << desc.ccDefault[i] << "\n"; + if (!desc.ccLabel[i].empty()) + os << " label: " << desc.ccLabel[i].c_str() << "\n"; + } + } + + return os; +} diff --git a/plugins/common/plugin/InstrumentDescription.h b/plugins/common/plugin/InstrumentDescription.h new file mode 100644 index 00000000..9caf5ede --- /dev/null +++ b/plugins/common/plugin/InstrumentDescription.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "sfizz/Config.h" +#include "sfizz/utility/bit_array/BitArray.h" +#include +#include +#include +#include +struct sfizz_synth_t; + +struct InstrumentDescription { + BitArray<128> keyUsed {}; + BitArray<128> keyswitchUsed {}; + BitArray ccUsed {}; + std::array keyLabel {}; + std::array keyswitchLabel {}; + std::array ccLabel {}; + std::array ccDefault {}; +}; + +std::string getDescriptionBlob(sfizz_synth_t* handle); +InstrumentDescription parseDescriptionBlob(absl::string_view blob); + +std::ostream& operator<<(std::ostream& os, const InstrumentDescription& desc); From 9d3e365f2e8445552fd38bb2f5869670ea7b08d0 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 2 Apr 2021 04:05:14 +0200 Subject: [PATCH 03/17] Instrument description in VST and LV2 --- plugins/CMakeLists.txt | 2 +- .../common/plugin/InstrumentDescription.cpp | 23 ++- plugins/common/plugin/InstrumentDescription.h | 22 +++ plugins/lv2/CMakeLists.txt | 2 + plugins/lv2/sfizz.cpp | 175 ++++-------------- plugins/lv2/sfizz_lv2.h | 40 ++++ plugins/lv2/sfizz_lv2_common.cpp | 31 ++++ plugins/lv2/sfizz_lv2_plugin.h | 131 +++++++++++++ plugins/lv2/sfizz_ui.cpp | 45 +++++ plugins/lv2/sfizz_ui.ttl.in | 3 +- plugins/vst/SfizzVstController.cpp | 56 +++--- plugins/vst/SfizzVstController.h | 4 +- plugins/vst/SfizzVstEditor.cpp | 17 +- plugins/vst/SfizzVstProcessor.cpp | 52 ++++-- plugins/vst/SfizzVstProcessor.h | 5 +- plugins/vst/SfizzVstUpdates.h | 43 +++-- src/sfizz/SynthMessaging.cpp | 22 +++ 17 files changed, 464 insertions(+), 209 deletions(-) create mode 100644 plugins/lv2/sfizz_lv2_common.cpp create mode 100644 plugins/lv2/sfizz_lv2_plugin.h diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index ad2c4871..4e0d1600 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -7,7 +7,7 @@ target_include_directories(plugins-common PUBLIC "common") target_link_libraries(plugins-common PUBLIC sfizz::spin_mutex PUBLIC sfizz::filesystem absl::strings - PRIVATE sfizz::internal) + PRIVATE sfizz::internal sfizz::sfizz) add_library(sfizz::plugins-common ALIAS plugins-common) if((SFIZZ_LV2 AND SFIZZ_LV2_UI) OR SFIZZ_VST) diff --git a/plugins/common/plugin/InstrumentDescription.cpp b/plugins/common/plugin/InstrumentDescription.cpp index 98865bfb..e104cc10 100644 --- a/plugins/common/plugin/InstrumentDescription.cpp +++ b/plugins/common/plugin/InstrumentDescription.cpp @@ -93,6 +93,11 @@ std::string getDescriptionBlob(sfizz_synth_t* handle) } }); + synth.sendMessage(*client, 0, "/num_regions", "", nullptr); + synth.sendMessage(*client, 0, "/num_groups", "", nullptr); + synth.sendMessage(*client, 0, "/num_masters", "", nullptr); + synth.sendMessage(*client, 0, "/num_curves", "", nullptr); + synth.sendMessage(*client, 0, "/num_samples", "", nullptr); synth.sendMessage(*client, 0, "/key/slots", "", nullptr); synth.sendMessage(*client, 0, "/sw/last/slots", "", nullptr); synth.sendMessage(*client, 0, "/cc/slots", "", nullptr); @@ -125,7 +130,17 @@ InstrumentDescription parseDescriptionBlob(absl::string_view blob) }; // - if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b")) + if (Messages::matchOSC("/num_regions", path, indices) && !strcmp(sig, "i")) + desc.numRegions = uint32_t(args[0].i); + else if (Messages::matchOSC("/num_groups", path, indices) && !strcmp(sig, "i")) + desc.numGroups = uint32_t(args[0].i); + else if (Messages::matchOSC("/num_masters", path, indices) && !strcmp(sig, "i")) + desc.numMasters = uint32_t(args[0].i); + else if (Messages::matchOSC("/num_curves", path, indices) && !strcmp(sig, "i")) + desc.numCurves = uint32_t(args[0].i); + else if (Messages::matchOSC("/num_samples", path, indices) && !strcmp(sig, "i")) + desc.numSamples = uint32_t(args[0].i); + else if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b")) copyArgToBitSpan(args[0], desc.keyUsed.span()); else if (Messages::matchOSC("/sw/last/slots", path, indices) && !strcmp(sig, "b")) copyArgToBitSpan(args[0], desc.keyswitchUsed.span()); @@ -151,6 +166,12 @@ std::ostream& operator<<(std::ostream& os, const InstrumentDescription& desc) { os << "instrument:\n"; + os << " regions: " << desc.numRegions << "\n"; + os << " groups: " << desc.numGroups << "\n"; + os << " masters: " << desc.numMasters << "\n"; + os << " curves: " << desc.numCurves << "\n"; + os << " samples: " << desc.numSamples << "\n"; + os << " keys:\n"; for (unsigned i = 0; i < 128; ++i) { if (desc.keyUsed.test(i)) { diff --git a/plugins/common/plugin/InstrumentDescription.h b/plugins/common/plugin/InstrumentDescription.h index 9caf5ede..88478a2d 100644 --- a/plugins/common/plugin/InstrumentDescription.h +++ b/plugins/common/plugin/InstrumentDescription.h @@ -13,7 +13,15 @@ #include struct sfizz_synth_t; +/** + * @brief Description of user-interactible elements of the SFZ instrument + */ struct InstrumentDescription { + uint32_t numRegions {}; + uint32_t numGroups {}; + uint32_t numMasters {}; + uint32_t numCurves {}; + uint32_t numSamples {}; BitArray<128> keyUsed {}; BitArray<128> keyswitchUsed {}; BitArray ccUsed {}; @@ -23,7 +31,21 @@ struct InstrumentDescription { std::array ccDefault {}; }; +/** + * @brief Produce a description of the currently loaded instrument in the synth, + * in the form of a concatenation of OSC messages. + * + * This form is a message transmissible over binary channels. + */ std::string getDescriptionBlob(sfizz_synth_t* handle); + +/** + * @brief Extract the information from the OSC blob and rearrange it in a + * structured form. + */ InstrumentDescription parseDescriptionBlob(absl::string_view blob); +/** + * @brief Display the description in human-readable format. + */ std::ostream& operator<<(std::ostream& os, const InstrumentDescription& desc); diff --git a/plugins/lv2/CMakeLists.txt b/plugins/lv2/CMakeLists.txt index ff97e859..3f9a7247 100644 --- a/plugins/lv2/CMakeLists.txt +++ b/plugins/lv2/CMakeLists.txt @@ -20,12 +20,14 @@ source_group("Turtle Files" FILES ) add_library(${LV2PLUGIN_PRJ_NAME} MODULE ${PROJECT_NAME}.cpp + ${PROJECT_NAME}_lv2_common.cpp ${LV2PLUGIN_TTL_SRC_FILES}) target_link_libraries(${LV2PLUGIN_PRJ_NAME} PRIVATE sfizz::sfizz sfizz::import sfizz::plugins-common) if(SFIZZ_LV2_UI) add_library(${LV2PLUGIN_PRJ_NAME}_ui MODULE ${PROJECT_NAME}_ui.cpp + ${PROJECT_NAME}_lv2_common.cpp vstgui_helpers.h vstgui_helpers.cpp) target_link_libraries(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE sfizz::editor sfizz::vstgui sfizz::plugins-common) diff --git a/plugins/lv2/sfizz.cpp b/plugins/lv2/sfizz.cpp index 4ba2503c..edf486bf 100644 --- a/plugins/lv2/sfizz.cpp +++ b/plugins/lv2/sfizz.cpp @@ -33,38 +33,15 @@ */ #include "sfizz_lv2.h" +#include "sfizz_lv2_plugin.h" #include "sfizz/import/ForeignInstrument.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include +#include "plugin/InstrumentDescription.h" #include -#include -#include #include #include -#include - #define CHANNEL_MASK 0x0F #define MIDI_CHANNEL(byte) (byte & CHANNEL_MASK) #define MIDI_STATUS(byte) (byte & ~CHANNEL_MASK) @@ -77,122 +54,12 @@ #define LOG_SAMPLE_COUNT 48000 #define UNUSED(x) (void)(x) -#define DEFAULT_SCALA_FILE "Contents/Resources/DefaultScale.scl" -#define DEFAULT_SFZ_FILE "Contents/Resources/DefaultInstrument.sfz" -// This assumes that the longest path is the default sfz file; if not, change it -#define MAX_BUNDLE_PATH_SIZE (MAX_PATH_SIZE - sizeof(DEFAULT_SFZ_FILE)) - #ifndef NDEBUG #define LV2_DEBUG(...) lv2_log_note(&self->logger, "[DEBUG] " __VA_ARGS__) #else #define LV2_DEBUG(...) #endif -struct sfizz_plugin_t -{ - // Features - LV2_URID_Map *map {}; - LV2_URID_Unmap *unmap {}; - LV2_Worker_Schedule *worker {}; - LV2_Log_Log *log {}; - LV2_Midnam *midnam {}; - - // Ports - const LV2_Atom_Sequence *control_port {}; - LV2_Atom_Sequence *notify_port {}; - float *output_buffers[2] {}; - const float *volume_port {}; - const float *polyphony_port {}; - const float *oversampling_port {}; - const float *preload_port {}; - const float *freewheel_port {}; - const float *scala_root_key_port {}; - const float *tuning_frequency_port {}; - const float *stretch_tuning_port {}; - float *active_voices_port {}; - float *num_curves_port {}; - float *num_masters_port {}; - float *num_groups_port {}; - float *num_regions_port {}; - float *num_samples_port {}; - - // Atom forge - LV2_Atom_Forge forge {}; ///< Forge for writing atoms in run thread - LV2_Atom_Forge forge_secondary {}; ///< Forge for writing into other buffers - - // Logger - LV2_Log_Logger logger {}; - - // URIs - LV2_URID midi_event_uri {}; - LV2_URID options_interface_uri {}; - LV2_URID max_block_length_uri {}; - LV2_URID nominal_block_length_uri {}; - LV2_URID sample_rate_uri {}; - LV2_URID atom_object_uri {}; - LV2_URID atom_blank_uri {}; - LV2_URID atom_float_uri {}; - LV2_URID atom_double_uri {}; - LV2_URID atom_int_uri {}; - LV2_URID atom_long_uri {}; - LV2_URID atom_urid_uri {}; - LV2_URID atom_path_uri {}; - LV2_URID patch_set_uri {}; - LV2_URID patch_get_uri {}; - LV2_URID patch_put_uri {}; - LV2_URID patch_property_uri {}; - LV2_URID patch_value_uri {}; - LV2_URID patch_body_uri {}; - LV2_URID state_changed_uri {}; - LV2_URID sfizz_sfz_file_uri {}; - LV2_URID sfizz_scala_file_uri {}; - LV2_URID sfizz_num_voices_uri {}; - LV2_URID sfizz_preload_size_uri {}; - LV2_URID sfizz_oversampling_uri {}; - LV2_URID sfizz_log_status_uri {}; - LV2_URID sfizz_check_modification_uri {}; - LV2_URID sfizz_active_voices_uri {}; - LV2_URID sfizz_osc_blob_uri {}; - LV2_URID time_position_uri {}; - LV2_URID time_bar_uri {}; - LV2_URID time_bar_beat_uri {}; - LV2_URID time_beat_unit_uri {}; - LV2_URID time_beats_per_bar_uri {}; - LV2_URID time_beats_per_minute_uri {}; - LV2_URID time_speed_uri {}; - - // Sfizz related data - sfizz_synth_t *synth {}; - sfizz_client_t *client {}; - spin_mutex_t *synth_mutex {}; - bool expect_nominal_block_length {}; - char sfz_file_path[MAX_PATH_SIZE] {}; - char scala_file_path[MAX_PATH_SIZE] {}; - int num_voices {}; - unsigned int preload_size {}; - sfizz_oversampling_factor_t oversampling {}; - float stretch_tuning {}; - volatile bool check_modification {}; - int max_block_size {}; - int sample_counter {}; - float sample_rate {}; - std::atomic must_update_midnam {}; - - // Timing data - int bar {}; - double bar_beat {}; - int beats_per_bar {}; - int beat_unit {}; - double bpm_tempo {}; - double speed {}; - - // Paths - char bundle_path[MAX_BUNDLE_PATH_SIZE] {}; - - // OSC - uint8_t osc_temp[OSC_TEMP_SIZE] {}; -}; - enum { SFIZZ_TIMEINFO_POSITION = 1 << 0, @@ -201,6 +68,14 @@ enum SFIZZ_TIMEINFO_SPEED = 1 << 3, }; +/// +static bool +sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path); + +static bool +sfizz_lv2_load_scala_file(sfizz_plugin_t *self, const char *file_path); + +/// static void sfizz_lv2_state_free_path(LV2_State_Free_Path_Handle handle, char *path) @@ -607,11 +482,10 @@ instantiate(const LV2_Descriptor *descriptor, sfizz_set_broadcast_callback(self->synth, &sfizz_lv2_receive_message, self); sfizz_set_receive_callback(self->client, &sfizz_lv2_receive_message); - sfizz_lv2_get_default_sfz_path(self, self->sfz_file_path, MAX_PATH_SIZE); - sfizz_lv2_get_default_scala_path(self, self->scala_file_path, MAX_PATH_SIZE); + self->sfz_blob_mutex = new std::mutex; - sfizz_load_file(self->synth, self->sfz_file_path); - sfizz_load_scala_file(self->synth, self->scala_file_path); + sfizz_lv2_load_file(self, self->sfz_file_path); + sfizz_lv2_load_scala_file(self, self->scala_file_path); sfizz_lv2_update_timeinfo(self, 0, ~0); @@ -622,6 +496,8 @@ static void cleanup(LV2_Handle instance) { sfizz_plugin_t *self = (sfizz_plugin_t *)instance; + delete[] self->sfz_blob_data; + delete self->sfz_blob_mutex; spin_mutex_destroy(self->synth_mutex); sfizz_delete_client(self->client); sfizz_free(self->synth); @@ -1172,6 +1048,25 @@ sfizz_lv2_update_file_info(sfizz_plugin_t* self, const char *file_path) self->must_update_midnam.store(1); } +static void +sfizz_lv2_update_sfz_info(sfizz_plugin_t *self) +{ + const std::string blob = getDescriptionBlob(self->synth); + + uint32_t size = uint32_t(blob.size()); + uint8_t *data = new uint8_t[size]; + memcpy(data, blob.data(), size); + + self->sfz_blob_mutex->lock(); + self->sfz_blob_serial += 1; + const uint8_t *old_data = self->sfz_blob_data; + self->sfz_blob_data = data; + self->sfz_blob_size = size; + self->sfz_blob_mutex->unlock(); + + delete[] old_data; +} + static bool sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path) { @@ -1197,7 +1092,7 @@ sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path) status = sfizz_load_string(self->synth, virtual_path.c_str(), sfz_text.c_str()); } - /// + sfizz_lv2_update_sfz_info(self); sfizz_lv2_update_file_info(self, file_path); return status; } diff --git a/plugins/lv2/sfizz_lv2.h b/plugins/lv2/sfizz_lv2.h index e8662cec..68cb03be 100644 --- a/plugins/lv2/sfizz_lv2.h +++ b/plugins/lv2/sfizz_lv2.h @@ -6,6 +6,27 @@ #pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + #define MAX_PATH_SIZE 1024 #define ATOM_TEMP_SIZE 8192 #define OSC_TEMP_SIZE 8192 @@ -45,3 +66,22 @@ enum SFIZZ_NUM_REGIONS = 16, SFIZZ_NUM_SAMPLES = 17, }; + +// For use with instance-access +struct sfizz_plugin_t; + +/** + * @brief Fetch a copy of the current description, if more recent than what the + * version we already have. + * + * @param self The LV2 plugin + * @param serial The optional serial number to compare against + * @param desc The memory zone which receives the copy + * @param sizep The memory zone which receives the size + * @param serialp The memory zone which receives the new serial + * @return if serial is not null and its value isn't older, false + * otherwise, true, and the pointer returned in descp must be freed with delete[] after use + */ +bool sfizz_lv2_fetch_description( + sfizz_plugin_t *self, const int *serial, + uint8_t **descp, uint32_t *sizep, int *serialp); diff --git a/plugins/lv2/sfizz_lv2_common.cpp b/plugins/lv2/sfizz_lv2_common.cpp new file mode 100644 index 00000000..ea6562bb --- /dev/null +++ b/plugins/lv2/sfizz_lv2_common.cpp @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "sfizz_lv2.h" +#include "sfizz_lv2_plugin.h" + +bool sfizz_lv2_fetch_description( + sfizz_plugin_t *self, const int *serial, + uint8_t **descp, uint32_t *sizep, int *serialp) +{ + // must be thread-safe + + if (serial && self->sfz_blob_serial == *serial) + return false; + + self->sfz_blob_mutex->lock(); + uint32_t size = self->sfz_blob_size; + uint8_t *data = new uint8_t[size]; + int new_serial = self->sfz_blob_serial; + memcpy(data, self->sfz_blob_data, size); + self->sfz_blob_mutex->unlock(); + + *descp = data; + *sizep = size; + *serialp = new_serial; + + return true; +} diff --git a/plugins/lv2/sfizz_lv2_plugin.h b/plugins/lv2/sfizz_lv2_plugin.h new file mode 100644 index 00000000..0d05a00b --- /dev/null +++ b/plugins/lv2/sfizz_lv2_plugin.h @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once + +#include "sfizz_lv2.h" +#include +#include +#include +#include +#include +#include + +#define DEFAULT_SCALA_FILE "Contents/Resources/DefaultScale.scl" +#define DEFAULT_SFZ_FILE "Contents/Resources/DefaultInstrument.sfz" +// This assumes that the longest path is the default sfz file; if not, change it +#define MAX_BUNDLE_PATH_SIZE (MAX_PATH_SIZE - sizeof(DEFAULT_SFZ_FILE)) + +struct sfizz_plugin_t +{ + // Features + LV2_URID_Map *map {}; + LV2_URID_Unmap *unmap {}; + LV2_Worker_Schedule *worker {}; + LV2_Log_Log *log {}; + LV2_Midnam *midnam {}; + + // Ports + const LV2_Atom_Sequence *control_port {}; + LV2_Atom_Sequence *notify_port {}; + float *output_buffers[2] {}; + const float *volume_port {}; + const float *polyphony_port {}; + const float *oversampling_port {}; + const float *preload_port {}; + const float *freewheel_port {}; + const float *scala_root_key_port {}; + const float *tuning_frequency_port {}; + const float *stretch_tuning_port {}; + float *active_voices_port {}; + float *num_curves_port {}; + float *num_masters_port {}; + float *num_groups_port {}; + float *num_regions_port {}; + float *num_samples_port {}; + + // Atom forge + LV2_Atom_Forge forge {}; ///< Forge for writing atoms in run thread + LV2_Atom_Forge forge_secondary {}; ///< Forge for writing into other buffers + + // Logger + LV2_Log_Logger logger {}; + + // URIs + LV2_URID midi_event_uri {}; + LV2_URID options_interface_uri {}; + LV2_URID max_block_length_uri {}; + LV2_URID nominal_block_length_uri {}; + LV2_URID sample_rate_uri {}; + LV2_URID atom_object_uri {}; + LV2_URID atom_blank_uri {}; + LV2_URID atom_float_uri {}; + LV2_URID atom_double_uri {}; + LV2_URID atom_int_uri {}; + LV2_URID atom_long_uri {}; + LV2_URID atom_urid_uri {}; + LV2_URID atom_path_uri {}; + LV2_URID patch_set_uri {}; + LV2_URID patch_get_uri {}; + LV2_URID patch_put_uri {}; + LV2_URID patch_property_uri {}; + LV2_URID patch_value_uri {}; + LV2_URID patch_body_uri {}; + LV2_URID state_changed_uri {}; + LV2_URID sfizz_sfz_file_uri {}; + LV2_URID sfizz_scala_file_uri {}; + LV2_URID sfizz_num_voices_uri {}; + LV2_URID sfizz_preload_size_uri {}; + LV2_URID sfizz_oversampling_uri {}; + LV2_URID sfizz_log_status_uri {}; + LV2_URID sfizz_check_modification_uri {}; + LV2_URID sfizz_active_voices_uri {}; + LV2_URID sfizz_osc_blob_uri {}; + LV2_URID time_position_uri {}; + LV2_URID time_bar_uri {}; + LV2_URID time_bar_beat_uri {}; + LV2_URID time_beat_unit_uri {}; + LV2_URID time_beats_per_bar_uri {}; + LV2_URID time_beats_per_minute_uri {}; + LV2_URID time_speed_uri {}; + + // Sfizz related data + sfizz_synth_t *synth {}; + sfizz_client_t *client {}; + spin_mutex_t *synth_mutex {}; + bool expect_nominal_block_length {}; + char sfz_file_path[MAX_PATH_SIZE] {}; + char scala_file_path[MAX_PATH_SIZE] {}; + int num_voices {}; + unsigned int preload_size {}; + sfizz_oversampling_factor_t oversampling {}; + float stretch_tuning {}; + volatile bool check_modification {}; + int max_block_size {}; + int sample_counter {}; + float sample_rate {}; + std::atomic must_update_midnam {}; + + // Current instrument description + std::mutex *sfz_blob_mutex {}; + volatile int sfz_blob_serial {}; + const uint8_t *volatile sfz_blob_data {}; + volatile uint32_t sfz_blob_size {}; + + // Timing data + int bar {}; + double bar_beat {}; + int beats_per_bar {}; + int beat_unit {}; + double bpm_tempo {}; + double speed {}; + + // Paths + char bundle_path[MAX_BUNDLE_PATH_SIZE] {}; + + // OSC + uint8_t osc_temp[OSC_TEMP_SIZE] {}; +}; diff --git a/plugins/lv2/sfizz_ui.cpp b/plugins/lv2/sfizz_ui.cpp index 47faf8a4..8a061ea7 100644 --- a/plugins/lv2/sfizz_ui.cpp +++ b/plugins/lv2/sfizz_ui.cpp @@ -37,6 +37,7 @@ #include "editor/Editor.h" #include "editor/EditorController.h" #include "editor/EditIds.h" +#include "plugin/InstrumentDescription.h" #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +88,7 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface { LV2_URID_Unmap *unmap = nullptr; LV2UI_Resize *resize = nullptr; LV2UI_Touch *touch = nullptr; + sfizz_plugin_t *plugin = nullptr; FrameHolder uiFrame; std::unique_ptr editor; #if LINUX @@ -112,6 +115,9 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface { uint8_t osc_temp[OSC_TEMP_SIZE]; alignas(LV2_Atom) uint8_t atom_temp[ATOM_TEMP_SIZE]; + int sfz_serial = 0; + bool valid_sfz_serial = false; + protected: void uiSendValue(EditId id, const EditValue& v) override; void uiBeginSend(EditId id) override; @@ -178,12 +184,18 @@ instantiate(const LV2UI_Descriptor *descriptor, self->touch = (LV2UI_Touch*)(**f).data; else if (!strcmp((**f).URI, LV2_UI__parent)) parentWindowId = (**f).data; + else if (!strcmp((**f).URI, LV2_INSTANCE_ACCESS_URI)) + self->plugin = (sfizz_plugin_t *)(**f).data; } // The map feature is required if (!map || !unmap) return nullptr; + // The instance-access feature is required + if (!self->plugin) + return nullptr; + LV2_Atom_Forge *forge = &self->atom_forge; lv2_atom_forge_init(forge, map); self->atom_event_transfer_uri = map->map(map->handle, LV2_ATOM__eventTransfer); @@ -358,11 +370,44 @@ port_event(LV2UI_Handle ui, (void)buffer_size; } +static void +sfizz_ui_update_description(const InstrumentDescription& desc) +{ + // TODO(jpc) perform editor updates here + + //std::cerr << desc << std::endl; +} + +static void +sfizz_ui_check_sfz_update(sfizz_ui_t *self) +{ + uint8_t *data = nullptr; + uint32_t size = 0; + int new_serial = 0; + const int *serial = self->valid_sfz_serial ? &self->sfz_serial : nullptr; + + bool update = sfizz_lv2_fetch_description( + self->plugin, serial, &data, &size, &new_serial); + + if (update) { + std::unique_ptr cleanup(data); + self->sfz_serial = new_serial; + self->valid_sfz_serial = true; + + const InstrumentDescription desc = parseDescriptionBlob( + absl::string_view(reinterpret_cast(data), size)); + sfizz_ui_update_description(desc); + } +} + static int idle(LV2UI_Handle ui) { sfizz_ui_t *self = (sfizz_ui_t *)ui; + // check if there are news regarding the current SFZ + sfizz_ui_check_sfz_update(self); + #if LINUX self->runLoop->execIdle(); #else diff --git a/plugins/lv2/sfizz_ui.ttl.in b/plugins/lv2/sfizz_ui.ttl.in index b5fb3470..06ee2e31 100644 --- a/plugins/lv2/sfizz_ui.ttl.in +++ b/plugins/lv2/sfizz_ui.ttl.in @@ -11,4 +11,5 @@ lv2:optionalFeature ui:parent ; lv2:optionalFeature ui:touch ; lv2:requiredFeature urid:map ; - lv2:requiredFeature urid:unmap . + lv2:requiredFeature urid:unmap ; + lv2:requiredFeature . diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index 2c3cb320..e695e6ba 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -22,8 +22,8 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) // create update objects oscUpdate_ = Steinberg::owned(new OSCUpdate); noteUpdate_ = Steinberg::owned(new NoteUpdate); - sfzPathUpdate_ = Steinberg::owned(new FilePathUpdate(kFilePathUpdateSfz)); - scalaPathUpdate_ = Steinberg::owned(new FilePathUpdate(kFilePathUpdateScala)); + sfzUpdate_ = Steinberg::owned(new SfzUpdate); + scalaUpdate_ = Steinberg::owned(new ScalaUpdate); processorStateUpdate_ = Steinberg::owned(new ProcessorStateUpdate); playStateUpdate_ = Steinberg::owned(new PlayStateUpdate); @@ -211,10 +211,8 @@ tresult PLUGIN_API SfizzVstControllerNoUi::setComponentState(IBStream* stream) setParam(kPidTuningFrequency, s.tuningFrequency); setParam(kPidStretchedTuning, s.stretchedTuning); - sfzPathUpdate_->setPath(s.sfzFile); - sfzPathUpdate_->deferUpdate(); - scalaPathUpdate_->setPath(s.scalaFile); - scalaPathUpdate_->deferUpdate(); + scalaUpdate_->setPath(s.scalaFile); + scalaUpdate_->deferUpdate(); return kResultTrue; } @@ -230,35 +228,47 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) const char* id = message->getMessageID(); Vst::IAttributeList* attr = message->getAttributes(); - if (!strcmp(id, "LoadedSfz")) { + /// + auto stringFromBinaryAttribute = [attr](const char* id, absl::string_view& string) -> tresult { const void* data = nullptr; uint32 size = 0; - result = attr->getBinary("File", data, size); + tresult result = attr->getBinary(id, data, size); + if (result == kResultTrue) + string = absl::string_view(reinterpret_cast(data), size); + return result; + }; + /// + if (!strcmp(id, "LoadedSfz")) { + absl::string_view sfzFile; + absl::string_view sfzDescriptionBlob; + + result = stringFromBinaryAttribute("File", sfzFile); if (result != kResultTrue) return result; - std::string sfzFile(static_cast(data), size); - processorStateUpdate_->access([&sfzFile](SfizzVstState& state) { - state.sfzFile = sfzFile; + result = stringFromBinaryAttribute("Description", sfzDescriptionBlob); + if (result != kResultTrue) + return result; + + processorStateUpdate_->access([sfzFile](SfizzVstState& state) { + state.sfzFile = std::string(sfzFile); }); - sfzPathUpdate_->setPath(std::move(sfzFile)); - sfzPathUpdate_->deferUpdate(); + sfzUpdate_->setPath(std::string(sfzFile), std::string(sfzDescriptionBlob)); + sfzUpdate_->deferUpdate(); } else if (!strcmp(id, "LoadedScala")) { - const void* data = nullptr; - uint32 size = 0; - result = attr->getBinary("File", data, size); + absl::string_view scalaFile; + result = stringFromBinaryAttribute("File", scalaFile); if (result != kResultTrue) return result; - std::string scalaFile(static_cast(data), size); - processorStateUpdate_->access([&scalaFile](SfizzVstState& state) { - state.scalaFile = scalaFile; + processorStateUpdate_->access([scalaFile](SfizzVstState& state) { + state.scalaFile = std::string(scalaFile); }); - scalaPathUpdate_->setPath(std::move(scalaFile)); - scalaPathUpdate_->deferUpdate(); + scalaUpdate_->setPath(std::string(scalaFile)); + scalaUpdate_->deferUpdate(); } else if (!strcmp(id, "NotifiedPlayState")) { const void* data = nullptr; @@ -314,8 +324,8 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name) return nullptr; std::vector continuousUpdates; - continuousUpdates.push_back(sfzPathUpdate_); - continuousUpdates.push_back(scalaPathUpdate_); + continuousUpdates.push_back(sfzUpdate_); + continuousUpdates.push_back(scalaUpdate_); continuousUpdates.push_back(playStateUpdate_); for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i) continuousUpdates.push_back(parameters.getParameterByIndex(i)); diff --git a/plugins/vst/SfizzVstController.h b/plugins/vst/SfizzVstController.h index 83e94eff..4d4192df 100644 --- a/plugins/vst/SfizzVstController.h +++ b/plugins/vst/SfizzVstController.h @@ -47,8 +47,8 @@ public: protected: Steinberg::IPtr oscUpdate_; Steinberg::IPtr noteUpdate_; - Steinberg::IPtr sfzPathUpdate_; - Steinberg::IPtr scalaPathUpdate_; + Steinberg::IPtr sfzUpdate_; + Steinberg::IPtr scalaUpdate_; Steinberg::IPtr processorStateUpdate_; Steinberg::IPtr playStateUpdate_; Vst::ParamID midiMapping_[Vst::kCountCtrlNumber] {}; diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index 6b19f0c7..c9c83b47 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -188,17 +188,14 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) } } - if (FilePathUpdate* update = FCast(changedUnknown)) { + if (SfzUpdate* update = FCast(changedUnknown)) { const std::string path = update->getPath(); - switch (update->getType()) { - case kFilePathUpdateSfz: - uiReceiveValue(EditId::SfzFile, path); - break; - case kFilePathUpdateScala: - uiReceiveValue(EditId::ScalaFile, path); - break; - } - return; + uiReceiveValue(EditId::SfzFile, path); + } + + if (ScalaUpdate* update = FCast(changedUnknown)) { + const std::string path = update->getPath(); + uiReceiveValue(EditId::ScalaFile, path); } if (ProcessorStateUpdate* update = FCast(changedUnknown)) { diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index 764e50fb..31e673c7 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -10,6 +10,7 @@ #include "SfizzVstParameters.h" #include "SfizzFileScan.h" #include "sfizz/import/ForeignInstrument.h" +#include "plugin/InstrumentDescription.h" #include "base/source/fstreamer.h" #include "pluginterfaces/vst/ivstevents.h" #include "pluginterfaces/vst/ivstparameterchanges.h" @@ -97,7 +98,7 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context) _synth->setBroadcastCallback(onMessage, this); _currentStretchedTuning = 0.0; - loadSfzFileOrDefault(*_synth, {}); + loadSfzFileOrDefault({}); _synth->tempo(0, 0.5); _timeSigNumerator = 4; @@ -121,6 +122,19 @@ tresult PLUGIN_API SfizzVstProcessor::setBusArrangements(Vst::SpeakerArrangement return AudioEffect::setBusArrangements(inputs, numIns, outputs, numOuts); } +tresult PLUGIN_API SfizzVstProcessor::connect(IConnectionPoint* other) +{ + tresult result = AudioEffect::connect(other); + if (result != kResultTrue) + return result; + + // when controller connects, send these messages that we couldn't earlier + if (_loadedSfzMessage) + sendMessage(_loadedSfzMessage); + + return kResultTrue; +} + tresult PLUGIN_API SfizzVstProcessor::setState(IBStream* stream) { SfizzVstState s; @@ -173,7 +187,7 @@ void SfizzVstProcessor::syncStateToSynth() if (!synth) return; - loadSfzFileOrDefault(*synth, _state.sfzFile); + loadSfzFileOrDefault(_state.sfzFile); synth->setVolume(_state.volume); synth->setNumVoices(_state.numVoices); synth->setOversamplingFactor(1 << _state.oversamplingLog2); @@ -556,13 +570,8 @@ tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message) std::unique_lock lock(_processMutex); _state.sfzFile.assign(static_cast(data), size); - loadSfzFileOrDefault(*_synth, _state.sfzFile); + loadSfzFileOrDefault(_state.sfzFile); lock.unlock(); - - Steinberg::OPtr reply { allocateMessage() }; - reply->setMessageID("LoadedSfz"); - reply->getAttributes()->setBinary("File", _state.sfzFile.data(), _state.sfzFile.size()); - sendMessage(reply); } else if (!std::strcmp(id, "LoadScala")) { const void* data = nullptr; @@ -614,8 +623,10 @@ void SfizzVstProcessor::receiveMessage(int delay, const char* path, const char* } } -void SfizzVstProcessor::loadSfzFileOrDefault(sfz::Sfizz& synth, const std::string& filePath) +void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath) { + sfz::Sfizz& synth = *_synth; + if (!filePath.empty()) { const sfz::InstrumentFormatRegistry& formatRegistry = sfz::InstrumentFormatRegistry::getInstance(); const sfz::InstrumentFormat* format = formatRegistry.getMatchingFormat(filePath); @@ -628,8 +639,22 @@ void SfizzVstProcessor::loadSfzFileOrDefault(sfz::Sfizz& synth, const std::strin synth.loadSfzString(virtualPath, sfzText); } } - else + else { synth.loadSfzString("default.sfz", defaultSfzText); + } + + const std::string desc = getDescriptionBlob(synth.handle()); + + Steinberg::OPtr message { allocateMessage() }; + message->setMessageID("LoadedSfz"); + Vst::IAttributeList* attrs = message->getAttributes(); + attrs->setBinary("File", filePath.data(), filePath.size()); + attrs->setBinary("Description", desc.data(), desc.size()); + + // sending can fail if controller is not connected yet, so keep it around + _loadedSfzMessage = message; + + sendMessage(message); } void SfizzVstProcessor::doBackgroundWork() @@ -706,12 +731,7 @@ void SfizzVstProcessor::doBackgroundIdle() if (_synth->shouldReloadFile()) { fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n"); std::lock_guard lock(_processMutex); - loadSfzFileOrDefault(*_synth, _state.sfzFile); - - Steinberg::OPtr reply { allocateMessage() }; - reply->setMessageID("LoadedSfz"); - reply->getAttributes()->setBinary("File", _state.sfzFile.data(), _state.sfzFile.size()); - sendMessage(reply); + loadSfzFileOrDefault(_state.sfzFile); } if (_synth->shouldReloadScala()) { fprintf(stderr, "[Sfizz] scala file has changed, reloading\n"); diff --git a/plugins/vst/SfizzVstProcessor.h b/plugins/vst/SfizzVstProcessor.h index f34e1b4d..f7d73f2c 100644 --- a/plugins/vst/SfizzVstProcessor.h +++ b/plugins/vst/SfizzVstProcessor.h @@ -26,6 +26,8 @@ public: tresult PLUGIN_API initialize(FUnknown* context) override; tresult PLUGIN_API setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) override; + tresult PLUGIN_API connect(IConnectionPoint* other) override; + tresult PLUGIN_API setState(IBStream* stream) override; tresult PLUGIN_API getState(IBStream* stream) override; void syncStateToSynth(); @@ -49,6 +51,7 @@ public: private: // synth state. acquire processMutex before accessing std::unique_ptr _synth; + Steinberg::IPtr _loadedSfzMessage; bool _isActive = false; SfizzVstState _state; float _currentStretchedTuning = 0; @@ -59,7 +62,7 @@ private: void receiveMessage(int delay, const char* path, const char* sig, const sfizz_arg_t* args); // misc - static void loadSfzFileOrDefault(sfz::Sfizz& synth, const std::string& filePath); + void loadSfzFileOrDefault(const std::string& filePath); // note event tracking std::array _noteEventsCurrentCycle; // 0: off, >0: on, <0: no change diff --git a/plugins/vst/SfizzVstUpdates.h b/plugins/vst/SfizzVstUpdates.h index 145e39df..716ff6e5 100644 --- a/plugins/vst/SfizzVstUpdates.h +++ b/plugins/vst/SfizzVstUpdates.h @@ -67,21 +67,42 @@ private: }; /** - * @brief Update which notifies a change of file path pseudo-parameter - * The message ID is used to indicate which path it is. + * @brief Update which notifies a change of SFZ file. */ -class FilePathUpdate : public Steinberg::FObject { +class SfzUpdate : public Steinberg::FObject { public: - explicit FilePathUpdate(int32 type) - : type_(type) + void setPath(std::string newPath, std::string newDescription) { + std::lock_guard lock(mutex_); + path_ = std::move(newPath); + description_ = std::move(newDescription); } - int32 getType() const noexcept + std::string getPath() const { - return type_; + std::lock_guard lock(mutex_); + return path_; } + std::string getDescription() const + { + std::lock_guard lock(mutex_); + return description_; + } + + OBJ_METHODS(SfzUpdate, FObject) + +private: + std::string path_; + std::string description_; + mutable std::mutex mutex_; +}; + +/** + * @brief Update which notifies a change of scala file. + */ +class ScalaUpdate : public Steinberg::FObject { +public: void setPath(std::string newPath) { std::lock_guard lock(mutex_); @@ -94,19 +115,13 @@ public: return path_; } - OBJ_METHODS(FilePathUpdate, FObject) + OBJ_METHODS(ScalaUpdate, FObject) private: - int32 type_ {}; std::string path_; mutable std::mutex mutex_; }; -enum { - kFilePathUpdateSfz, - kFilePathUpdateScala, -}; - /** * @brief Update which indicates the processor status. */ diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 0b4f2922..940bb2a9 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -39,6 +39,28 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co //---------------------------------------------------------------------- + MATCH("/num_regions", "") { + client.receive<'i'>(delay, path, int(impl.layers_.size())); + } break; + + MATCH("/num_groups", "") { + client.receive<'i'>(delay, path, impl.numGroups_); + } break; + + MATCH("/num_masters", "") { + client.receive<'i'>(delay, path, impl.numMasters_); + } break; + + MATCH("/num_curves", "") { + client.receive<'i'>(delay, path, int(impl.resources_.curves.getNumCurves())); + } break; + + MATCH("/num_samples", "") { + client.receive<'i'>(delay, path, int(impl.resources_.filePool.getNumPreloadedSamples())); + } break; + + //---------------------------------------------------------------------- + MATCH("/key/slots", "") { const BitArray<128>& keys = impl.keySlots_; sfizz_blob_t blob { keys.data(), static_cast(keys.byte_size()) }; From 5fbb499d9ba23b525429f7382ed41ff05baca662 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 2 Apr 2021 16:58:54 +0200 Subject: [PATCH 04/17] Help clang-tidy find new files --- scripts/run_clang_tidy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_clang_tidy.sh b/scripts/run_clang_tidy.sh index 52b555fc..e970a75f 100755 --- a/scripts/run_clang_tidy.sh +++ b/scripts/run_clang_tidy.sh @@ -30,6 +30,6 @@ clang-tidy \ -- -Iexternal/abseil-cpp -Iexternal/jsl/include -Iexternal/filesystem/include -Iexternal/atomic_queue/include -Iexternal/threadpool -Isrc/external/hiir -Isrc/external/pugixml/src \ -Iexternal/st_audiofile/src -Iexternal/st_audiofile/thirdparty/dr_libs \ -Isrc/sfizz -Isrc -Isrc/sfizz/utility/bit_array -Isrc/sfizz/utility/spin_mutex -Isrc/external/spline -Isrc/external/cpuid/src -Iexternal/simde \ - -Iplugins/vst -Iplugins/vst/external/VST_SDK/VST3_SDK -Iplugins/editor/external/vstgui4 -Iplugins/vst/external/ring_buffer \ + -Iplugins/common -Iplugins/vst -Iplugins/vst/external/VST_SDK/VST3_SDK -Iplugins/editor/external/vstgui4 -Iplugins/vst/external/ring_buffer \ -Iplugins/editor/src \ -DNDEBUG -std=c++17 From 0d8deb1fb911b26522799abc8b5803954ecdc2b9 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 2 Apr 2021 17:43:15 +0200 Subject: [PATCH 05/17] Keep path and description updated separately --- plugins/vst/SfizzVstController.cpp | 8 +++++++- plugins/vst/SfizzVstController.h | 1 + plugins/vst/SfizzVstEditor.cpp | 6 ++++++ plugins/vst/SfizzVstUpdates.h | 24 ++++++++++++++++++++---- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index e695e6ba..de2f1c32 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -23,6 +23,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) oscUpdate_ = Steinberg::owned(new OSCUpdate); noteUpdate_ = Steinberg::owned(new NoteUpdate); sfzUpdate_ = Steinberg::owned(new SfzUpdate); + sfzDescriptionUpdate_ = Steinberg::owned(new SfzDescriptionUpdate); scalaUpdate_ = Steinberg::owned(new ScalaUpdate); processorStateUpdate_ = Steinberg::owned(new ProcessorStateUpdate); playStateUpdate_ = Steinberg::owned(new PlayStateUpdate); @@ -211,6 +212,8 @@ tresult PLUGIN_API SfizzVstControllerNoUi::setComponentState(IBStream* stream) setParam(kPidTuningFrequency, s.tuningFrequency); setParam(kPidStretchedTuning, s.stretchedTuning); + sfzUpdate_->setPath(s.sfzFile); + sfzUpdate_->deferUpdate(); scalaUpdate_->setPath(s.scalaFile); scalaUpdate_->deferUpdate(); @@ -254,8 +257,10 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) processorStateUpdate_->access([sfzFile](SfizzVstState& state) { state.sfzFile = std::string(sfzFile); }); - sfzUpdate_->setPath(std::string(sfzFile), std::string(sfzDescriptionBlob)); + sfzUpdate_->setPath(std::string(sfzFile)); sfzUpdate_->deferUpdate(); + sfzDescriptionUpdate_->setDescription(std::string(sfzDescriptionBlob)); + sfzDescriptionUpdate_->deferUpdate(); } else if (!strcmp(id, "LoadedScala")) { absl::string_view scalaFile; @@ -325,6 +330,7 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name) std::vector continuousUpdates; continuousUpdates.push_back(sfzUpdate_); + continuousUpdates.push_back(sfzDescriptionUpdate_); continuousUpdates.push_back(scalaUpdate_); continuousUpdates.push_back(playStateUpdate_); for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i) diff --git a/plugins/vst/SfizzVstController.h b/plugins/vst/SfizzVstController.h index 4d4192df..7ea01f60 100644 --- a/plugins/vst/SfizzVstController.h +++ b/plugins/vst/SfizzVstController.h @@ -48,6 +48,7 @@ protected: Steinberg::IPtr oscUpdate_; Steinberg::IPtr noteUpdate_; Steinberg::IPtr sfzUpdate_; + Steinberg::IPtr sfzDescriptionUpdate_; Steinberg::IPtr scalaUpdate_; Steinberg::IPtr processorStateUpdate_; Steinberg::IPtr playStateUpdate_; diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index c9c83b47..dea3d3e6 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -11,6 +11,7 @@ #include "SfizzFileScan.h" #include "editor/Editor.h" #include "editor/EditIds.h" +#include "plugin/InstrumentDescription.h" #include "IdleUpdateHandler.h" #if !defined(__APPLE__) && !defined(_WIN32) #include "X11RunLoop.h" @@ -193,6 +194,11 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) uiReceiveValue(EditId::SfzFile, path); } + if (SfzDescriptionUpdate* update = FCast(changedUnknown)) { + const InstrumentDescription desc = parseDescriptionBlob(update->getDescription()); + // TODO(jpc) instrument description + } + if (ScalaUpdate* update = FCast(changedUnknown)) { const std::string path = update->getPath(); uiReceiveValue(EditId::ScalaFile, path); diff --git a/plugins/vst/SfizzVstUpdates.h b/plugins/vst/SfizzVstUpdates.h index 716ff6e5..3d0ce944 100644 --- a/plugins/vst/SfizzVstUpdates.h +++ b/plugins/vst/SfizzVstUpdates.h @@ -71,11 +71,10 @@ private: */ class SfzUpdate : public Steinberg::FObject { public: - void setPath(std::string newPath, std::string newDescription) + void setPath(std::string newPath) { std::lock_guard lock(mutex_); path_ = std::move(newPath); - description_ = std::move(newDescription); } std::string getPath() const @@ -84,16 +83,33 @@ public: return path_; } + OBJ_METHODS(SfzUpdate, FObject) + +private: + std::string path_; + mutable std::mutex mutex_; +}; + +/** + * @brief Update which notifies a change of SFZ description. + */ +class SfzDescriptionUpdate : public Steinberg::FObject { +public: + void setDescription(std::string newDescription) + { + std::lock_guard lock(mutex_); + description_ = std::move(newDescription); + } + std::string getDescription() const { std::lock_guard lock(mutex_); return description_; } - OBJ_METHODS(SfzUpdate, FObject) + OBJ_METHODS(SfzDescriptionUpdate, FObject) private: - std::string path_; std::string description_; mutable std::mutex mutex_; }; From 10b36293b62f78fddeeb42012ef313785366e393 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 2 Apr 2021 18:20:18 +0200 Subject: [PATCH 06/17] Add a helper macro for ranged EditIds --- plugins/editor/src/editor/EditIds.h | 44 +++++++++++++---------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/plugins/editor/src/editor/EditIds.h b/plugins/editor/src/editor/EditIds.h index fd1556bc..cb38ed65 100644 --- a/plugins/editor/src/editor/EditIds.h +++ b/plugins/editor/src/editor/EditIds.h @@ -48,29 +48,23 @@ struct EditRange { static EditRange get(EditId id); }; -inline EditId editIdForCC(int cc) -{ - return EditId(int(EditId::Controller0) + cc); -} -inline int ccForEditId(EditId id) -{ - return int(id) - int(EditId::Controller0); -} -inline bool editIdIsCC(EditId id) -{ - return int(id) >= int(EditId::Controller0) && - int(id) <= int(EditId::ControllerLast); -} +#define DEFINE_EDIT_ID_RANGE_HELPERS(type, Type, IdPrefix) \ + inline EditId editIdFor##Type(int value) \ + { \ + return EditId(int(EditId::IdPrefix##0) + value); \ + } \ + inline int type##ForEditId(EditId id) \ + { \ + return int(id) - int(EditId::IdPrefix##0); \ + } \ + inline bool editIdIs##Type(EditId id) \ + { \ + return int(id) >= int(EditId::IdPrefix##0) && \ + int(id) <= int(EditId::IdPrefix##Last); \ + } -inline EditId editIdForKey(int key) -{ - return EditId(int(EditId::Key0) + key); -} -inline int keyForEditId(EditId id) -{ - return int(id) - int(EditId::Key0); -} -inline bool editIdIsKey(EditId id) -{ - return int(id) >= int(EditId::Key0) && int(id) <= int(EditId::KeyLast); -} +// defines editIdForCC, ccForEditId, editIdIsCC, etc.. +DEFINE_EDIT_ID_RANGE_HELPERS(cc, CC, Controller) +DEFINE_EDIT_ID_RANGE_HELPERS(key, Key, Key) + +#undef DEFINE_EDIT_ID_RANGE_HELPERS From 5e06aba5954cd3556ec401c497c0fb0f84fbc23d Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 2 Apr 2021 18:39:52 +0200 Subject: [PATCH 07/17] Send UI the instrument description --- plugins/editor/src/editor/EditIds.h | 26 +++++++++++++++++++++---- plugins/lv2/sfizz_ui.cpp | 30 +++++++++++++++++++++++++---- plugins/vst/SfizzVstEditor.cpp | 27 +++++++++++++++++++++++++- 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/plugins/editor/src/editor/EditIds.h b/plugins/editor/src/editor/EditIds.h index cb38ed65..5696e27d 100644 --- a/plugins/editor/src/editor/EditIds.h +++ b/plugins/editor/src/editor/EditIds.h @@ -22,11 +22,19 @@ enum class EditId : int { UserFilesDir, FallbackFilesDir, // - Key0, - KeyLast = Key0 + 128 - 1, + #define KEY_RANGE(Name) Name##0, Name##Last = Name##0 + 128 - 1 + #define CC_RANGE(Name) Name##0, Name##Last = Name##0 + sfz::config::numCCs - 1 // - Controller0, - ControllerLast = Controller0 + sfz::config::numCCs - 1, + KEY_RANGE(Key), + CC_RANGE(Controller), + // + KEY_RANGE(KeyUsed), + KEY_RANGE(KeyLabel), + KEY_RANGE(KeyswitchUsed), + KEY_RANGE(KeyswitchLabel), + CC_RANGE(ControllerUsed), + CC_RANGE(ControllerDefault), + CC_RANGE(ControllerLabel), // UINumCurves, UINumMasters, @@ -35,6 +43,9 @@ enum class EditId : int { UINumPreloadedSamples, UINumActiveVoices, UIActivePanel, + // + #undef KEY_RANGE + #undef CC_RANGE }; struct EditRange { @@ -66,5 +77,12 @@ struct EditRange { // defines editIdForCC, ccForEditId, editIdIsCC, etc.. DEFINE_EDIT_ID_RANGE_HELPERS(cc, CC, Controller) DEFINE_EDIT_ID_RANGE_HELPERS(key, Key, Key) +DEFINE_EDIT_ID_RANGE_HELPERS(keyUsed, KeyUsed, KeyUsed) +DEFINE_EDIT_ID_RANGE_HELPERS(keyLabel, KeyLabel, KeyLabel) +DEFINE_EDIT_ID_RANGE_HELPERS(keyswitchUsed, KeyswitchUsed, KeyswitchUsed) +DEFINE_EDIT_ID_RANGE_HELPERS(keyswitchLabel, KeyswitchLabel, KeyswitchLabel) +DEFINE_EDIT_ID_RANGE_HELPERS(ccUsed, CCUsed, ControllerUsed) +DEFINE_EDIT_ID_RANGE_HELPERS(ccDefault, CCDefault, ControllerDefault) +DEFINE_EDIT_ID_RANGE_HELPERS(ccLabel, CCLabel, ControllerLabel) #undef DEFINE_EDIT_ID_RANGE_HELPERS diff --git a/plugins/lv2/sfizz_ui.cpp b/plugins/lv2/sfizz_ui.cpp index 8a061ea7..68c846b7 100644 --- a/plugins/lv2/sfizz_ui.cpp +++ b/plugins/lv2/sfizz_ui.cpp @@ -371,11 +371,33 @@ port_event(LV2UI_Handle ui, } static void -sfizz_ui_update_description(const InstrumentDescription& desc) +sfizz_ui_update_description(sfizz_ui_t *self, const InstrumentDescription& desc) { - // TODO(jpc) perform editor updates here + self->uiReceiveValue(EditId::UINumCurves, desc.numCurves); + self->uiReceiveValue(EditId::UINumMasters, desc.numMasters); + self->uiReceiveValue(EditId::UINumGroups, desc.numGroups); + self->uiReceiveValue(EditId::UINumRegions, desc.numRegions); + self->uiReceiveValue(EditId::UINumPreloadedSamples, desc.numSamples); - //std::cerr << desc << std::endl; + for (unsigned key = 0; key < 128; ++key) { + bool keyUsed = desc.keyUsed.test(key); + bool keyswitchUsed = desc.keyswitchUsed.test(key); + self->uiReceiveValue(editIdForKeyUsed(key), float(keyUsed)); + self->uiReceiveValue(editIdForKeyswitchUsed(key), float(keyswitchUsed)); + if (keyUsed) + self->uiReceiveValue(editIdForKeyLabel(key), desc.keyLabel[key]); + if (keyswitchUsed) + self->uiReceiveValue(editIdForKeyswitchLabel(key), desc.keyswitchLabel[key]); + } + + for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { + bool ccUsed = desc.ccUsed.test(cc); + self->uiReceiveValue(editIdForCCUsed(cc), float(ccUsed)); + if (ccUsed) { + self->uiReceiveValue(editIdForCCDefault(cc), desc.ccDefault[cc]); + self->uiReceiveValue(editIdForCCLabel(cc), desc.ccLabel[cc]); + } + } } static void @@ -396,7 +418,7 @@ sfizz_ui_check_sfz_update(sfizz_ui_t *self) const InstrumentDescription desc = parseDescriptionBlob( absl::string_view(reinterpret_cast(data), size)); - sfizz_ui_update_description(desc); + sfizz_ui_update_description(self, desc); } } diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index dea3d3e6..19472000 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -196,7 +196,32 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) if (SfzDescriptionUpdate* update = FCast(changedUnknown)) { const InstrumentDescription desc = parseDescriptionBlob(update->getDescription()); - // TODO(jpc) instrument description + + uiReceiveValue(EditId::UINumCurves, desc.numCurves); + uiReceiveValue(EditId::UINumMasters, desc.numMasters); + uiReceiveValue(EditId::UINumGroups, desc.numGroups); + uiReceiveValue(EditId::UINumRegions, desc.numRegions); + uiReceiveValue(EditId::UINumPreloadedSamples, desc.numSamples); + + for (unsigned key = 0; key < 128; ++key) { + bool keyUsed = desc.keyUsed.test(key); + bool keyswitchUsed = desc.keyswitchUsed.test(key); + uiReceiveValue(editIdForKeyUsed(key), float(keyUsed)); + uiReceiveValue(editIdForKeyswitchUsed(key), float(keyswitchUsed)); + if (keyUsed) + uiReceiveValue(editIdForKeyLabel(key), desc.keyLabel[key]); + if (keyswitchUsed) + uiReceiveValue(editIdForKeyswitchLabel(key), desc.keyswitchLabel[key]); + } + + for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { + bool ccUsed = desc.ccUsed.test(cc); + uiReceiveValue(editIdForCCUsed(cc), float(ccUsed)); + if (ccUsed) { + uiReceiveValue(editIdForCCDefault(cc), desc.ccDefault[cc]); + uiReceiveValue(editIdForCCLabel(cc), desc.ccLabel[cc]); + } + } } if (ScalaUpdate* update = FCast(changedUnknown)) { From 39c6df0719418af118a11fb89b9c07839e5cf0bc Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 2 Apr 2021 18:50:58 +0200 Subject: [PATCH 08/17] Handle description data in editor --- plugins/editor/src/editor/Editor.cpp | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp index e00e7795..fb09b25d 100644 --- a/plugins/editor/src/editor/Editor.cpp +++ b/plugins/editor/src/editor/Editor.cpp @@ -200,6 +200,7 @@ struct Editor::Impl : EditorController::Receiver, IControlListener { void updateKeyswitchNameLabel(); void updateKeyUsed(unsigned key, bool used); + void updateKeyLabel(unsigned key, const char* label); void updateKeyswitchUsed(unsigned key, bool used); void updateCCUsed(unsigned cc, bool used); void updateCCValue(unsigned cc, float value); @@ -461,6 +462,27 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v) if (SPiano* piano = piano_) piano->setKeyValue(key, value); } + else if (editIdIsKeyUsed(id)) { + updateKeyUsed(keyUsedForEditId(id), v.to_float() != 0); + } + else if (editIdIsKeyLabel(id)) { + updateKeyLabel(keyLabelForEditId(id), v.to_string().c_str()); + } + else if (editIdIsKeyswitchUsed(id)) { + updateKeyswitchUsed(keyswitchUsedForEditId(id), v.to_float() != 0); + } + else if (editIdIsKeyswitchLabel(id)) { + updateSWLastLabel(keyswitchLabelForEditId(id), v.to_string().c_str()); + } + else if (editIdIsCCUsed(id)) { + updateCCUsed(ccUsedForEditId(id), v.to_float() != 0); + } + else if (editIdIsCCDefault(id)) { + updateCCDefaultValue(ccDefaultForEditId(id), v.to_float()); + } + else if (editIdIsCCLabel(id)) { + updateCCLabel(ccLabelForEditId(id), v.to_string().c_str()); + } break; } } @@ -1519,6 +1541,13 @@ void Editor::Impl::updateKeyUsed(unsigned key, bool used) piano->setKeyUsed(key, used); } +void Editor::Impl::updateKeyLabel(unsigned key, const char* label) +{ + // TODO nothing done with this info currently + (void)key; + (void)label; +} + void Editor::Impl::updateKeyswitchUsed(unsigned key, bool used) { if (SPiano* piano = piano_) From c39cae79f9b1c0765cc4420d85e6d8d067cbc172 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 2 Apr 2021 18:59:44 +0200 Subject: [PATCH 09/17] Add some assertions --- plugins/editor/src/editor/EditIds.h | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/plugins/editor/src/editor/EditIds.h b/plugins/editor/src/editor/EditIds.h index 5696e27d..facb6a13 100644 --- a/plugins/editor/src/editor/EditIds.h +++ b/plugins/editor/src/editor/EditIds.h @@ -60,18 +60,21 @@ struct EditRange { }; #define DEFINE_EDIT_ID_RANGE_HELPERS(type, Type, IdPrefix) \ - inline EditId editIdFor##Type(int value) \ - { \ - return EditId(int(EditId::IdPrefix##0) + value); \ - } \ - inline int type##ForEditId(EditId id) \ - { \ - return int(id) - int(EditId::IdPrefix##0); \ - } \ inline bool editIdIs##Type(EditId id) \ { \ return int(id) >= int(EditId::IdPrefix##0) && \ int(id) <= int(EditId::IdPrefix##Last); \ + } \ + inline EditId editIdFor##Type(int value) \ + { \ + EditId id = EditId(int(EditId::IdPrefix##0) + value); \ + assert(editIdIs##Type(id)); \ + return id; \ + } \ + inline int type##ForEditId(EditId id) \ + { \ + assert(editIdIs##Type(id)); \ + return int(id) - int(EditId::IdPrefix##0); \ } // defines editIdForCC, ccForEditId, editIdIsCC, etc.. From d03495d5686b15c0ddd233a6bcdd6f0a421edba7 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 3 Apr 2021 03:07:13 +0200 Subject: [PATCH 10/17] Handle message /cc/changed --- src/sfizz/Synth.cpp | 2 ++ src/sfizz/SynthMessaging.cpp | 12 ++++++++++++ src/sfizz/SynthPrivate.h | 1 + 3 files changed, 15 insertions(+) diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 8f51064a..2b713909 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -258,6 +258,7 @@ void Synth::Impl::clear() clearCCLabels(); currentUsedCCs_.clear(); changedCCsThisCycle_.clear(); + changedCCsLastCycle_.clear(); clearKeyLabels(); keySlots_.clear(); swLastSlots_.clear(); @@ -1025,6 +1026,7 @@ void Synth::renderBlock(AudioSpan buffer) noexcept sfizz_blob_t blob { changedCCs.data(), static_cast(changedCCs.byte_size()) }; broadcaster.receive<'b'>(finalFrameNumber, "/cc/changed", &blob); } + impl.changedCCsLastCycle_ = impl.changedCCsThisCycle_; impl.changedCCsThisCycle_.clear(); // Send the changed keyswitch if (impl.currentSwitchChanged_) { diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 940bb2a9..e96f0040 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -134,6 +134,18 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'s'>(delay, path, label ? label->c_str() : ""); } break; + MATCH("/cc/changed", "") { + const BitArray& changedCCs = impl.changedCCsThisCycle_; + sfizz_blob_t blob { changedCCs.data(), static_cast(changedCCs.byte_size()) }; + client.receive<'b'>(delay, path, &blob); + } break; + + MATCH("/cc/changed~", "") { + const BitArray& changedCCs = impl.changedCCsLastCycle_; + sfizz_blob_t blob { changedCCs.data(), static_cast(changedCCs.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 0f698124..960fe9f2 100644 --- a/src/sfizz/SynthPrivate.h +++ b/src/sfizz/SynthPrivate.h @@ -315,6 +315,7 @@ struct Synth::Impl final: public Parser::Listener { std::array defaultCCValues_; BitArray currentUsedCCs_; BitArray changedCCsThisCycle_; + BitArray changedCCsLastCycle_; // Messaging sfizz_receive_t* broadcastReceiver = nullptr; From d729ba66e64caa45da8c8eb001a6b12975c8bbea Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 3 Apr 2021 03:22:49 +0200 Subject: [PATCH 11/17] Consistency in /sw/last/current replies --- plugins/editor/src/editor/Editor.cpp | 3 +++ src/sfizz/SynthMessaging.cpp | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp index fb09b25d..2866d04c 100644 --- a/plugins/editor/src/editor/Editor.cpp +++ b/plugins/editor/src/editor/Editor.cpp @@ -554,6 +554,9 @@ void Editor::Impl::uiReceiveMessage(const char* path, const char* sig, const sfi else if (Messages::matchOSC("/sw/last/current", path, indices) && !strcmp(sig, "i")) { updateSWLastCurrent(args[0].i); } + else if (Messages::matchOSC("/sw/last/current", path, indices) && !strcmp(sig, "N")) { + updateSWLastCurrent(-1); + } else if (Messages::matchOSC("/sw/last/&/label", path, indices) && !strcmp(sig, "s")) { updateSWLastLabel(indices[0], args[0].s); } diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index e96f0040..70fc6ed5 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -87,10 +87,10 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co } break; MATCH("/sw/last/current", "") { - int32_t value = -1; if (impl.currentSwitch_) - value = *impl.currentSwitch_; - client.receive<'i'>(delay, path, value); + client.receive<'i'>(delay, path, *impl.currentSwitch_); + else + client.receive<'N'>(delay, path, {}); } break; MATCH("/sw/last/&/label", "") { From 53a5e989e57df5d12194e28064eeb4ad78eb8f9f Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 3 Apr 2021 03:59:43 +0200 Subject: [PATCH 12/17] Pull model for current CC and switch --- plugins/editor/src/editor/Editor.cpp | 75 ++-------------------------- plugins/lv2/sfizz.cpp | 4 ++ plugins/vst/SfizzVstProcessor.cpp | 5 ++ src/sfizz/Synth.cpp | 23 +-------- src/sfizz/SynthPrivate.h | 1 - 5 files changed, 15 insertions(+), 93 deletions(-) diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp index 2866d04c..6c2af261 100644 --- a/plugins/editor/src/editor/Editor.cpp +++ b/plugins/editor/src/editor/Editor.cpp @@ -279,12 +279,6 @@ void Editor::open(CFrame& frame) impl.oscSendQueueTimer_ = makeOwned( [this](CVSTGUITimer* timer) { impl_->tickOSCQueue(timer); }, oscSendInterval, false); - - // request the whole Key and CC information - impl.sendQueuedOSC("/key/slots", "", nullptr); - impl.sendQueuedOSC("/sw/last/slots", "", nullptr); - impl.sendQueuedOSC("/cc/slots", "", nullptr); - impl.sendQueuedOSC("/image", "", nullptr); } void Editor::close() @@ -312,12 +306,6 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v) const std::string& value = v.to_string(); currentSfzFile_ = value; updateSfzFileLabel(value); - - // request the whole Key and CC information - sendQueuedOSC("/key/slots", "", nullptr); - sendQueuedOSC("/sw/last/slots", "", nullptr); - sendQueuedOSC("/cc/slots", "", nullptr); - sendQueuedOSC("/image", "", nullptr); } break; case EditId::Volume: @@ -476,6 +464,10 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v) } else if (editIdIsCCUsed(id)) { updateCCUsed(ccUsedForEditId(id), v.to_float() != 0); + // TODO(jpc) remove value requests, when implementing CC automation + char pathBuf[256]; + sprintf(pathBuf, "/cc%u/value", ccUsedForEditId(id)); + sendQueuedOSC(pathBuf, "", nullptr); } else if (editIdIsCCDefault(id)) { updateCCDefaultValue(ccDefaultForEditId(id), v.to_float()); @@ -491,46 +483,7 @@ void Editor::Impl::uiReceiveMessage(const char* path, const char* sig, const sfi { unsigned indices[8]; - if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b")) { - size_t numBits = 8 * args[0].b->size; - ConstBitSpan bits { args[0].b->data, numBits }; - for (unsigned key = 0; key < 128; ++key) { - bool used = key < numBits && bits.test(key); - updateKeyUsed(key, used); - } - } - else if (Messages::matchOSC("/sw/last/slots", path, indices) && !strcmp(sig, "b")) { - size_t numBits = 8 * args[0].b->size; - ConstBitSpan bits { args[0].b->data, numBits }; - for (unsigned key = 0; key < 128; ++key) { - bool used = key < numBits && bits.test(key); - updateKeyswitchUsed(key, used); - if (used) { - char pathBuf[256]; - sprintf(pathBuf, "/sw/last/%u/label", key); - sendQueuedOSC(pathBuf, "", nullptr); - } - } - sendQueuedOSC("/sw/last/current", "", nullptr); - } - else if (Messages::matchOSC("/cc/slots", path, indices) && !strcmp(sig, "b")) { - size_t numBits = 8 * args[0].b->size; - ConstBitSpan bits { args[0].b->data, numBits }; - for (unsigned cc = 0; cc < numBits; ++cc) { - bool used = bits.test(cc); - updateCCUsed(cc, used); - if (used) { - char pathBuf[256]; - sprintf(pathBuf, "/cc%u/value", cc); - sendQueuedOSC(pathBuf, "", nullptr); - sprintf(pathBuf, "/cc%u/default", cc); - sendQueuedOSC(pathBuf, "", nullptr); - sprintf(pathBuf, "/cc%u/label", cc); - sendQueuedOSC(pathBuf, "", nullptr); - } - } - } - else if (Messages::matchOSC("/cc/changed", path, indices) && !strcmp(sig, "b")) { + if (Messages::matchOSC("/cc/changed~", path, indices) && !strcmp(sig, "b")) { size_t numBits = 8 * args[0].b->size; ConstBitSpan bits { args[0].b->data, numBits }; for (unsigned cc = 0; cc < numBits; ++cc) { @@ -545,24 +498,12 @@ void Editor::Impl::uiReceiveMessage(const char* path, const char* sig, const sfi else if (Messages::matchOSC("/cc&/value", path, indices) && !strcmp(sig, "f")) { updateCCValue(indices[0], args[0].f); } - else if (Messages::matchOSC("/cc&/default", path, indices) && !strcmp(sig, "f")) { - updateCCDefaultValue(indices[0], args[0].f); - } - else if (Messages::matchOSC("/cc&/label", path, indices) && !strcmp(sig, "s")) { - updateCCLabel(indices[0], args[0].s); - } else if (Messages::matchOSC("/sw/last/current", path, indices) && !strcmp(sig, "i")) { updateSWLastCurrent(args[0].i); } else if (Messages::matchOSC("/sw/last/current", path, indices) && !strcmp(sig, "N")) { updateSWLastCurrent(-1); } - else if (Messages::matchOSC("/sw/last/&/label", path, indices) && !strcmp(sig, "s")) { - updateSWLastLabel(indices[0], args[0].s); - } - else if (Messages::matchOSC("/image", path, indices) && !strcmp(sig, "s")) { - updateBackgroundImage(args[0].s); - } else if (Messages::matchOSC("/mem/buffers", path, indices) && !strcmp(sig, "h")) { updateMemoryUsed(args[0].h); } @@ -1176,12 +1117,6 @@ void Editor::Impl::changeSfzFile(const std::string& filePath) ctrl_->uiSendValue(EditId::SfzFile, filePath); currentSfzFile_ = filePath; updateSfzFileLabel(filePath); - - // request the whole Key and CC information - sendQueuedOSC("/key/slots", "", nullptr); - sendQueuedOSC("/sw/last/slots", "", nullptr); - sendQueuedOSC("/cc/slots", "", nullptr); - sendQueuedOSC("/image", "", nullptr); } void Editor::Impl::changeToNextSfzFile(long offset) diff --git a/plugins/lv2/sfizz.cpp b/plugins/lv2/sfizz.cpp index edf486bf..f597d2e9 100644 --- a/plugins/lv2/sfizz.cpp +++ b/plugins/lv2/sfizz.cpp @@ -942,6 +942,10 @@ run(LV2_Handle instance, uint32_t sample_count) // Render the block sfizz_render_block(self->synth, self->output_buffers, 2, (int)sample_count); + // Request OSC updates + sfizz_send_message(self->synth, self->client, 0, "/cc/changed~", "", nullptr); + sfizz_send_message(self->synth, self->client, 0, "/sw/last/current", "", nullptr); + spin_mutex_unlock(self->synth_mutex); if (self->midnam && self->must_update_midnam.exchange(0)) diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index 31e673c7..1bc97468 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -286,6 +286,11 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data) synth.renderBlock(outputs, numFrames, numChannels); + // Request OSC updates + sfz::Client& client = *_client; + synth.sendMessage(client, 0, "/cc/changed~", "", nullptr); + synth.sendMessage(client, 0, "/sw/last/current", "", nullptr); + _playStateChangeCounter += numFrames; if (_playStateChangeCounter > _playStateChangePeriod) { _playStateChangeCounter %= _playStateChangePeriod; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 2b713909..ea7914ac 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -249,7 +249,6 @@ void Synth::Impl::clear() numGroups_ = 0; numMasters_ = 0; currentSwitch_ = absl::nullopt; - currentSwitchChanged_ = true; defaultPath_ = ""; image_ = ""; resources_.midiState.reset(); @@ -791,8 +790,6 @@ void Synth::Impl::finalizeSfzLoad() swLastSlots_.set(key); } } - // resend current keyswitch - currentSwitchChanged_ = true; } bool Synth::loadScalaFile(const fs::path& path) @@ -1018,26 +1015,9 @@ void Synth::renderBlock(AudioSpan buffer) noexcept // Advance the clock to the end of cycle bc.endCycle(); - // Send the set of changed CCs - Client broadcaster = impl.getBroadcaster(); - const BitArray& changedCCs = impl.changedCCsThisCycle_; - const int finalFrameNumber = int(numFrames - 1); - if (broadcaster.canReceive()) { - sfizz_blob_t blob { changedCCs.data(), static_cast(changedCCs.byte_size()) }; - broadcaster.receive<'b'>(finalFrameNumber, "/cc/changed", &blob); - } + // Update sets of changed CCs impl.changedCCsLastCycle_ = impl.changedCCsThisCycle_; impl.changedCCsThisCycle_.clear(); - // Send the changed keyswitch - if (impl.currentSwitchChanged_) { - if (broadcaster.canReceive()) { - int32_t value = -1; - if (impl.currentSwitch_) - value = *impl.currentSwitch_; - broadcaster.receive<'i'>(finalFrameNumber, "/sw/last/current", value); - } - impl.currentSwitchChanged_ = false; - } { // Clear events and advance midi time ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; @@ -1137,7 +1117,6 @@ void Synth::Impl::noteOnDispatch(int delay, int noteNumber, float velocity) noex layer->keySwitched_ = false; } currentSwitch_ = noteNumber; - currentSwitchChanged_ = true; } for (Layer* layer : lastKeyswitchLists_[noteNumber]) diff --git a/src/sfizz/SynthPrivate.h b/src/sfizz/SynthPrivate.h index 960fe9f2..74fa5e34 100644 --- a/src/sfizz/SynthPrivate.h +++ b/src/sfizz/SynthPrivate.h @@ -240,7 +240,6 @@ struct Synth::Impl final: public Parser::Listener { // Set as sw_default if present in the file absl::optional currentSwitch_; - bool currentSwitchChanged_ = true; std::vector unknownOpcodes_; using RegionViewVector = std::vector; using LayerViewVector = std::vector; From 9636b3c733b027faa8cc8f26f5c2bfedb9479d47 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 3 Apr 2021 04:23:47 +0200 Subject: [PATCH 13/17] Delete redundant information --- plugins/vst/SfizzVstEditor.cpp | 5 ----- plugins/vst/SfizzVstProcessor.cpp | 5 ----- plugins/vst/SfizzVstState.h | 5 ----- 3 files changed, 15 deletions(-) diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index 19472000..14a2fd21 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -244,11 +244,6 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) if (PlayStateUpdate* update = FCast(changedUnknown)) { const SfizzPlayState playState = update->getState(); - uiReceiveValue(EditId::UINumCurves, playState.curves); - uiReceiveValue(EditId::UINumMasters, playState.masters); - uiReceiveValue(EditId::UINumGroups, playState.groups); - uiReceiveValue(EditId::UINumRegions, playState.regions); - uiReceiveValue(EditId::UINumPreloadedSamples, playState.preloadedSamples); uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices); return; } diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index 1bc97468..b782bd58 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -295,11 +295,6 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data) if (_playStateChangeCounter > _playStateChangePeriod) { _playStateChangeCounter %= _playStateChangePeriod; SfizzPlayState playState {}; - playState.curves = synth.getNumCurves(); - playState.masters = synth.getNumMasters(); - playState.groups = synth.getNumGroups(); - playState.regions = synth.getNumRegions(); - playState.preloadedSamples = synth.getNumPreloadedSamples(); playState.activeVoices = synth.getNumActiveVoices(); if (writeWorkerMessage(kMsgIdNotifyPlayState, &playState, sizeof(playState))) _semaToWorker.post(); diff --git a/plugins/vst/SfizzVstState.h b/plugins/vst/SfizzVstState.h index de35acb8..dbae0a3e 100644 --- a/plugins/vst/SfizzVstState.h +++ b/plugins/vst/SfizzVstState.h @@ -31,10 +31,5 @@ public: }; struct SfizzPlayState { - uint32 curves; - uint32 masters; - uint32 groups; - uint32 regions; - uint32 preloadedSamples; uint32 activeVoices; }; From 12896fe168ab1e93240e0a9f21fd55b1dbb1b1b1 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 3 Apr 2021 14:39:56 +0200 Subject: [PATCH 14/17] clang-tidy fixes --- plugins/vst/SfizzVstEditor.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index 14a2fd21..7bb7cfc5 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -206,20 +206,20 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) for (unsigned key = 0; key < 128; ++key) { bool keyUsed = desc.keyUsed.test(key); bool keyswitchUsed = desc.keyswitchUsed.test(key); - uiReceiveValue(editIdForKeyUsed(key), float(keyUsed)); - uiReceiveValue(editIdForKeyswitchUsed(key), float(keyswitchUsed)); + uiReceiveValue(editIdForKeyUsed(int(key)), float(keyUsed)); + uiReceiveValue(editIdForKeyswitchUsed(int(key)), float(keyswitchUsed)); if (keyUsed) - uiReceiveValue(editIdForKeyLabel(key), desc.keyLabel[key]); + uiReceiveValue(editIdForKeyLabel(int(key)), desc.keyLabel[key]); if (keyswitchUsed) - uiReceiveValue(editIdForKeyswitchLabel(key), desc.keyswitchLabel[key]); + uiReceiveValue(editIdForKeyswitchLabel(int(key)), desc.keyswitchLabel[key]); } for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) { bool ccUsed = desc.ccUsed.test(cc); - uiReceiveValue(editIdForCCUsed(cc), float(ccUsed)); + uiReceiveValue(editIdForCCUsed(int(cc)), float(ccUsed)); if (ccUsed) { - uiReceiveValue(editIdForCCDefault(cc), desc.ccDefault[cc]); - uiReceiveValue(editIdForCCLabel(cc), desc.ccLabel[cc]); + uiReceiveValue(editIdForCCDefault(int(cc)), desc.ccDefault[cc]); + uiReceiveValue(editIdForCCLabel(int(cc)), desc.ccLabel[cc]); } } } From 0bcfbc54959d7c4174c75e79cfc5e562d439e81d Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 3 Apr 2021 17:47:31 +0200 Subject: [PATCH 15/17] Update active voices with interval based on system clock --- plugins/vst/SfizzVstProcessor.cpp | 54 +++++++++++++------------------ plugins/vst/SfizzVstProcessor.h | 6 +--- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index b782bd58..6dd841a7 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -39,11 +39,10 @@ static const char* kRingIdOsc = "Osc"; static const char* kMsgIdSetNumVoices = "SetNumVoices"; static const char* kMsgIdSetOversampling = "SetOversampling"; static const char* kMsgIdSetPreloadSize = "SetPreloadSize"; -static const char* kMsgIdNotifyPlayState = "NotifyPlayState"; static const char* kMsgIdReceiveMessage = "ReceiveMessage"; static const char* kMsgIdNoteEvents = "NoteEvents"; -static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 500 }; +static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 50 }; SfizzVstProcessor::SfizzVstProcessor() : _fifoToWorker(64 * 1024), _fifoMessageFromUi(64 * 1024), @@ -219,9 +218,6 @@ tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state) if (state) { synth->setSampleRate(processSetup.sampleRate); synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock); - - _playStateChangePeriod = static_cast(50e-3 * processSetup.sampleRate); - startBackgroundWork(); } else { stopBackgroundWork(); @@ -291,15 +287,6 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data) synth.sendMessage(client, 0, "/cc/changed~", "", nullptr); synth.sendMessage(client, 0, "/sw/last/current", "", nullptr); - _playStateChangeCounter += numFrames; - if (_playStateChangeCounter > _playStateChangePeriod) { - _playStateChangeCounter %= _playStateChangePeriod; - SfizzPlayState playState {}; - playState.activeVoices = synth.getNumActiveVoices(); - if (writeWorkerMessage(kMsgIdNotifyPlayState, &playState, sizeof(playState))) - _semaToWorker.post(); - } - // std::pair noteEvents[128]; size_t numNoteEvents = 0; @@ -663,6 +650,7 @@ void SfizzVstProcessor::doBackgroundWork() bool haveDoneIdleWork = false; Clock::time_point lastIdleWorkTime; + size_t idleCounter = 0; for (;;) { bool isNotified = _semaToWorker.timed_wait(kBackgroundIdleInterval.count()); @@ -697,13 +685,6 @@ void SfizzVstProcessor::doBackgroundWork() std::lock_guard lock(_processMutex); _synth->setPreloadSize(value); } - else if (id == kMsgIdNotifyPlayState) { - SfizzPlayState playState = *msg->payload(); - Steinberg::OPtr notification { allocateMessage() }; - notification->setMessageID("NotifiedPlayState"); - notification->getAttributes()->setBinary("PlayState", &playState, sizeof(playState)); - sendMessage(notification); - } else if (id == kMsgIdReceiveMessage) { Steinberg::OPtr notification { allocateMessage() }; notification->setMessageID("ReceivedMessage"); @@ -719,24 +700,35 @@ void SfizzVstProcessor::doBackgroundWork() Clock::time_point currentTime = Clock::now(); if (!haveDoneIdleWork || currentTime - lastIdleWorkTime > kBackgroundIdleInterval) { - doBackgroundIdle(); + doBackgroundIdle(idleCounter++); haveDoneIdleWork = true; lastIdleWorkTime = currentTime; } } } -void SfizzVstProcessor::doBackgroundIdle() +void SfizzVstProcessor::doBackgroundIdle(size_t idleCounter) { - if (_synth->shouldReloadFile()) { - fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n"); - std::lock_guard lock(_processMutex); - loadSfzFileOrDefault(_state.sfzFile); + { + SfizzPlayState ps; + ps.activeVoices = _synth->getNumActiveVoices(); + Steinberg::OPtr notification { allocateMessage() }; + notification->setMessageID("NotifiedPlayState"); + notification->getAttributes()->setBinary("PlayState", &ps, sizeof(ps)); + sendMessage(notification); } - if (_synth->shouldReloadScala()) { - fprintf(stderr, "[Sfizz] scala file has changed, reloading\n"); - std::lock_guard lock(_processMutex); - _synth->loadScalaFile(_state.scalaFile); + + if (idleCounter % 10 == 0) { + if (_synth->shouldReloadFile()) { + fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n"); + std::lock_guard lock(_processMutex); + loadSfzFileOrDefault(_state.sfzFile); + } + if (_synth->shouldReloadScala()) { + fprintf(stderr, "[Sfizz] scala file has changed, reloading\n"); + std::lock_guard lock(_processMutex); + _synth->loadScalaFile(_state.scalaFile); + } } } diff --git a/plugins/vst/SfizzVstProcessor.h b/plugins/vst/SfizzVstProcessor.h index f7d73f2c..55dcd039 100644 --- a/plugins/vst/SfizzVstProcessor.h +++ b/plugins/vst/SfizzVstProcessor.h @@ -75,10 +75,6 @@ private: Ring_Buffer _fifoMessageFromUi; SpinMutex _processMutex; - // state notification periodic timer - uint32 _playStateChangeCounter = 0; - uint32 _playStateChangePeriod = 0; - // time info int _timeSigNumerator = 0; int _timeSigDenominator = 0; @@ -98,7 +94,7 @@ private: // worker void doBackgroundWork(); - void doBackgroundIdle(); + void doBackgroundIdle(size_t idleCounter); void startBackgroundWork(); void stopBackgroundWork(); // writer From bbac05281dfba92b3396aba25362d52afbcb5c64 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 3 Apr 2021 23:31:45 +0200 Subject: [PATCH 16/17] Update InstrumentDescription to receive the image name --- plugins/common/plugin/InstrumentDescription.cpp | 9 +++++++++ plugins/common/plugin/InstrumentDescription.h | 2 ++ src/sfizz/Synth.cpp | 7 ++++++- src/sfizz/SynthMessaging.cpp | 6 ++++++ src/sfizz/SynthPrivate.h | 3 +++ 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/plugins/common/plugin/InstrumentDescription.cpp b/plugins/common/plugin/InstrumentDescription.cpp index e104cc10..7378fd03 100644 --- a/plugins/common/plugin/InstrumentDescription.cpp +++ b/plugins/common/plugin/InstrumentDescription.cpp @@ -98,6 +98,8 @@ std::string getDescriptionBlob(sfizz_synth_t* handle) synth.sendMessage(*client, 0, "/num_masters", "", nullptr); synth.sendMessage(*client, 0, "/num_curves", "", nullptr); synth.sendMessage(*client, 0, "/num_samples", "", nullptr); + synth.sendMessage(*client, 0, "/root_path", "", nullptr); + synth.sendMessage(*client, 0, "/image", "", nullptr); synth.sendMessage(*client, 0, "/key/slots", "", nullptr); synth.sendMessage(*client, 0, "/sw/last/slots", "", nullptr); synth.sendMessage(*client, 0, "/cc/slots", "", nullptr); @@ -140,6 +142,10 @@ InstrumentDescription parseDescriptionBlob(absl::string_view blob) desc.numCurves = uint32_t(args[0].i); else if (Messages::matchOSC("/num_samples", path, indices) && !strcmp(sig, "i")) desc.numSamples = uint32_t(args[0].i); + else if (Messages::matchOSC("/root_path", path, indices) && !strcmp(sig, "s")) + desc.rootPath = args[0].s; + else if (Messages::matchOSC("/image", path, indices) && !strcmp(sig, "s")) + desc.image = args[0].s; else if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b")) copyArgToBitSpan(args[0], desc.keyUsed.span()); else if (Messages::matchOSC("/sw/last/slots", path, indices) && !strcmp(sig, "b")) @@ -172,6 +178,9 @@ std::ostream& operator<<(std::ostream& os, const InstrumentDescription& desc) os << " curves: " << desc.numCurves << "\n"; os << " samples: " << desc.numSamples << "\n"; + os << " root_path: " << desc.rootPath << "\n"; + os << " image: " << desc.image << "\n"; + os << " keys:\n"; for (unsigned i = 0; i < 128; ++i) { if (desc.keyUsed.test(i)) { diff --git a/plugins/common/plugin/InstrumentDescription.h b/plugins/common/plugin/InstrumentDescription.h index 88478a2d..b943dcba 100644 --- a/plugins/common/plugin/InstrumentDescription.h +++ b/plugins/common/plugin/InstrumentDescription.h @@ -22,6 +22,8 @@ struct InstrumentDescription { uint32_t numMasters {}; uint32_t numCurves {}; uint32_t numSamples {}; + std::string rootPath; + std::string image; BitArray<128> keyUsed {}; BitArray<128> keyswitchUsed {}; BitArray ccUsed {}; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index ea7914ac..e1bc441f 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -246,6 +246,7 @@ void Synth::Impl::clear() effectBuses_[0]->setSampleRate(sampleRate_); effectBuses_[0]->clearInputs(samplesPerBlock_); resources_.clear(); + rootPath_.clear(); numGroups_ = 0; numMasters_ = 0; currentSwitch_ = absl::nullopt; @@ -547,7 +548,11 @@ bool Synth::loadSfzString(const fs::path& path, absl::string_view text) void Synth::Impl::finalizeSfzLoad() { - resources_.filePool.setRootDirectory(parser_.originalDirectory()); + const fs::path& rootDirectory = parser_.originalDirectory(); + resources_.filePool.setRootDirectory(rootDirectory); + + // a string representation used for OSC purposes + rootPath_ = rootDirectory.u8string(); size_t currentRegionIndex = 0; size_t currentRegionCount = layers_.size(); diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 70fc6ed5..5cead784 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -76,10 +76,16 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co //---------------------------------------------------------------------- + MATCH("/root_path", "") { + client.receive<'s'>(delay, path, impl.rootPath_.c_str()); + } break; + MATCH("/image", "") { client.receive<'s'>(delay, path, impl.image_.c_str()); } break; + //---------------------------------------------------------------------- + MATCH("/sw/last/slots", "") { const BitArray<128>& switches = impl.swLastSlots_; sfizz_blob_t blob { switches.data(), static_cast(switches.byte_size()) }; diff --git a/src/sfizz/SynthPrivate.h b/src/sfizz/SynthPrivate.h index 74fa5e34..3b8835a6 100644 --- a/src/sfizz/SynthPrivate.h +++ b/src/sfizz/SynthPrivate.h @@ -278,6 +278,9 @@ struct Synth::Impl final: public Parser::Listener { // Singletons passed as references to the voices Resources resources_; + // Root path + std::string rootPath_; + // Control opcodes std::string defaultPath_ { "" }; std::string image_ { "" }; From 06ae5d2f52329c051a70cb0499114a8840e2b829 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 3 Apr 2021 23:58:46 +0200 Subject: [PATCH 17/17] Add support of background image --- plugins/editor/src/editor/EditIds.h | 2 ++ plugins/editor/src/editor/Editor.cpp | 11 +++++++---- plugins/lv2/sfizz_ui.cpp | 5 +++++ plugins/vst/SfizzVstEditor.cpp | 5 +++++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/plugins/editor/src/editor/EditIds.h b/plugins/editor/src/editor/EditIds.h index facb6a13..19016456 100644 --- a/plugins/editor/src/editor/EditIds.h +++ b/plugins/editor/src/editor/EditIds.h @@ -44,6 +44,8 @@ enum class EditId : int { UINumActiveVoices, UIActivePanel, // + BackgroundImage, + // #undef KEY_RANGE #undef CC_RANGE }; diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp index 6c2af261..25108200 100644 --- a/plugins/editor/src/editor/Editor.cpp +++ b/plugins/editor/src/editor/Editor.cpp @@ -443,6 +443,12 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v) setActivePanel(value); } break; + case EditId::BackgroundImage: + { + const std::string& value = v.to_string(); + updateBackgroundImage(value.c_str()); + } + break; default: if (editIdIsKey(id)) { const int key = keyForEditId(id); @@ -1541,10 +1547,7 @@ void Editor::Impl::updateSWLastLabel(unsigned sw, const char* label) void Editor::Impl::updateBackgroundImage(const char* filepath) { - const fs::path sfzFilePath = fs::u8path(currentSfzFile_); - const fs::path sfzDirPath = sfzFilePath.parent_path(); - const fs::path imagePath = sfzDirPath / fs::u8path(filepath); - SharedPointer bitmap = loadAnyFormatImage(imagePath); + SharedPointer bitmap = loadAnyFormatImage(filepath); if (!bitmap) bitmap = owned(new CBitmap("background.png")); diff --git a/plugins/lv2/sfizz_ui.cpp b/plugins/lv2/sfizz_ui.cpp index 68c846b7..d6073939 100644 --- a/plugins/lv2/sfizz_ui.cpp +++ b/plugins/lv2/sfizz_ui.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -379,6 +380,10 @@ sfizz_ui_update_description(sfizz_ui_t *self, const InstrumentDescription& desc) self->uiReceiveValue(EditId::UINumRegions, desc.numRegions); self->uiReceiveValue(EditId::UINumPreloadedSamples, desc.numSamples); + const fs::path rootPath = fs::u8path(desc.rootPath); + const fs::path imagePath = rootPath / fs::u8path(desc.image); + self->uiReceiveValue(EditId::BackgroundImage, imagePath.u8string()); + for (unsigned key = 0; key < 128; ++key) { bool keyUsed = desc.keyUsed.test(key); bool keyswitchUsed = desc.keyswitchUsed.test(key); diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index 7bb7cfc5..decc6f43 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -16,6 +16,7 @@ #if !defined(__APPLE__) && !defined(_WIN32) #include "X11RunLoop.h" #endif +#include using namespace VSTGUI; @@ -203,6 +204,10 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) uiReceiveValue(EditId::UINumRegions, desc.numRegions); uiReceiveValue(EditId::UINumPreloadedSamples, desc.numSamples); + const fs::path rootPath = fs::u8path(desc.rootPath); + const fs::path imagePath = rootPath / fs::u8path(desc.image); + uiReceiveValue(EditId::BackgroundImage, imagePath.u8string()); + for (unsigned key = 0; key < 128; ++key) { bool keyUsed = desc.keyUsed.test(key); bool keyswitchUsed = desc.keyswitchUsed.test(key);