aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--src/jack.c121
-rw-r--r--src/jalv.c251
-rw-r--r--src/jalv.h72
-rw-r--r--src/jalv_console.c9
-rw-r--r--src/jalv_qt.cpp8
-rw-r--r--src/port.h15
-rw-r--r--src/portaudio.c39
-rw-r--r--src/process.c78
-rw-r--r--src/process.h63
-rw-r--r--src/process_setup.c198
-rw-r--r--src/process_setup.h70
-rw-r--r--src/state.c36
13 files changed, 589 insertions, 374 deletions
diff --git a/NEWS b/NEWS
index cfc3867..011128b 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,7 @@ jalv (1.6.9) unstable; urgency=medium
* Build Qt UI with -fPIC
* Clean up and strengthen code
* Clean up command line help output
+ * Cleanly separate audio thread from the rest of the application
* Fix Jack latency recomputation when plugin latency changes
* Fix clashing command line options
* Fix minor memory leaks
@@ -21,7 +22,7 @@ jalv (1.6.9) unstable; urgency=medium
* Use fewer platform-specific APIs
* Use portable zix filesystem API
- -- David Robillard <d@drobilla.net> Sat, 16 Nov 2024 01:37:46 +0000
+ -- David Robillard <d@drobilla.net> Fri, 22 Nov 2024 18:13:15 +0000
jalv (1.6.8) stable; urgency=medium
diff --git a/src/jack.c b/src/jack.c
index bb16026..758e39a 100644
--- a/src/jack.c
+++ b/src/jack.c
@@ -10,7 +10,6 @@
#include "jalv_config.h"
#include "log.h"
#include "lv2_evbuf.h"
-#include "port.h"
#include "process.h"
#include "process_setup.h"
#include "settings.h"
@@ -54,14 +53,15 @@ buffer_size_cb(jack_nframes_t nframes, void* data)
{
Jalv* const jalv = (Jalv*)data;
JalvSettings* const settings = &jalv->settings;
+ JalvProcess* const proc = &jalv->process;
settings->block_length = nframes;
#if USE_JACK_PORT_TYPE_GET_BUFFER_SIZE
settings->midi_buf_size = jack_port_type_get_buffer_size(
jalv->backend->client, JACK_DEFAULT_MIDI_TYPE);
#endif
- if (jalv->run_state == JALV_RUNNING) {
- jalv_allocate_port_buffers(jalv);
+ if (proc->run_state == JALV_RUNNING) {
+ jalv_process_activate(proc, &jalv->urids, proc->instance, &jalv->settings);
}
return 0;
}
@@ -102,11 +102,11 @@ forge_position(LV2_Atom_Forge* const forge,
}
static int
-process_silent(Jalv* const jalv, const jack_nframes_t nframes)
+process_silent(JalvProcess* const proc, const jack_nframes_t nframes)
{
- for (uint32_t p = 0U; p < jalv->num_ports; ++p) {
- JalvPort* const port = &jalv->ports[p];
- jack_port_t* const jport = (jack_port_t*)jalv->ports[p].sys_port;
+ for (uint32_t p = 0U; p < proc->num_ports; ++p) {
+ JalvProcessPort* const port = &proc->ports[p];
+ jack_port_t* const jport = (jack_port_t*)proc->ports[p].sys_port;
if (jport && port->flow == FLOW_OUTPUT) {
void* const buf = jack_port_get_buffer(jport, nframes);
if (port->type == TYPE_EVENT) {
@@ -117,11 +117,11 @@ process_silent(Jalv* const jalv, const jack_nframes_t nframes)
}
}
- return jalv_bypass(jalv, nframes);
+ return jalv_bypass(proc, nframes);
}
static bool
-process_transport(Jalv* const jalv,
+process_transport(JalvProcess* const proc,
const jack_transport_state_t state,
const jack_position_t pos,
const jack_nframes_t nframes)
@@ -130,13 +130,13 @@ process_transport(Jalv* const jalv,
const bool rolling = state == JackTransportRolling;
const bool has_bbt = (pos.valid & JackPositionBBT);
const bool xport_changed =
- (rolling != jalv->rolling || pos.frame != jalv->position ||
- (has_bbt && pos.beats_per_minute != jalv->bpm));
+ (rolling != proc->rolling || pos.frame != proc->position ||
+ (has_bbt && pos.beats_per_minute != proc->bpm));
// Update transport state to expected values for next cycle
- jalv->position = rolling ? pos.frame + nframes : pos.frame;
- jalv->bpm = has_bbt ? pos.beats_per_minute : jalv->bpm;
- jalv->rolling = rolling;
+ proc->position = rolling ? pos.frame + nframes : pos.frame;
+ proc->bpm = has_bbt ? pos.beats_per_minute : proc->bpm;
+ proc->rolling = rolling;
return xport_changed;
}
@@ -147,13 +147,14 @@ process_cb(jack_nframes_t nframes, void* data)
{
Jalv* const jalv = (Jalv*)data;
const JalvURIDs* const urids = &jalv->urids;
+ JalvProcess* const proc = &jalv->process;
jack_client_t* const client = jalv->backend->client;
uint64_t pos_buf[64] = {0U};
LV2_Atom* const lv2_pos = (LV2_Atom*)pos_buf;
// If execution is paused, emit silence and return
- if (jalv->run_state == JALV_PAUSED) {
- return process_silent(jalv, nframes);
+ if (proc->run_state == JALV_PAUSED) {
+ return process_silent(proc, nframes);
}
// Get transport state and position
@@ -161,20 +162,20 @@ process_cb(jack_nframes_t nframes, void* data)
const jack_transport_state_t state = jack_transport_query(client, &pos);
// Check if transport is discontinuous from last time and update state
- const bool xport_changed = process_transport(jalv, state, pos, nframes);
+ const bool xport_changed = process_transport(proc, state, pos, nframes);
if (xport_changed) {
// Build an LV2 position object to report change to plugin
- lv2_atom_forge_set_buffer(&jalv->forge, (uint8_t*)pos_buf, sizeof(pos_buf));
- forge_position(&jalv->forge, urids, state, pos);
+ lv2_atom_forge_set_buffer(&proc->forge, (uint8_t*)pos_buf, sizeof(pos_buf));
+ forge_position(&proc->forge, urids, state, pos);
}
// Prepare port buffers
- for (uint32_t p = 0; p < jalv->num_ports; ++p) {
- JalvPort* const port = &jalv->ports[p];
+ for (uint32_t p = 0; p < proc->num_ports; ++p) {
+ JalvProcessPort* const port = &proc->ports[p];
if (port->sys_port && (port->type == TYPE_AUDIO || port->type == TYPE_CV)) {
// Connect plugin port directly to Jack port buffer
lilv_instance_connect_port(
- jalv->instance, p, jack_port_get_buffer(port->sys_port, nframes));
+ proc->instance, p, jack_port_get_buffer(port->sys_port, nframes));
} else if (port->type == TYPE_EVENT && port->flow == FLOW_INPUT) {
lv2_evbuf_reset(port->evbuf, true);
LV2_Evbuf_Iterator iter = lv2_evbuf_begin(port->evbuf);
@@ -202,26 +203,26 @@ process_cb(jack_nframes_t nframes, void* data)
}
// Run plugin for this cycle
- const bool send_ui_updates = jalv_run(jalv, nframes);
+ const bool send_ui_updates = jalv_run(proc, nframes);
// Deliver MIDI output and UI events
- for (uint32_t p = 0; p < jalv->num_ports; ++p) {
- JalvPort* const port = &jalv->ports[p];
+ for (uint32_t p = 0; p < proc->num_ports; ++p) {
+ JalvProcessPort* const port = &proc->ports[p];
if (port->flow == FLOW_OUTPUT && port->type == TYPE_CONTROL &&
port->reports_latency) {
// Get the latency in frames from the control output truncated to integer
- const float value = jalv->controls_buf[p];
+ const float value = proc->controls_buf[p];
const uint32_t frames =
(value >= 0.0f && value <= max_latency) ? (uint32_t)value : 0U;
- if (jalv->plugin_latency != frames) {
+ if (proc->plugin_latency != frames) {
// Update the cached value and notify the UI if the latency changed
- jalv->plugin_latency = frames;
+ proc->plugin_latency = frames;
const JalvLatencyChange body = {frames};
const JalvMessageHeader header = {LATENCY_CHANGE, sizeof(body)};
jalv_write_split_message(
- jalv->plugin_to_ui, &header, sizeof(header), &body, sizeof(body));
+ proc->plugin_to_ui, &header, sizeof(header), &body, sizeof(body));
}
} else if (port->flow == FLOW_OUTPUT && port->type == TYPE_EVENT) {
void* buf = NULL;
@@ -246,14 +247,14 @@ process_cb(jack_nframes_t nframes, void* data)
jack_midi_event_write(buf, frames, body, size);
}
- if (jalv->has_ui) {
+ if (proc->has_ui) {
// Forward event to UI
- jalv_write_event(jalv->plugin_to_ui, p, size, type, body);
+ jalv_write_event(proc->plugin_to_ui, p, size, type, body);
}
}
} else if (send_ui_updates && port->flow == FLOW_OUTPUT &&
port->type == TYPE_CONTROL) {
- jalv_write_control(jalv->plugin_to_ui, p, jalv->controls_buf[p]);
+ jalv_write_control(proc->plugin_to_ui, p, proc->controls_buf[p]);
}
}
@@ -266,15 +267,16 @@ latency_cb(const jack_latency_callback_mode_t mode, void* const data)
{
// Calculate latency assuming all ports depend on each other
- const Jalv* const jalv = (const Jalv*)data;
- const PortFlow flow =
+ const Jalv* const jalv = (const Jalv*)data;
+ const JalvProcess* const proc = &jalv->process;
+ const PortFlow flow =
((mode == JackCaptureLatency) ? FLOW_INPUT : FLOW_OUTPUT);
// First calculate the min/max latency of all feeding ports
uint32_t ports_found = 0;
jack_latency_range_t range = {UINT32_MAX, 0};
- for (uint32_t p = 0; p < jalv->num_ports; ++p) {
- JalvPort* const port = &jalv->ports[p];
+ for (uint32_t p = 0; p < proc->num_ports; ++p) {
+ JalvProcessPort* const port = &proc->ports[p];
if (port->sys_port && port->flow == flow) {
jack_latency_range_t r;
jack_port_get_latency_range(port->sys_port, mode, &r);
@@ -293,12 +295,12 @@ latency_cb(const jack_latency_callback_mode_t mode, void* const data)
}
// Add the plugin's own latency
- range.min += jalv->plugin_latency;
- range.max += jalv->plugin_latency;
+ range.min += proc->plugin_latency;
+ range.max += proc->plugin_latency;
// Tell Jack about it
- for (uint32_t p = 0; p < jalv->num_ports; ++p) {
- const JalvPort* const port = &jalv->ports[p];
+ for (uint32_t p = 0; p < proc->num_ports; ++p) {
+ const JalvProcessPort* const port = &proc->ports[p];
if (port->sys_port && port->flow == flow) {
jack_port_set_latency_range(port->sys_port, mode, &range);
}
@@ -408,14 +410,13 @@ jalv_backend_deactivate(Jalv* jalv)
void
jalv_backend_activate_port(Jalv* jalv, uint32_t port_index)
{
- jack_client_t* client = jalv->backend->client;
- JalvPort* const port = &jalv->ports[port_index];
-
- const LilvNode* sym = lilv_port_get_symbol(jalv->plugin, port->lilv_port);
+ jack_client_t* const client = jalv->backend->client;
+ JalvProcess* const proc = &jalv->process;
+ JalvProcessPort* const port = &proc->ports[port_index];
// Connect unsupported ports to NULL (known to be optional by this point)
if (port->flow == FLOW_UNKNOWN || port->type == TYPE_UNKNOWN) {
- lilv_instance_connect_port(jalv->instance, port_index, NULL);
+ lilv_instance_connect_port(proc->instance, port_index, NULL);
return;
}
@@ -429,16 +430,16 @@ jalv_backend_activate_port(Jalv* jalv, uint32_t port_index)
break;
case TYPE_CONTROL:
lilv_instance_connect_port(
- jalv->instance, port_index, &jalv->controls_buf[port_index]);
+ proc->instance, port_index, &proc->controls_buf[port_index]);
break;
case TYPE_AUDIO:
port->sys_port = jack_port_register(
- client, lilv_node_as_string(sym), JACK_DEFAULT_AUDIO_TYPE, jack_flags, 0);
+ client, port->symbol, JACK_DEFAULT_AUDIO_TYPE, jack_flags, 0);
break;
#if USE_JACK_METADATA
case TYPE_CV:
port->sys_port = jack_port_register(
- client, lilv_node_as_string(sym), JACK_DEFAULT_AUDIO_TYPE, jack_flags, 0);
+ client, port->symbol, JACK_DEFAULT_AUDIO_TYPE, jack_flags, 0);
if (port->sys_port) {
jack_set_property(client,
jack_port_uuid(port->sys_port),
@@ -449,13 +450,9 @@ jalv_backend_activate_port(Jalv* jalv, uint32_t port_index)
break;
#endif
case TYPE_EVENT:
- if (lilv_port_supports_event(
- jalv->plugin, port->lilv_port, jalv->nodes.midi_MidiEvent)) {
- port->sys_port = jack_port_register(client,
- lilv_node_as_string(sym),
- JACK_DEFAULT_MIDI_TYPE,
- jack_flags,
- 0);
+ if (port->supports_midi) {
+ port->sys_port = jack_port_register(
+ client, port->symbol, JACK_DEFAULT_MIDI_TYPE, jack_flags, 0);
}
break;
}
@@ -472,13 +469,13 @@ jalv_backend_activate_port(Jalv* jalv, uint32_t port_index)
"http://www.w3.org/2001/XMLSchema#integer");
// Set port pretty name to label
- LilvNode* name = lilv_port_get_name(jalv->plugin, port->lilv_port);
- jack_set_property(client,
- jack_port_uuid(port->sys_port),
- JACK_METADATA_PRETTY_NAME,
- lilv_node_as_string(name),
- "text/plain");
- lilv_node_free(name);
+ if (port->label) {
+ jack_set_property(client,
+ jack_port_uuid(port->sys_port),
+ JACK_METADATA_PRETTY_NAME,
+ port->label,
+ "text/plain");
+ }
}
#endif
}
diff --git a/src/jalv.c b/src/jalv.c
index 5dc45d1..a4c9931 100644
--- a/src/jalv.c
+++ b/src/jalv.c
@@ -1,4 +1,4 @@
-// Copyright 2007-2022 David Robillard <d@drobilla.net>
+// Copyright 2007-2024 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
#include "jalv.h"
@@ -16,8 +16,8 @@
#include "nodes.h"
#include "options.h"
#include "port.h"
+#include "process.h"
#include "process_setup.h"
-#include "query.h"
#include "settings.h"
#include "state.h"
#include "string_utils.h"
@@ -101,36 +101,27 @@ create_port(Jalv* jalv, uint32_t port_index)
JalvPort* const port = &jalv->ports[port_index];
port->lilv_port = lilv_plugin_get_port_by_index(jalv->plugin, port_index);
- port->sys_port = NULL;
- port->evbuf = NULL;
- port->buf_size = 0;
port->index = port_index;
port->flow = FLOW_UNKNOWN;
- const bool optional = lilv_port_has_property(
- jalv->plugin, port->lilv_port, jalv->nodes.lv2_connectionOptional);
-
- // Set the port flow (input or output)
- if (lilv_port_is_a(
- jalv->plugin, port->lilv_port, jalv->nodes.lv2_InputPort)) {
- port->flow = FLOW_INPUT;
- } else if (lilv_port_is_a(
- jalv->plugin, port->lilv_port, jalv->nodes.lv2_OutputPort)) {
- port->flow = FLOW_OUTPUT;
- } else if (!optional) {
- jalv_log(JALV_LOG_ERR, "Mandatory port is neither input nor output\n");
+ JalvProcessPort* const pport = &jalv->process.ports[port_index];
+ if (jalv_process_port_init(&jalv->process.ports[port_index],
+ &jalv->nodes,
+ jalv->plugin,
+ port->lilv_port)) {
return 1;
}
- const bool hidden = !jalv->opts.show_hidden &&
- lilv_port_has_property(jalv->plugin,
- port->lilv_port,
- jalv->nodes.pprops_notOnGUI);
+ port->type = pport->type;
+ port->flow = pport->flow;
- // Set control values
if (lilv_port_is_a(
jalv->plugin, port->lilv_port, jalv->nodes.lv2_ControlPort)) {
- port->type = TYPE_CONTROL;
+ const bool hidden = !jalv->opts.show_hidden &&
+ lilv_port_has_property(jalv->plugin,
+ port->lilv_port,
+ jalv->nodes.pprops_notOnGUI);
+
if (!hidden) {
add_control(&jalv->controls,
new_port_control(jalv->world,
@@ -141,52 +132,19 @@ create_port(Jalv* jalv, uint32_t port_index)
&jalv->nodes,
&jalv->forge));
}
- } else if (lilv_port_is_a(
- jalv->plugin, port->lilv_port, jalv->nodes.lv2_AudioPort)) {
- port->type = TYPE_AUDIO;
-#if USE_JACK_METADATA
- } else if (lilv_port_is_a(
- jalv->plugin, port->lilv_port, jalv->nodes.lv2_CVPort)) {
- port->type = TYPE_CV;
-#endif
- } else if (lilv_port_is_a(
- jalv->plugin, port->lilv_port, jalv->nodes.atom_AtomPort)) {
- port->type = TYPE_EVENT;
- } else if (!optional) {
- jalv_log(JALV_LOG_ERR, "Mandatory port has unknown data type\n");
- return 2;
}
- // Set buffer size
- LilvNode* min_size =
- lilv_port_get(jalv->plugin, port->lilv_port, jalv->nodes.rsz_minimumSize);
- if (min_size && lilv_node_is_int(min_size)) {
- port->buf_size = lilv_node_as_int(min_size);
- jalv->opts.ring_size =
- MAX(jalv->opts.ring_size, port->buf_size * N_BUFFER_CYCLES);
- jalv->msg_buf_size = MAX(jalv->msg_buf_size, port->buf_size);
- }
- lilv_node_free(min_size);
-
- // Set primary flag for designated control port
- if (port->type == TYPE_EVENT &&
- jalv_port_has_designation(
- &jalv->nodes, jalv->plugin, port->lilv_port, jalv->nodes.lv2_control)) {
- port->is_primary = true;
- if (port->flow == FLOW_INPUT && jalv->control_in == UINT32_MAX) {
- jalv->control_in = port->index;
- }
+ // Store index if this is the designated control input port
+ if (jalv->process.control_in == UINT32_MAX && pport->is_primary &&
+ port->flow == FLOW_INPUT && port->type == TYPE_EVENT) {
+ jalv->process.control_in = port_index;
}
- // Set reports_latency flag
- if (port->flow == FLOW_OUTPUT && port->type == TYPE_CONTROL &&
- (lilv_port_has_property(
- jalv->plugin, port->lilv_port, jalv->nodes.lv2_reportsLatency) ||
- jalv_port_has_designation(&jalv->nodes,
- jalv->plugin,
- port->lilv_port,
- jalv->nodes.lv2_latency))) {
- port->reports_latency = true;
+ // Update maximum buffer sizes
+ const uint32_t buf_size = pport->buf_size;
+ jalv->opts.ring_size = MAX(jalv->opts.ring_size, buf_size * N_BUFFER_CYCLES);
+ if (port->flow == FLOW_OUTPUT) {
+ jalv->ui_msg_size = MAX(jalv->ui_msg_size, buf_size);
}
return 0;
@@ -196,13 +154,18 @@ create_port(Jalv* jalv, uint32_t port_index)
static int
jalv_create_ports(Jalv* jalv)
{
- jalv->num_ports = lilv_plugin_get_num_ports(jalv->plugin);
- jalv->ports = (JalvPort*)calloc(jalv->num_ports, sizeof(JalvPort));
+ const uint32_t n_ports = lilv_plugin_get_num_ports(jalv->plugin);
+
+ jalv->num_ports = n_ports;
+ jalv->ports = (JalvPort*)calloc(n_ports, sizeof(JalvPort));
+ jalv->process.num_ports = n_ports;
+ jalv->process.ports =
+ (JalvProcessPort*)calloc(n_ports, sizeof(JalvProcessPort));
// Allocate control port buffers array and set to default values
- jalv->controls_buf = (float*)calloc(jalv->num_ports, sizeof(float));
+ jalv->process.controls_buf = (float*)calloc(n_ports, sizeof(float));
lilv_plugin_get_port_ranges_float(
- jalv->plugin, NULL, NULL, jalv->controls_buf);
+ jalv->plugin, NULL, NULL, jalv->process.controls_buf);
for (uint32_t i = 0; i < jalv->num_ports; ++i) {
if (create_port(jalv, i)) {
@@ -314,8 +277,9 @@ jalv_send_to_plugin(void* const jalv_handle,
const uint32_t protocol,
const void* const buffer)
{
- Jalv* const jalv = (Jalv*)jalv_handle;
- ZixStatus st = ZIX_STATUS_SUCCESS;
+ Jalv* const jalv = (Jalv*)jalv_handle;
+ JalvProcess* const proc = &jalv->process;
+ ZixStatus st = ZIX_STATUS_SUCCESS;
if (port_index >= jalv->num_ports) {
jalv_log(JALV_LOG_ERR, "UI wrote to invalid port index %u\n", port_index);
@@ -325,7 +289,7 @@ jalv_send_to_plugin(void* const jalv_handle,
st = ZIX_STATUS_BAD_ARG;
} else {
const float value = *(const float*)buffer;
- st = jalv_write_control(jalv->ui_to_plugin, port_index, value);
+ st = jalv_write_control(proc->ui_to_plugin, port_index, value);
}
} else if (protocol == jalv->urids.atom_eventTransfer) {
@@ -336,7 +300,7 @@ jalv_send_to_plugin(void* const jalv_handle,
} else {
jalv_dump_atom(jalv->dumper, stdout, "UI => Plugin", atom, 36);
st = jalv_write_event(
- jalv->ui_to_plugin, port_index, atom->size, atom->type, atom + 1U);
+ proc->ui_to_plugin, port_index, atom->size, atom->type, atom + 1U);
}
} else {
@@ -361,23 +325,22 @@ jalv_set_control(Jalv* jalv,
const void* body)
{
if (control->type == PORT && type == jalv->forge.Float) {
- jalv->controls_buf[control->index] = *(const float*)body;
- } else if (control->type == PROPERTY && jalv->control_in != UINT32_MAX) {
- // Copy forge since it is used by process thread
- LV2_Atom_Forge forge = jalv->forge;
+ jalv->process.controls_buf[control->index] = *(const float*)body;
+ } else if (control->type == PROPERTY &&
+ jalv->process.control_in != UINT32_MAX) {
LV2_Atom_Forge_Frame frame;
- lv2_atom_forge_set_buffer(&forge, jalv->ui_msg, jalv->msg_buf_size);
+ lv2_atom_forge_set_buffer(&jalv->forge, jalv->ui_msg, jalv->ui_msg_size);
- lv2_atom_forge_object(&forge, &frame, 0, jalv->urids.patch_Set);
- lv2_atom_forge_key(&forge, jalv->urids.patch_property);
- lv2_atom_forge_urid(&forge, control->property);
- lv2_atom_forge_key(&forge, jalv->urids.patch_value);
- lv2_atom_forge_atom(&forge, size, type);
- lv2_atom_forge_write(&forge, body, size);
+ lv2_atom_forge_object(&jalv->forge, &frame, 0, jalv->urids.patch_Set);
+ lv2_atom_forge_key(&jalv->forge, jalv->urids.patch_property);
+ lv2_atom_forge_urid(&jalv->forge, control->property);
+ lv2_atom_forge_key(&jalv->forge, jalv->urids.patch_value);
+ lv2_atom_forge_atom(&jalv->forge, size, type);
+ lv2_atom_forge_write(&jalv->forge, body, size);
- const LV2_Atom* atom = lv2_atom_forge_deref(&forge, frame.ref);
+ const LV2_Atom* atom = lv2_atom_forge_deref(&jalv->forge, frame.ref);
jalv_send_to_plugin(jalv,
- jalv->control_in,
+ jalv->process.control_in,
lv2_atom_total_size(atom),
jalv->urids.atom_eventTransfer,
atom);
@@ -399,13 +362,15 @@ void
jalv_ui_instantiate(Jalv* jalv, const char* native_ui_type, void* parent)
{
#if USE_SUIL
+ LilvInstance* const instance = jalv->process.instance;
+
jalv->ui_host =
suil_host_new(jalv_send_to_plugin, jalv_ui_port_index, NULL, NULL);
const LV2_Feature parent_feature = {LV2_UI__parent, parent};
- const LV2_Feature instance_feature = {
- LV2_INSTANCE_ACCESS_URI, lilv_instance_get_handle(jalv->instance)};
+ const LV2_Feature instance_feature = {LV2_INSTANCE_ACCESS_URI,
+ lilv_instance_get_handle(instance)};
const LV2_Feature data_feature = {LV2_DATA_ACCESS_URI,
&jalv->features.ext_data};
@@ -455,25 +420,24 @@ jalv_init_ui(Jalv* jalv)
for (uint32_t i = 0; i < jalv->num_ports; ++i) {
if (jalv->ports[i].type == TYPE_CONTROL) {
jalv_frontend_port_event(
- jalv, i, sizeof(float), 0, &jalv->controls_buf[i]);
+ jalv, i, sizeof(float), 0, &jalv->process.controls_buf[i]);
}
}
- if (jalv->control_in != UINT32_MAX) {
+ if (jalv->process.control_in != UINT32_MAX) {
// Send patch:Get message for initial parameters/etc
- LV2_Atom_Forge forge = jalv->forge;
LV2_Atom_Forge_Frame frame;
uint64_t buf[4U] = {0U, 0U, 0U, 0U};
- lv2_atom_forge_set_buffer(&forge, (uint8_t*)buf, sizeof(buf));
- lv2_atom_forge_object(&forge, &frame, 0, jalv->urids.patch_Get);
+ lv2_atom_forge_set_buffer(&jalv->forge, (uint8_t*)buf, sizeof(buf));
+ lv2_atom_forge_object(&jalv->forge, &frame, 0, jalv->urids.patch_Get);
- const LV2_Atom* atom = lv2_atom_forge_deref(&forge, frame.ref);
+ const LV2_Atom* atom = lv2_atom_forge_deref(&jalv->forge, frame.ref);
jalv_send_to_plugin(jalv,
- jalv->control_in,
+ jalv->process.control_in,
lv2_atom_total_size(atom),
jalv->urids.atom_eventTransfer,
atom);
- lv2_atom_forge_pop(&forge, &frame);
+ lv2_atom_forge_pop(&jalv->forge, &frame);
}
}
@@ -494,7 +458,7 @@ jalv_update(Jalv* jalv)
}
// Emit UI events
- ZixRing* const ring = jalv->plugin_to_ui;
+ ZixRing* const ring = jalv->process.plugin_to_ui;
JalvMessageHeader header = {NO_MESSAGE, 0U};
const size_t space = zix_ring_read_space(ring);
for (size_t i = 0; i < space; i += sizeof(header) + header.size) {
@@ -744,14 +708,10 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
LilvWorld* const world = lilv_world_new();
lilv_world_load_all(world);
- jalv->world = world;
- jalv->mapper = jalv_mapper_new();
- jalv->msg_buf_size = 1024U;
- jalv->run_state = JALV_PAUSED;
- jalv->bpm = 120.0f;
- jalv->control_in = UINT32_MAX;
- jalv->log.urids = &jalv->urids;
- jalv->log.tracing = jalv->opts.trace;
+ jalv->world = world;
+ jalv->mapper = jalv_mapper_new();
+ jalv->log.urids = &jalv->urids;
+ jalv->log.tracing = jalv->opts.trace;
// Set up atom dumping for debugging if enabled
LV2_URID_Map* const urid_map = jalv_mapper_urid_map(jalv->mapper);
@@ -762,8 +722,6 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
zix_sem_init(&jalv->work_lock, 1);
zix_sem_init(&jalv->done, 0);
- zix_sem_init(&jalv->paused, 0);
-
jalv_init_urids(jalv->mapper, &jalv->urids);
jalv_init_nodes(world, &jalv->nodes);
jalv_init_features(jalv);
@@ -812,11 +770,11 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
// Create workers if necessary
if (lilv_plugin_has_extension_data(jalv->plugin,
jalv->nodes.work_interface)) {
- jalv->worker = jalv_worker_new(&jalv->work_lock, true);
- jalv->features.sched.handle = jalv->worker;
+ jalv->process.worker = jalv_worker_new(&jalv->work_lock, true);
+ jalv->features.sched.handle = jalv->process.worker;
if (jalv->safe_restore) {
- jalv->state_worker = jalv_worker_new(&jalv->work_lock, false);
- jalv->features.ssched.handle = jalv->state_worker;
+ jalv->process.state_worker = jalv_worker_new(&jalv->work_lock, false);
+ jalv->features.ssched.handle = jalv->process.state_worker;
}
}
@@ -868,6 +826,11 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
}
}
+ // Initialize process thread
+ const uint32_t update_frames =
+ (uint32_t)(settings->sample_rate / settings->ui_update_hz);
+ jalv_process_init(&jalv->process, &jalv->urids, jalv->mapper, update_frames);
+
// Create port structures
if (jalv_create_ports(jalv)) {
return -10;
@@ -888,15 +851,10 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
jalv_log(JALV_LOG_INFO, "MIDI buffers: %zu bytes\n", settings->midi_buf_size);
jalv_init_ui_settings(jalv);
- jalv_init_lv2_options(&jalv->features, &jalv->urids, &jalv->settings);
+ jalv_init_lv2_options(&jalv->features, &jalv->urids, settings);
- // Create Plugin <=> UI communication buffers
- jalv->audio_msg = zix_aligned_alloc(NULL, 8U, jalv->msg_buf_size);
- jalv->ui_msg = zix_aligned_alloc(NULL, 8U, jalv->msg_buf_size);
- jalv->ui_to_plugin = zix_ring_new(NULL, jalv->opts.ring_size);
- jalv->plugin_to_ui = zix_ring_new(NULL, jalv->opts.ring_size);
- zix_ring_mlock(jalv->ui_to_plugin);
- zix_ring_mlock(jalv->plugin_to_ui);
+ // Create Plugin => UI communication buffers
+ jalv->ui_msg = zix_aligned_alloc(NULL, 8U, jalv->ui_msg_size);
// Build feature list for passing to plugins
const LV2_Feature* const features[] = {&jalv->features.map_feature,
@@ -929,28 +887,31 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
lilv_nodes_free(req_feats);
// Instantiate the plugin
- jalv->instance = lilv_plugin_instantiate(
+ LilvInstance* const instance = lilv_plugin_instantiate(
jalv->plugin, settings->sample_rate, jalv->feature_list);
- if (!jalv->instance) {
+ if (!instance) {
jalv_log(JALV_LOG_ERR, "Failed to instantiate plugin\n");
return -9;
}
// Point things to the instance that require it
+ // jalv->process.instance = instance;
jalv->features.ext_data.data_access =
- lilv_instance_get_descriptor(jalv->instance)->extension_data;
+ lilv_instance_get_descriptor(instance)->extension_data;
const LV2_Worker_Interface* worker_iface =
(const LV2_Worker_Interface*)lilv_instance_get_extension_data(
- jalv->instance, LV2_WORKER__interface);
+ instance, LV2_WORKER__interface);
- jalv_worker_attach(jalv->worker, worker_iface, jalv->instance->lv2_handle);
+ jalv_worker_attach(jalv->process.worker, worker_iface, instance->lv2_handle);
jalv_worker_attach(
- jalv->state_worker, worker_iface, jalv->instance->lv2_handle);
-
+ jalv->process.state_worker, worker_iface, instance->lv2_handle);
jalv_log(JALV_LOG_INFO, "\n");
- jalv_allocate_port_buffers(jalv);
+
+ // Allocate port buffers
+ jalv_process_activate(
+ &jalv->process, &jalv->urids, instance, &jalv->settings);
// Apply loaded state to plugin instance if necessary
if (state) {
@@ -971,20 +932,20 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
}
// Discover UI
- jalv->has_ui = jalv_frontend_discover(jalv);
+ jalv->process.has_ui = jalv_frontend_discover(jalv);
return 0;
}
int
jalv_activate(Jalv* const jalv)
{
- jalv->run_state = JALV_RUNNING;
+ jalv->process.run_state = JALV_RUNNING;
if (jalv->backend) {
- if (jalv->worker) {
- jalv_worker_launch(jalv->worker);
+ if (jalv->process.worker) {
+ jalv_worker_launch(jalv->process.worker);
}
- lilv_instance_activate(jalv->instance);
+ lilv_instance_activate(jalv->process.instance);
jalv_backend_activate(jalv);
}
@@ -997,14 +958,14 @@ jalv_deactivate(Jalv* const jalv)
if (jalv->backend) {
jalv_backend_deactivate(jalv);
}
- if (jalv->instance) {
- lilv_instance_deactivate(jalv->instance);
+ if (jalv->process.instance) {
+ lilv_instance_deactivate(jalv->process.instance);
}
- if (jalv->worker) {
- jalv_worker_exit(jalv->worker);
+ if (jalv->process.worker) {
+ jalv_worker_exit(jalv->process.worker);
}
- jalv->run_state = JALV_PAUSED;
+ jalv->process.run_state = JALV_PAUSED;
return 0;
}
@@ -1013,31 +974,25 @@ jalv_close(Jalv* const jalv)
{
// Stop audio processing, free event port buffers, and close backend
jalv_deactivate(jalv);
- jalv_free_port_buffers(jalv);
+ jalv_process_deactivate(&jalv->process);
if (jalv->backend) {
jalv_backend_close(jalv);
}
- // Destroy the worker
- jalv_worker_free(jalv->worker);
- jalv_worker_free(jalv->state_worker);
-
// Free UI and plugin instances
#if USE_SUIL
suil_instance_free(jalv->ui_instance);
#endif
- if (jalv->instance) {
- lilv_instance_free(jalv->instance);
+ if (jalv->process.instance) {
+ lilv_instance_free(jalv->process.instance);
}
// Clean up
lilv_state_free(jalv->preset);
free(jalv->ports);
- zix_ring_free(jalv->ui_to_plugin);
- zix_ring_free(jalv->plugin_to_ui);
+ jalv_process_cleanup(&jalv->process);
zix_free(NULL, jalv->ui_msg);
- zix_free(NULL, jalv->audio_msg);
- free(jalv->controls_buf);
+ free(jalv->process.controls_buf);
jalv_free_nodes(&jalv->nodes);
#if USE_SUIL
suil_host_free(jalv->ui_host);
diff --git a/src/jalv.h b/src/jalv.h
index 07e24e8..518f94d 100644
--- a/src/jalv.h
+++ b/src/jalv.h
@@ -14,10 +14,10 @@
#include "nodes.h"
#include "options.h"
#include "port.h"
+#include "process.h"
#include "settings.h"
#include "types.h"
#include "urids.h"
-#include "worker.h"
#if USE_SUIL
# include <suil/suil.h>
@@ -27,7 +27,6 @@
#include <lv2/atom/forge.h>
#include <lv2/core/lv2.h>
#include <lv2/urid/urid.h>
-#include <zix/ring.h>
#include <zix/sem.h>
#include <stdbool.h>
@@ -39,52 +38,37 @@ JALV_BEGIN_DECLS
/// Internal application state
struct JalvImpl {
- JalvOptions opts; ///< Command-line options
- LilvWorld* world; ///< Lilv World
- JalvMapper* mapper; ///< URI mapper/unmapper
- JalvURIDs urids; ///< URIDs
- JalvNodes nodes; ///< Nodes
- JalvLog log; ///< Log for error/warning/debug messages
- LV2_Atom_Forge forge; ///< Atom forge
- JalvDumper* dumper; ///< Atom dumper (console debug output)
- JalvBackend* backend; ///< Audio system backend
- JalvSettings settings; ///< Processing settings
- ZixRing* ui_to_plugin; ///< Port events from UI
- ZixRing* plugin_to_ui; ///< Port events from plugin
- void* audio_msg; ///< Buffer for messages in the audio thread
- void* ui_msg; ///< Buffer for messages in the UI thread
- JalvWorker* worker; ///< Worker thread implementation
- JalvWorker* state_worker; ///< Synchronous worker for state restore
- ZixSem work_lock; ///< Lock for plugin work() method
- ZixSem done; ///< Exit semaphore
- ZixSem paused; ///< Paused signal from process thread
- JalvRunState run_state; ///< Current process thread run state
- char* temp_dir; ///< Temporary plugin state directory
- char* save_dir; ///< Plugin save directory
- const LilvPlugin* plugin; ///< Plugin class (RDF data)
- LilvState* preset; ///< Current preset
- LilvUIs* uis; ///< All plugin UIs (RDF data)
- const LilvUI* ui; ///< Plugin UI (RDF data)
- const LilvNode* ui_type; ///< Plugin UI type (unwrapped)
- LilvInstance* instance; ///< Plugin instance (shared library)
+ JalvOptions opts; ///< Command-line options
+ LilvWorld* world; ///< Lilv World
+ JalvMapper* mapper; ///< URI mapper/unmapper
+ JalvURIDs urids; ///< URIDs
+ JalvNodes nodes; ///< Nodes
+ JalvLog log; ///< Log for error/warning/debug messages
+ LV2_Atom_Forge forge; ///< Atom forge
+ JalvDumper* dumper; ///< Atom dumper (console debug output)
+ JalvBackend* backend; ///< Audio system backend
+ JalvSettings settings; ///< Processing settings
+ void* ui_msg; ///< Buffer for messages in the UI thread
+ ZixSem work_lock; ///< Lock for plugin work() method
+ ZixSem done; ///< Exit semaphore
+ char* temp_dir; ///< Temporary plugin state directory
+ char* save_dir; ///< Plugin save directory
+ const LilvPlugin* plugin; ///< Plugin class (RDF data)
+ LilvState* preset; ///< Current preset
+ LilvUIs* uis; ///< All plugin UIs (RDF data)
+ const LilvUI* ui; ///< Plugin UI (RDF data)
+ const LilvNode* ui_type; ///< Plugin UI type (unwrapped)
+ JalvProcess process; ///< Process thread state
#if USE_SUIL
SuilHost* ui_host; ///< Plugin UI host support
SuilInstance* ui_instance; ///< Plugin UI instance (shared library)
#endif
- void* window; ///< Window (if applicable)
- JalvPort* ports; ///< Port array of size num_ports
- Controls controls; ///< Available plugin controls
- float* controls_buf; ///< Control port buffers array
- size_t msg_buf_size; ///< Maximum size of a single message
- uint32_t control_in; ///< Index of control input port
- uint32_t num_ports; ///< Total number of ports on the plugin
- uint32_t plugin_latency; ///< Latency reported by plugin (if any)
- uint32_t event_delta_t; ///< Frames since last update sent to UI
- uint32_t position; ///< Transport position in frames
- float bpm; ///< Transport tempo in beats per minute
- bool rolling; ///< Transport speed (0=stop, 1=play)
- bool has_ui; ///< True iff a control UI is present
- bool safe_restore; ///< Plugin restore() is thread-safe
+ void* window; ///< Window (if applicable)
+ JalvPort* ports; ///< Port array of size num_ports
+ Controls controls; ///< Available plugin controls
+ size_t ui_msg_size; ///< Maximum size of a single message
+ uint32_t num_ports; ///< Total number of ports on the plugin
+ bool safe_restore; ///< Plugin restore() is thread-safe
JalvFeatures features;
const LV2_Feature** feature_list;
};
diff --git a/src/jalv_console.c b/src/jalv_console.c
index 0ea85b1..664d175 100644
--- a/src/jalv_console.c
+++ b/src/jalv_console.c
@@ -198,7 +198,7 @@ print_controls(const Jalv* const jalv, const bool writable, const bool readable)
jalv_log(JALV_LOG_INFO,
"%s = %f\n",
lilv_node_as_string(control->symbol),
- jalv->controls_buf[control->index]);
+ jalv->process.controls_buf[control->index]);
}
}
@@ -247,7 +247,7 @@ jalv_process_command(Jalv* jalv, const char* cmd)
print_controls(jalv, false, true);
} else if (sscanf(cmd, "set %u %f", &index, &value) == 2) {
if (index < jalv->num_ports) {
- jalv->controls_buf[index] = value;
+ jalv->process.controls_buf[index] = value;
print_control_port(jalv, &jalv->ports[index], value);
} else {
fprintf(stderr, "error: port index out of range\n");
@@ -256,7 +256,7 @@ jalv_process_command(Jalv* jalv, const char* cmd)
sscanf(cmd, "%1023[a-zA-Z0-9_] = %f", sym, &value) == 2) {
JalvPort* const port = jalv_port_by_symbol(jalv, sym);
if (port) {
- jalv->controls_buf[port->index] = value;
+ jalv->process.controls_buf[port->index] = value;
print_control_port(jalv, port, value);
} else {
fprintf(stderr, "error: no control named `%s'\n", sym);
@@ -341,7 +341,8 @@ jalv_frontend_open(Jalv* jalv)
ControlID* control = jalv->controls.controls[i];
if (control->type == PORT && control->is_writable) {
const JalvPort* const port = &jalv->ports[control->index];
- print_control_port(jalv, port, jalv->controls_buf[control->index]);
+ print_control_port(
+ jalv, port, jalv->process.controls_buf[control->index]);
}
}
diff --git a/src/jalv_qt.cpp b/src/jalv_qt.cpp
index 8fd5495..9b3b145 100644
--- a/src/jalv_qt.cpp
+++ b/src/jalv_qt.cpp
@@ -365,9 +365,9 @@ Control::Control(PortContainer portContainer, QWidget* parent)
}
// Find and set min, max and default values for port
- const float defaultValue = ndef
- ? lilv_node_as_float(ndef)
- : portContainer.jalv->controls_buf[_port->index];
+ const float defaultValue =
+ ndef ? lilv_node_as_float(ndef)
+ : portContainer.jalv->process.controls_buf[_port->index];
setRange(lilv_node_as_float(nmin), lilv_node_as_float(nmax));
setValue(defaultValue);
@@ -499,7 +499,7 @@ Control::dialChanged(int)
const float value = getValue();
_label->setText(getValueLabel(value));
- _jalv->controls_buf[_port->index] = value;
+ _jalv->process.controls_buf[_port->index] = value;
}
namespace {
diff --git a/src/port.h b/src/port.h
index df6e1f4..8a4644c 100644
--- a/src/port.h
+++ b/src/port.h
@@ -17,16 +17,11 @@
JALV_BEGIN_DECLS
typedef struct {
- const LilvPort* lilv_port; ///< LV2 port
- PortType type; ///< Data type
- PortFlow flow; ///< Data flow direction
- void* sys_port; ///< For audio/MIDI ports, otherwise NULL
- LV2_Evbuf* evbuf; ///< For MIDI ports, otherwise NULL
- void* widget; ///< Control widget, if applicable
- size_t buf_size; ///< Custom buffer size, or 0
- uint32_t index; ///< Port index
- bool reports_latency; ///< For control port outputs
- bool is_primary; ///< True for main control/reponse channel
+ const LilvPort* lilv_port; ///< LV2 port
+ PortType type; ///< Data type
+ PortFlow flow; ///< Data flow direction
+ void* widget; ///< Control widget, if applicable
+ uint32_t index; ///< Port index
} JalvPort;
JALV_END_DECLS
diff --git a/src/portaudio.c b/src/portaudio.c
index c61fb37..d39b7e8 100644
--- a/src/portaudio.c
+++ b/src/portaudio.c
@@ -6,7 +6,6 @@
#include "jalv.h"
#include "log.h"
#include "lv2_evbuf.h"
-#include "port.h"
#include "process.h"
#include "types.h"
@@ -25,15 +24,15 @@ struct JalvBackendImpl {
};
static int
-process_silent(Jalv* const jalv,
+process_silent(JalvProcess* const proc,
void* const outputs,
const unsigned long nframes)
{
- for (uint32_t i = 0; i < jalv->num_ports; ++i) {
+ for (uint32_t i = 0; i < proc->num_ports; ++i) {
memset(((float**)outputs)[i], '\0', nframes * sizeof(float));
}
- return jalv_bypass(jalv, nframes);
+ return jalv_bypass(proc, nframes);
}
static int
@@ -47,25 +46,26 @@ process_cb(const void* inputs,
(void)time;
(void)flags;
- Jalv* jalv = (Jalv*)handle;
+ Jalv* const jalv = (Jalv*)handle;
+ JalvProcess* const proc = &jalv->process;
// If execution is paused, emit silence and return
- if (jalv->run_state == JALV_PAUSED) {
- return process_silent(jalv, outputs, nframes);
+ if (proc->run_state == JALV_PAUSED) {
+ return process_silent(proc, outputs, nframes);
}
// Prepare port buffers
uint32_t in_index = 0;
uint32_t out_index = 0;
- for (uint32_t i = 0; i < jalv->num_ports; ++i) {
- JalvPort* const port = &jalv->ports[i];
+ for (uint32_t i = 0; i < proc->num_ports; ++i) {
+ JalvProcessPort* const port = &proc->ports[i];
if (port->type == TYPE_AUDIO) {
if (port->flow == FLOW_INPUT) {
lilv_instance_connect_port(
- jalv->instance, i, ((float**)inputs)[in_index++]);
+ proc->instance, i, ((float**)inputs)[in_index++]);
} else if (port->flow == FLOW_OUTPUT) {
lilv_instance_connect_port(
- jalv->instance, i, ((float**)outputs)[out_index++]);
+ proc->instance, i, ((float**)outputs)[out_index++]);
}
} else if (port->type == TYPE_EVENT) {
lv2_evbuf_reset(port->evbuf, port->flow == FLOW_INPUT);
@@ -73,11 +73,11 @@ process_cb(const void* inputs,
}
// Run plugin for this cycle
- const bool send_ui_updates = jalv_run(jalv, nframes);
+ const bool send_ui_updates = jalv_run(proc, nframes);
// Deliver UI events
- for (uint32_t p = 0; p < jalv->num_ports; ++p) {
- JalvPort* const port = &jalv->ports[p];
+ for (uint32_t p = 0; p < proc->num_ports; ++p) {
+ JalvProcessPort* const port = &proc->ports[p];
if (port->flow == FLOW_OUTPUT && port->type == TYPE_EVENT) {
for (LV2_Evbuf_Iterator i = lv2_evbuf_begin(port->evbuf);
lv2_evbuf_is_valid(i);
@@ -90,14 +90,14 @@ process_cb(const void* inputs,
void* body = NULL;
lv2_evbuf_get(i, &frames, &subframes, &type, &size, &body);
- if (jalv->has_ui) {
+ if (proc->has_ui) {
// Forward event to UI
- jalv_write_event(jalv->plugin_to_ui, p, size, type, body);
+ jalv_write_event(proc->plugin_to_ui, p, size, type, body);
}
}
} else if (send_ui_updates && port->flow == FLOW_OUTPUT &&
port->type == TYPE_CONTROL) {
- jalv_write_control(jalv->plugin_to_ui, p, jalv->controls_buf[p]);
+ jalv_write_control(proc->plugin_to_ui, p, proc->controls_buf[p]);
}
}
@@ -230,11 +230,12 @@ jalv_backend_deactivate(Jalv* jalv)
void
jalv_backend_activate_port(Jalv* jalv, uint32_t port_index)
{
- JalvPort* const port = &jalv->ports[port_index];
+ JalvProcess* const proc = &jalv->process;
+ JalvProcessPort* const port = &proc->ports[port_index];
if (port->type == TYPE_CONTROL) {
lilv_instance_connect_port(
- jalv->instance, port_index, &jalv->controls_buf[port_index]);
+ proc->instance, port_index, &proc->controls_buf[port_index]);
}
}
diff --git a/src/process.c b/src/process.c
index 3348877..efb2602 100644
--- a/src/process.c
+++ b/src/process.c
@@ -4,10 +4,8 @@
#include "process.h"
#include "comm.h"
-#include "jalv.h"
#include "log.h"
#include "lv2_evbuf.h"
-#include "port.h"
#include "types.h"
#include "worker.h"
@@ -28,13 +26,9 @@ ring_error(const char* const message)
}
static int
-apply_ui_events(Jalv* const jalv, const uint32_t nframes)
+apply_ui_events(JalvProcess* const proc, const uint32_t nframes)
{
- if (!jalv->has_ui) {
- return 0;
- }
-
- ZixRing* const ring = jalv->ui_to_plugin;
+ ZixRing* const ring = proc->ui_to_plugin;
JalvMessageHeader header = {NO_MESSAGE, 0U};
const size_t space = zix_ring_read_space(ring);
for (size_t i = 0; i < space; i += sizeof(header) + header.size) {
@@ -50,38 +44,37 @@ apply_ui_events(Jalv* const jalv, const uint32_t nframes)
return ring_error("Failed to read control value from UI ring\n");
}
- assert(msg.port_index < jalv->num_ports);
- jalv->controls_buf[msg.port_index] = msg.value;
+ assert(msg.port_index < proc->num_ports);
+ proc->controls_buf[msg.port_index] = msg.value;
} else if (header.type == EVENT_TRANSFER) {
- assert(header.size <= jalv->msg_buf_size);
- void* const body = jalv->audio_msg;
+ assert(header.size <= proc->process_msg_size);
+ void* const body = proc->process_msg;
if (zix_ring_read(ring, body, header.size) != header.size) {
return ring_error("Failed to read event from UI ring\n");
}
const JalvEventTransfer* const msg = (const JalvEventTransfer*)body;
- assert(msg->port_index < jalv->num_ports);
- JalvPort* const port = &jalv->ports[msg->port_index];
- LV2_Evbuf_Iterator e = lv2_evbuf_end(port->evbuf);
- const LV2_Atom* const atom = &msg->atom;
+ assert(msg->port_index < proc->num_ports);
+ JalvProcessPort* const port = &proc->ports[msg->port_index];
+ LV2_Evbuf_Iterator e = lv2_evbuf_end(port->evbuf);
+ const LV2_Atom* const atom = &msg->atom;
lv2_evbuf_write(
&e, nframes, 0U, atom->type, atom->size, LV2_ATOM_BODY_CONST(atom));
} else if (header.type == STATE_REQUEST) {
- JalvPort* const port = &jalv->ports[jalv->control_in];
+ JalvProcessPort* const port = &proc->ports[proc->control_in];
assert(port->type == TYPE_EVENT);
assert(port->flow == FLOW_INPUT);
assert(port->evbuf);
- LV2_Evbuf_Iterator iter = lv2_evbuf_end(port->evbuf);
- const LV2_Atom_Object get = {
- {sizeof(LV2_Atom_Object_Body), jalv->urids.atom_Object},
- {0U, jalv->urids.patch_Get},
- };
-
- lv2_evbuf_write(
- &iter, nframes, 0U, get.atom.type, get.atom.size, &get.body);
+ LV2_Evbuf_Iterator e = lv2_evbuf_end(port->evbuf);
+ lv2_evbuf_write(&e,
+ nframes,
+ 0U,
+ proc->get_msg.atom.type,
+ proc->get_msg.atom.size,
+ &proc->get_msg.body);
} else if (header.type == RUN_STATE_CHANGE) {
assert(header.size == sizeof(JalvRunStateChange));
@@ -90,9 +83,9 @@ apply_ui_events(Jalv* const jalv, const uint32_t nframes)
return ring_error("Failed to read run state change from UI ring\n");
}
- jalv->run_state = msg.state;
+ proc->run_state = msg.state;
if (msg.state == JALV_PAUSED) {
- zix_sem_post(&jalv->paused);
+ zix_sem_post(&proc->paused);
}
} else {
@@ -104,37 +97,34 @@ apply_ui_events(Jalv* const jalv, const uint32_t nframes)
}
bool
-jalv_run(Jalv* const jalv, const uint32_t nframes)
+jalv_run(JalvProcess* const proc, const uint32_t nframes)
{
// Read and apply control change events from UI
- apply_ui_events(jalv, nframes);
+ apply_ui_events(proc, nframes);
// Run plugin for this cycle
- lilv_instance_run(jalv->instance, nframes);
+ lilv_instance_run(proc->instance, nframes);
// Process any worker replies and end the cycle
- LV2_Handle handle = lilv_instance_get_handle(jalv->instance);
- jalv_worker_emit_responses(jalv->state_worker, handle);
- jalv_worker_emit_responses(jalv->worker, handle);
- jalv_worker_end_run(jalv->worker);
+ LV2_Handle handle = lilv_instance_get_handle(proc->instance);
+ jalv_worker_emit_responses(proc->state_worker, handle);
+ jalv_worker_emit_responses(proc->worker, handle);
+ jalv_worker_end_run(proc->worker);
// Check if it's time to send updates to the UI
- jalv->event_delta_t += nframes;
- bool send_ui_updates = false;
- const uint32_t update_frames =
- (uint32_t)(jalv->settings.sample_rate / jalv->settings.ui_update_hz);
- if (jalv->has_ui && (jalv->event_delta_t > update_frames)) {
- send_ui_updates = true;
- jalv->event_delta_t = 0U;
+ proc->pending_frames += nframes;
+ if (proc->update_frames && proc->pending_frames > proc->update_frames) {
+ proc->pending_frames = 0U;
+ return true;
}
- return send_ui_updates;
+ return false;
}
int
-jalv_bypass(Jalv* const jalv, const uint32_t nframes)
+jalv_bypass(JalvProcess* const proc, const uint32_t nframes)
{
// Read and apply control change events from UI
- apply_ui_events(jalv, nframes);
+ apply_ui_events(proc, nframes);
return 0;
}
diff --git a/src/process.h b/src/process.h
index 3dba625..c5d049b 100644
--- a/src/process.h
+++ b/src/process.h
@@ -5,26 +5,81 @@
#define JALV_PROCESS_H
#include "attributes.h"
+#include "lv2_evbuf.h"
#include "types.h"
+#include "worker.h"
+
+#include <lilv/lilv.h>
+#include <lv2/atom/atom.h>
+#include <lv2/atom/forge.h>
+#include <zix/ring.h>
+#include <zix/sem.h>
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
// Code and data used in the realtime process thread
JALV_BEGIN_DECLS
+/// Port state used in the process thread
+typedef struct {
+ PortType type; ///< Data type
+ PortFlow flow; ///< Data flow direction
+ void* sys_port; ///< For audio/MIDI ports, otherwise NULL
+ char* symbol; ///< Port symbol (stable/unique C-like identifier)
+ char* label; ///< Human-readable label
+ LV2_Evbuf* evbuf; ///< Sequence port event buffer
+ uint32_t buf_size; ///< Custom buffer size, or 0
+ bool reports_latency; ///< Whether control port reports latency
+ bool is_primary; ///< True for main control/reponse channel
+ bool supports_midi; ///< Whether event port supports MIDI
+} JalvProcessPort;
+
+/**
+ State accessed in the process thread.
+
+ Everything accessed by the process thread is stored here, to keep it
+ somewhat insulated from the UI and to make references to it stand out in the
+ code.
+*/
+typedef struct {
+ LilvInstance* instance; ///< Plugin instance
+ ZixRing* ui_to_plugin; ///< Messages from UI to plugin/process
+ ZixRing* plugin_to_ui; ///< Messages from plugin/process to UI
+ JalvWorker* worker; ///< Worker thread implementation
+ JalvWorker* state_worker; ///< Synchronous worker for state restore
+ JalvProcessPort* ports; ///< Port array of size num_ports
+ LV2_Atom_Forge forge; ///< Atom forge
+ LV2_Atom_Object get_msg; ///< General patch:Get message
+ float* controls_buf; ///< Control port buffers array
+ size_t process_msg_size; ///< Maximum size of a single message
+ void* process_msg; ///< Buffer for receiving messages
+ ZixSem paused; ///< Paused signal from process thread
+ JalvRunState run_state; ///< Current run state
+ uint32_t control_in; ///< Index of control input port
+ uint32_t num_ports; ///< Total number of ports on the plugin
+ uint32_t pending_frames; ///< Frames since last UI update sent
+ uint32_t update_frames; ///< UI update period in frames, or zero
+ uint32_t plugin_latency; ///< Latency reported by plugin (if any)
+ uint32_t position; ///< Transport position in frames
+ float bpm; ///< Transport tempo in beats per minute
+ bool rolling; ///< Transport speed (0=stop, 1=play)
+ bool has_ui; ///< True iff a control UI is present
+} JalvProcess;
+
/**
Run the plugin for a block of frames.
Applies any pending messages from the UI, runs the plugin instance, and
processes any worker replies.
- @param jalv Application state.
+ @param proc Process thread state.
@param nframes Number of frames to process.
@return Whether output value updates should be sent to the UI now.
*/
bool
-jalv_run(Jalv* jalv, uint32_t nframes);
+jalv_run(JalvProcess* proc, uint32_t nframes);
/**
Bypass the plugin for a block of frames.
@@ -32,12 +87,12 @@ jalv_run(Jalv* jalv, uint32_t nframes);
This is like jalv_run(), but doesn't actually run the plugin and only does
the minimum necessary internal work for the cycle.
- @param jalv Application state.
+ @param proc Process thread state.
@param nframes Number of frames to bypass.
@return Zero.
*/
int
-jalv_bypass(Jalv* jalv, uint32_t nframes);
+jalv_bypass(JalvProcess* proc, uint32_t nframes);
JALV_END_DECLS
diff --git a/src/process_setup.c b/src/process_setup.c
index 3f64d02..0e03932 100644
--- a/src/process_setup.c
+++ b/src/process_setup.c
@@ -3,27 +3,94 @@
#include "process_setup.h"
-#include "jalv.h"
+#include "jalv_config.h"
+#include "log.h"
#include "lv2_evbuf.h"
-#include "port.h"
+#include "macros.h"
+#include "mapper.h"
+#include "nodes.h"
+#include "process.h"
+#include "query.h"
+#include "settings.h"
+#include "string_utils.h"
#include "types.h"
#include "urids.h"
+#include "worker.h"
#include <lilv/lilv.h>
+#include <lv2/atom/atom.h>
+#include <lv2/atom/forge.h>
+#include <zix/allocator.h>
+#include <zix/ring.h>
+#include <zix/sem.h>
-#include <stddef.h>
+#include <stdbool.h>
#include <stdint.h>
+#include <stdlib.h>
+
+int
+jalv_process_init(JalvProcess* const proc,
+ const JalvURIDs* const urids,
+ JalvMapper* const mapper,
+ const uint32_t update_frames)
+{
+ proc->get_msg.atom.size = sizeof(LV2_Atom_Object_Body);
+ proc->get_msg.atom.type = urids->atom_Object;
+ proc->get_msg.body.id = 0U;
+ proc->get_msg.body.otype = urids->patch_Get;
+
+ proc->instance = NULL;
+ proc->ui_to_plugin = NULL;
+ proc->plugin_to_ui = NULL;
+ proc->worker = NULL;
+ proc->state_worker = NULL;
+ proc->ports = NULL;
+ proc->process_msg_size = 1024U;
+ proc->process_msg = NULL;
+ proc->run_state = JALV_PAUSED;
+ proc->control_in = UINT32_MAX;
+ proc->num_ports = 0U;
+ proc->pending_frames = 0U;
+ proc->update_frames = update_frames;
+ proc->position = 0U;
+ proc->bpm = 120.0f;
+ proc->rolling = false;
+ proc->has_ui = false;
+
+ zix_sem_init(&proc->paused, 0);
+ lv2_atom_forge_init(&proc->forge, jalv_mapper_urid_map(mapper));
+
+ return 0;
+}
void
-jalv_allocate_port_buffers(Jalv* const jalv)
+jalv_process_cleanup(JalvProcess* const proc)
{
- const JalvURIDs* const urids = &jalv->urids;
+ zix_sem_destroy(&proc->paused);
+ jalv_worker_free(proc->worker);
+ jalv_worker_free(proc->state_worker);
+ zix_ring_free(proc->ui_to_plugin);
+ zix_ring_free(proc->plugin_to_ui);
+ zix_free(NULL, proc->process_msg);
- for (uint32_t i = 0; i < jalv->num_ports; ++i) {
- JalvPort* const port = &jalv->ports[i];
+ for (uint32_t i = 0U; i < proc->num_ports; ++i) {
+ jalv_process_port_cleanup(&proc->ports[i]);
+ }
+}
+
+void
+jalv_process_activate(JalvProcess* const proc,
+ const JalvURIDs* const urids,
+ LilvInstance* const instance,
+ const JalvSettings* const settings)
+{
+ proc->instance = instance;
+
+ for (uint32_t i = 0U; i < proc->num_ports; ++i) {
+ JalvProcessPort* const port = &proc->ports[i];
if (port->type == TYPE_EVENT) {
const size_t size =
- port->buf_size ? port->buf_size : jalv->settings.midi_buf_size;
+ port->buf_size ? port->buf_size : settings->midi_buf_size;
lv2_evbuf_free(port->evbuf);
port->evbuf =
@@ -31,16 +98,121 @@ jalv_allocate_port_buffers(Jalv* const jalv)
lv2_evbuf_reset(port->evbuf, port->flow == FLOW_INPUT);
lilv_instance_connect_port(
- jalv->instance, i, lv2_evbuf_get_buffer(port->evbuf));
+ proc->instance, i, lv2_evbuf_get_buffer(port->evbuf));
+
+ if (port->flow == FLOW_INPUT) {
+ proc->process_msg_size = MAX(proc->process_msg_size, port->buf_size);
+ }
}
}
+
+ // Allocate UI<=>process communication rings and process receive buffer
+ proc->ui_to_plugin = zix_ring_new(NULL, settings->ring_size);
+ proc->plugin_to_ui = zix_ring_new(NULL, settings->ring_size);
+ proc->process_msg = zix_aligned_alloc(NULL, 8U, proc->process_msg_size);
+ zix_ring_mlock(proc->ui_to_plugin);
+ zix_ring_mlock(proc->plugin_to_ui);
+ zix_ring_mlock(proc->process_msg);
+}
+
+void
+jalv_process_deactivate(JalvProcess* const proc)
+{
+ zix_free(NULL, proc->process_msg);
+ proc->process_msg = NULL;
+
+ for (uint32_t i = 0U; i < proc->num_ports; ++i) {
+ lv2_evbuf_free(proc->ports[i].evbuf);
+ lilv_instance_connect_port(proc->instance, i, NULL);
+ proc->ports[i].evbuf = NULL;
+ }
+}
+
+int
+jalv_process_port_init(JalvProcessPort* const port,
+ const JalvNodes* const nodes,
+ const LilvPlugin* const lilv_plugin,
+ const LilvPort* const lilv_port)
+{
+ const LilvNode* const symbol = lilv_port_get_symbol(lilv_plugin, lilv_port);
+
+ port->type = TYPE_UNKNOWN;
+ port->flow = FLOW_UNKNOWN;
+ port->sys_port = NULL;
+ port->evbuf = NULL;
+ port->buf_size = 0U;
+ port->reports_latency = false;
+
+ const bool optional = lilv_port_has_property(
+ lilv_plugin, lilv_port, nodes->lv2_connectionOptional);
+
+ // Set port flow (input or output)
+ if (lilv_port_is_a(lilv_plugin, lilv_port, nodes->lv2_InputPort)) {
+ port->flow = FLOW_INPUT;
+ } else if (lilv_port_is_a(lilv_plugin, lilv_port, nodes->lv2_OutputPort)) {
+ port->flow = FLOW_OUTPUT;
+ } else if (!optional) {
+ jalv_log(JALV_LOG_ERR,
+ "Mandatory port \"%s\" is neither input nor output\n",
+ lilv_node_as_string(symbol));
+ return 1;
+ }
+
+ // Set port type
+ if (lilv_port_is_a(lilv_plugin, lilv_port, nodes->lv2_ControlPort)) {
+ port->type = TYPE_CONTROL;
+ } else if (lilv_port_is_a(lilv_plugin, lilv_port, nodes->lv2_AudioPort)) {
+ port->type = TYPE_AUDIO;
+#if USE_JACK_METADATA
+ } else if (lilv_port_is_a(lilv_plugin, lilv_port, nodes->lv2_CVPort)) {
+ port->type = TYPE_CV;
+#endif
+ } else if (lilv_port_is_a(lilv_plugin, lilv_port, nodes->atom_AtomPort)) {
+ port->type = TYPE_EVENT;
+ } else if (!optional) {
+ jalv_log(JALV_LOG_ERR,
+ "Mandatory port \"%s\" has unknown data type\n",
+ lilv_node_as_string(symbol));
+ return 1;
+ }
+
+ // Set symbol and label
+ LilvNode* const name = lilv_port_get_name(lilv_plugin, lilv_port);
+ port->symbol = symbol ? jalv_strdup(lilv_node_as_string(symbol)) : NULL;
+ port->label = name ? jalv_strdup(lilv_node_as_string(name)) : NULL;
+
+ // Set buffer size
+ LilvNode* const min_size =
+ lilv_port_get(lilv_plugin, lilv_port, nodes->rsz_minimumSize);
+ if (min_size && lilv_node_is_int(min_size)) {
+ port->buf_size = (uint32_t)MAX(lilv_node_as_int(min_size), 0);
+ }
+ lilv_node_free(min_size);
+
+ // Set reports_latency flag
+ if (port->flow == FLOW_OUTPUT && port->type == TYPE_CONTROL &&
+ (lilv_port_has_property(
+ lilv_plugin, lilv_port, nodes->lv2_reportsLatency) ||
+ jalv_port_has_designation(
+ nodes, lilv_plugin, lilv_port, nodes->lv2_latency))) {
+ port->reports_latency = true;
+ }
+
+ // Set supports_midi flag
+ port->supports_midi =
+ lilv_port_supports_event(lilv_plugin, lilv_port, nodes->midi_MidiEvent);
+
+ return 0;
}
void
-jalv_free_port_buffers(Jalv* const jalv)
+jalv_process_port_cleanup(JalvProcessPort* const port)
{
- for (uint32_t i = 0; i < jalv->num_ports; ++i) {
- lv2_evbuf_free(jalv->ports[i].evbuf);
- lilv_instance_connect_port(jalv->instance, i, NULL);
+ if (port) {
+ if (port->evbuf) {
+ lv2_evbuf_free(port->evbuf);
+ }
+ free(port->label);
+ free(port->symbol);
}
}
diff --git a/src/process_setup.h b/src/process_setup.h
index 07a76c2..8c99312 100644
--- a/src/process_setup.h
+++ b/src/process_setup.h
@@ -5,18 +5,78 @@
#define JALV_PROCESS_SETUP_H
#include "attributes.h"
-#include "types.h"
+#include "mapper.h"
+#include "nodes.h"
+#include "process.h"
+#include "settings.h"
+#include "urids.h"
+
+#include <lilv/lilv.h>
+
+#include <stdint.h>
// Code for setting up the realtime process thread (but that isn't used in it)
JALV_BEGIN_DECLS
-/// Allocate appropriately-sized port buffers and connect the plugin to them
+/**
+ Initialize process thread and allocate necessary structures.
+
+ This only initializes the state structure, it doesn't create any threads or
+ start plugin execution.
+*/
+int
+jalv_process_init(JalvProcess* proc,
+ const JalvURIDs* urids,
+ JalvMapper* mapper,
+ uint32_t update_frames);
+
+/**
+ Clean up process thread.
+
+ This frees everything allocated by jalv_process_init() and
+ jalv_process_activate().
+*/
void
-jalv_allocate_port_buffers(Jalv* jalv);
+jalv_process_cleanup(JalvProcess* proc);
+
+/**
+ Allocate necessary buffers, connect the plugin to them, and prepare to run.
+
+ @param proc Process thread state.
+ @param urids Application vocabulary.
+ @param instance Plugin instance to run.
+ @param settings Process thread settings.
+*/
+void
+jalv_process_activate(JalvProcess* proc,
+ const JalvURIDs* urids,
+ LilvInstance* instance,
+ const JalvSettings* settings);
+
+/**
+ Clean up after jalv_process_activate() and disconnect plugin.
+
+ @param proc Process thread state.
+*/
+void
+jalv_process_deactivate(JalvProcess* proc);
+
+/**
+ Initialize the process thread state for a port.
+
+ @return Zero on success.
+*/
+int
+jalv_process_port_init(JalvProcessPort* port,
+ const JalvNodes* nodes,
+ const LilvPlugin* lilv_plugin,
+ const LilvPort* lilv_port);
-/// Clean up memory allocated by jalv_process_activate() and disconnect plugin
+/**
+ Free memory allocated by jalv_setup_init_port().
+*/
void
-jalv_free_port_buffers(Jalv* jalv);
+jalv_process_port_cleanup(JalvProcessPort* port);
JALV_END_DECLS
diff --git a/src/state.c b/src/state.c
index b0ba53b..85b85bd 100644
--- a/src/state.c
+++ b/src/state.c
@@ -8,6 +8,7 @@
#include "log.h"
#include "mapper.h"
#include "port.h"
+#include "process.h"
#include "string_utils.h"
#include "types.h"
@@ -45,7 +46,7 @@ get_port_value(const char* port_symbol,
if (port && port->flow == FLOW_INPUT && port->type == TYPE_CONTROL) {
*size = sizeof(float);
*type = jalv->forge.Float;
- return &jalv->controls_buf[port->index];
+ return &jalv->process.controls_buf[port->index];
}
*size = *type = 0;
return NULL;
@@ -61,7 +62,7 @@ jalv_save(Jalv* jalv, const char* dir)
LilvState* const state =
lilv_state_new_from_instance(jalv->plugin,
- jalv->instance,
+ jalv->process.instance,
map,
jalv->temp_dir,
dir,
@@ -128,8 +129,9 @@ set_port_value(const char* port_symbol,
uint32_t ZIX_UNUSED(size),
uint32_t type)
{
- Jalv* const jalv = (Jalv*)user_data;
- JalvPort* const port = jalv_port_by_symbol(jalv, port_symbol);
+ Jalv* const jalv = (Jalv*)user_data;
+ JalvProcess* const proc = &jalv->process;
+ JalvPort* const port = jalv_port_by_symbol(jalv, port_symbol);
if (!port) {
jalv_log(JALV_LOG_ERR, "Preset port `%s' is missing\n", port_symbol);
return;
@@ -153,17 +155,17 @@ set_port_value(const char* port_symbol,
}
ZixStatus st = ZIX_STATUS_SUCCESS;
- if (jalv->run_state != JALV_RUNNING) {
+ if (proc->run_state != JALV_RUNNING) {
// Set value on port struct directly
- jalv->controls_buf[port->index] = fvalue;
+ proc->controls_buf[port->index] = fvalue;
} else {
// Send value to plugin (as if from UI)
- st = jalv_write_control(jalv->ui_to_plugin, port->index, fvalue);
+ st = jalv_write_control(proc->ui_to_plugin, port->index, fvalue);
}
- if (jalv->has_ui) {
+ if (proc->has_ui) {
// Update UI (as if from plugin)
- st = jalv_write_control(jalv->plugin_to_ui, port->index, fvalue);
+ st = jalv_write_control(proc->plugin_to_ui, port->index, fvalue);
}
if (st) {
@@ -175,19 +177,21 @@ set_port_value(const char* port_symbol,
void
jalv_apply_state(Jalv* jalv, const LilvState* state)
{
+ JalvProcess* const proc = &jalv->process;
+
typedef struct {
JalvMessageHeader head;
JalvRunStateChange body;
} PauseMessage;
const bool must_pause =
- !jalv->safe_restore && jalv->run_state == JALV_RUNNING;
+ !jalv->safe_restore && proc->run_state == JALV_RUNNING;
if (must_pause) {
const PauseMessage pause_msg = {
{RUN_STATE_CHANGE, sizeof(JalvRunStateChange)}, {JALV_PAUSED}};
- zix_ring_write(jalv->ui_to_plugin, &pause_msg, sizeof(pause_msg));
+ zix_ring_write(proc->ui_to_plugin, &pause_msg, sizeof(pause_msg));
- zix_sem_wait(&jalv->paused);
+ zix_sem_wait(&proc->paused);
}
const LV2_Feature* state_features[9] = {
@@ -202,15 +206,15 @@ jalv_apply_state(Jalv* jalv, const LilvState* state)
};
lilv_state_restore(
- state, jalv->instance, set_port_value, jalv, 0, state_features);
+ state, proc->instance, set_port_value, jalv, 0, state_features);
if (must_pause) {
const JalvMessageHeader state_msg = {STATE_REQUEST, 0U};
- zix_ring_write(jalv->ui_to_plugin, &state_msg, sizeof(state_msg));
+ zix_ring_write(proc->ui_to_plugin, &state_msg, sizeof(state_msg));
const PauseMessage run_msg = {
{RUN_STATE_CHANGE, sizeof(JalvRunStateChange)}, {JALV_RUNNING}};
- zix_ring_write(jalv->ui_to_plugin, &run_msg, sizeof(run_msg));
+ zix_ring_write(proc->ui_to_plugin, &run_msg, sizeof(run_msg));
}
}
@@ -238,7 +242,7 @@ jalv_save_preset(Jalv* jalv,
LilvState* const state =
lilv_state_new_from_instance(jalv->plugin,
- jalv->instance,
+ jalv->process.instance,
map,
jalv->temp_dir,
dir,