Merge pull request #540 from jpcima/osc
Add OSC and plugin-side communication
This commit is contained in:
commit
bf4fd71ea6
27 changed files with 1232 additions and 59 deletions
|
|
@ -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 \
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
44
lv2/sfizz.c
44
lv2/sfizz.c
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 ;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
74
src/sfizz.h
74
src/sfizz.h
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
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_ { };
|
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);
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
|
|
|
||||||
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(',', h);
|
||||||
|
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
|
// 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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
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
|
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
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(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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?
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue