Merge pull request #776 from jpcima/lv2-parameters
Present the controllers as LV2 parameters
This commit is contained in:
commit
539fbf6442
13 changed files with 450 additions and 83 deletions
|
|
@ -40,3 +40,59 @@ else()
|
||||||
set(LV2PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/lv2" CACHE STRING
|
set(LV2PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/lv2" CACHE STRING
|
||||||
"Install destination for LV2 bundle [default: ${CMAKE_INSTALL_PREFIX}/lib/lv2]")
|
"Install destination for LV2 bundle [default: ${CMAKE_INSTALL_PREFIX}/lib/lv2]")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
include(StringUtility)
|
||||||
|
|
||||||
|
function(sfizz_lv2_generate_controllers_ttl FILE)
|
||||||
|
file(WRITE "${FILE}" "# LV2 parameters for SFZ controllers
|
||||||
|
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
|
||||||
|
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
|
||||||
|
@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
|
||||||
|
@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
|
||||||
|
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||||
|
@prefix sfizz: <${LV2PLUGIN_URI}#> .
|
||||||
|
")
|
||||||
|
math(EXPR _j "${SFIZZ_NUM_CCS}-1")
|
||||||
|
foreach(_i RANGE "${_j}")
|
||||||
|
string_left_pad(_i "${_i}" 3 0)
|
||||||
|
file(APPEND "${FILE}" "
|
||||||
|
sfizz:cc${_i}
|
||||||
|
a lv2:Parameter ;
|
||||||
|
rdfs:label \"Controller ${_i}\" ;
|
||||||
|
rdfs:range atom:Float")
|
||||||
|
|
||||||
|
if(_i LESS 128)
|
||||||
|
math(EXPR _digit1 "${_i}>>4")
|
||||||
|
math(EXPR _digit2 "${_i}&15")
|
||||||
|
string(SUBSTRING "0123456789ABCDEF" "${_digit1}" 1 _digit1)
|
||||||
|
string(SUBSTRING "0123456789ABCDEF" "${_digit2}" 1 _digit2)
|
||||||
|
file(APPEND "${FILE}" " ;
|
||||||
|
midi:binding \"B0${_digit1}${_digit2}00\"^^midi:MidiEvent .
|
||||||
|
")
|
||||||
|
else()
|
||||||
|
file(APPEND "${FILE}" " .
|
||||||
|
")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
file(APPEND "${FILE}" "
|
||||||
|
<${LV2PLUGIN_URI}>
|
||||||
|
a lv2:Plugin ;
|
||||||
|
")
|
||||||
|
|
||||||
|
file(APPEND "${FILE}" " patch:readable sfizz:cc000")
|
||||||
|
foreach(_i RANGE 1 "${_j}")
|
||||||
|
string_left_pad(_i "${_i}" 3 0)
|
||||||
|
file(APPEND "${FILE}" ", sfizz:cc${_i}")
|
||||||
|
endforeach()
|
||||||
|
file(APPEND "${FILE}" " ;
|
||||||
|
")
|
||||||
|
|
||||||
|
file(APPEND "${FILE}" " patch:writable sfizz:cc000")
|
||||||
|
foreach(_i RANGE 1 "${_j}")
|
||||||
|
string_left_pad(_i "${_i}" 3 0)
|
||||||
|
file(APPEND "${FILE}" ", sfizz:cc${_i}")
|
||||||
|
endforeach()
|
||||||
|
file(APPEND "${FILE}" " .
|
||||||
|
")
|
||||||
|
endfunction()
|
||||||
|
|
|
||||||
11
cmake/StringUtility.cmake
Normal file
11
cmake/StringUtility.cmake
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
function(string_left_pad VAR INPUT LENGTH FILLCHAR)
|
||||||
|
set(_output "${INPUT}")
|
||||||
|
string(LENGTH "${_output}" _length)
|
||||||
|
while(_length LESS "${LENGTH}")
|
||||||
|
set(_output "${FILLCHAR}${_output}")
|
||||||
|
string(LENGTH "${_output}" _length)
|
||||||
|
endwhile()
|
||||||
|
set("${VAR}" "${_output}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
@ -306,6 +306,12 @@ void Editor::close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Editor::sendQueuedOSC(const char* path, const char* sig, const sfizz_arg_t* args)
|
||||||
|
{
|
||||||
|
Impl& impl = *impl_;
|
||||||
|
impl.sendQueuedOSC(path, sig, args);
|
||||||
|
}
|
||||||
|
|
||||||
void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
||||||
{
|
{
|
||||||
switch (id) {
|
switch (id) {
|
||||||
|
|
@ -476,12 +482,18 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
||||||
else if (editIdIsKeyswitchLabel(id)) {
|
else if (editIdIsKeyswitchLabel(id)) {
|
||||||
updateSWLastLabel(keyswitchLabelForEditId(id), v.to_string().c_str());
|
updateSWLastLabel(keyswitchLabelForEditId(id), v.to_string().c_str());
|
||||||
}
|
}
|
||||||
|
else if (editIdIsCC(id)) {
|
||||||
|
updateCCValue(unsigned(ccForEditId(id)), v.to_float());
|
||||||
|
}
|
||||||
else if (editIdIsCCUsed(id)) {
|
else if (editIdIsCCUsed(id)) {
|
||||||
updateCCUsed(ccUsedForEditId(id), v.to_float() != 0);
|
bool used = v.to_float() != 0;
|
||||||
|
updateCCUsed(ccUsedForEditId(id), used);
|
||||||
// TODO(jpc) remove value requests, when implementing CC automation
|
// TODO(jpc) remove value requests, when implementing CC automation
|
||||||
char pathBuf[256];
|
if (used) {
|
||||||
sprintf(pathBuf, "/cc%u/value", ccUsedForEditId(id));
|
char pathBuf[256];
|
||||||
sendQueuedOSC(pathBuf, "", nullptr);
|
sprintf(pathBuf, "/cc%u/value", ccUsedForEditId(id));
|
||||||
|
sendQueuedOSC(pathBuf, "", nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (editIdIsCCDefault(id)) {
|
else if (editIdIsCCDefault(id)) {
|
||||||
updateCCDefaultValue(ccDefaultForEditId(id), v.to_float());
|
updateCCDefaultValue(ccDefaultForEditId(id), v.to_float());
|
||||||
|
|
@ -1610,13 +1622,8 @@ void Editor::Impl::updateMemoryUsed(uint64_t mem)
|
||||||
|
|
||||||
void Editor::Impl::performCCValueChange(unsigned cc, float value)
|
void Editor::Impl::performCCValueChange(unsigned cc, float value)
|
||||||
{
|
{
|
||||||
// TODO(jpc) CC as parameters and automation
|
EditorController& ctrl = *ctrl_;
|
||||||
|
ctrl.uiSendValue(editIdForCC(int(cc)), value);
|
||||||
char pathBuf[256];
|
|
||||||
sprintf(pathBuf, "/cc%u/value", cc);
|
|
||||||
sfizz_arg_t args[1];
|
|
||||||
args[0].f = value;
|
|
||||||
sendQueuedOSC(pathBuf, "f", args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::Impl::performCCBeginEdit(unsigned cc)
|
void Editor::Impl::performCCBeginEdit(unsigned cc)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <sfizz_message.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
class EditorController;
|
class EditorController;
|
||||||
|
|
||||||
|
|
@ -24,6 +25,9 @@ public:
|
||||||
void open(CFrame& frame);
|
void open(CFrame& frame);
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
|
// TODO(jpc) remove me after doing parameter automations
|
||||||
|
void sendQueuedOSC(const char* path, const char* sig, const sfizz_arg_t* args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Impl;
|
struct Impl;
|
||||||
std::unique_ptr<Impl> impl_;
|
std::unique_ptr<Impl> impl_;
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,9 @@ if(SFIZZ_USE_VCPKG OR SFIZZ_STATIC_DEPENDENCIES OR CMAKE_CXX_COMPILER_ID MATCHES
|
||||||
file(COPY "lgpl-3.0.txt" DESTINATION ${PROJECT_BINARY_DIR})
|
file(COPY "lgpl-3.0.txt" DESTINATION ${PROJECT_BINARY_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Generate controllers.ttl
|
||||||
|
sfizz_lv2_generate_controllers_ttl("${PROJECT_BINARY_DIR}/controllers.ttl")
|
||||||
|
|
||||||
# Copy resource files into the bundle
|
# Copy resource files into the bundle
|
||||||
set(LV2_RESOURCES
|
set(LV2_RESOURCES
|
||||||
DefaultInstrument.sfz
|
DefaultInstrument.sfz
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<@LV2PLUGIN_URI@>
|
<@LV2PLUGIN_URI@>
|
||||||
a lv2:Plugin ;
|
a lv2:Plugin ;
|
||||||
lv2:binary <Contents/Binary/@PROJECT_NAME@@CMAKE_SHARED_MODULE_SUFFIX@> ;
|
lv2:binary <Contents/Binary/@PROJECT_NAME@@CMAKE_SHARED_MODULE_SUFFIX@> ;
|
||||||
rdfs:seeAlso <@PROJECT_NAME@.ttl> .
|
rdfs:seeAlso <@PROJECT_NAME@.ttl>, <controllers.ttl> .
|
||||||
|
|
||||||
@LV2PLUGIN_IF_ENABLE_UI@<@LV2PLUGIN_URI@#ui>
|
@LV2PLUGIN_IF_ENABLE_UI@<@LV2PLUGIN_URI@#ui>
|
||||||
@LV2PLUGIN_IF_ENABLE_UI@ a ui:@LV2_UI_TYPE@ ;
|
@LV2PLUGIN_IF_ENABLE_UI@ a ui:@LV2_UI_TYPE@ ;
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#define CHANNEL_MASK 0x0F
|
#define CHANNEL_MASK 0x0F
|
||||||
#define MIDI_CHANNEL(byte) (byte & CHANNEL_MASK)
|
#define MIDI_CHANNEL(byte) (byte & CHANNEL_MASK)
|
||||||
#define MIDI_STATUS(byte) (byte & ~CHANNEL_MASK)
|
#define MIDI_STATUS(byte) (byte & ~CHANNEL_MASK)
|
||||||
|
|
@ -201,6 +203,9 @@ connect_port(LV2_Handle instance,
|
||||||
case SFIZZ_NOTIFY:
|
case SFIZZ_NOTIFY:
|
||||||
self->notify_port = (LV2_Atom_Sequence *)data;
|
self->notify_port = (LV2_Atom_Sequence *)data;
|
||||||
break;
|
break;
|
||||||
|
case SFIZZ_AUTOMATE:
|
||||||
|
self->automate_port = (LV2_Atom_Sequence *)data;
|
||||||
|
break;
|
||||||
case SFIZZ_LEFT:
|
case SFIZZ_LEFT:
|
||||||
self->output_buffers[0] = (float *)data;
|
self->output_buffers[0] = (float *)data;
|
||||||
break;
|
break;
|
||||||
|
|
@ -320,11 +325,12 @@ sfizz_lv2_receive_message(void* data, int delay, const char* path, const char* s
|
||||||
if (osc_size > OSC_TEMP_SIZE)
|
if (osc_size > OSC_TEMP_SIZE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
LV2_Atom_Forge* forge = &self->forge_notify;
|
||||||
bool write_ok =
|
bool write_ok =
|
||||||
lv2_atom_forge_frame_time(&self->forge, 0) &&
|
lv2_atom_forge_frame_time(forge, 0) &&
|
||||||
lv2_atom_forge_atom(&self->forge, osc_size, self->sfizz_osc_blob_uri) &&
|
lv2_atom_forge_atom(forge, osc_size, self->sfizz_osc_blob_uri) &&
|
||||||
lv2_atom_forge_raw(&self->forge, osc_temp, osc_size);
|
lv2_atom_forge_raw(forge, osc_temp, osc_size);
|
||||||
lv2_atom_forge_pad(&self->forge, osc_size);
|
lv2_atom_forge_pad(forge, osc_size);
|
||||||
|
|
||||||
(void)write_ok;
|
(void)write_ok;
|
||||||
}
|
}
|
||||||
|
|
@ -426,7 +432,8 @@ instantiate(const LV2_Descriptor *descriptor,
|
||||||
sfizz_lv2_map_required_uris(self);
|
sfizz_lv2_map_required_uris(self);
|
||||||
|
|
||||||
// Initialize the forge
|
// Initialize the forge
|
||||||
lv2_atom_forge_init(&self->forge, self->map);
|
lv2_atom_forge_init(&self->forge_notify, self->map);
|
||||||
|
lv2_atom_forge_init(&self->forge_automate, self->map);
|
||||||
lv2_atom_forge_init(&self->forge_secondary, self->map);
|
lv2_atom_forge_init(&self->forge_secondary, self->map);
|
||||||
|
|
||||||
// Check the options for the block size and sample rate parameters
|
// Check the options for the block size and sample rate parameters
|
||||||
|
|
@ -476,6 +483,10 @@ instantiate(const LV2_Descriptor *descriptor,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self->ccmap = sfizz_lv2_ccmap_create(self->map);
|
||||||
|
self->cc_current = new float[sfz::config::numCCs]();
|
||||||
|
self->ccauto = new absl::optional<float>[sfz::config::numCCs];
|
||||||
|
|
||||||
self->synth = sfizz_create_synth();
|
self->synth = sfizz_create_synth();
|
||||||
self->client = sfizz_create_client(self);
|
self->client = sfizz_create_client(self);
|
||||||
self->synth_mutex = spin_mutex_create();
|
self->synth_mutex = spin_mutex_create();
|
||||||
|
|
@ -501,6 +512,9 @@ cleanup(LV2_Handle instance)
|
||||||
spin_mutex_destroy(self->synth_mutex);
|
spin_mutex_destroy(self->synth_mutex);
|
||||||
sfizz_delete_client(self->client);
|
sfizz_delete_client(self->client);
|
||||||
sfizz_free(self->synth);
|
sfizz_free(self->synth);
|
||||||
|
delete[] self->ccauto;
|
||||||
|
delete[] self->cc_current;
|
||||||
|
sfizz_lv2_ccmap_free(self->ccmap);
|
||||||
delete self;
|
delete self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -521,24 +535,42 @@ deactivate(LV2_Handle instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_URID urid, const char *path)
|
sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_Atom_Forge* forge, LV2_URID urid, const char *path)
|
||||||
{
|
{
|
||||||
LV2_Atom_Forge_Frame frame;
|
LV2_Atom_Forge_Frame frame;
|
||||||
|
|
||||||
bool write_ok =
|
bool write_ok =
|
||||||
lv2_atom_forge_frame_time(&self->forge, 0) &&
|
lv2_atom_forge_frame_time(forge, 0) &&
|
||||||
lv2_atom_forge_object(&self->forge, &frame, 0, self->patch_set_uri) &&
|
lv2_atom_forge_object(forge, &frame, 0, self->patch_set_uri) &&
|
||||||
lv2_atom_forge_key(&self->forge, self->patch_property_uri) &&
|
lv2_atom_forge_key(forge, self->patch_property_uri) &&
|
||||||
lv2_atom_forge_urid(&self->forge, urid) &&
|
lv2_atom_forge_urid(forge, urid) &&
|
||||||
lv2_atom_forge_key(&self->forge, self->patch_value_uri) &&
|
lv2_atom_forge_key(forge, self->patch_value_uri) &&
|
||||||
lv2_atom_forge_path(&self->forge, path, (uint32_t)strlen(path));
|
lv2_atom_forge_path(forge, path, (uint32_t)strlen(path));
|
||||||
|
|
||||||
if (write_ok)
|
if (write_ok)
|
||||||
lv2_atom_forge_pop(&self->forge, &frame);
|
lv2_atom_forge_pop(forge, &frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, const LV2_Atom_Object *obj)
|
sfizz_lv2_send_controller(sfizz_plugin_t *self, LV2_Atom_Forge* forge, unsigned cc, float value)
|
||||||
|
{
|
||||||
|
LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc));
|
||||||
|
LV2_Atom_Forge_Frame frame;
|
||||||
|
|
||||||
|
bool write_ok =
|
||||||
|
lv2_atom_forge_frame_time(forge, 0) &&
|
||||||
|
lv2_atom_forge_object(forge, &frame, 0, self->patch_set_uri) &&
|
||||||
|
lv2_atom_forge_key(forge, self->patch_property_uri) &&
|
||||||
|
lv2_atom_forge_urid(forge, urid) &&
|
||||||
|
lv2_atom_forge_key(forge, self->patch_value_uri) &&
|
||||||
|
lv2_atom_forge_float(forge, value);
|
||||||
|
|
||||||
|
if (write_ok)
|
||||||
|
lv2_atom_forge_pop(forge, &frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, int delay, const LV2_Atom_Object *obj)
|
||||||
{
|
{
|
||||||
const LV2_Atom *property = NULL;
|
const LV2_Atom *property = NULL;
|
||||||
lv2_atom_object_get(obj, self->patch_property_uri, &property, 0);
|
lv2_atom_object_get(obj, self->patch_property_uri, &property, 0);
|
||||||
|
|
@ -575,7 +607,15 @@ sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, const LV2_Atom_Object *obj)
|
||||||
char body[MAX_PATH_SIZE];
|
char body[MAX_PATH_SIZE];
|
||||||
} sfizz_path_atom_buffer_t;
|
} sfizz_path_atom_buffer_t;
|
||||||
|
|
||||||
if (key == self->sfizz_sfz_file_uri)
|
int cc = sfizz_lv2_ccmap_unmap(self->ccmap, key);
|
||||||
|
if (cc != -1) {
|
||||||
|
if (atom->type == self->atom_float_uri && atom->size == sizeof(float)) {
|
||||||
|
float value = *(const float *)LV2_ATOM_BODY_CONST(atom);
|
||||||
|
sfizz_send_hdcc(self->synth, delay, cc, value);
|
||||||
|
self->cc_current[cc] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (key == self->sfizz_sfz_file_uri)
|
||||||
{
|
{
|
||||||
LV2_Atom_Forge *forge = &self->forge_secondary;
|
LV2_Atom_Forge *forge = &self->forge_secondary;
|
||||||
sfizz_path_atom_buffer_t buffer;
|
sfizz_path_atom_buffer_t buffer;
|
||||||
|
|
@ -629,12 +669,14 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
|
||||||
(int)msg[1],
|
(int)msg[1],
|
||||||
msg[2]);
|
msg[2]);
|
||||||
break;
|
break;
|
||||||
case LV2_MIDI_MSG_CONTROLLER:
|
// Note(jpc) CC must be mapped by host, not handled here.
|
||||||
|
// See LV2 midi:binding.
|
||||||
|
/*case LV2_MIDI_MSG_CONTROLLER:
|
||||||
sfizz_send_cc(self->synth,
|
sfizz_send_cc(self->synth,
|
||||||
(int)ev->time.frames,
|
(int)ev->time.frames,
|
||||||
(int)msg[1],
|
(int)msg[1],
|
||||||
msg[2]);
|
msg[2]);
|
||||||
break;
|
break;*/
|
||||||
case LV2_MIDI_MSG_CHANNEL_PRESSURE:
|
case LV2_MIDI_MSG_CHANNEL_PRESSURE:
|
||||||
sfizz_send_aftertouch(self->synth,
|
sfizz_send_aftertouch(self->synth,
|
||||||
(int)ev->time.frames,
|
(int)ev->time.frames,
|
||||||
|
|
@ -770,7 +812,7 @@ static void
|
||||||
run(LV2_Handle instance, uint32_t sample_count)
|
run(LV2_Handle instance, uint32_t sample_count)
|
||||||
{
|
{
|
||||||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||||
assert(self->control_port && self->notify_port);
|
assert(self->control_port && self->notify_port && self->automate_port);
|
||||||
|
|
||||||
if (!spin_mutex_trylock(self->synth_mutex))
|
if (!spin_mutex_trylock(self->synth_mutex))
|
||||||
{
|
{
|
||||||
|
|
@ -779,13 +821,18 @@ run(LV2_Handle instance, uint32_t sample_count)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up forge to write directly to notify output port.
|
// Set up dedicated forges to write on their respective ports.
|
||||||
const size_t notify_capacity = self->notify_port->atom.size;
|
const size_t notify_capacity = self->notify_port->atom.size;
|
||||||
lv2_atom_forge_set_buffer(&self->forge, (uint8_t *)self->notify_port, notify_capacity);
|
lv2_atom_forge_set_buffer(&self->forge_notify, (uint8_t *)self->notify_port, notify_capacity);
|
||||||
|
const size_t automate_capacity = self->automate_port->atom.size;
|
||||||
|
lv2_atom_forge_set_buffer(&self->forge_automate, (uint8_t *)self->automate_port, automate_capacity);
|
||||||
|
|
||||||
// Start a sequence in the notify output port.
|
// Start sequences in the respective output ports.
|
||||||
LV2_Atom_Forge_Frame notify_frame;
|
LV2_Atom_Forge_Frame notify_frame;
|
||||||
if (!lv2_atom_forge_sequence_head(&self->forge, ¬ify_frame, 0))
|
if (!lv2_atom_forge_sequence_head(&self->forge_notify, ¬ify_frame, 0))
|
||||||
|
assert(false);
|
||||||
|
LV2_Atom_Forge_Frame automate_frame;
|
||||||
|
if (!lv2_atom_forge_sequence_head(&self->forge_automate, &automate_frame, 0))
|
||||||
assert(false);
|
assert(false);
|
||||||
|
|
||||||
LV2_ATOM_SEQUENCE_FOREACH(self->control_port, ev)
|
LV2_ATOM_SEQUENCE_FOREACH(self->control_port, ev)
|
||||||
|
|
@ -796,9 +843,10 @@ run(LV2_Handle instance, uint32_t sample_count)
|
||||||
if (ev->body.type == self->atom_object_uri || ev->body.type == self->atom_blank_uri)
|
if (ev->body.type == self->atom_object_uri || ev->body.type == self->atom_blank_uri)
|
||||||
{
|
{
|
||||||
const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
|
const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
|
||||||
|
|
||||||
if (obj->body.otype == self->patch_set_uri)
|
if (obj->body.otype == self->patch_set_uri)
|
||||||
{
|
{
|
||||||
sfizz_lv2_handle_atom_object(self, obj);
|
sfizz_lv2_handle_atom_object(self, delay, obj);
|
||||||
}
|
}
|
||||||
else if (obj->body.otype == self->patch_get_uri)
|
else if (obj->body.otype == self->patch_get_uri)
|
||||||
{
|
{
|
||||||
|
|
@ -806,16 +854,25 @@ run(LV2_Handle instance, uint32_t sample_count)
|
||||||
lv2_atom_object_get(obj, self->patch_property_uri, &property, 0);
|
lv2_atom_object_get(obj, self->patch_property_uri, &property, 0);
|
||||||
if (!property) // Send the full state
|
if (!property) // Send the full state
|
||||||
{
|
{
|
||||||
sfizz_lv2_send_file_path(self, self->sfizz_sfz_file_uri, self->sfz_file_path);
|
sfizz_lv2_send_file_path(self, &self->forge_notify, self->sfizz_sfz_file_uri, self->sfz_file_path);
|
||||||
sfizz_lv2_send_file_path(self, self->sfizz_scala_file_uri, self->scala_file_path);
|
sfizz_lv2_send_file_path(self, &self->forge_notify, self->sfizz_scala_file_uri, self->scala_file_path);
|
||||||
|
|
||||||
|
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc)
|
||||||
|
sfizz_lv2_send_controller(self, &self->forge_notify, cc, self->cc_current[cc]);
|
||||||
}
|
}
|
||||||
else if (property->body == self->sfizz_sfz_file_uri)
|
else if (property->body == self->sfizz_sfz_file_uri)
|
||||||
{
|
{
|
||||||
sfizz_lv2_send_file_path(self, self->sfizz_sfz_file_uri, self->sfz_file_path);
|
sfizz_lv2_send_file_path(self, &self->forge_notify, self->sfizz_sfz_file_uri, self->sfz_file_path);
|
||||||
}
|
}
|
||||||
else if (property->body == self->sfizz_scala_file_uri)
|
else if (property->body == self->sfizz_scala_file_uri)
|
||||||
{
|
{
|
||||||
sfizz_lv2_send_file_path(self, self->sfizz_scala_file_uri, self->scala_file_path);
|
sfizz_lv2_send_file_path(self, &self->forge_notify, self->sfizz_scala_file_uri, self->scala_file_path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int cc = sfizz_lv2_ccmap_unmap(self->ccmap, property->body);
|
||||||
|
if (cc != -1)
|
||||||
|
sfizz_lv2_send_controller(self, &self->forge_notify, unsigned(cc), self->cc_current[cc]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (obj->body.otype == self->time_position_uri)
|
else if (obj->body.otype == self->time_position_uri)
|
||||||
|
|
@ -949,17 +1006,29 @@ run(LV2_Handle instance, uint32_t sample_count)
|
||||||
sfizz_render_block(self->synth, self->output_buffers, 2, (int)sample_count);
|
sfizz_render_block(self->synth, self->output_buffers, 2, (int)sample_count);
|
||||||
|
|
||||||
// Request OSC updates
|
// Request OSC updates
|
||||||
sfizz_send_message(self->synth, self->client, 0, "/cc/changed~", "", nullptr);
|
|
||||||
sfizz_send_message(self->synth, self->client, 0, "/sw/last/current", "", nullptr);
|
sfizz_send_message(self->synth, self->client, 0, "/sw/last/current", "", nullptr);
|
||||||
|
|
||||||
spin_mutex_unlock(self->synth_mutex);
|
|
||||||
|
|
||||||
if (self->midnam && self->must_update_midnam.exchange(0))
|
if (self->midnam && self->must_update_midnam.exchange(0))
|
||||||
{
|
{
|
||||||
self->midnam->update(self->midnam->handle);
|
self->midnam->update(self->midnam->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
lv2_atom_forge_pop(&self->forge, ¬ify_frame);
|
if (self->have_ccauto)
|
||||||
|
{
|
||||||
|
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||||
|
absl::optional<float> value = self->ccauto[cc];
|
||||||
|
if (value) {
|
||||||
|
sfizz_lv2_send_controller(self, &self->forge_automate, cc, *value);
|
||||||
|
self->ccauto[cc] = absl::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self->have_ccauto = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_mutex_unlock(self->synth_mutex);
|
||||||
|
|
||||||
|
lv2_atom_forge_pop(&self->forge_notify, ¬ify_frame);
|
||||||
|
lv2_atom_forge_pop(&self->forge_automate, &automate_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t
|
static uint32_t
|
||||||
|
|
@ -1063,6 +1132,7 @@ sfizz_lv2_update_sfz_info(sfizz_plugin_t *self)
|
||||||
{
|
{
|
||||||
const std::string blob = getDescriptionBlob(self->synth);
|
const std::string blob = getDescriptionBlob(self->synth);
|
||||||
|
|
||||||
|
// Update description blob that UI can fetch, thread-safely
|
||||||
uint32_t size = uint32_t(blob.size());
|
uint32_t size = uint32_t(blob.size());
|
||||||
uint8_t *data = new uint8_t[size];
|
uint8_t *data = new uint8_t[size];
|
||||||
memcpy(data, blob.data(), size);
|
memcpy(data, blob.data(), size);
|
||||||
|
|
@ -1075,6 +1145,18 @@ sfizz_lv2_update_sfz_info(sfizz_plugin_t *self)
|
||||||
self->sfz_blob_mutex->unlock();
|
self->sfz_blob_mutex->unlock();
|
||||||
|
|
||||||
delete[] old_data;
|
delete[] old_data;
|
||||||
|
|
||||||
|
//
|
||||||
|
const InstrumentDescription desc = parseDescriptionBlob(blob);
|
||||||
|
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||||
|
if (desc.ccUsed.test(cc)) {
|
||||||
|
// Mark all the used CCs for automation with default values
|
||||||
|
self->ccauto[cc] = desc.ccDefault[cc];
|
||||||
|
self->have_ccauto = true;
|
||||||
|
// Update the current CCs
|
||||||
|
self->cc_current[cc] = desc.ccDefault[cc];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -1220,6 +1302,17 @@ restore(LV2_Handle instance,
|
||||||
self->oversampling = oversampling;
|
self->oversampling = oversampling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect all CC values present in the state
|
||||||
|
std::unique_ptr<absl::optional<float>[]> cc_values(
|
||||||
|
new absl::optional<float>[sfz::config::numCCs]);
|
||||||
|
|
||||||
|
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||||
|
LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc));
|
||||||
|
value = retrieve(handle, urid, &size, &type, &val_flags);
|
||||||
|
if (value && type == self->atom_float_uri)
|
||||||
|
cc_values[cc] = *(const float *)value;
|
||||||
|
}
|
||||||
|
|
||||||
// Sync the parameters to the synth
|
// Sync the parameters to the synth
|
||||||
spin_mutex_lock(self->synth_mutex);
|
spin_mutex_lock(self->synth_mutex);
|
||||||
|
|
||||||
|
|
@ -1258,6 +1351,20 @@ restore(LV2_Handle instance,
|
||||||
lv2_log_note(&self->logger, "[sfizz] Restoring the oversampling to %d\n", self->oversampling);
|
lv2_log_note(&self->logger, "[sfizz] Restoring the oversampling to %d\n", self->oversampling);
|
||||||
sfizz_set_oversampling_factor(self->synth, self->oversampling);
|
sfizz_set_oversampling_factor(self->synth, self->oversampling);
|
||||||
|
|
||||||
|
// Override default automation values with these from the state file
|
||||||
|
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||||
|
absl::optional<float> value = cc_values[cc];
|
||||||
|
if (value) {
|
||||||
|
// Set CC in the synth
|
||||||
|
sfizz_send_hdcc(self->synth, 0, int(cc), *value);
|
||||||
|
// Mark CCs for automation with state values
|
||||||
|
self->ccauto[cc] = *value;
|
||||||
|
self->have_ccauto = true;
|
||||||
|
// Update the current CCs
|
||||||
|
self->cc_current[cc] = *value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spin_mutex_unlock(self->synth_mutex);
|
spin_mutex_unlock(self->synth_mutex);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
@ -1327,7 +1434,7 @@ save(LV2_Handle instance,
|
||||||
&self->num_voices,
|
&self->num_voices,
|
||||||
sizeof(int),
|
sizeof(int),
|
||||||
self->atom_int_uri,
|
self->atom_int_uri,
|
||||||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
|
LV2_STATE_IS_POD);
|
||||||
|
|
||||||
// Save the preload size
|
// Save the preload size
|
||||||
store(handle,
|
store(handle,
|
||||||
|
|
@ -1335,7 +1442,7 @@ save(LV2_Handle instance,
|
||||||
&self->preload_size,
|
&self->preload_size,
|
||||||
sizeof(unsigned int),
|
sizeof(unsigned int),
|
||||||
self->atom_int_uri,
|
self->atom_int_uri,
|
||||||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
|
LV2_STATE_IS_POD);
|
||||||
|
|
||||||
// Save the preload size
|
// Save the preload size
|
||||||
store(handle,
|
store(handle,
|
||||||
|
|
@ -1343,7 +1450,25 @@ save(LV2_Handle instance,
|
||||||
&self->oversampling,
|
&self->oversampling,
|
||||||
sizeof(int),
|
sizeof(int),
|
||||||
self->atom_int_uri,
|
self->atom_int_uri,
|
||||||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
|
LV2_STATE_IS_POD);
|
||||||
|
|
||||||
|
// Save the CCs (used only)
|
||||||
|
self->sfz_blob_mutex->lock();
|
||||||
|
const InstrumentDescription desc = parseDescriptionBlob(
|
||||||
|
absl::string_view((const char*)self->sfz_blob_data, self->sfz_blob_size));
|
||||||
|
self->sfz_blob_mutex->unlock();
|
||||||
|
|
||||||
|
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||||
|
if (desc.ccUsed.test(cc)) {
|
||||||
|
LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc));
|
||||||
|
store(handle,
|
||||||
|
urid,
|
||||||
|
&self->cc_current[cc],
|
||||||
|
sizeof(float),
|
||||||
|
self->atom_float_uri,
|
||||||
|
LV2_STATE_IS_POD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return LV2_STATE_SUCCESS;
|
return LV2_STATE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,29 +104,38 @@ midnam:update a lv2:Feature .
|
||||||
a lv2:OutputPort, atom:AtomPort ;
|
a lv2:OutputPort, atom:AtomPort ;
|
||||||
atom:bufferType atom:Sequence ;
|
atom:bufferType atom:Sequence ;
|
||||||
atom:supports patch:Message, <@LV2PLUGIN_URI@:OSCBlob> ;
|
atom:supports patch:Message, <@LV2PLUGIN_URI@:OSCBlob> ;
|
||||||
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 ;
|
rsz:minimumSize 65536 ;
|
||||||
] , [
|
] , [
|
||||||
a lv2:AudioPort, lv2:OutputPort ;
|
a lv2:OutputPort, atom:AtomPort ;
|
||||||
|
atom:bufferType atom:Sequence ;
|
||||||
|
atom:supports patch:Message ;
|
||||||
|
lv2:designation lv2:control ;
|
||||||
lv2:index 2 ;
|
lv2:index 2 ;
|
||||||
|
lv2:symbol "automate" ;
|
||||||
|
lv2:name "Automate",
|
||||||
|
"Automatisation"@fr ;
|
||||||
|
rsz:minimumSize 65536 ;
|
||||||
|
] , [
|
||||||
|
a lv2:AudioPort, lv2:OutputPort ;
|
||||||
|
lv2:index 3 ;
|
||||||
lv2:symbol "out_left" ;
|
lv2:symbol "out_left" ;
|
||||||
lv2:name "Left Output",
|
lv2:name "Left Output",
|
||||||
"Sortie gauche"@fr ,
|
"Sortie gauche"@fr ,
|
||||||
"Uscita Sinistra"@it
|
"Uscita Sinistra"@it
|
||||||
] , [
|
] , [
|
||||||
a lv2:AudioPort, lv2:OutputPort ;
|
a lv2:AudioPort, lv2:OutputPort ;
|
||||||
lv2:index 3 ;
|
lv2:index 4 ;
|
||||||
lv2:symbol "out_right" ;
|
lv2:symbol "out_right" ;
|
||||||
lv2:name "Right Output",
|
lv2:name "Right Output",
|
||||||
"Sortie droite"@fr ,
|
"Sortie droite"@fr ,
|
||||||
"Uscita Destra"@it
|
"Uscita Destra"@it
|
||||||
] , [
|
] , [
|
||||||
a lv2:InputPort, lv2:ControlPort ;
|
a lv2:InputPort, lv2:ControlPort ;
|
||||||
lv2:index 4 ;
|
lv2:index 5 ;
|
||||||
lv2:symbol "volume" ;
|
lv2:symbol "volume" ;
|
||||||
lv2:name "Volume" ;
|
lv2:name "Volume" ;
|
||||||
lv2:default 0.0 ;
|
lv2:default 0.0 ;
|
||||||
|
|
@ -135,7 +144,7 @@ midnam:update a lv2:Feature .
|
||||||
units:unit units:db
|
units:unit units:db
|
||||||
] , [
|
] , [
|
||||||
a lv2:InputPort, lv2:ControlPort ;
|
a lv2:InputPort, lv2:ControlPort ;
|
||||||
lv2:index 5 ;
|
lv2:index 6 ;
|
||||||
lv2:symbol "num_voices" ;
|
lv2:symbol "num_voices" ;
|
||||||
lv2:name "Polyphony",
|
lv2:name "Polyphony",
|
||||||
"Polyphonie"@fr ,
|
"Polyphonie"@fr ,
|
||||||
|
|
@ -180,7 +189,7 @@ midnam:update a lv2:Feature .
|
||||||
] ;
|
] ;
|
||||||
] , [
|
] , [
|
||||||
a lv2:InputPort, lv2:ControlPort ;
|
a lv2:InputPort, lv2:ControlPort ;
|
||||||
lv2:index 6 ;
|
lv2:index 7 ;
|
||||||
lv2:symbol "oversampling" ;
|
lv2:symbol "oversampling" ;
|
||||||
lv2:name "Internal oversampling factor",
|
lv2:name "Internal oversampling factor",
|
||||||
"Facteur de suréchantillonnage interne"@fr ,
|
"Facteur de suréchantillonnage interne"@fr ,
|
||||||
|
|
@ -215,7 +224,7 @@ midnam:update a lv2:Feature .
|
||||||
] ;
|
] ;
|
||||||
] , [
|
] , [
|
||||||
a lv2:InputPort, lv2:ControlPort ;
|
a lv2:InputPort, lv2:ControlPort ;
|
||||||
lv2:index 7 ;
|
lv2:index 8 ;
|
||||||
lv2:symbol "preload_size" ;
|
lv2:symbol "preload_size" ;
|
||||||
lv2:name "Preload size",
|
lv2:name "Preload size",
|
||||||
"Taille préchargée"@fr ,
|
"Taille préchargée"@fr ,
|
||||||
|
|
@ -258,7 +267,7 @@ midnam:update a lv2:Feature .
|
||||||
] ;
|
] ;
|
||||||
] , [
|
] , [
|
||||||
a lv2:InputPort, lv2:ControlPort ;
|
a lv2:InputPort, lv2:ControlPort ;
|
||||||
lv2:index 8 ;
|
lv2:index 9 ;
|
||||||
lv2:symbol "freewheeling" ;
|
lv2:symbol "freewheeling" ;
|
||||||
lv2:name "Freewheeling",
|
lv2:name "Freewheeling",
|
||||||
"En roue libre (freewheeling)"@fr ,
|
"En roue libre (freewheeling)"@fr ,
|
||||||
|
|
@ -270,7 +279,7 @@ midnam:update a lv2:Feature .
|
||||||
lv2:maximum 1 ;
|
lv2:maximum 1 ;
|
||||||
] , [
|
] , [
|
||||||
a lv2:InputPort, lv2:ControlPort ;
|
a lv2:InputPort, lv2:ControlPort ;
|
||||||
lv2:index 9 ;
|
lv2:index 10 ;
|
||||||
lv2:symbol "scala_root_key" ;
|
lv2:symbol "scala_root_key" ;
|
||||||
lv2:name "Scala root key",
|
lv2:name "Scala root key",
|
||||||
"Tonalité de base Scala"@fr ,
|
"Tonalité de base Scala"@fr ,
|
||||||
|
|
@ -283,7 +292,7 @@ midnam:update a lv2:Feature .
|
||||||
units:unit units:midiNote
|
units:unit units:midiNote
|
||||||
] , [
|
] , [
|
||||||
a lv2:InputPort, lv2:ControlPort ;
|
a lv2:InputPort, lv2:ControlPort ;
|
||||||
lv2:index 10 ;
|
lv2:index 11 ;
|
||||||
lv2:symbol "tuning_frequency" ;
|
lv2:symbol "tuning_frequency" ;
|
||||||
lv2:name "Tuning frequency",
|
lv2:name "Tuning frequency",
|
||||||
"Fréquence d'accordage"@fr ,
|
"Fréquence d'accordage"@fr ,
|
||||||
|
|
@ -295,7 +304,7 @@ midnam:update a lv2:Feature .
|
||||||
units:unit units:hz
|
units:unit units:hz
|
||||||
] , [
|
] , [
|
||||||
a lv2:InputPort, lv2:ControlPort ;
|
a lv2:InputPort, lv2:ControlPort ;
|
||||||
lv2:index 11 ;
|
lv2:index 12 ;
|
||||||
lv2:symbol "stretched_tuning" ;
|
lv2:symbol "stretched_tuning" ;
|
||||||
lv2:name "Stretched tuning",
|
lv2:name "Stretched tuning",
|
||||||
"Accordage étiré"@fr ,
|
"Accordage étiré"@fr ,
|
||||||
|
|
@ -307,7 +316,7 @@ midnam:update a lv2:Feature .
|
||||||
units:unit units:coef
|
units:unit units:coef
|
||||||
] , [
|
] , [
|
||||||
a lv2:OutputPort, lv2:ControlPort ;
|
a lv2:OutputPort, lv2:ControlPort ;
|
||||||
lv2:index 12 ;
|
lv2:index 13 ;
|
||||||
lv2:symbol "active_voices" ;
|
lv2:symbol "active_voices" ;
|
||||||
lv2:name "Active voices",
|
lv2:name "Active voices",
|
||||||
"Voix utilisées"@fr ;
|
"Voix utilisées"@fr ;
|
||||||
|
|
@ -318,7 +327,7 @@ midnam:update a lv2:Feature .
|
||||||
lv2:maximum 256 ;
|
lv2:maximum 256 ;
|
||||||
] , [
|
] , [
|
||||||
a lv2:OutputPort, lv2:ControlPort ;
|
a lv2:OutputPort, lv2:ControlPort ;
|
||||||
lv2:index 13 ;
|
lv2:index 14 ;
|
||||||
lv2:symbol "num_curves" ;
|
lv2:symbol "num_curves" ;
|
||||||
lv2:name "Number of curves",
|
lv2:name "Number of curves",
|
||||||
"Nombre de courbes"@fr ;
|
"Nombre de courbes"@fr ;
|
||||||
|
|
@ -329,7 +338,7 @@ midnam:update a lv2:Feature .
|
||||||
lv2:maximum 65535 ;
|
lv2:maximum 65535 ;
|
||||||
] , [
|
] , [
|
||||||
a lv2:OutputPort, lv2:ControlPort ;
|
a lv2:OutputPort, lv2:ControlPort ;
|
||||||
lv2:index 14 ;
|
lv2:index 15 ;
|
||||||
lv2:symbol "num_masters" ;
|
lv2:symbol "num_masters" ;
|
||||||
lv2:name "Number of masters",
|
lv2:name "Number of masters",
|
||||||
"Nombre de maîtres"@fr ;
|
"Nombre de maîtres"@fr ;
|
||||||
|
|
@ -340,7 +349,7 @@ midnam:update a lv2:Feature .
|
||||||
lv2:maximum 65535 ;
|
lv2:maximum 65535 ;
|
||||||
] , [
|
] , [
|
||||||
a lv2:OutputPort, lv2:ControlPort ;
|
a lv2:OutputPort, lv2:ControlPort ;
|
||||||
lv2:index 15 ;
|
lv2:index 16 ;
|
||||||
lv2:symbol "num_groups" ;
|
lv2:symbol "num_groups" ;
|
||||||
lv2:name "Number of groups",
|
lv2:name "Number of groups",
|
||||||
"Nombre de groupes"@fr ;
|
"Nombre de groupes"@fr ;
|
||||||
|
|
@ -351,7 +360,7 @@ midnam:update a lv2:Feature .
|
||||||
lv2:maximum 65535 ;
|
lv2:maximum 65535 ;
|
||||||
] , [
|
] , [
|
||||||
a lv2:OutputPort, lv2:ControlPort ;
|
a lv2:OutputPort, lv2:ControlPort ;
|
||||||
lv2:index 16 ;
|
lv2:index 17 ;
|
||||||
lv2:symbol "num_regions" ;
|
lv2:symbol "num_regions" ;
|
||||||
lv2:name "Number of regions",
|
lv2:name "Number of regions",
|
||||||
"Nombre de régions"@fr ;
|
"Nombre de régions"@fr ;
|
||||||
|
|
@ -362,7 +371,7 @@ midnam:update a lv2:Feature .
|
||||||
lv2:maximum 65535 ;
|
lv2:maximum 65535 ;
|
||||||
] , [
|
] , [
|
||||||
a lv2:OutputPort, lv2:ControlPort ;
|
a lv2:OutputPort, lv2:ControlPort ;
|
||||||
lv2:index 17 ;
|
lv2:index 18 ;
|
||||||
lv2:symbol "num_samples" ;
|
lv2:symbol "num_samples" ;
|
||||||
lv2:name "Number of samples",
|
lv2:name "Number of samples",
|
||||||
"Nombre d'échantillons"@fr ;
|
"Nombre d'échantillons"@fr ;
|
||||||
|
|
@ -371,4 +380,6 @@ midnam:update a lv2:Feature .
|
||||||
lv2:default 0 ;
|
lv2:default 0 ;
|
||||||
lv2:minimum 0 ;
|
lv2:minimum 0 ;
|
||||||
lv2:maximum 65535 ;
|
lv2:maximum 65535 ;
|
||||||
] .
|
] ;
|
||||||
|
|
||||||
|
rdfs:seeAlso <controllers.ttl> .
|
||||||
|
|
|
||||||
|
|
@ -49,22 +49,23 @@ enum
|
||||||
{
|
{
|
||||||
SFIZZ_CONTROL = 0,
|
SFIZZ_CONTROL = 0,
|
||||||
SFIZZ_NOTIFY = 1,
|
SFIZZ_NOTIFY = 1,
|
||||||
SFIZZ_LEFT = 2,
|
SFIZZ_AUTOMATE = 2,
|
||||||
SFIZZ_RIGHT = 3,
|
SFIZZ_LEFT = 3,
|
||||||
SFIZZ_VOLUME = 4,
|
SFIZZ_RIGHT = 4,
|
||||||
SFIZZ_POLYPHONY = 5,
|
SFIZZ_VOLUME = 5,
|
||||||
SFIZZ_OVERSAMPLING = 6,
|
SFIZZ_POLYPHONY = 6,
|
||||||
SFIZZ_PRELOAD = 7,
|
SFIZZ_OVERSAMPLING = 7,
|
||||||
SFIZZ_FREEWHEELING = 8,
|
SFIZZ_PRELOAD = 8,
|
||||||
SFIZZ_SCALA_ROOT_KEY = 9,
|
SFIZZ_FREEWHEELING = 9,
|
||||||
SFIZZ_TUNING_FREQUENCY = 10,
|
SFIZZ_SCALA_ROOT_KEY = 10,
|
||||||
SFIZZ_STRETCH_TUNING = 11,
|
SFIZZ_TUNING_FREQUENCY = 11,
|
||||||
SFIZZ_ACTIVE_VOICES = 12,
|
SFIZZ_STRETCH_TUNING = 12,
|
||||||
SFIZZ_NUM_CURVES = 13,
|
SFIZZ_ACTIVE_VOICES = 13,
|
||||||
SFIZZ_NUM_MASTERS = 14,
|
SFIZZ_NUM_CURVES = 14,
|
||||||
SFIZZ_NUM_GROUPS = 15,
|
SFIZZ_NUM_MASTERS = 15,
|
||||||
SFIZZ_NUM_REGIONS = 16,
|
SFIZZ_NUM_GROUPS = 16,
|
||||||
SFIZZ_NUM_SAMPLES = 17,
|
SFIZZ_NUM_REGIONS = 17,
|
||||||
|
SFIZZ_NUM_SAMPLES = 18,
|
||||||
};
|
};
|
||||||
|
|
||||||
// For use with instance-access
|
// For use with instance-access
|
||||||
|
|
@ -85,3 +86,14 @@ struct sfizz_plugin_t;
|
||||||
bool sfizz_lv2_fetch_description(
|
bool sfizz_lv2_fetch_description(
|
||||||
sfizz_plugin_t *self, const int *serial,
|
sfizz_plugin_t *self, const int *serial,
|
||||||
uint8_t **descp, uint32_t *sizep, int *serialp);
|
uint8_t **descp, uint32_t *sizep, int *serialp);
|
||||||
|
|
||||||
|
// Mapping URID to CC and vice-versa
|
||||||
|
struct sfizz_lv2_ccmap;
|
||||||
|
sfizz_lv2_ccmap *sfizz_lv2_ccmap_create(LV2_URID_Map* map);
|
||||||
|
void sfizz_lv2_ccmap_free(sfizz_lv2_ccmap *ccmap);
|
||||||
|
LV2_URID sfizz_lv2_ccmap_map(const sfizz_lv2_ccmap *ccmap, int cc);
|
||||||
|
int sfizz_lv2_ccmap_unmap(const sfizz_lv2_ccmap *ccmap, LV2_URID urid);
|
||||||
|
|
||||||
|
struct sfizz_lv2_ccmap_delete {
|
||||||
|
void operator()(sfizz_lv2_ccmap* ccmap) const noexcept { sfizz_lv2_ccmap_free(ccmap); }
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "sfizz_lv2.h"
|
#include "sfizz_lv2.h"
|
||||||
#include "sfizz_lv2_plugin.h"
|
#include "sfizz_lv2_plugin.h"
|
||||||
|
#include "sfizz/Config.h"
|
||||||
|
|
||||||
bool sfizz_lv2_fetch_description(
|
bool sfizz_lv2_fetch_description(
|
||||||
sfizz_plugin_t *self, const int *serial,
|
sfizz_plugin_t *self, const int *serial,
|
||||||
|
|
@ -29,3 +30,81 @@ bool sfizz_lv2_fetch_description(
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct sfizz_lv2_ccmap {
|
||||||
|
LV2_URID *cc_to_urid;
|
||||||
|
int *urid_to_cc;
|
||||||
|
LV2_URID min_cc_urid;
|
||||||
|
LV2_URID max_cc_urid;
|
||||||
|
};
|
||||||
|
|
||||||
|
sfizz_lv2_ccmap *
|
||||||
|
sfizz_lv2_ccmap_create(LV2_URID_Map* map)
|
||||||
|
{
|
||||||
|
// store the associations between CC number and parameter URID
|
||||||
|
// construct it trivially in the form of a tabulated perfect hash function
|
||||||
|
|
||||||
|
LV2_URID* cc_to_urid = new LV2_URID[sfz::config::numCCs];
|
||||||
|
int* urid_to_cc;
|
||||||
|
int urid_to_cc_size;
|
||||||
|
|
||||||
|
LV2_URID min_cc_urid {};
|
||||||
|
LV2_URID max_cc_urid {};
|
||||||
|
for (int cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||||
|
char name[256];
|
||||||
|
sprintf(name, SFIZZ_URI "#cc%03d", cc);
|
||||||
|
LV2_URID urid = map->map(map->handle, name);
|
||||||
|
if (cc == 0) {
|
||||||
|
min_cc_urid = urid;
|
||||||
|
max_cc_urid = urid;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
min_cc_urid = (urid < min_cc_urid) ? urid : min_cc_urid;
|
||||||
|
max_cc_urid = (urid > max_cc_urid) ? urid : max_cc_urid;
|
||||||
|
}
|
||||||
|
cc_to_urid[cc] = urid;
|
||||||
|
}
|
||||||
|
|
||||||
|
urid_to_cc_size = max_cc_urid - min_cc_urid + 1;
|
||||||
|
urid_to_cc = new int[urid_to_cc_size];
|
||||||
|
|
||||||
|
for (int i = 0; i < urid_to_cc_size; ++i)
|
||||||
|
urid_to_cc[i] = -1;
|
||||||
|
|
||||||
|
for (int cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||||
|
LV2_URID urid = cc_to_urid[cc];
|
||||||
|
urid_to_cc[urid - min_cc_urid] = cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
sfizz_lv2_ccmap *self = new sfizz_lv2_ccmap;
|
||||||
|
self->cc_to_urid = cc_to_urid;
|
||||||
|
self->urid_to_cc = urid_to_cc;
|
||||||
|
self->min_cc_urid = min_cc_urid;
|
||||||
|
self->max_cc_urid = max_cc_urid;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfizz_lv2_ccmap_free(sfizz_lv2_ccmap *ccmap)
|
||||||
|
{
|
||||||
|
if (ccmap) {
|
||||||
|
delete[] ccmap->cc_to_urid;
|
||||||
|
delete[] ccmap->urid_to_cc;
|
||||||
|
delete ccmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LV2_URID sfizz_lv2_ccmap_map(const sfizz_lv2_ccmap *ccmap, int cc)
|
||||||
|
{
|
||||||
|
LV2_URID urid = 0;
|
||||||
|
if (cc >= 0 && cc < sfz::config::numCCs)
|
||||||
|
urid = ccmap->cc_to_urid[cc];
|
||||||
|
return urid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sfizz_lv2_ccmap_unmap(const sfizz_lv2_ccmap *ccmap, LV2_URID urid)
|
||||||
|
{
|
||||||
|
int cc = -1;
|
||||||
|
if (urid >= ccmap->min_cc_urid && urid <= ccmap->max_cc_urid)
|
||||||
|
cc = ccmap->urid_to_cc[urid - ccmap->min_cc_urid];
|
||||||
|
return cc;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <sfizz.h>
|
#include <sfizz.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <absl/types/optional.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
|
@ -31,6 +32,7 @@ struct sfizz_plugin_t
|
||||||
// Ports
|
// Ports
|
||||||
const LV2_Atom_Sequence *control_port {};
|
const LV2_Atom_Sequence *control_port {};
|
||||||
LV2_Atom_Sequence *notify_port {};
|
LV2_Atom_Sequence *notify_port {};
|
||||||
|
LV2_Atom_Sequence *automate_port {};
|
||||||
float *output_buffers[2] {};
|
float *output_buffers[2] {};
|
||||||
const float *volume_port {};
|
const float *volume_port {};
|
||||||
const float *polyphony_port {};
|
const float *polyphony_port {};
|
||||||
|
|
@ -48,7 +50,8 @@ struct sfizz_plugin_t
|
||||||
float *num_samples_port {};
|
float *num_samples_port {};
|
||||||
|
|
||||||
// Atom forge
|
// Atom forge
|
||||||
LV2_Atom_Forge forge {}; ///< Forge for writing atoms in run thread
|
LV2_Atom_Forge forge_notify {}; ///< Forge for writing notification atoms in run thread
|
||||||
|
LV2_Atom_Forge forge_automate {}; ///< Forge for writing automation atoms in run thread
|
||||||
LV2_Atom_Forge forge_secondary {}; ///< Forge for writing into other buffers
|
LV2_Atom_Forge forge_secondary {}; ///< Forge for writing into other buffers
|
||||||
|
|
||||||
// Logger
|
// Logger
|
||||||
|
|
@ -92,6 +95,9 @@ struct sfizz_plugin_t
|
||||||
LV2_URID time_beats_per_minute_uri {};
|
LV2_URID time_beats_per_minute_uri {};
|
||||||
LV2_URID time_speed_uri {};
|
LV2_URID time_speed_uri {};
|
||||||
|
|
||||||
|
// CC parameters
|
||||||
|
sfizz_lv2_ccmap* ccmap {};
|
||||||
|
|
||||||
// Sfizz related data
|
// Sfizz related data
|
||||||
sfizz_synth_t *synth {};
|
sfizz_synth_t *synth {};
|
||||||
sfizz_client_t *client {};
|
sfizz_client_t *client {};
|
||||||
|
|
@ -108,6 +114,7 @@ struct sfizz_plugin_t
|
||||||
int sample_counter {};
|
int sample_counter {};
|
||||||
float sample_rate {};
|
float sample_rate {};
|
||||||
std::atomic<int> must_update_midnam {};
|
std::atomic<int> must_update_midnam {};
|
||||||
|
volatile bool must_automate_cc {};
|
||||||
|
|
||||||
// Current instrument description
|
// Current instrument description
|
||||||
std::mutex *sfz_blob_mutex {};
|
std::mutex *sfz_blob_mutex {};
|
||||||
|
|
@ -115,6 +122,14 @@ struct sfizz_plugin_t
|
||||||
const uint8_t *volatile sfz_blob_data {};
|
const uint8_t *volatile sfz_blob_data {};
|
||||||
volatile uint32_t sfz_blob_size {};
|
volatile uint32_t sfz_blob_size {};
|
||||||
|
|
||||||
|
// Current CC values in the synth (synchronized by `synth_mutex`)
|
||||||
|
// updated by hdcc or file load
|
||||||
|
float *cc_current {};
|
||||||
|
|
||||||
|
// CC queued for automation on next run(). (synchronized by `synth_mutex`)
|
||||||
|
absl::optional<float>* ccauto {};
|
||||||
|
volatile bool have_ccauto {};
|
||||||
|
|
||||||
// Timing data
|
// Timing data
|
||||||
int bar {};
|
int bar {};
|
||||||
double bar_beat {};
|
double bar_beat {};
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
|
||||||
LV2_Atom_Forge atom_forge;
|
LV2_Atom_Forge atom_forge;
|
||||||
LV2_URID atom_event_transfer_uri;
|
LV2_URID atom_event_transfer_uri;
|
||||||
LV2_URID atom_object_uri;
|
LV2_URID atom_object_uri;
|
||||||
|
LV2_URID atom_float_uri;
|
||||||
LV2_URID atom_path_uri;
|
LV2_URID atom_path_uri;
|
||||||
LV2_URID atom_urid_uri;
|
LV2_URID atom_urid_uri;
|
||||||
LV2_URID midi_event_uri;
|
LV2_URID midi_event_uri;
|
||||||
|
|
@ -112,6 +113,7 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
|
||||||
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;
|
LV2_URID sfizz_osc_blob_uri;
|
||||||
|
std::unique_ptr<sfizz_lv2_ccmap, sfizz_lv2_ccmap_delete> ccmap;
|
||||||
|
|
||||||
uint8_t osc_temp[OSC_TEMP_SIZE];
|
uint8_t osc_temp[OSC_TEMP_SIZE];
|
||||||
alignas(LV2_Atom) uint8_t atom_temp[ATOM_TEMP_SIZE];
|
alignas(LV2_Atom) uint8_t atom_temp[ATOM_TEMP_SIZE];
|
||||||
|
|
@ -201,6 +203,7 @@ instantiate(const LV2UI_Descriptor *descriptor,
|
||||||
lv2_atom_forge_init(forge, map);
|
lv2_atom_forge_init(forge, map);
|
||||||
self->atom_event_transfer_uri = map->map(map->handle, LV2_ATOM__eventTransfer);
|
self->atom_event_transfer_uri = map->map(map->handle, LV2_ATOM__eventTransfer);
|
||||||
self->atom_object_uri = map->map(map->handle, LV2_ATOM__Object);
|
self->atom_object_uri = map->map(map->handle, LV2_ATOM__Object);
|
||||||
|
self->atom_float_uri = map->map(map->handle, LV2_ATOM__Float);
|
||||||
self->atom_path_uri = map->map(map->handle, LV2_ATOM__Path);
|
self->atom_path_uri = map->map(map->handle, LV2_ATOM__Path);
|
||||||
self->atom_urid_uri = map->map(map->handle, LV2_ATOM__URID);
|
self->atom_urid_uri = map->map(map->handle, LV2_ATOM__URID);
|
||||||
self->midi_event_uri = map->map(map->handle, LV2_MIDI__MidiEvent);
|
self->midi_event_uri = map->map(map->handle, LV2_MIDI__MidiEvent);
|
||||||
|
|
@ -211,6 +214,7 @@ instantiate(const LV2UI_Descriptor *descriptor,
|
||||||
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);
|
self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob);
|
||||||
|
self->ccmap.reset(sfizz_lv2_ccmap_create(map));
|
||||||
|
|
||||||
// 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
|
||||||
|
|
@ -348,7 +352,15 @@ port_event(LV2UI_Handle ui,
|
||||||
const LV2_URID prop_uri = reinterpret_cast<const LV2_Atom_URID *>(prop)->body;
|
const LV2_URID prop_uri = reinterpret_cast<const LV2_Atom_URID *>(prop)->body;
|
||||||
auto *value_body = reinterpret_cast<const char *>(LV2_ATOM_BODY_CONST(value));
|
auto *value_body = reinterpret_cast<const char *>(LV2_ATOM_BODY_CONST(value));
|
||||||
|
|
||||||
if (prop_uri == self->sfizz_sfz_file_uri && value->type == self->atom_path_uri) {
|
int cc = sfizz_lv2_ccmap_unmap(self->ccmap.get(), prop_uri);
|
||||||
|
|
||||||
|
if (cc != -1) {
|
||||||
|
if (value->type == self->atom_float_uri) {
|
||||||
|
float ccvalue = *reinterpret_cast<const float*>(value_body);
|
||||||
|
self->uiReceiveValue(editIdForCC(cc), ccvalue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (prop_uri == self->sfizz_sfz_file_uri && value->type == self->atom_path_uri) {
|
||||||
std::string path(value_body, strnlen(value_body, value->size));
|
std::string path(value_body, strnlen(value_body, value->size));
|
||||||
self->uiReceiveValue(EditId::SfzFile, path);
|
self->uiReceiveValue(EditId::SfzFile, path);
|
||||||
}
|
}
|
||||||
|
|
@ -525,6 +537,22 @@ void sfizz_ui_t::uiSendValue(EditId id, const EditValue& v)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto sendController = [this](LV2_URID property, float value) {
|
||||||
|
LV2_Atom_Forge *forge = &atom_forge;
|
||||||
|
LV2_Atom_Forge_Frame frame;
|
||||||
|
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) &&
|
||||||
|
lv2_atom_forge_key(forge, patch_value_uri) &&
|
||||||
|
lv2_atom_forge_float(forge, value))
|
||||||
|
{
|
||||||
|
lv2_atom_forge_pop(forge, &frame);
|
||||||
|
write(con, SFIZZ_CONTROL, lv2_atom_total_size(atom), atom_event_transfer_uri, atom);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case EditId::Volume:
|
case EditId::Volume:
|
||||||
sendFloat(SFIZZ_VOLUME, v.to_float());
|
sendFloat(SFIZZ_VOLUME, v.to_float());
|
||||||
|
|
@ -554,6 +582,11 @@ void sfizz_ui_t::uiSendValue(EditId id, const EditValue& v)
|
||||||
sendPath(sfizz_scala_file_uri, v.to_string());
|
sendPath(sfizz_scala_file_uri, v.to_string());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (editIdIsCC(id)) {
|
||||||
|
int cc = ccForEditId(id);
|
||||||
|
LV2_URID urid = sfizz_lv2_ccmap_map(ccmap.get(), cc);
|
||||||
|
sendController(urid, v.to_float());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,7 +329,18 @@ void SfizzVstEditor::processNoteEventQueue()
|
||||||
///
|
///
|
||||||
void SfizzVstEditor::uiSendValue(EditId id, const EditValue& v)
|
void SfizzVstEditor::uiSendValue(EditId id, const EditValue& v)
|
||||||
{
|
{
|
||||||
if (id == EditId::SfzFile)
|
if (editIdIsCC(id)) {
|
||||||
|
int cc = ccForEditId(id);
|
||||||
|
// TODO(jpc) CC as parameters and automation
|
||||||
|
if (Editor* editor = editor_.get()) {
|
||||||
|
char pathBuf[256];
|
||||||
|
sprintf(pathBuf, "/cc%u/value", cc);
|
||||||
|
sfizz_arg_t args[1];
|
||||||
|
args[0].f = v.to_float();
|
||||||
|
editor->sendQueuedOSC(pathBuf, "f", args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (id == EditId::SfzFile)
|
||||||
loadSfzFile(v.to_string());
|
loadSfzFile(v.to_string());
|
||||||
else if (id == EditId::ScalaFile)
|
else if (id == EditId::ScalaFile)
|
||||||
loadScalaFile(v.to_string());
|
loadScalaFile(v.to_string());
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue