Add OSC and plugin-side communication
This commit is contained in:
parent
0e33784307
commit
722225f9f2
27 changed files with 1232 additions and 59 deletions
|
|
@ -89,6 +89,7 @@ SFIZZ_SOURCES = \
|
|||
src/sfizz/Logger.cpp \
|
||||
src/sfizz/LFO.cpp \
|
||||
src/sfizz/LFODescription.cpp \
|
||||
src/sfizz/Messaging.cpp \
|
||||
src/sfizz/MidiState.cpp \
|
||||
src/sfizz/OpcodeCleanup.cpp \
|
||||
src/sfizz/Opcode.cpp \
|
||||
|
|
@ -112,6 +113,7 @@ SFIZZ_SOURCES = \
|
|||
src/sfizz/simd/HelpersAVX.cpp \
|
||||
src/sfizz/Smoothers.cpp \
|
||||
src/sfizz/Synth.cpp \
|
||||
src/sfizz/SynthMessaging.cpp \
|
||||
src/sfizz/Tuning.cpp \
|
||||
src/sfizz/utility/SpinMutex.cpp \
|
||||
src/sfizz/Voice.cpp \
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ add_library(sfizz_editor STATIC EXCLUDE_FROM_ALL
|
|||
src/editor/utility/vstgui_after.h
|
||||
src/editor/utility/vstgui_before.h)
|
||||
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 PUBLIC absl::strings)
|
||||
if(APPLE)
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
|
|||
SPiano* piano_ = nullptr;
|
||||
|
||||
void uiReceiveValue(EditId id, const EditValue& v) override;
|
||||
void uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args) override;
|
||||
|
||||
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()
|
||||
{
|
||||
CViewContainer* mainView;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
#include "EditValue.h"
|
||||
#include <sfizz_message.h>
|
||||
#include <absl/strings/string_view.h>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
|
@ -20,6 +21,7 @@ public:
|
|||
virtual void uiBeginSend(EditId id) = 0;
|
||||
virtual void uiEndSend(EditId id) = 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;
|
||||
void decorate(Receiver* r) { r_ = r; }
|
||||
|
||||
|
|
@ -27,10 +29,12 @@ public:
|
|||
public:
|
||||
virtual ~Receiver() {}
|
||||
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
|
||||
void uiReceiveValue(EditId id, const EditValue& v);
|
||||
void uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args);
|
||||
|
||||
private:
|
||||
Receiver* r_ = nullptr;
|
||||
|
|
@ -41,3 +45,9 @@ inline void EditorController::uiReceiveValue(EditId id, const EditValue& v)
|
|||
if (r_)
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
44
lv2/sfizz.c
44
lv2/sfizz.c
|
|
@ -148,6 +148,7 @@ typedef struct
|
|||
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;
|
||||
|
|
@ -158,6 +159,7 @@ typedef struct
|
|||
|
||||
// Sfizz related data
|
||||
sfizz_synth_t *synth;
|
||||
sfizz_client_t *client;
|
||||
bool expect_nominal_block_length;
|
||||
char sfz_file_path[MAX_PATH_SIZE];
|
||||
char scala_file_path[MAX_PATH_SIZE];
|
||||
|
|
@ -181,6 +183,9 @@ typedef struct
|
|||
|
||||
// Paths
|
||||
char bundle_path[MAX_BUNDLE_PATH_SIZE];
|
||||
|
||||
// OSC
|
||||
uint8_t osc_temp[OSC_TEMP_SIZE];
|
||||
} sfizz_plugin_t;
|
||||
|
||||
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_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_bar_uri = map->map(map->handle, LV2_TIME__bar);
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
instantiate(const LV2_Descriptor *descriptor,
|
||||
double rate,
|
||||
|
|
@ -570,6 +598,9 @@ instantiate(const LV2_Descriptor *descriptor,
|
|||
}
|
||||
|
||||
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_scala_path(instance, self->scala_file_path, MAX_PATH_SIZE);
|
||||
|
|
@ -586,6 +617,7 @@ static void
|
|||
cleanup(LV2_Handle instance)
|
||||
{
|
||||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
sfizz_delete_client(self->client);
|
||||
sfizz_free(self->synth);
|
||||
free(self);
|
||||
}
|
||||
|
|
@ -952,12 +984,22 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
self->unmap->unmap(self->unmap->handle, obj->body.otype));
|
||||
continue;
|
||||
}
|
||||
// Got an atom that is a MIDI event
|
||||
}
|
||||
else if (ev->body.type == self->midi_event_uri)
|
||||
{
|
||||
// Got an atom that is a MIDI event
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
@prefix pprop: <http://lv2plug.in/ns/ext/port-props#> .
|
||||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
||||
@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 time: <http://lv2plug.in/ns/ext/time#> .
|
||||
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
|
||||
|
|
@ -92,21 +93,23 @@ midnam:update a lv2:Feature .
|
|||
lv2:port [
|
||||
a lv2:InputPort, atom:AtomPort ;
|
||||
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:index 0 ;
|
||||
lv2:symbol "control" ;
|
||||
lv2:name "Control",
|
||||
"Contrôle"@fr ;
|
||||
rsz:minimumSize 65536 ;
|
||||
] , [
|
||||
a lv2:OutputPort, atom:AtomPort ;
|
||||
atom:bufferType atom:Sequence ;
|
||||
atom:supports patch:Message ;
|
||||
atom:supports patch:Message, <@LV2PLUGIN_URI@:OSCBlob> ;
|
||||
lv2:designation lv2:control ;
|
||||
lv2:index 1 ;
|
||||
lv2:symbol "notify" ;
|
||||
lv2:name "Notify",
|
||||
"Notification"@fr ;
|
||||
rsz:minimumSize 65536 ;
|
||||
] , [
|
||||
a lv2:AudioPort, lv2:OutputPort ;
|
||||
lv2:index 2 ;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
#pragma once
|
||||
|
||||
#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_UI_URI "http://sfztools.github.io/sfizz#ui"
|
||||
|
|
@ -19,6 +21,8 @@
|
|||
// These ones are just for the worker
|
||||
#define SFIZZ__logStatus SFIZZ_URI ":" "log_status"
|
||||
#define SFIZZ__checkModification SFIZZ_URI ":" "check_modification"
|
||||
// OSC atoms
|
||||
#define SFIZZ__OSCBlob SFIZZ_URI ":" "OSCBlob"
|
||||
|
||||
enum
|
||||
{
|
||||
|
|
|
|||
|
|
@ -107,12 +107,17 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
|
|||
LV2_URID patch_value_uri;
|
||||
LV2_URID sfizz_sfz_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:
|
||||
void uiSendValue(EditId id, const EditValue& v) override;
|
||||
void uiBeginSend(EditId id) override;
|
||||
void uiEndSend(EditId id) 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:
|
||||
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->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile);
|
||||
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
|
||||
// * 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;
|
||||
|
|
@ -422,9 +436,8 @@ void sfizz_ui_t::uiSendValue(EditId id, const EditValue& v)
|
|||
auto sendPath = [this](LV2_URID property, const std::string& value) {
|
||||
LV2_Atom_Forge *forge = &atom_forge;
|
||||
LV2_Atom_Forge_Frame frame;
|
||||
alignas(LV2_Atom) uint8_t buffer[MAX_PATH_SIZE + 512];
|
||||
auto *atom = reinterpret_cast<const LV2_Atom *>(buffer);
|
||||
lv2_atom_forge_set_buffer(forge, (uint8_t *)&buffer, sizeof(buffer));
|
||||
auto *atom = reinterpret_cast<const LV2_Atom *>(atom_temp);
|
||||
lv2_atom_forge_set_buffer(forge, atom_temp, sizeof(atom_temp));
|
||||
if (lv2_atom_forge_object(forge, &frame, 0, patch_set_uri) &&
|
||||
lv2_atom_forge_key(forge, patch_property_uri) &&
|
||||
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)
|
||||
{
|
||||
LV2_Atom_Forge *forge = &atom_forge;
|
||||
alignas(LV2_Atom) uint8_t buffer[512];
|
||||
auto *atom = reinterpret_cast<const LV2_Atom *>(buffer);
|
||||
lv2_atom_forge_set_buffer(forge, (uint8_t *)&buffer, sizeof(buffer));
|
||||
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, len, midi_event_uri) &&
|
||||
lv2_atom_forge_write(forge, msg, len))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ set (SFIZZ_SOURCES
|
|||
sfizz/PowerFollower.cpp
|
||||
sfizz/FlexEGDescription.cpp
|
||||
sfizz/FlexEnvelope.cpp
|
||||
sfizz/SynthMessaging.cpp
|
||||
sfizz/modulations/ModId.cpp
|
||||
sfizz/modulations/ModKey.cpp
|
||||
sfizz/modulations/ModKeyHash.cpp
|
||||
|
|
@ -206,6 +207,7 @@ set (SFIZZ_PARSER_SOURCES
|
|||
set (SFIZZ_PARSER_OTHER sfizz/OpcodeCleanup.re)
|
||||
source_group ("Other Files" FILES ${SFIZZ_PARSER_OTHER})
|
||||
|
||||
# Sfizz parser library
|
||||
add_library (sfizz_parser STATIC)
|
||||
target_sources (sfizz_parser PRIVATE
|
||||
${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_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
|
||||
add_library(sfizz_static STATIC)
|
||||
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 external)
|
||||
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)
|
||||
set_target_properties (sfizz_static PROPERTIES OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp")
|
||||
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;sfizz_message.h")
|
||||
if(SFIZZ_USE_SNDFILE)
|
||||
target_compile_definitions (sfizz_static PUBLIC SFIZZ_USE_SNDFILE=1)
|
||||
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)
|
||||
target_include_directories (sfizz_shared PRIVATE .)
|
||||
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)
|
||||
target_compile_definitions (sfizz_shared PUBLIC SFIZZ_USE_SNDFILE=1)
|
||||
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")
|
||||
endif()
|
||||
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_fast_math(sfizz_shared)
|
||||
|
||||
|
|
|
|||
74
src/sfizz.h
74
src/sfizz.h
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#include "sfizz_message.h"
|
||||
#include <stddef.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);
|
||||
|
||||
/**
|
||||
* @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
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#include "sfizz_message.h"
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
namespace sfz
|
||||
{
|
||||
class Synth;
|
||||
class Client;
|
||||
/**
|
||||
* @brief Main class.
|
||||
*/
|
||||
|
|
@ -565,7 +567,75 @@ public:
|
|||
*/
|
||||
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:
|
||||
std::unique_ptr<sfz::Synth> synth;
|
||||
};
|
||||
|
||||
using ClientPtr = Sfizz::ClientPtr;
|
||||
|
||||
}
|
||||
|
|
|
|||
367
src/sfizz/Messaging.cpp
Normal file
367
src/sfizz/Messaging.cpp
Normal 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
31
src/sfizz/Messaging.h
Normal 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
|
||||
|
|
@ -298,6 +298,10 @@ struct Synth::Impl final: public Parser::Listener {
|
|||
fs::file_time_type modificationTime_ { };
|
||||
|
||||
std::array<float, config::numCCs> defaultCCValues_;
|
||||
|
||||
// Messaging
|
||||
sfizz_receive_t* broadcastReceiver = nullptr;
|
||||
void* broadcastData = nullptr;
|
||||
};
|
||||
|
||||
Synth::Synth()
|
||||
|
|
@ -1953,6 +1957,13 @@ std::bitset<config::numCCs> Synth::getUsedCCs() const noexcept
|
|||
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)
|
||||
{
|
||||
updateUsedCCsFromCCMap(usedCCs, region.offsetCC);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "AudioSpan.h"
|
||||
#include "LeakDetector.h"
|
||||
#include "Resources.h"
|
||||
#include "Messaging.h"
|
||||
#include "utility/NumericId.h"
|
||||
#include "parser/Parser.h"
|
||||
#include <ghc/fs_std.hpp>
|
||||
|
|
@ -585,6 +586,27 @@ public:
|
|||
*/
|
||||
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:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
|
|
|
|||
81
src/sfizz/SynthMessaging.cpp
Normal file
81
src/sfizz/SynthMessaging.cpp
Normal 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(',');
|
||||
while (unsigned char c = *sig++)
|
||||
h = hashByte(c, h);
|
||||
return h;
|
||||
}
|
||||
|
||||
} // namespace sfz
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "Synth.h"
|
||||
#include "Messaging.h"
|
||||
#include "sfizz.hpp"
|
||||
#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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "Config.h"
|
||||
#include "Macros.h"
|
||||
#include "Synth.h"
|
||||
#include "Messaging.h"
|
||||
#include "sfizz.h"
|
||||
#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();
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
91
src/sfizz_message.h
Normal file
91
src/sfizz_message.h
Normal 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
|
||||
|
|
@ -41,6 +41,7 @@ set(SFIZZ_TEST_SOURCES
|
|||
ConcurrencyT.cpp
|
||||
ModulationsT.cpp
|
||||
LFOT.cpp
|
||||
MessagingT.cpp
|
||||
DataHelpers.h
|
||||
DataHelpers.cpp
|
||||
)
|
||||
|
|
|
|||
95
tests/MessagingT.cpp
Normal file
95
tests/MessagingT.cpp
Normal 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(args[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(args[0].i == 1000);
|
||||
REQUIRE(args[1].i == -1);
|
||||
REQUIRE(!strcmp(args[2].s, "hello"));
|
||||
REQUIRE(args[3].f == 1.234f);
|
||||
REQUIRE(args[4].f == 5.678f);
|
||||
}
|
||||
}
|
||||
|
|
@ -293,6 +293,24 @@ tresult SfizzVstController::notify(Vst::IMessage* message)
|
|||
|
||||
_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)
|
||||
listener->onStateChanged();
|
||||
|
|
@ -312,6 +330,18 @@ void SfizzVstController::removeSfizzStateListener(StateListener* listener)
|
|||
_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*)
|
||||
{
|
||||
return static_cast<Vst::IEditController*>(new SfizzVstController);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "public.sdk/source/vst/vsteditcontroller.h"
|
||||
#include "public.sdk/source/vst/vstparameters.h"
|
||||
#include "vstgui/plugin-bindings/vst3editor.h"
|
||||
#include <sfizz_message.h>
|
||||
class SfizzVstState;
|
||||
|
||||
using namespace Steinberg;
|
||||
|
|
@ -49,6 +50,9 @@ public:
|
|||
struct StateListener {
|
||||
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; }
|
||||
SfizzVstState& getSfizzState() { return _state; }
|
||||
|
|
@ -62,6 +66,9 @@ public:
|
|||
void addSfizzStateListener(StateListener* listener);
|
||||
void removeSfizzStateListener(StateListener* listener);
|
||||
|
||||
void addSfizzMessageListener(MessageListener* listener);
|
||||
void removeSfizzMessageListener(MessageListener* listener);
|
||||
|
||||
///
|
||||
static FUnknown* createInstance(void*);
|
||||
|
||||
|
|
@ -72,4 +79,5 @@ private:
|
|||
SfizzUiState _uiState;
|
||||
SfizzPlayState _playState {};
|
||||
std::vector<StateListener*> _stateListeners;
|
||||
std::vector<MessageListener*> _messageListeners;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,15 +16,22 @@ using namespace VSTGUI;
|
|||
|
||||
static ViewRect sfizzUiViewRect { 0, 0, Editor::viewWidth, Editor::viewHeight };
|
||||
|
||||
enum {
|
||||
kOscTempSize = 8192,
|
||||
};
|
||||
|
||||
SfizzVstEditor::SfizzVstEditor(void *controller)
|
||||
: VSTGUIEditor(controller, &sfizzUiViewRect)
|
||||
: VSTGUIEditor(controller, &sfizzUiViewRect),
|
||||
oscTemp_(new uint8_t[kOscTempSize])
|
||||
{
|
||||
getController()->addSfizzStateListener(this);
|
||||
getController()->addSfizzMessageListener(this);
|
||||
}
|
||||
|
||||
SfizzVstEditor::~SfizzVstEditor()
|
||||
{
|
||||
getController()->removeSfizzStateListener(this);
|
||||
getController()->removeSfizzMessageListener(this);
|
||||
}
|
||||
|
||||
bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& platformType)
|
||||
|
|
@ -105,6 +112,11 @@ void SfizzVstEditor::onStateChanged()
|
|||
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)
|
||||
{
|
||||
|
|
@ -192,6 +204,26 @@ void SfizzVstEditor::uiSendMIDI(const uint8_t* data, uint32_t len)
|
|||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ namespace VSTGUI { class RunLoop; }
|
|||
using namespace Steinberg;
|
||||
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:
|
||||
explicit SfizzVstEditor(void *controller);
|
||||
~SfizzVstEditor();
|
||||
|
|
@ -35,12 +38,16 @@ public:
|
|||
// SfizzVstController::StateListener
|
||||
void onStateChanged() override;
|
||||
|
||||
// SfizzVstController::MessageListener
|
||||
void onMessageReceived(const char* path, const char* sig, const sfizz_arg_t* args) override;
|
||||
|
||||
protected:
|
||||
// EditorController
|
||||
void uiSendValue(EditId id, const EditValue& v) override;
|
||||
void uiBeginSend(EditId id) override;
|
||||
void uiEndSend(EditId id) 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:
|
||||
void loadSfzFile(const std::string& filePath);
|
||||
|
|
@ -55,4 +62,7 @@ private:
|
|||
#if !defined(__APPLE__) && !defined(_WIN32)
|
||||
SharedPointer<RunLoop> _runLoop;
|
||||
#endif
|
||||
|
||||
// messaging
|
||||
std::unique_ptr<uint8[]> oscTemp_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,10 +24,17 @@ static const char defaultSfzText[] =
|
|||
"<region>sample=*sine" "\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()
|
||||
: _fifoToWorker(64 * 1024), _fifoMidiFromUi(64 * 1024)
|
||||
: _fifoToWorker(64 * 1024), _fifoMessageFromUi(64 * 1024),
|
||||
_oscTemp(new uint8_t[kOscTempSize])
|
||||
{
|
||||
setControllerClass(SfizzVstController::cid);
|
||||
|
||||
|
|
@ -56,6 +63,16 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
|
|||
|
||||
fprintf(stderr, "[sfizz] new synth\n");
|
||||
_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;
|
||||
loadSfzFileOrDefault(*_synth, {});
|
||||
|
||||
|
|
@ -210,7 +227,7 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
|||
else
|
||||
synth.disableFreeWheeling();
|
||||
|
||||
processMidiFromUi();
|
||||
processMessagesFromUi();
|
||||
|
||||
if (Vst::IParameterChanges* pc = data.inputParameterChanges)
|
||||
processControllerChanges(*pc);
|
||||
|
|
@ -404,36 +421,61 @@ void SfizzVstProcessor::processEvents(Vst::IEventList& events)
|
|||
}
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::processMidiFromUi()
|
||||
void SfizzVstProcessor::processMessagesFromUi()
|
||||
{
|
||||
sfz::Sfizz& synth = *_synth;
|
||||
sfz::Client& client = *_client;
|
||||
Ring_Buffer& fifo = _fifoMessageFromUi;
|
||||
RTMessage header;
|
||||
|
||||
for (uint32 size = 0; _fifoMidiFromUi.peek(size) &&
|
||||
_fifoMidiFromUi.size_used() >= sizeof(size) + size; ) {
|
||||
_fifoMidiFromUi.discard(sizeof(size));
|
||||
while (fifo.peek(header) && fifo.size_used() >= sizeof(header) + header.size) {
|
||||
fifo.discard(sizeof(header));
|
||||
|
||||
if (size > kMidiEventMaximumSize) {
|
||||
_fifoMidiFromUi.discard(size);
|
||||
continue;
|
||||
if (header.type == kRingIdMidi) {
|
||||
if (header.size > kMidiEventMaximumSize) {
|
||||
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] = {};
|
||||
_fifoMidiFromUi.get(data, size);
|
||||
if (header.size > kOscTempSize) {
|
||||
fifo.discard(header.size);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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;
|
||||
fifo.get(oscTemp, header.size);
|
||||
|
||||
const char* path;
|
||||
const char* sig;
|
||||
const sfizz_arg_t* args;
|
||||
uint8_t buffer[1024];
|
||||
if (sfizz_extract_message(oscTemp, header.size, buffer, sizeof(buffer), &path, &sig, &args) > 0)
|
||||
synth.sendMessage(client, 0, path, sig, args);
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -494,12 +536,14 @@ tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
|
|||
const void* data = nullptr;
|
||||
uint32 size = 0;
|
||||
result = attr->getBinary("Data", data, size);
|
||||
if (size < kMidiEventMaximumSize) {
|
||||
if (_fifoMidiFromUi.size_free() >= sizeof(size) + size) {
|
||||
_fifoMidiFromUi.put(size);
|
||||
_fifoMidiFromUi.put(reinterpret_cast<const uint8_t*>(data), size);
|
||||
}
|
||||
}
|
||||
if (size < kMidiEventMaximumSize)
|
||||
writeMessage(_fifoMessageFromUi, kRingIdMidi, 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;
|
||||
|
|
@ -510,6 +554,14 @@ FUnknown* SfizzVstProcessor::createInstance(void*)
|
|||
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)
|
||||
{
|
||||
if (!filePath.empty())
|
||||
|
|
@ -563,6 +615,12 @@ void SfizzVstProcessor::doBackgroundWork()
|
|||
notification->getAttributes()->setBinary("PlayState", &playState, sizeof(playState));
|
||||
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)
|
||||
{
|
||||
RTMessage header;
|
||||
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;
|
||||
return writeMessage(_fifoToWorker, type, data, size);
|
||||
}
|
||||
|
||||
SfizzVstProcessor::RTMessagePtr SfizzVstProcessor::readWorkerMessage()
|
||||
|
|
@ -631,6 +680,20 @@ bool SfizzVstProcessor::discardWorkerMessage()
|
|||
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.
|
||||
Can't find docs on it... maybe it's to register somewhere?
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public:
|
|||
void processParameterChanges(Vst::IParameterChanges& pc);
|
||||
void processControllerChanges(Vst::IParameterChanges& pc);
|
||||
void processEvents(Vst::IEventList& events);
|
||||
void processMidiFromUi();
|
||||
void processMessagesFromUi();
|
||||
static int convertVelocityFromFloat(float x);
|
||||
|
||||
tresult PLUGIN_API notify(Vst::IMessage* message) override;
|
||||
|
|
@ -51,6 +51,11 @@ private:
|
|||
SfizzVstState _state;
|
||||
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
|
||||
static void loadSfzFileOrDefault(sfz::Sfizz& synth, const std::string& filePath);
|
||||
|
||||
|
|
@ -59,7 +64,7 @@ private:
|
|||
volatile bool _workRunning = false;
|
||||
Ring_Buffer _fifoToWorker;
|
||||
RTSemaphore _semaToWorker;
|
||||
Ring_Buffer _fifoMidiFromUi;
|
||||
Ring_Buffer _fifoMessageFromUi;
|
||||
std::mutex _processMutex;
|
||||
|
||||
// file modification periodic checker
|
||||
|
|
@ -95,6 +100,9 @@ private:
|
|||
// reader
|
||||
RTMessagePtr readWorkerMessage();
|
||||
bool discardWorkerMessage();
|
||||
|
||||
// generic
|
||||
static bool writeMessage(Ring_Buffer& fifo, const char* type, const void* data, uintptr_t size);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue