Merge pull request #540 from jpcima/osc

Add OSC and plugin-side communication
This commit is contained in:
JP Cimalando 2020-11-09 10:06:51 +01:00 committed by GitHub
commit bf4fd71ea6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 1232 additions and 59 deletions

View file

@ -89,6 +89,7 @@ SFIZZ_SOURCES = \
src/sfizz/Logger.cpp \ src/sfizz/Logger.cpp \
src/sfizz/LFO.cpp \ src/sfizz/LFO.cpp \
src/sfizz/LFODescription.cpp \ src/sfizz/LFODescription.cpp \
src/sfizz/Messaging.cpp \
src/sfizz/MidiState.cpp \ src/sfizz/MidiState.cpp \
src/sfizz/OpcodeCleanup.cpp \ src/sfizz/OpcodeCleanup.cpp \
src/sfizz/Opcode.cpp \ src/sfizz/Opcode.cpp \
@ -112,6 +113,7 @@ SFIZZ_SOURCES = \
src/sfizz/simd/HelpersAVX.cpp \ src/sfizz/simd/HelpersAVX.cpp \
src/sfizz/Smoothers.cpp \ src/sfizz/Smoothers.cpp \
src/sfizz/Synth.cpp \ src/sfizz/Synth.cpp \
src/sfizz/SynthMessaging.cpp \
src/sfizz/Tuning.cpp \ src/sfizz/Tuning.cpp \
src/sfizz/utility/SpinMutex.cpp \ src/sfizz/utility/SpinMutex.cpp \
src/sfizz/Voice.cpp \ src/sfizz/Voice.cpp \

View file

@ -42,6 +42,7 @@ add_library(sfizz_editor STATIC EXCLUDE_FROM_ALL
src/editor/utility/vstgui_after.h src/editor/utility/vstgui_after.h
src/editor/utility/vstgui_before.h) src/editor/utility/vstgui_before.h)
target_include_directories(sfizz_editor PUBLIC "src") target_include_directories(sfizz_editor PUBLIC "src")
target_link_libraries(sfizz_editor PUBLIC sfizz_messaging)
target_link_libraries(sfizz_editor PRIVATE sfizz-vstgui) target_link_libraries(sfizz_editor PRIVATE sfizz-vstgui)
target_link_libraries(sfizz_editor PUBLIC absl::strings) target_link_libraries(sfizz_editor PUBLIC absl::strings)
if(APPLE) if(APPLE)

View file

@ -100,6 +100,7 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
SPiano* piano_ = nullptr; SPiano* piano_ = nullptr;
void uiReceiveValue(EditId id, const EditValue& v) override; void uiReceiveValue(EditId id, const EditValue& v) override;
void uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args) override;
void createFrameContents(); void createFrameContents();
@ -318,6 +319,11 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
} }
} }
void Editor::Impl::uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args)
{
// TODO handle the message...
}
void Editor::Impl::createFrameContents() void Editor::Impl::createFrameContents()
{ {
CViewContainer* mainView; CViewContainer* mainView;

View file

@ -6,6 +6,7 @@
#pragma once #pragma once
#include "EditValue.h" #include "EditValue.h"
#include <sfizz_message.h>
#include <absl/strings/string_view.h> #include <absl/strings/string_view.h>
#include <string> #include <string>
#include <cstdint> #include <cstdint>
@ -20,6 +21,7 @@ public:
virtual void uiBeginSend(EditId id) = 0; virtual void uiBeginSend(EditId id) = 0;
virtual void uiEndSend(EditId id) = 0; virtual void uiEndSend(EditId id) = 0;
virtual void uiSendMIDI(const uint8_t* msg, uint32_t len) = 0; virtual void uiSendMIDI(const uint8_t* msg, uint32_t len) = 0;
virtual void uiSendMessage(const char* path, const char* sig, const sfizz_arg_t* args) = 0;
class Receiver; class Receiver;
void decorate(Receiver* r) { r_ = r; } void decorate(Receiver* r) { r_ = r; }
@ -27,10 +29,12 @@ public:
public: public:
virtual ~Receiver() {} virtual ~Receiver() {}
virtual void uiReceiveValue(EditId id, const EditValue& v) = 0; virtual void uiReceiveValue(EditId id, const EditValue& v) = 0;
virtual void uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args) = 0;
}; };
// called by DSP // called by DSP
void uiReceiveValue(EditId id, const EditValue& v); void uiReceiveValue(EditId id, const EditValue& v);
void uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args);
private: private:
Receiver* r_ = nullptr; Receiver* r_ = nullptr;
@ -41,3 +45,9 @@ inline void EditorController::uiReceiveValue(EditId id, const EditValue& v)
if (r_) if (r_)
r_->uiReceiveValue(id, v); r_->uiReceiveValue(id, v);
} }
inline void EditorController::uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args)
{
if (r_)
r_->uiReceiveMessage(path, sig, args);
}

View file

@ -148,6 +148,7 @@ typedef struct
LV2_URID sfizz_log_status_uri; LV2_URID sfizz_log_status_uri;
LV2_URID sfizz_check_modification_uri; LV2_URID sfizz_check_modification_uri;
LV2_URID sfizz_active_voices_uri; LV2_URID sfizz_active_voices_uri;
LV2_URID sfizz_osc_blob_uri;
LV2_URID time_position_uri; LV2_URID time_position_uri;
LV2_URID time_bar_uri; LV2_URID time_bar_uri;
LV2_URID time_bar_beat_uri; LV2_URID time_bar_beat_uri;
@ -158,6 +159,7 @@ typedef struct
// Sfizz related data // Sfizz related data
sfizz_synth_t *synth; sfizz_synth_t *synth;
sfizz_client_t *client;
bool expect_nominal_block_length; bool expect_nominal_block_length;
char sfz_file_path[MAX_PATH_SIZE]; char sfz_file_path[MAX_PATH_SIZE];
char scala_file_path[MAX_PATH_SIZE]; char scala_file_path[MAX_PATH_SIZE];
@ -181,6 +183,9 @@ typedef struct
// Paths // Paths
char bundle_path[MAX_BUNDLE_PATH_SIZE]; char bundle_path[MAX_BUNDLE_PATH_SIZE];
// OSC
uint8_t osc_temp[OSC_TEMP_SIZE];
} sfizz_plugin_t; } sfizz_plugin_t;
enum enum
@ -236,6 +241,7 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self)
self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus); self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus);
self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus); self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus);
self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification); self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification);
self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob);
self->time_position_uri = map->map(map->handle, LV2_TIME__Position); self->time_position_uri = map->map(map->handle, LV2_TIME__Position);
self->time_bar_uri = map->map(map->handle, LV2_TIME__bar); self->time_bar_uri = map->map(map->handle, LV2_TIME__bar);
self->time_bar_beat_uri = map->map(map->handle, LV2_TIME__barBeat); self->time_bar_beat_uri = map->map(map->handle, LV2_TIME__barBeat);
@ -423,6 +429,28 @@ sfizz_lv2_update_timeinfo(sfizz_plugin_t *self, int delay, int updates)
sfizz_send_playback_state(self->synth, delay, self->speed > 0); sfizz_send_playback_state(self->synth, delay, self->speed > 0);
} }
static void
sfizz_lv2_receive_message(void* data, int delay, const char* path, const char* sig, const sfizz_arg_t* args)
{
(void)delay;
sfizz_plugin_t *self = (sfizz_plugin_t *)data;
// transmit to UI as OSC blob
uint8_t *osc_temp = self->osc_temp;
uint32_t osc_size = sfizz_prepare_message(osc_temp, OSC_TEMP_SIZE, path, sig, args);
if (osc_size > OSC_TEMP_SIZE)
return;
bool write_ok =
lv2_atom_forge_frame_time(&self->forge, 0) &&
lv2_atom_forge_atom(&self->forge, osc_size, self->sfizz_osc_blob_uri) &&
lv2_atom_forge_raw(&self->forge, osc_temp, osc_size);
lv2_atom_forge_pad(&self->forge, osc_size);
(void)write_ok;
}
static LV2_Handle static LV2_Handle
instantiate(const LV2_Descriptor *descriptor, instantiate(const LV2_Descriptor *descriptor,
double rate, double rate,
@ -570,6 +598,9 @@ instantiate(const LV2_Descriptor *descriptor,
} }
self->synth = sfizz_create_synth(); self->synth = sfizz_create_synth();
self->client = sfizz_create_client(self);
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(instance, self->sfz_file_path, MAX_PATH_SIZE); sfizz_lv2_get_default_sfz_path(instance, self->sfz_file_path, MAX_PATH_SIZE);
sfizz_lv2_get_default_scala_path(instance, self->scala_file_path, MAX_PATH_SIZE); sfizz_lv2_get_default_scala_path(instance, self->scala_file_path, MAX_PATH_SIZE);
@ -586,6 +617,7 @@ static void
cleanup(LV2_Handle instance) cleanup(LV2_Handle instance)
{ {
sfizz_plugin_t *self = (sfizz_plugin_t *)instance; sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
sfizz_delete_client(self->client);
sfizz_free(self->synth); sfizz_free(self->synth);
free(self); free(self);
} }
@ -952,12 +984,22 @@ run(LV2_Handle instance, uint32_t sample_count)
self->unmap->unmap(self->unmap->handle, obj->body.otype)); self->unmap->unmap(self->unmap->handle, obj->body.otype));
continue; continue;
} }
// Got an atom that is a MIDI event
} }
else if (ev->body.type == self->midi_event_uri) else if (ev->body.type == self->midi_event_uri)
{ {
// Got an atom that is a MIDI event
sfizz_lv2_process_midi_event(self, ev); sfizz_lv2_process_midi_event(self, ev);
} }
else if (ev->body.type == self->sfizz_osc_blob_uri)
{
// Got an atom that is a OSC event
const char *path;
const char *sig;
const sfizz_arg_t *args;
uint8_t buffer[1024];
if (sfizz_extract_message(LV2_ATOM_BODY_CONST(&ev->body), ev->body.size, buffer, sizeof(buffer), &path, &sig, &args) > 0)
sfizz_send_message(self->synth, self->client, (int)ev->time.frames, path, sig, args);
}
} }

View file

@ -11,6 +11,7 @@
@prefix pprop: <http://lv2plug.in/ns/ext/port-props#> . @prefix pprop: <http://lv2plug.in/ns/ext/port-props#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rsz: <http://lv2plug.in/ns/ext/resize-port#> .
@prefix state: <http://lv2plug.in/ns/ext/state#> . @prefix state: <http://lv2plug.in/ns/ext/state#> .
@prefix time: <http://lv2plug.in/ns/ext/time#> . @prefix time: <http://lv2plug.in/ns/ext/time#> .
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . @prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
@ -92,21 +93,23 @@ midnam:update a lv2:Feature .
lv2:port [ lv2:port [
a lv2:InputPort, atom:AtomPort ; a lv2:InputPort, atom:AtomPort ;
atom:bufferType atom:Sequence ; atom:bufferType atom:Sequence ;
atom:supports patch:Message, midi:MidiEvent, time:Position ; atom:supports patch:Message, midi:MidiEvent, time:Position, <@LV2PLUGIN_URI@:OSCBlob> ;
lv2:designation lv2:control ; lv2:designation lv2:control ;
lv2:index 0 ; lv2:index 0 ;
lv2:symbol "control" ; lv2:symbol "control" ;
lv2:name "Control", lv2:name "Control",
"Contrôle"@fr ; "Contrôle"@fr ;
rsz:minimumSize 65536 ;
] , [ ] , [
a lv2:OutputPort, atom:AtomPort ; a lv2:OutputPort, atom:AtomPort ;
atom:bufferType atom:Sequence ; atom:bufferType atom:Sequence ;
atom:supports patch:Message ; atom:supports patch:Message, <@LV2PLUGIN_URI@:OSCBlob> ;
lv2:designation lv2:control ; lv2:designation lv2:control ;
lv2:index 1 ; lv2:index 1 ;
lv2:symbol "notify" ; lv2:symbol "notify" ;
lv2:name "Notify", lv2:name "Notify",
"Notification"@fr ; "Notification"@fr ;
rsz:minimumSize 65536 ;
] , [ ] , [
a lv2:AudioPort, lv2:OutputPort ; a lv2:AudioPort, lv2:OutputPort ;
lv2:index 2 ; lv2:index 2 ;

View file

@ -7,6 +7,8 @@
#pragma once #pragma once
#define MAX_PATH_SIZE 1024 #define MAX_PATH_SIZE 1024
#define ATOM_TEMP_SIZE 8192
#define OSC_TEMP_SIZE 8192
#define SFIZZ_URI "http://sfztools.github.io/sfizz" #define SFIZZ_URI "http://sfztools.github.io/sfizz"
#define SFIZZ_UI_URI "http://sfztools.github.io/sfizz#ui" #define SFIZZ_UI_URI "http://sfztools.github.io/sfizz#ui"
@ -19,6 +21,8 @@
// These ones are just for the worker // These ones are just for the worker
#define SFIZZ__logStatus SFIZZ_URI ":" "log_status" #define SFIZZ__logStatus SFIZZ_URI ":" "log_status"
#define SFIZZ__checkModification SFIZZ_URI ":" "check_modification" #define SFIZZ__checkModification SFIZZ_URI ":" "check_modification"
// OSC atoms
#define SFIZZ__OSCBlob SFIZZ_URI ":" "OSCBlob"
enum enum
{ {

View file

@ -107,12 +107,17 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
LV2_URID patch_value_uri; LV2_URID patch_value_uri;
LV2_URID sfizz_sfz_file_uri; LV2_URID sfizz_sfz_file_uri;
LV2_URID sfizz_scala_file_uri; LV2_URID sfizz_scala_file_uri;
LV2_URID sfizz_osc_blob_uri;
uint8_t osc_temp[OSC_TEMP_SIZE];
alignas(LV2_Atom) uint8_t atom_temp[ATOM_TEMP_SIZE];
protected: protected:
void uiSendValue(EditId id, const EditValue& v) override; void uiSendValue(EditId id, const EditValue& v) override;
void uiBeginSend(EditId id) override; void uiBeginSend(EditId id) override;
void uiEndSend(EditId id) override; void uiEndSend(EditId id) override;
void uiSendMIDI(const uint8_t* msg, uint32_t len) override; void uiSendMIDI(const uint8_t* msg, uint32_t len) override;
void uiSendMessage(const char* path, const char* sig, const sfizz_arg_t* args) override;
private: private:
void uiTouch(EditId id, bool t); void uiTouch(EditId id, bool t);
@ -192,6 +197,7 @@ instantiate(const LV2UI_Descriptor *descriptor,
self->patch_value_uri = map->map(map->handle, LV2_PATCH__value); self->patch_value_uri = map->map(map->handle, LV2_PATCH__value);
self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile); self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile);
self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__tuningfile); self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__tuningfile);
self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob);
// set up the resource path // set up the resource path
// * on Linux, this is determined by going 2 folders back from the SO path // * on Linux, this is determined by going 2 folders back from the SO path
@ -335,6 +341,14 @@ port_event(LV2UI_Handle ui,
} }
} }
} }
else if (atom->type == self->sfizz_osc_blob_uri) {
const char *path;
const char *sig;
const sfizz_arg_t *args;
uint8_t buffer[1024];
if (sfizz_extract_message(LV2_ATOM_BODY_CONST(atom), atom->size, buffer, sizeof(buffer), &path, &sig, &args) > 0)
self->uiReceiveMessage(path, sig, args);
}
} }
(void)buffer_size; (void)buffer_size;
@ -422,9 +436,8 @@ void sfizz_ui_t::uiSendValue(EditId id, const EditValue& v)
auto sendPath = [this](LV2_URID property, const std::string& value) { auto sendPath = [this](LV2_URID property, const std::string& value) {
LV2_Atom_Forge *forge = &atom_forge; LV2_Atom_Forge *forge = &atom_forge;
LV2_Atom_Forge_Frame frame; LV2_Atom_Forge_Frame frame;
alignas(LV2_Atom) uint8_t buffer[MAX_PATH_SIZE + 512]; auto *atom = reinterpret_cast<const LV2_Atom *>(atom_temp);
auto *atom = reinterpret_cast<const LV2_Atom *>(buffer); lv2_atom_forge_set_buffer(forge, atom_temp, sizeof(atom_temp));
lv2_atom_forge_set_buffer(forge, (uint8_t *)&buffer, sizeof(buffer));
if (lv2_atom_forge_object(forge, &frame, 0, patch_set_uri) && if (lv2_atom_forge_object(forge, &frame, 0, patch_set_uri) &&
lv2_atom_forge_key(forge, patch_property_uri) && lv2_atom_forge_key(forge, patch_property_uri) &&
lv2_atom_forge_urid(forge, property) && lv2_atom_forge_urid(forge, property) &&
@ -514,12 +527,30 @@ void sfizz_ui_t::uiTouch(EditId id, bool t)
void sfizz_ui_t::uiSendMIDI(const uint8_t* msg, uint32_t len) void sfizz_ui_t::uiSendMIDI(const uint8_t* msg, uint32_t len)
{ {
LV2_Atom_Forge *forge = &atom_forge; LV2_Atom_Forge *forge = &atom_forge;
alignas(LV2_Atom) uint8_t buffer[512]; auto *atom = reinterpret_cast<const LV2_Atom *>(atom_temp);
auto *atom = reinterpret_cast<const LV2_Atom *>(buffer); lv2_atom_forge_set_buffer(forge, atom_temp, sizeof(atom_temp));
lv2_atom_forge_set_buffer(forge, (uint8_t *)&buffer, sizeof(buffer));
if (lv2_atom_forge_atom(forge, len, midi_event_uri) && if (lv2_atom_forge_atom(forge, len, midi_event_uri) &&
lv2_atom_forge_write(forge, msg, len)) lv2_atom_forge_write(forge, msg, len))
{ {
write(con, SFIZZ_CONTROL, lv2_atom_total_size(atom), atom_event_transfer_uri, atom); write(con, SFIZZ_CONTROL, lv2_atom_total_size(atom), atom_event_transfer_uri, atom);
} }
} }
void sfizz_ui_t::uiSendMessage(const char* path, const char* sig, const sfizz_arg_t* args)
{
uint8_t *osc_temp = this->osc_temp;
uint32_t osc_size = sfizz_prepare_message(osc_temp, OSC_TEMP_SIZE, path, sig, args);
if (osc_size > OSC_TEMP_SIZE)
return;
LV2_Atom_Forge *forge = &atom_forge;
auto *atom = reinterpret_cast<const LV2_Atom *>(atom_temp);
lv2_atom_forge_set_buffer(forge, atom_temp, sizeof(atom_temp));
if (lv2_atom_forge_atom(forge, osc_size, sfizz_osc_blob_uri) &&
lv2_atom_forge_raw(forge, osc_temp, osc_size))
{
write(con, SFIZZ_CONTROL, lv2_atom_total_size(atom), atom_event_transfer_uri, atom);
}
}

View file

@ -148,6 +148,7 @@ set (SFIZZ_SOURCES
sfizz/PowerFollower.cpp sfizz/PowerFollower.cpp
sfizz/FlexEGDescription.cpp sfizz/FlexEGDescription.cpp
sfizz/FlexEnvelope.cpp sfizz/FlexEnvelope.cpp
sfizz/SynthMessaging.cpp
sfizz/modulations/ModId.cpp sfizz/modulations/ModId.cpp
sfizz/modulations/ModKey.cpp sfizz/modulations/ModKey.cpp
sfizz/modulations/ModKeyHash.cpp sfizz/modulations/ModKeyHash.cpp
@ -206,6 +207,7 @@ set (SFIZZ_PARSER_SOURCES
set (SFIZZ_PARSER_OTHER sfizz/OpcodeCleanup.re) set (SFIZZ_PARSER_OTHER sfizz/OpcodeCleanup.re)
source_group ("Other Files" FILES ${SFIZZ_PARSER_OTHER}) source_group ("Other Files" FILES ${SFIZZ_PARSER_OTHER})
# Sfizz parser library
add_library (sfizz_parser STATIC) add_library (sfizz_parser STATIC)
target_sources (sfizz_parser PRIVATE target_sources (sfizz_parser PRIVATE
${SFIZZ_PARSER_HEADERS} ${SFIZZ_PARSER_SOURCES} ${SFIZZ_PARSER_OTHER}) ${SFIZZ_PARSER_HEADERS} ${SFIZZ_PARSER_SOURCES} ${SFIZZ_PARSER_OTHER})
@ -213,6 +215,20 @@ target_include_directories (sfizz_parser PUBLIC sfizz)
target_include_directories (sfizz_parser PUBLIC external) target_include_directories (sfizz_parser PUBLIC external)
target_link_libraries (sfizz_parser PUBLIC absl::strings PRIVATE absl::flat_hash_map) target_link_libraries (sfizz_parser PUBLIC absl::strings PRIVATE absl::flat_hash_map)
# OSC messaging library
set (SFIZZ_MESSAGING_HEADERS
sfizz/Messaging.h
sfizz_message.h)
set (SFIZZ_MESSAGING_SOURCES
sfizz/Messaging.cpp)
add_library (sfizz_messaging STATIC)
target_sources (sfizz_messaging PRIVATE
${SFIZZ_MESSAGING_HEADERS} ${SFIZZ_MESSAGING_SOURCES})
target_include_directories (sfizz_messaging PUBLIC ".")
target_link_libraries (sfizz_messaging PUBLIC absl::strings)
# Sfizz static library # Sfizz static library
add_library(sfizz_static STATIC) add_library(sfizz_static STATIC)
target_sources(sfizz_static PRIVATE target_sources(sfizz_static PRIVATE
@ -220,8 +236,8 @@ target_sources(sfizz_static PRIVATE
target_include_directories (sfizz_static PUBLIC .) target_include_directories (sfizz_static PUBLIC .)
target_include_directories (sfizz_static PUBLIC external) target_include_directories (sfizz_static PUBLIC external)
target_link_libraries (sfizz_static PUBLIC absl::strings absl::span) target_link_libraries (sfizz_static PUBLIC absl::strings absl::span)
target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads st_audiofile sfizz-pugixml sfizz-spline sfizz-tunings sfizz-kissfft sfizz-cpuid sfizz-jsl sfizz-atomic) target_link_libraries (sfizz_static PRIVATE sfizz_parser sfizz_messaging absl::flat_hash_map Threads::Threads st_audiofile sfizz-pugixml sfizz-spline sfizz-tunings sfizz-kissfft sfizz-cpuid sfizz-jsl sfizz-atomic)
set_target_properties (sfizz_static PROPERTIES OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp") set_target_properties (sfizz_static PROPERTIES OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp;sfizz_message.h")
if(SFIZZ_USE_SNDFILE) if(SFIZZ_USE_SNDFILE)
target_compile_definitions (sfizz_static PUBLIC SFIZZ_USE_SNDFILE=1) target_compile_definitions (sfizz_static PUBLIC SFIZZ_USE_SNDFILE=1)
target_link_libraries (sfizz_static PUBLIC st_audiofile) target_link_libraries (sfizz_static PUBLIC st_audiofile)
@ -251,7 +267,7 @@ if (SFIZZ_SHARED)
${SFIZZ_HEADERS} ${SFIZZ_SOURCES} ${FAUST_FILES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp) ${SFIZZ_HEADERS} ${SFIZZ_SOURCES} ${FAUST_FILES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp)
target_include_directories (sfizz_shared PRIVATE .) target_include_directories (sfizz_shared PRIVATE .)
target_include_directories (sfizz_shared PRIVATE external) target_include_directories (sfizz_shared PRIVATE external)
target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads st_audiofile sfizz-pugixml sfizz-spline sfizz-tunings sfizz-kissfft sfizz-cpuid sfizz-jsl sfizz-atomic) target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser sfizz_messaging absl::flat_hash_map Threads::Threads st_audiofile sfizz-pugixml sfizz-spline sfizz-tunings sfizz-kissfft sfizz-cpuid sfizz-jsl sfizz-atomic)
if(SFIZZ_USE_SNDFILE) if(SFIZZ_USE_SNDFILE)
target_compile_definitions (sfizz_shared PUBLIC SFIZZ_USE_SNDFILE=1) target_compile_definitions (sfizz_shared PUBLIC SFIZZ_USE_SNDFILE=1)
target_link_libraries (sfizz_shared PUBLIC st_audiofile) target_link_libraries (sfizz_shared PUBLIC st_audiofile)
@ -263,7 +279,7 @@ if (SFIZZ_SHARED)
target_compile_definitions (sfizz_shared PRIVATE "SFIZZ_ENABLE_RELEASE_ASSERT=1") target_compile_definitions (sfizz_shared PRIVATE "SFIZZ_ENABLE_RELEASE_ASSERT=1")
endif() endif()
target_compile_definitions(sfizz_shared PRIVATE SFIZZ_EXPORT_SYMBOLS) target_compile_definitions(sfizz_shared PRIVATE SFIZZ_EXPORT_SYMBOLS)
set_target_properties (sfizz_shared PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR} OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp") set_target_properties (sfizz_shared PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR} OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp;sfizz_message.h")
sfizz_enable_lto_if_needed(sfizz_shared) sfizz_enable_lto_if_needed(sfizz_shared)
sfizz_enable_fast_math(sfizz_shared) sfizz_enable_fast_math(sfizz_shared)

View file

@ -10,6 +10,7 @@
*/ */
#pragma once #pragma once
#include "sfizz_message.h"
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
@ -731,6 +732,79 @@ SFIZZ_EXPORTED_API int sfizz_get_cc_label_number(sfizz_synth_t* synth, int label
*/ */
SFIZZ_EXPORTED_API const char * sfizz_get_cc_label_text(sfizz_synth_t* synth, int label_index); SFIZZ_EXPORTED_API const char * sfizz_get_cc_label_text(sfizz_synth_t* synth, int label_index);
/**
* @addtogroup Messaging
* @{
*/
/**
* @brief Client for communicating with the synth engine in either direction
* @since 0.6.0
*/
typedef struct sfizz_client_t sfizz_client_t;
/**
* @brief Create a new messaging client
* @since 0.6.0
*
* @param data The opaque data pointer which is passed to the receiver.
* @return The new client.
*/
SFIZZ_EXPORTED_API sfizz_client_t* sfizz_create_client(void* data);
/**
* @brief Destroy a messaging client
* @since 0.6.0
*
* @param client The client.
*/
SFIZZ_EXPORTED_API void sfizz_delete_client(sfizz_client_t* client);
/**
* @brief Get the client data
* @since 0.6.0
*
* @param client The client.
* @return The client data.
*/
SFIZZ_EXPORTED_API void* sfizz_get_client_data(sfizz_client_t* client);
/**
* @brief Set the function which receives reply messages from the synth engine.
* @since 0.6.0
*
* @param client The client.
* @param receive The pointer to the receiving function.
*/
SFIZZ_EXPORTED_API void sfizz_set_receive_callback(sfizz_client_t* client, sfizz_receive_t* receive);
/**
* @brief Send a message to the synth engine
* @since 0.6.0
*
* @param synth The synth.
* @param client The client sending the message.
* @param delay The delay of the message in the block, in samples.
* @param path The OSC address pattern.
* @param sig The OSC type tag string.
* @param args The OSC arguments, whose number and format is determined the type tag string.
*/
SFIZZ_EXPORTED_API void sfizz_send_message(sfizz_synth_t* synth, sfizz_client_t* client, int delay, const char* path, const char* sig, const sfizz_arg_t* args);
/**
* @brief Set the function which receives broadcast messages from the synth engine.
* @since 0.6.0
*
* @param synth The synth.
* @param broadcast The pointer to the receiving function.
* @param data The opaque data pointer which is passed to the receiver.
*/
SFIZZ_EXPORTED_API void sfizz_set_broadcast_callback(sfizz_synth_t* synth, sfizz_receive_t* broadcast, void* data);
/**
* @}
*/
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -10,6 +10,7 @@
*/ */
#pragma once #pragma once
#include "sfizz_message.h"
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -28,6 +29,7 @@
namespace sfz namespace sfz
{ {
class Synth; class Synth;
class Client;
/** /**
* @brief Main class. * @brief Main class.
*/ */
@ -565,7 +567,75 @@ public:
*/ */
const std::vector<std::pair<uint16_t, std::string>>& getCCLabels() const noexcept; const std::vector<std::pair<uint16_t, std::string>>& getCCLabels() const noexcept;
/**
* @addtogroup Messaging
* @{
*/
private:
struct ClientDeleter {
void operator()(Client *client) const noexcept;
};
public:
using ClientPtr = std::unique_ptr<Client, ClientDeleter>;
/**
* @brief Create a new messaging client
* @since 0.6.0
*
* @param data The opaque data pointer which is passed to the receiver.
* @return The new client.
*/
static ClientPtr createClient(void* data);
/**
* @brief Get the client data
* @since 0.6.0
*
* @param client The client.
* @return The client data.
*/
static void* getClientData(Client& client);
/**
* @brief Set the function which receives reply messages from the synth engine.
* @since 0.6.0
*
* @param client The client.
* @param receive The pointer to the receiving function.
*/
static void setReceiveCallback(Client& client, sfizz_receive_t* receive);
/**
* @brief Send a message to the synth engine
* @since 0.6.0
*
* @param client The client sending the message.
* @param delay The delay of the message in the block, in samples.
* @param path The OSC address pattern.
* @param sig The OSC type tag string.
* @param args The OSC arguments, whose number and format is determined the type tag string.
*/
void sendMessage(Client& client, int delay, const char* path, const char* sig, const sfizz_arg_t* args);
/**
* @brief Set the function which receives broadcast messages from the synth engine.
* @since 0.6.0
*
* @param broadcast The pointer to the receiving function.
* @param data The opaque data pointer which is passed to the receiver.
*/
void setBroadcastCallback(sfizz_receive_t* broadcast, void* data);
/**
* @}
*/
private: private:
std::unique_ptr<sfz::Synth> synth; std::unique_ptr<sfz::Synth> synth;
}; };
using ClientPtr = Sfizz::ClientPtr;
} }

367
src/sfizz/Messaging.cpp Normal file
View file

@ -0,0 +1,367 @@
// 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 "Messaging.h"
#include <absl/strings/string_view.h>
#include <algorithm>
#include <type_traits>
#include <cstring>
#ifdef __cplusplus
static_assert(
sizeof(sfizz_arg_t) == sizeof(int64_t) && alignof(sfizz_arg_t) == 8,
"The ABI stability check has failed.");
#endif
template <class T>
static T paddingSize(T count, unsigned align) {
unsigned mask = align - 1;
return (align - (count & mask)) & mask;
};
///
class OSCWriter {
public:
void setOutputBuffer(void* buffer, uint32_t capacity);
uint32_t writeMessage(const char* path, const char* sig, const sfizz_arg_t* args);
private:
uint32_t appendBytes(const void* src, uint32_t count);
uint32_t appendZeros(uint32_t count);
template <class T> uint32_t appendInteger(T integer);
uint32_t appendFloat(float f);
uint32_t appendDouble(float d);
private:
uint8_t* dstBuffer_ = nullptr;
uint32_t dstCapacity_ = 0;
};
class OSCReader {
public:
void setInputBuffer(const void* buffer, uint32_t capacity);
void setAllocationBuffer(void* buffer, uint32_t capacity);
int32_t extractMessage(const char** outPath, const char** outSig, const sfizz_arg_t** outArgs);
private:
template <class T> T* allocate(uint32_t count);
bool extractString(const char*& outStr, uint32_t& outLen);
template <class T> bool extractInteger(T& outValue);
bool extractFloat(float& f);
bool extractDouble(double& d);
private:
const uint8_t* srcBuffer_ = nullptr;
uint32_t srcCapacity_ = 0;
uint8_t* allocBuffer_ = nullptr;
uint32_t allocCapacity_ = 0;
};
///
extern "C" uint32_t sfizz_prepare_message(
void* buffer, uint32_t capacity,
const char* path, const char* sig, const sfizz_arg_t* args)
{
OSCWriter writer;
writer.setOutputBuffer(buffer, capacity);
return writer.writeMessage(path, sig, args);
}
extern "C" int32_t sfizz_extract_message(
const void* srcBuffer, uint32_t srcCapacity,
void* argsBuffer, uint32_t argsCapacity,
const char** outPath, const char** outSig, const sfizz_arg_t** outArgs)
{
OSCReader reader;
reader.setInputBuffer(srcBuffer, srcCapacity);
reader.setAllocationBuffer(argsBuffer, argsCapacity);
return reader.extractMessage(outPath, outSig, outArgs);
}
///
void OSCWriter::setOutputBuffer(void* buffer, uint32_t capacity)
{
dstBuffer_ = reinterpret_cast<uint8_t*>(buffer);
dstCapacity_ = capacity;
}
uint32_t OSCWriter::writeMessage(const char* path, const char* sig, const sfizz_arg_t* args)
{
uint32_t msglen = 0;
// write path, null byte, and 4byte padding
uint32_t pathlen = static_cast<uint32_t>(strlen(path));
msglen += appendBytes(path, pathlen + 1);
msglen += appendZeros(paddingSize(pathlen + 1, 4));
// write comma, signature, null byte, and 4byte padding
uint32_t siglen = static_cast<uint32_t>(strlen(sig));
msglen += appendBytes(",", 1);
msglen += appendBytes(sig, siglen + 1);
msglen += appendZeros(paddingSize(siglen + 2, 4));
// write arguments
for (uint32_t i = 0; i < siglen; ++i) {
switch (sig[i]) {
default:
return 0;
case 'i':
case 'c':
case 'r':
msglen += appendInteger(args[i].i);
break;
case 'm':
msglen += appendBytes(args[i].m, 4);
break;
case 'h':
msglen += appendInteger(args[i].h);
break;
case 'f':
msglen += appendFloat(args[i].f);
break;
case 'd':
msglen += appendDouble(args[i].d);
break;
case 's':
case 'S':
{
size_t len = strlen(args[i].s);
msglen += appendBytes(args[i].s, len + 1);
msglen += appendZeros(paddingSize(len + 1, 4));
}
break;
case 'b':
{
msglen += appendInteger(args[i].b->size);
msglen += appendBytes(args[i].b->data, args[i].b->size);
msglen += appendZeros(paddingSize(args[i].b->size, 4));
}
break;
case 'T':
case 'F':
case 'N':
case 'I':
break;
}
}
return msglen;
}
uint32_t OSCWriter::appendBytes(const void* src, uint32_t count)
{
uint32_t written = std::min(dstCapacity_, count);
memcpy(dstBuffer_, src, written);
dstBuffer_ += written;
dstCapacity_ -= written;
return count;
}
uint32_t OSCWriter::appendZeros(uint32_t count)
{
uint32_t written = std::min(dstCapacity_, count);
memset(dstBuffer_, '\0', written);
dstBuffer_ += written;
dstCapacity_ -= written;
return count;
}
template <class T> uint32_t OSCWriter::appendInteger(T integer)
{
using U = typename std::make_unsigned<T>::type;
const U uinteger = static_cast<U>(integer);
uint8_t data[sizeof(U)];
for (unsigned i = 0; i < sizeof(U); ++i) {
unsigned sh = 8 * (sizeof(U) - 1 - i);
data[i] = (uint8_t)((uinteger >> sh) & 0xff);
}
return appendBytes(data, sizeof(U));
}
uint32_t OSCWriter::appendFloat(float f)
{
union { float f; uint32_t i; } u;
u.f = f;
return appendInteger(u.i);
}
uint32_t OSCWriter::appendDouble(float d)
{
union { double d; uint64_t i; } u;
u.d = d;
return appendInteger(u.i);
}
///
void OSCReader::setInputBuffer(const void* buffer, uint32_t capacity)
{
srcBuffer_ = reinterpret_cast<const uint8_t*>(buffer);
srcCapacity_ = capacity;
}
void OSCReader::setAllocationBuffer(void* buffer, uint32_t capacity)
{
allocBuffer_ = reinterpret_cast<uint8_t*>(buffer);
allocCapacity_ = capacity;
}
int32_t OSCReader::extractMessage(const char** outPath, const char** outSig, const sfizz_arg_t** outArgs)
{
const uint8_t* const srcStart = srcBuffer_;
// read path, null byte
const char* path;
uint32_t pathlen;
if (!extractString(path, pathlen))
return 0;
if (outPath)
*outPath = path;
// read signature, null byte
const char* sig;
uint32_t siglen;
if (!extractString(sig, siglen) || sig[0] != ',')
return 0;
++sig;
--siglen;
if (outSig)
*outSig = sig;
// read arguments
sfizz_arg_t* args = allocate<sfizz_arg_t>(siglen);
if (!args)
return -1;
if (outArgs)
*outArgs = args;
for (uint32_t i = 0, n = siglen; i < n; ++i) {
switch (sig[i]) {
default:
return 0;
case 'i':
case 'c':
case 'r':
if (!extractInteger(args[i].i))
return 0;
break;
case 'm':
if (srcCapacity_ < 4)
return 0;
memcpy(args[i].m, srcBuffer_, 4);
srcBuffer_ += 4;
srcCapacity_ -= 4;
break;
case 'h':
if (!extractInteger(args[i].h))
return 0;
break;
case 'f':
if (!extractFloat(args[i].f))
return 0;
break;
case 'd':
if (!extractDouble(args[i].d))
return 0;
break;
case 's':
case 'S':
{
const char* str;
uint32_t len;
if (!extractString(str, len))
return 0;
args[i].s = str;
}
break;
case 'b':
{
sfizz_blob_t* blob = allocate<sfizz_blob_t>(1);
if (!blob)
return -1;
args[i].b = blob;
uint32_t len = blob->size;
if (!extractInteger(len))
return 0;
uint32_t padlen = len + paddingSize(len, 4);
if (srcCapacity_ < padlen)
return 0;
blob->data = srcBuffer_;
blob->size = len;
srcBuffer_ += padlen;
srcCapacity_ -= padlen;
}
break;
case 'T':
case 'F':
case 'N':
case 'I':
break;
}
}
return srcBuffer_ - srcStart;
}
template <class T> T* OSCReader::allocate(uint32_t count)
{
uintptr_t pad = paddingSize(reinterpret_cast<uintptr_t>(allocBuffer_), alignof(T));
uint32_t size = count * sizeof(T);
if (allocCapacity_ < pad + size)
return nullptr;
void* ptr = allocBuffer_ + pad;
allocBuffer_ += pad + size;
allocCapacity_ -= pad + size;
return reinterpret_cast<T *>(ptr);
}
bool OSCReader::extractString(const char*& outStr, uint32_t& outLen)
{
const char* str = reinterpret_cast<const char*>(srcBuffer_);
uint32_t len = static_cast<uint32_t>(strnlen(str, srcCapacity_));
if (len == srcCapacity_)
return false;
uint32_t padlen = len + 1 + paddingSize(len + 1, 4);
if (padlen > srcCapacity_)
return false;
srcBuffer_ += padlen;
srcCapacity_ -= padlen;
outStr = str;
outLen = len;
return true;
}
template <class T> bool OSCReader::extractInteger(T& outValue)
{
if (srcCapacity_ < sizeof(T))
return false;
using U = typename std::make_unsigned<T>::type;
U value = 0;
const uint8_t* src = reinterpret_cast<const uint8_t*>(srcBuffer_);
for (unsigned i = 0; i < sizeof(T); ++i)
value = (value << 8) | src[i];
srcBuffer_ += sizeof(T);
srcCapacity_ -= sizeof(T);
outValue = static_cast<T>(value);
return true;
};
bool OSCReader::extractFloat(float& f)
{
union { float f; uint32_t i; } u;
if (!extractInteger(u.i))
return false;
f = u.f;
return true;
}
bool OSCReader::extractDouble(double& d)
{
union { double d; uint64_t i; } u;
if (!extractInteger(u.i))
return false;
d = u.d;
return true;
}

31
src/sfizz/Messaging.h Normal file
View file

@ -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
#pragma once
#include "sfizz_message.h"
namespace sfz {
class Client {
public:
explicit Client(void* data) : data_(data) {}
void* getClientData() const { return data_; }
void setReceiveCallback(sfizz_receive_t* receive) { receive_ = receive; }
bool canReceive() const { return receive_ != nullptr; }
void receive(int delay, const char* path, const char* sig, const sfizz_arg_t* args);
private:
void* data_ = nullptr;
sfizz_receive_t* receive_ = nullptr;
};
inline void Client::receive(int delay, const char* path, const char* sig, const sfizz_arg_t* args)
{
if (receive_)
receive_(data_, delay, path, sig, args);
}
} // namespace sfz

View file

@ -298,6 +298,10 @@ struct Synth::Impl final: public Parser::Listener {
fs::file_time_type modificationTime_ { }; fs::file_time_type modificationTime_ { };
std::array<float, config::numCCs> defaultCCValues_; std::array<float, config::numCCs> defaultCCValues_;
// Messaging
sfizz_receive_t* broadcastReceiver = nullptr;
void* broadcastData = nullptr;
}; };
Synth::Synth() Synth::Synth()
@ -1953,6 +1957,13 @@ std::bitset<config::numCCs> Synth::getUsedCCs() const noexcept
return used; return used;
} }
void sfz::Synth::setBroadcastCallback(sfizz_receive_t* broadcast, void* data)
{
Impl& impl = *impl_;
impl.broadcastReceiver = broadcast;
impl.broadcastData = data;
}
void Synth::Impl::updateUsedCCsFromRegion(std::bitset<config::numCCs>& usedCCs, const Region& region) void Synth::Impl::updateUsedCCsFromRegion(std::bitset<config::numCCs>& usedCCs, const Region& region)
{ {
updateUsedCCsFromCCMap(usedCCs, region.offsetCC); updateUsedCCsFromCCMap(usedCCs, region.offsetCC);

View file

@ -8,6 +8,7 @@
#include "AudioSpan.h" #include "AudioSpan.h"
#include "LeakDetector.h" #include "LeakDetector.h"
#include "Resources.h" #include "Resources.h"
#include "Messaging.h"
#include "utility/NumericId.h" #include "utility/NumericId.h"
#include "parser/Parser.h" #include "parser/Parser.h"
#include <ghc/fs_std.hpp> #include <ghc/fs_std.hpp>
@ -585,6 +586,27 @@ public:
*/ */
std::bitset<config::numCCs> getUsedCCs() const noexcept; std::bitset<config::numCCs> getUsedCCs() const noexcept;
/**
* @brief Dispatch the incoming message to the synth engine
* @since 0.6.0
*
* @param client The client sending the message.
* @param delay The delay of the message in the block, in samples.
* @param path The OSC address pattern.
* @param sig The OSC type tag string.
* @param args The OSC arguments, whose number and format is determined the type tag string.
*/
void dispatchMessage(Client& client, int delay, const char* path, const char* sig, const sfizz_arg_t* args);
/**
* @brief Set the function which receives broadcast messages from the synth engine.
* @since 0.6.0
*
* @param broadcast The pointer to the receiving function.
* @param data The opaque data pointer which is passed to the receiver.
*/
void setBroadcastCallback(sfizz_receive_t* broadcast, void* data);
private: private:
struct Impl; struct Impl;
std::unique_ptr<Impl> impl_; std::unique_ptr<Impl> impl_;

View file

@ -0,0 +1,81 @@
// 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 "Synth.h"
#include "StringViewHelpers.h"
#include <absl/strings/ascii.h>
#include <cstring>
namespace sfz {
static constexpr unsigned maxIndices = 8;
static bool extractMessage(const char* pattern, const char* path, unsigned* indices);
static uint64_t hashMessagePath(const char* path, const char* sig);
void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, const char* sig, const sfizz_arg_t* args)
{
unsigned indices[maxIndices];
switch (hashMessagePath(path, sig)) {
#define MATCH(p, s) case hash(p "," s): \
if (extractMessage(p, path, indices) && !strcmp(sig, s))
MATCH("/hello", "") {
client.receive(delay, "/hello", "", nullptr);
break;
}
// TODO...
}
}
static bool extractMessage(const char* pattern, const char* path, unsigned* indices)
{
unsigned nthIndex = 0;
while (const char *endp = strchr(pattern, '&')) {
if (nthIndex == maxIndices)
return false;
size_t length = endp - pattern;
if (strncmp(pattern, path, length))
return false;
pattern += length;
path += length;
length = 0;
while (absl::ascii_isdigit(path[length]))
++length;
if (!absl::SimpleAtoi(absl::string_view(path, length), &indices[nthIndex++]))
return false;
pattern += 1;
path += length;
}
return !strcmp(path, pattern);
}
static uint64_t hashMessagePath(const char* path, const char* sig)
{
uint64_t h = Fnv1aBasis;
while (unsigned char c = *path++) {
if (!absl::ascii_isdigit(c))
h = hashByte(c, h);
else {
h = hashByte('&', h);
while (absl::ascii_isdigit(*path))
++path;
}
}
h = hashByte(',', h);
while (unsigned char c = *sig++)
h = hashByte(c, h);
return h;
}
} // namespace sfz

View file

@ -5,6 +5,7 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "Synth.h" #include "Synth.h"
#include "Messaging.h"
#include "sfizz.hpp" #include "sfizz.hpp"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
@ -305,3 +306,33 @@ const std::vector<std::pair<uint16_t, std::string>>& sfz::Sfizz::getCCLabels() c
{ {
return synth->getCCLabels(); return synth->getCCLabels();
} }
void sfz::Sfizz::ClientDeleter::operator()(Client *client) const noexcept
{
delete client;
}
auto sfz::Sfizz::createClient(void* data) -> ClientPtr
{
return ClientPtr(new Client(data));
}
void* sfz::Sfizz::getClientData(Client& client)
{
return client.getClientData();
}
void sfz::Sfizz::setReceiveCallback(Client& client, sfizz_receive_t* receive)
{
client.setReceiveCallback(receive);
}
void sfz::Sfizz::sendMessage(Client& client, int delay, const char* path, const char* sig, const sfizz_arg_t* args)
{
synth->dispatchMessage(client, delay, path, sig, args);
}
void sfz::Sfizz::setBroadcastCallback(sfizz_receive_t* broadcast, void* data)
{
synth->setBroadcastCallback(broadcast, data);
}

View file

@ -7,6 +7,7 @@
#include "Config.h" #include "Config.h"
#include "Macros.h" #include "Macros.h"
#include "Synth.h" #include "Synth.h"
#include "Messaging.h"
#include "sfizz.h" #include "sfizz.h"
#include <limits> #include <limits>
@ -429,6 +430,38 @@ const char * sfizz_get_cc_label_text(sfizz_synth_t* synth, int label_index)
return ccLabels[label_index].second.c_str(); return ccLabels[label_index].second.c_str();
} }
sfizz_client_t* sfizz_create_client(void* data)
{
return reinterpret_cast<sfizz_client_t*>(new sfz::Client(data));
}
void sfizz_delete_client(sfizz_client_t* client)
{
delete reinterpret_cast<sfz::Client*>(client);
}
void* sfizz_get_client_data(sfizz_client_t* client)
{
return reinterpret_cast<sfz::Client*>(client)->getClientData();
}
void sfizz_set_receive_callback(sfizz_client_t* client, sfizz_receive_t* receive)
{
reinterpret_cast<sfz::Client*>(client)->setReceiveCallback(receive);
}
void sfizz_send_message(sfizz_synth_t* synth, sfizz_client_t* client, int delay, const char* path, const char* sig, const sfizz_arg_t* args)
{
auto* self = reinterpret_cast<sfz::Synth*>(synth);
self->dispatchMessage(*reinterpret_cast<sfz::Client*>(client), delay, path, sig, args);
}
void sfizz_set_broadcast_callback(sfizz_synth_t* synth, sfizz_receive_t* broadcast, void* data)
{
auto* self = reinterpret_cast<sfz::Synth*>(synth);
self->setBroadcastCallback(broadcast, data);
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

91
src/sfizz_message.h Normal file
View file

@ -0,0 +1,91 @@
// 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 <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup Messaging
* @{
*/
/**
* @brief Representation of a binary blob in OSC format
* @since 0.6.0
*/
typedef struct {
const uint8_t* data;
uint32_t size;
} sfizz_blob_t;
/**
* @brief Representation of an argument of variant type in OSC format
* @since 0.6.0
*/
typedef union {
int32_t i;
int64_t h;
float f;
double d;
const char* s;
const sfizz_blob_t* b;
uint8_t m[4];
} sfizz_arg_t;
/**
* @brief Generic message receiving function
* @since 0.6.0
*/
typedef void (sfizz_receive_t)(void* data, int delay, const char* path, const char* sig, const sfizz_arg_t* args);
/**
* @brief Convert the message to OSC using the provided output buffer
* @since 0.6.0
*
* @param buffer The output buffer
* @param capacity The capacity of the buffer
* @param path The path
* @param sig The signature
* @param args The arguments
* @return The size necessary to store the converted message in
* entirety, <= capacity if the written message is valid.
*/
uint32_t sfizz_prepare_message(
void* buffer, uint32_t capacity,
const char* path, const char* sig, const sfizz_arg_t* args);
/**
* @brief Extract the contents of an OSC message
* @since 0.6.0
*
* @param srcBuffer The data of the OSC message
* @param srcCapacity The size of the OSC message
* @param argsBuffer A buffer where the function can allocate the arguments
* @param argsCapacity The capacity of the argument buffer
* @param outPath A pointer to the variable which receives the path
* @param outSig A pointer to the variable which receives the signature
* @param outArgs A pointer to the variable which receives the arguments
* @return On success, this is the number of bytes read.
* On failure, it is 0 if the OSC message is invalid,
* -1 if there was not enough buffer for the arguments.
*/
int32_t sfizz_extract_message(
const void* srcBuffer, uint32_t srcCapacity,
void* argsBuffer, uint32_t argsCapacity,
const char** outPath, const char** outSig, const sfizz_arg_t** outArgs);
/**
* @}
*/
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -41,6 +41,7 @@ set(SFIZZ_TEST_SOURCES
ConcurrencyT.cpp ConcurrencyT.cpp
ModulationsT.cpp ModulationsT.cpp
LFOT.cpp LFOT.cpp
MessagingT.cpp
DataHelpers.h DataHelpers.h
DataHelpers.cpp DataHelpers.cpp
) )

95
tests/MessagingT.cpp Normal file
View file

@ -0,0 +1,95 @@
// 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/Messaging.h"
#include "catch2/catch.hpp"
#include <absl/types/span.h>
#include <cstring>
TEST_CASE("[Messaging] OSC message creation")
{
// http://opensoundcontrol.org/spec-1_0-examples
{
const char* path = "/oscillator/4/frequency";
const char* sig = "f";
sfizz_arg_t args[1];
args[0].f = 440.0f;
const uint8_t expected[] = {
0x2f, /* / */ 0x6f, /* o */ 0x73, /* s */ 0x63, /* c */
0x69, /* i */ 0x6c, /* l */ 0x6c, /* l */ 0x61, /* a */
0x74, /* t */ 0x6f, /* o */ 0x72, /* r */ 0x2f, /* / */
0x34, /* 4 */ 0x2f, /* / */ 0x66, /* f */ 0x72, /* r */
0x65, /* e */ 0x71, /* q */ 0x75, /* u */ 0x65, /* e */
0x6e, /* n */ 0x63, /* c */ 0x79, /* y */ 0x00,
0x2c, /* , */ 0x66, /* f */ 0x00, 0x00,
0x43, 0xdc, 0x00, 0x00,
};
uint32_t size = sfizz_prepare_message(nullptr, 0, path, sig, args);
REQUIRE(size == sizeof(expected));
uint8_t actual[sizeof(expected)];
size = sfizz_prepare_message(actual, sizeof(actual), path, sig, args);
REQUIRE(size == sizeof(expected));
REQUIRE(absl::MakeSpan(actual) == absl::MakeSpan(expected));
const char* path2;
const char* sig2;
const sfizz_arg_t* args2;
uint8_t buffer[256];
REQUIRE(sfizz_extract_message(actual, sizeof(actual), buffer, sizeof(buffer), &path2, &sig2, &args2) > 0);
REQUIRE(!strcmp(path, path2));
REQUIRE(!strcmp(sig, sig2));
REQUIRE(args2[0].f == 440.0f);
}
{
const char* path = "/foo";
const char* sig = "iisff";
sfizz_arg_t args[5];
args[0].i = 1000;
args[1].i = -1;
args[2].s = "hello";
args[3].f = 1.234f;
args[4].f = 5.678f;
const uint8_t expected[] = {
0x2f, /* / */ 0x66, /* f */ 0x6f, /* o */ 0x6f, /* o */
0x00, 0x00, 0x00, 0x00,
0x2c, /* , */ 0x69, /* i */ 0x69, /* i */ 0x73, /* s */
0x66, /* f */ 0x66, /* f */ 0x00, 0x00,
0x00, 0x00, 0x03, 0xe8,
0xff, 0xff, 0xff, 0xff,
0x68, 0x65, 0x6c, 0x6c,
0x6f, 0x00, 0x00, 0x00,
0x3f, 0x9d, 0xf3, 0xb6,
0x40, 0xb5, 0xb2, 0x2d,
};
uint32_t size = sfizz_prepare_message(nullptr, 0, path, sig, args);
REQUIRE(size == sizeof(expected));
uint8_t actual[sizeof(expected)];
size = sfizz_prepare_message(actual, sizeof(actual), path, sig, args);
REQUIRE(size == sizeof(expected));
REQUIRE(absl::MakeSpan(actual) == absl::MakeSpan(expected));
const char* path2;
const char* sig2;
const sfizz_arg_t* args2;
uint8_t buffer[256];
REQUIRE(sfizz_extract_message(actual, sizeof(actual), buffer, sizeof(buffer), &path2, &sig2, &args2) > 0);
REQUIRE(!strcmp(path, path2));
REQUIRE(!strcmp(sig, sig2));
REQUIRE(args2[0].i == 1000);
REQUIRE(args2[1].i == -1);
REQUIRE(!strcmp(args2[2].s, "hello"));
REQUIRE(args2[3].f == 1.234f);
REQUIRE(args2[4].f == 5.678f);
}
}

View file

@ -293,6 +293,24 @@ tresult SfizzVstController::notify(Vst::IMessage* message)
_playState = *static_cast<const SfizzPlayState*>(data); _playState = *static_cast<const SfizzPlayState*>(data);
} }
else if (!strcmp(id, "ReceivedMessage")) {
const void* data = nullptr;
uint32 size = 0;
result = attr->getBinary("Message", data, size);
if (result != kResultTrue)
return result;
const char* path;
const char* sig;
const sfizz_arg_t* args;
uint8_t buffer[1024];
if (sfizz_extract_message(data, size, buffer, sizeof(buffer), &path, &sig, &args) > 0) {
for (MessageListener* listener : _messageListeners)
listener->onMessageReceived(path, sig, args);
}
}
for (StateListener* listener : _stateListeners) for (StateListener* listener : _stateListeners)
listener->onStateChanged(); listener->onStateChanged();
@ -312,6 +330,18 @@ void SfizzVstController::removeSfizzStateListener(StateListener* listener)
_stateListeners.erase(it); _stateListeners.erase(it);
} }
void SfizzVstController::addSfizzMessageListener(MessageListener* listener)
{
_messageListeners.push_back(listener);
}
void SfizzVstController::removeSfizzMessageListener(MessageListener* listener)
{
auto it = std::find(_messageListeners.begin(), _messageListeners.end(), listener);
if (it != _messageListeners.end())
_messageListeners.erase(it);
}
FUnknown* SfizzVstController::createInstance(void*) FUnknown* SfizzVstController::createInstance(void*)
{ {
return static_cast<Vst::IEditController*>(new SfizzVstController); return static_cast<Vst::IEditController*>(new SfizzVstController);

View file

@ -9,6 +9,7 @@
#include "public.sdk/source/vst/vsteditcontroller.h" #include "public.sdk/source/vst/vsteditcontroller.h"
#include "public.sdk/source/vst/vstparameters.h" #include "public.sdk/source/vst/vstparameters.h"
#include "vstgui/plugin-bindings/vst3editor.h" #include "vstgui/plugin-bindings/vst3editor.h"
#include <sfizz_message.h>
class SfizzVstState; class SfizzVstState;
using namespace Steinberg; using namespace Steinberg;
@ -49,6 +50,9 @@ public:
struct StateListener { struct StateListener {
virtual void onStateChanged() = 0; virtual void onStateChanged() = 0;
}; };
struct MessageListener {
virtual void onMessageReceived(const char* path, const char* sig, const sfizz_arg_t* args) = 0;
};
const SfizzVstState& getSfizzState() const { return _state; } const SfizzVstState& getSfizzState() const { return _state; }
SfizzVstState& getSfizzState() { return _state; } SfizzVstState& getSfizzState() { return _state; }
@ -62,6 +66,9 @@ public:
void addSfizzStateListener(StateListener* listener); void addSfizzStateListener(StateListener* listener);
void removeSfizzStateListener(StateListener* listener); void removeSfizzStateListener(StateListener* listener);
void addSfizzMessageListener(MessageListener* listener);
void removeSfizzMessageListener(MessageListener* listener);
/// ///
static FUnknown* createInstance(void*); static FUnknown* createInstance(void*);
@ -72,4 +79,5 @@ private:
SfizzUiState _uiState; SfizzUiState _uiState;
SfizzPlayState _playState {}; SfizzPlayState _playState {};
std::vector<StateListener*> _stateListeners; std::vector<StateListener*> _stateListeners;
std::vector<MessageListener*> _messageListeners;
}; };

View file

@ -16,15 +16,22 @@ using namespace VSTGUI;
static ViewRect sfizzUiViewRect { 0, 0, Editor::viewWidth, Editor::viewHeight }; static ViewRect sfizzUiViewRect { 0, 0, Editor::viewWidth, Editor::viewHeight };
enum {
kOscTempSize = 8192,
};
SfizzVstEditor::SfizzVstEditor(void *controller) SfizzVstEditor::SfizzVstEditor(void *controller)
: VSTGUIEditor(controller, &sfizzUiViewRect) : VSTGUIEditor(controller, &sfizzUiViewRect),
oscTemp_(new uint8_t[kOscTempSize])
{ {
getController()->addSfizzStateListener(this); getController()->addSfizzStateListener(this);
getController()->addSfizzMessageListener(this);
} }
SfizzVstEditor::~SfizzVstEditor() SfizzVstEditor::~SfizzVstEditor()
{ {
getController()->removeSfizzStateListener(this); getController()->removeSfizzStateListener(this);
getController()->removeSfizzMessageListener(this);
} }
bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& platformType) bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& platformType)
@ -105,6 +112,11 @@ void SfizzVstEditor::onStateChanged()
updateStateDisplay(); updateStateDisplay();
} }
void SfizzVstEditor::onMessageReceived(const char* path, const char* sig, const sfizz_arg_t* args)
{
uiReceiveMessage(path, sig, args);
}
/// ///
void SfizzVstEditor::uiSendValue(EditId id, const EditValue& v) void SfizzVstEditor::uiSendValue(EditId id, const EditValue& v)
{ {
@ -192,6 +204,26 @@ void SfizzVstEditor::uiSendMIDI(const uint8_t* data, uint32_t len)
ctl->sendMessage(msg); ctl->sendMessage(msg);
} }
void SfizzVstEditor::uiSendMessage(const char* path, const char* sig, const sfizz_arg_t* args)
{
SfizzVstController* ctl = getController();
Steinberg::OPtr<Vst::IMessage> msg { ctl->allocateMessage() };
if (!msg) {
fprintf(stderr, "[Sfizz] UI could not allocate message\n");
return;
}
uint8_t* oscTemp = oscTemp_.get();
uint32_t oscSize = sfizz_prepare_message(oscTemp, kOscTempSize, path, sig, args);
if (oscSize <= kOscTempSize) {
msg->setMessageID("OscMessage");
Vst::IAttributeList* attr = msg->getAttributes();
attr->setBinary("Data", oscTemp, oscSize);
ctl->sendMessage(msg);
}
}
/// ///
void SfizzVstEditor::loadSfzFile(const std::string& filePath) void SfizzVstEditor::loadSfzFile(const std::string& filePath)
{ {

View file

@ -16,7 +16,10 @@ namespace VSTGUI { class RunLoop; }
using namespace Steinberg; using namespace Steinberg;
using namespace VSTGUI; using namespace VSTGUI;
class SfizzVstEditor : public Vst::VSTGUIEditor, public SfizzVstController::StateListener, public EditorController { class SfizzVstEditor : public Vst::VSTGUIEditor,
public SfizzVstController::StateListener,
public SfizzVstController::MessageListener,
public EditorController {
public: public:
explicit SfizzVstEditor(void *controller); explicit SfizzVstEditor(void *controller);
~SfizzVstEditor(); ~SfizzVstEditor();
@ -35,12 +38,16 @@ public:
// SfizzVstController::StateListener // SfizzVstController::StateListener
void onStateChanged() override; void onStateChanged() override;
// SfizzVstController::MessageListener
void onMessageReceived(const char* path, const char* sig, const sfizz_arg_t* args) override;
protected: protected:
// EditorController // EditorController
void uiSendValue(EditId id, const EditValue& v) override; void uiSendValue(EditId id, const EditValue& v) override;
void uiBeginSend(EditId id) override; void uiBeginSend(EditId id) override;
void uiEndSend(EditId id) override; void uiEndSend(EditId id) override;
void uiSendMIDI(const uint8_t* data, uint32_t len) override; void uiSendMIDI(const uint8_t* data, uint32_t len) override;
void uiSendMessage(const char* path, const char* sig, const sfizz_arg_t* args) override;
private: private:
void loadSfzFile(const std::string& filePath); void loadSfzFile(const std::string& filePath);
@ -55,4 +62,7 @@ private:
#if !defined(__APPLE__) && !defined(_WIN32) #if !defined(__APPLE__) && !defined(_WIN32)
SharedPointer<RunLoop> _runLoop; SharedPointer<RunLoop> _runLoop;
#endif #endif
// messaging
std::unique_ptr<uint8[]> oscTemp_;
}; };

View file

@ -24,10 +24,17 @@ static const char defaultSfzText[] =
"<region>sample=*sine" "\n" "<region>sample=*sine" "\n"
"ampeg_attack=0.02 ampeg_release=0.1" "\n"; "ampeg_attack=0.02 ampeg_release=0.1" "\n";
enum { kMidiEventMaximumSize = 4 }; enum {
kMidiEventMaximumSize = 4,
kOscTempSize = 8192,
};
static const char* kRingIdMidi = "Mid";
static const char* kRingIdOsc = "Osc";
SfizzVstProcessor::SfizzVstProcessor() SfizzVstProcessor::SfizzVstProcessor()
: _fifoToWorker(64 * 1024), _fifoMidiFromUi(64 * 1024) : _fifoToWorker(64 * 1024), _fifoMessageFromUi(64 * 1024),
_oscTemp(new uint8_t[kOscTempSize])
{ {
setControllerClass(SfizzVstController::cid); setControllerClass(SfizzVstController::cid);
@ -56,6 +63,16 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
fprintf(stderr, "[sfizz] new synth\n"); fprintf(stderr, "[sfizz] new synth\n");
_synth.reset(new sfz::Sfizz); _synth.reset(new sfz::Sfizz);
auto onMessage = +[](void* data, int delay, const char* path, const char* sig, const sfizz_arg_t* args)
{
auto *self = reinterpret_cast<SfizzVstProcessor*>(data);
self->receiveMessage(delay, path, sig, args);
};
_client = _synth->createClient(this);
_synth->setReceiveCallback(*_client, onMessage);
_synth->setBroadcastCallback(onMessage, this);
_currentStretchedTuning = 0.0; _currentStretchedTuning = 0.0;
loadSfzFileOrDefault(*_synth, {}); loadSfzFileOrDefault(*_synth, {});
@ -210,7 +227,7 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
else else
synth.disableFreeWheeling(); synth.disableFreeWheeling();
processMidiFromUi(); processMessagesFromUi();
if (Vst::IParameterChanges* pc = data.inputParameterChanges) if (Vst::IParameterChanges* pc = data.inputParameterChanges)
processControllerChanges(*pc); processControllerChanges(*pc);
@ -404,36 +421,61 @@ void SfizzVstProcessor::processEvents(Vst::IEventList& events)
} }
} }
void SfizzVstProcessor::processMidiFromUi() void SfizzVstProcessor::processMessagesFromUi()
{ {
sfz::Sfizz& synth = *_synth; sfz::Sfizz& synth = *_synth;
sfz::Client& client = *_client;
Ring_Buffer& fifo = _fifoMessageFromUi;
RTMessage header;
for (uint32 size = 0; _fifoMidiFromUi.peek(size) && while (fifo.peek(header) && fifo.size_used() >= sizeof(header) + header.size) {
_fifoMidiFromUi.size_used() >= sizeof(size) + size; ) { fifo.discard(sizeof(header));
_fifoMidiFromUi.discard(sizeof(size));
if (size > kMidiEventMaximumSize) { if (header.type == kRingIdMidi) {
_fifoMidiFromUi.discard(size); if (header.size > kMidiEventMaximumSize) {
continue; fifo.discard(header.size);
continue;
}
uint8_t data[kMidiEventMaximumSize] = {};
fifo.get(data, header.size);
// interpret the MIDI message
switch (data[0] & 0xf0) {
case 0x80:
synth.noteOff(0, data[1] & 0x7f, data[2] & 0x7f);
break;
case 0x90:
synth.noteOn(0, data[1] & 0x7f, data[2] & 0x7f);
break;
case 0xb0:
synth.cc(0, data[1] & 0x7f, data[2] & 0x7f);
break;
case 0xe0:
synth.pitchWheel(0, (data[2] << 7) + data[1] - 8192);
break;
}
} }
else if (header.type == kRingIdOsc) {
uint8_t* oscTemp = _oscTemp.get();
uint8_t data[kMidiEventMaximumSize] = {}; if (header.size > kOscTempSize) {
_fifoMidiFromUi.get(data, size); fifo.discard(header.size);
continue;
}
// interpret the MIDI message fifo.get(oscTemp, header.size);
switch (data[0] & 0xf0) {
case 0x80: const char* path;
synth.noteOff(0, data[1] & 0x7f, data[2] & 0x7f); const char* sig;
break; const sfizz_arg_t* args;
case 0x90: uint8_t buffer[1024];
synth.noteOn(0, data[1] & 0x7f, data[2] & 0x7f); if (sfizz_extract_message(oscTemp, header.size, buffer, sizeof(buffer), &path, &sig, &args) > 0)
break; synth.sendMessage(client, 0, path, sig, args);
case 0xb0: }
synth.cc(0, data[1] & 0x7f, data[2] & 0x7f); else {
break; assert(false);
case 0xe0: return;
synth.pitchWheel(0, (data[2] << 7) + data[1] - 8192);
break;
} }
} }
} }
@ -494,12 +536,14 @@ tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
const void* data = nullptr; const void* data = nullptr;
uint32 size = 0; uint32 size = 0;
result = attr->getBinary("Data", data, size); result = attr->getBinary("Data", data, size);
if (size < kMidiEventMaximumSize) { if (size < kMidiEventMaximumSize)
if (_fifoMidiFromUi.size_free() >= sizeof(size) + size) { writeMessage(_fifoMessageFromUi, kRingIdMidi, data, size);
_fifoMidiFromUi.put(size); }
_fifoMidiFromUi.put(reinterpret_cast<const uint8_t*>(data), size); else if (!std::strcmp(id, "OscMessage")) {
} const void* data = nullptr;
} uint32 size = 0;
result = attr->getBinary("Data", data, size);
writeMessage(_fifoMessageFromUi, kRingIdOsc, data, size);
} }
return result; return result;
@ -510,6 +554,14 @@ FUnknown* SfizzVstProcessor::createInstance(void*)
return static_cast<Vst::IAudioProcessor*>(new SfizzVstProcessor); return static_cast<Vst::IAudioProcessor*>(new SfizzVstProcessor);
} }
void SfizzVstProcessor::receiveMessage(int delay, const char* path, const char* sig, const sfizz_arg_t* args)
{
uint8_t* oscTemp = _oscTemp.get();
uint32_t oscSize = sfizz_prepare_message(oscTemp, kOscTempSize, path, sig, args);
if (oscSize <= kOscTempSize)
writeWorkerMessage("ReceiveMessage", oscTemp, oscSize);
}
void SfizzVstProcessor::loadSfzFileOrDefault(sfz::Sfizz& synth, const std::string& filePath) void SfizzVstProcessor::loadSfzFileOrDefault(sfz::Sfizz& synth, const std::string& filePath)
{ {
if (!filePath.empty()) if (!filePath.empty())
@ -563,6 +615,12 @@ void SfizzVstProcessor::doBackgroundWork()
notification->getAttributes()->setBinary("PlayState", &playState, sizeof(playState)); notification->getAttributes()->setBinary("PlayState", &playState, sizeof(playState));
sendMessage(notification); sendMessage(notification);
} }
else if (!std::strcmp(id, "ReceiveMessage")) {
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
notification->setMessageID("ReceivedMessage");
notification->getAttributes()->setBinary("Message", msg->payload<uint8_t>(), msg->size);
sendMessage(notification);
}
} }
} }
@ -585,16 +643,7 @@ void SfizzVstProcessor::stopBackgroundWork()
bool SfizzVstProcessor::writeWorkerMessage(const char* type, const void* data, uintptr_t size) bool SfizzVstProcessor::writeWorkerMessage(const char* type, const void* data, uintptr_t size)
{ {
RTMessage header; return writeMessage(_fifoToWorker, type, data, size);
header.type = type;
header.size = size;
if (_fifoToWorker.size_free() < sizeof(header) + size)
return false;
_fifoToWorker.put(header);
_fifoToWorker.put(static_cast<const uint8*>(data), size);
return true;
} }
SfizzVstProcessor::RTMessagePtr SfizzVstProcessor::readWorkerMessage() SfizzVstProcessor::RTMessagePtr SfizzVstProcessor::readWorkerMessage()
@ -631,6 +680,20 @@ bool SfizzVstProcessor::discardWorkerMessage()
return true; return true;
} }
bool SfizzVstProcessor::writeMessage(Ring_Buffer& fifo, const char* type, const void* data, uintptr_t size)
{
RTMessage header;
header.type = type;
header.size = size;
if (fifo.size_free() < sizeof(header) + size)
return false;
fifo.put(header);
fifo.put(static_cast<const uint8*>(data), size);
return true;
}
/* /*
Note(jpc) Generated at random with uuidgen. Note(jpc) Generated at random with uuidgen.
Can't find docs on it... maybe it's to register somewhere? Can't find docs on it... maybe it's to register somewhere?

View file

@ -35,7 +35,7 @@ public:
void processParameterChanges(Vst::IParameterChanges& pc); void processParameterChanges(Vst::IParameterChanges& pc);
void processControllerChanges(Vst::IParameterChanges& pc); void processControllerChanges(Vst::IParameterChanges& pc);
void processEvents(Vst::IEventList& events); void processEvents(Vst::IEventList& events);
void processMidiFromUi(); void processMessagesFromUi();
static int convertVelocityFromFloat(float x); static int convertVelocityFromFloat(float x);
tresult PLUGIN_API notify(Vst::IMessage* message) override; tresult PLUGIN_API notify(Vst::IMessage* message) override;
@ -51,6 +51,11 @@ private:
SfizzVstState _state; SfizzVstState _state;
float _currentStretchedTuning = 0; float _currentStretchedTuning = 0;
// client
sfz::ClientPtr _client;
std::unique_ptr<uint8_t[]> _oscTemp;
void receiveMessage(int delay, const char* path, const char* sig, const sfizz_arg_t* args);
// misc // misc
static void loadSfzFileOrDefault(sfz::Sfizz& synth, const std::string& filePath); static void loadSfzFileOrDefault(sfz::Sfizz& synth, const std::string& filePath);
@ -59,7 +64,7 @@ private:
volatile bool _workRunning = false; volatile bool _workRunning = false;
Ring_Buffer _fifoToWorker; Ring_Buffer _fifoToWorker;
RTSemaphore _semaToWorker; RTSemaphore _semaToWorker;
Ring_Buffer _fifoMidiFromUi; Ring_Buffer _fifoMessageFromUi;
std::mutex _processMutex; std::mutex _processMutex;
// file modification periodic checker // file modification periodic checker
@ -95,6 +100,9 @@ private:
// reader // reader
RTMessagePtr readWorkerMessage(); RTMessagePtr readWorkerMessage();
bool discardWorkerMessage(); bool discardWorkerMessage();
// generic
static bool writeMessage(Ring_Buffer& fifo, const char* type, const void* data, uintptr_t size);
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------