aboutsummaryrefslogtreecommitdiffstats
path: root/src/jalv.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/jalv.c')
-rw-r--r--src/jalv.c1278
1 files changed, 406 insertions, 872 deletions
diff --git a/src/jalv.c b/src/jalv.c
index be45aa4..20cac1f 100644
--- a/src/jalv.c
+++ b/src/jalv.c
@@ -1,88 +1,60 @@
-// Copyright 2007-2022 David Robillard <d@drobilla.net>
+// Copyright 2007-2024 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
+#include "jalv.h"
+
#include "backend.h"
+#include "comm.h"
#include "control.h"
+#include "dumper.h"
+#include "features.h"
#include "frontend.h"
#include "jalv_config.h"
-#include "jalv_internal.h"
#include "log.h"
-#include "lv2_evbuf.h"
+#include "macros.h"
+#include "mapper.h"
#include "nodes.h"
+#include "options.h"
#include "port.h"
+#include "process.h"
+#include "process_setup.h"
+#include "settings.h"
#include "state.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 "lv2/atom/util.h"
-#include "lv2/buf-size/buf-size.h"
-#include "lv2/core/lv2.h"
-#include "lv2/data-access/data-access.h"
-#include "lv2/instance-access/instance-access.h"
-#include "lv2/log/log.h"
-#include "lv2/midi/midi.h"
-#include "lv2/options/options.h"
-#include "lv2/parameters/parameters.h"
-#include "lv2/patch/patch.h"
-#include "lv2/port-groups/port-groups.h"
-#include "lv2/port-props/port-props.h"
-#include "lv2/presets/presets.h"
-#include "lv2/resize-port/resize-port.h"
-#include "lv2/state/state.h"
-#include "lv2/time/time.h"
-#include "lv2/ui/ui.h"
-#include "lv2/urid/urid.h"
-#include "lv2/worker/worker.h"
-#include "serd/serd.h"
-#include "sratom/sratom.h"
-#include "symap.h"
-#include "zix/attributes.h"
-#include "zix/ring.h"
-#include "zix/sem.h"
+#include <lilv/lilv.h>
+#include <lv2/atom/atom.h>
+#include <lv2/atom/forge.h>
+#include <lv2/atom/util.h>
+#include <lv2/buf-size/buf-size.h>
+#include <lv2/core/lv2.h>
+#include <lv2/data-access/data-access.h>
+#include <lv2/instance-access/instance-access.h>
+#include <lv2/log/log.h>
+#include <lv2/patch/patch.h>
+#include <lv2/state/state.h>
+#include <lv2/ui/ui.h>
+#include <lv2/urid/urid.h>
+#include <lv2/worker/worker.h>
+#include <zix/allocator.h>
+#include <zix/filesystem.h>
+#include <zix/path.h>
+#include <zix/ring.h>
+#include <zix/sem.h>
+#include <zix/status.h>
#if USE_SUIL
-# include "suil/suil.h"
-#endif
-
-#if defined(_WIN32)
-# include <io.h> // for _mktemp
-# define snprintf _snprintf
-#elif defined(__APPLE__)
-# include <unistd.h> // for mkdtemp on Darwin
+# include <suil/suil.h>
#endif
-#include <assert.h>
-#include <math.h>
-#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/stat.h>
-
-#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-#define NS_XSD "http://www.w3.org/2001/XMLSchema#"
-
-#ifndef MIN
-# define MIN(a, b) (((a) < (b)) ? (a) : (b))
-#endif
-
-#ifndef MAX
-# define MAX(a, b) (((a) > (b)) ? (a) : (b))
-#endif
-
-#ifndef ARRAY_SIZE
-# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
-#endif
-
-#ifndef MSG_BUFFER_SIZE
-# define MSG_BUFFER_SIZE 1024
-#endif
/**
Size factor for UI ring buffers.
@@ -94,30 +66,6 @@
*/
#define N_BUFFER_CYCLES 16
-static ZixSem* exit_sem = NULL; ///< Exit semaphore used by signal handler
-
-static LV2_URID
-map_uri(LV2_URID_Map_Handle handle, const char* uri)
-{
- Jalv* jalv = (Jalv*)handle;
- zix_sem_wait(&jalv->symap_lock);
- const LV2_URID id = symap_map(jalv->symap, uri);
- zix_sem_post(&jalv->symap_lock);
- return id;
-}
-
-static const char*
-unmap_uri(LV2_URID_Unmap_Handle handle, LV2_URID urid)
-{
- Jalv* jalv = (Jalv*)handle;
- zix_sem_wait(&jalv->symap_lock);
- const char* uri = symap_unmap(jalv->symap, urid);
- zix_sem_post(&jalv->symap_lock);
- return uri;
-}
-
-#define NS_EXT "http://lv2plug.in/ns/ext/"
-
/// These features have no data
static const LV2_Feature static_features[] = {
{LV2_STATE__loadDefaultState, NULL},
@@ -127,7 +75,7 @@ static const LV2_Feature static_features[] = {
/// Return true iff Jalv supports the given feature
static bool
-feature_is_supported(Jalv* jalv, const char* uri)
+feature_is_supported(const Jalv* jalv, const char* uri)
{
if (!strcmp(uri, "http://lv2plug.in/ns/lv2core#isLive") ||
!strcmp(uri, "http://lv2plug.in/ns/lv2core#inPlaceBroken")) {
@@ -142,147 +90,91 @@ feature_is_supported(Jalv* jalv, const char* uri)
return false;
}
-/// Abort and exit on error
-static void
-die(const char* msg)
-{
- jalv_log(JALV_LOG_ERR, "%s\n", msg);
- exit(EXIT_FAILURE);
-}
-
/**
Create a port structure from data description.
This is called before plugin and Jack instantiation. The remaining
instance-specific setup (e.g. buffers) is done later in activate_port().
*/
-static void
-create_port(Jalv* jalv, uint32_t port_index, float default_value)
+static int
+create_port(Jalv* jalv, uint32_t port_index)
{
- struct Port* const port = &jalv->ports[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->control = 0.0f;
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) {
- die("Mandatory port has unknown type (neither input nor output)");
+ 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;
- port->control = isnan(default_value) ? 0.0f : default_value;
+ 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,
jalv->plugin,
port->lilv_port,
port->index,
- jalv->sample_rate,
+ jalv->settings.sample_rate,
&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) {
- die("Mandatory port has unknown data type");
- }
-
- 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.buffer_size =
- MAX(jalv->opts.buffer_size, port->buf_size * N_BUFFER_CYCLES);
}
- lilv_node_free(min_size);
-}
-
-/// Create port structures from data (via create_port()) for all ports
-void
-jalv_create_ports(Jalv* jalv)
-{
- jalv->num_ports = lilv_plugin_get_num_ports(jalv->plugin);
- jalv->ports = (struct Port*)calloc(jalv->num_ports, sizeof(struct Port));
- float* default_values =
- (float*)calloc(lilv_plugin_get_num_ports(jalv->plugin), sizeof(float));
- lilv_plugin_get_port_ranges_float(jalv->plugin, NULL, NULL, default_values);
- for (uint32_t i = 0; i < jalv->num_ports; ++i) {
- create_port(jalv, i, default_values[i]);
+ // 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;
}
- const LilvPort* control_input = lilv_plugin_get_port_by_designation(
- jalv->plugin, jalv->nodes.lv2_InputPort, jalv->nodes.lv2_control);
- if (control_input) {
- const uint32_t index = lilv_port_get_index(jalv->plugin, control_input);
- if (jalv->ports[index].type == TYPE_EVENT) {
- jalv->control_in = index;
- } else {
- jalv_log(JALV_LOG_WARNING,
- "Non-event port %u has lv2:control designation, ignored\n",
- index);
- }
+ // 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);
}
- free(default_values);
+ return 0;
}
-/// Allocate port buffers (only necessary for MIDI)
-void
-jalv_allocate_port_buffers(Jalv* jalv)
+/// Create port structures from data (via create_port()) for all ports
+static int
+jalv_create_ports(Jalv* jalv)
{
- const LV2_URID atom_Chunk = jalv->map.map(
- jalv->map.handle, lilv_node_as_string(jalv->nodes.atom_Chunk));
-
- const LV2_URID atom_Sequence = jalv->map.map(
- jalv->map.handle, lilv_node_as_string(jalv->nodes.atom_Sequence));
-
- for (uint32_t i = 0; i < jalv->num_ports; ++i) {
- struct Port* const port = &jalv->ports[i];
- if (port->type == TYPE_EVENT) {
- lv2_evbuf_free(port->evbuf);
+ const uint32_t n_ports = lilv_plugin_get_num_ports(jalv->plugin);
- const size_t size = port->buf_size ? port->buf_size : jalv->midi_buf_size;
+ 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));
- port->evbuf = lv2_evbuf_new(size, atom_Chunk, atom_Sequence);
+ // Allocate control port buffers array and set to default values
+ jalv->process.controls_buf = (float*)calloc(n_ports, sizeof(float));
+ lilv_plugin_get_port_ranges_float(
+ jalv->plugin, NULL, NULL, jalv->process.controls_buf);
- lilv_instance_connect_port(
- jalv->instance, i, lv2_evbuf_get_buffer(port->evbuf));
-
- lv2_evbuf_reset(port->evbuf, port->flow == FLOW_INPUT);
+ for (uint32_t i = 0; i < jalv->num_ports; ++i) {
+ if (create_port(jalv, i)) {
+ return 1;
}
}
+
+ return 0;
}
/**
@@ -291,12 +183,12 @@ jalv_allocate_port_buffers(Jalv* jalv)
TODO: Build an index to make this faster, currently O(n) which may be
a problem when restoring the state of plugins with many ports.
*/
-struct Port*
+JalvPort*
jalv_port_by_symbol(Jalv* jalv, const char* sym)
{
for (uint32_t i = 0; i < jalv->num_ports; ++i) {
- struct Port* const port = &jalv->ports[i];
- const LilvNode* port_sym =
+ JalvPort* const port = &jalv->ports[i];
+ const LilvNode* port_sym =
lilv_port_get_symbol(jalv->plugin, port->lilv_port);
if (!strcmp(lilv_node_as_string(port_sym), sym)) {
@@ -307,7 +199,7 @@ jalv_port_by_symbol(Jalv* jalv, const char* sym)
return NULL;
}
-ControlID*
+static ControlID*
jalv_control_by_symbol(Jalv* jalv, const char* sym)
{
for (size_t i = 0; i < jalv->controls.n_controls; ++i) {
@@ -318,7 +210,7 @@ jalv_control_by_symbol(Jalv* jalv, const char* sym)
return NULL;
}
-void
+static void
jalv_create_controls(Jalv* jalv, bool writable)
{
const LilvPlugin* plugin = jalv->plugin;
@@ -352,8 +244,11 @@ jalv_create_controls(Jalv* jalv, bool writable)
}
}
- record = new_property_control(
- jalv->world, property, &jalv->nodes, &jalv->map, &jalv->forge);
+ record = new_property_control(jalv->world,
+ property,
+ &jalv->nodes,
+ jalv_mapper_urid_map(jalv->mapper),
+ &jalv->forge);
if (writable) {
record->is_writable = true;
@@ -376,6 +271,53 @@ jalv_create_controls(Jalv* jalv, bool writable)
lilv_node_free(patch_writable);
}
+static void
+jalv_send_to_plugin(void* const jalv_handle,
+ const uint32_t port_index,
+ const uint32_t buffer_size,
+ const uint32_t protocol,
+ const void* const buffer)
+{
+ 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);
+
+ } else if (protocol == 0U) {
+ if (buffer_size != sizeof(float)) {
+ st = ZIX_STATUS_BAD_ARG;
+ } else {
+ const float value = *(const float*)buffer;
+ st = jalv_write_control(proc->ui_to_plugin, port_index, value);
+ }
+
+ } else if (protocol == jalv->urids.atom_eventTransfer) {
+ const LV2_Atom* const atom = (const LV2_Atom*)buffer;
+ if (buffer_size < sizeof(LV2_Atom) ||
+ (sizeof(LV2_Atom) + atom->size != buffer_size)) {
+ st = ZIX_STATUS_BAD_ARG;
+ } else {
+ jalv_dump_atom(jalv->dumper, stdout, "UI => Plugin", atom, 36);
+ st = jalv_write_event(
+ proc->ui_to_plugin, port_index, atom->size, atom->type, atom + 1U);
+ }
+
+ } else {
+ jalv_log(JALV_LOG_ERR,
+ "UI wrote with unsupported protocol %u (%s)\n",
+ protocol,
+ jalv_mapper_unmap_uri(jalv->mapper, protocol));
+ }
+
+ if (st) {
+ jalv_log(JALV_LOG_ERR,
+ "Failed to write to plugin from UI (%s)\n",
+ zix_strerror(st));
+ }
+}
+
void
jalv_set_control(Jalv* jalv,
const ControlID* control,
@@ -384,25 +326,23 @@ jalv_set_control(Jalv* jalv,
const void* body)
{
if (control->type == PORT && type == jalv->forge.Float) {
- struct Port* port = &jalv->ports[control->index];
- port->control = *(const float*)body;
- } else if (control->type == PROPERTY) {
- // Copy forge since it is used by process thread
- LV2_Atom_Forge forge = jalv->forge;
+ const float value = *(const float*)body;
+ jalv_write_control(jalv->process.ui_to_plugin, control->id.index, value);
+ } else if (control->type == PROPERTY &&
+ jalv->process.control_in != UINT32_MAX) {
LV2_Atom_Forge_Frame frame;
- uint8_t buf[MSG_BUFFER_SIZE];
- lv2_atom_forge_set_buffer(&forge, buf, sizeof(buf));
+ 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->id.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);
@@ -413,8 +353,8 @@ jalv_set_control(Jalv* jalv,
static uint32_t
jalv_ui_port_index(void* const controller, const char* symbol)
{
- Jalv* const jalv = (Jalv*)controller;
- struct Port* port = jalv_port_by_symbol(jalv, symbol);
+ Jalv* const jalv = (Jalv*)controller;
+ JalvPort* const port = jalv_port_by_symbol(jalv, symbol);
return port ? port->index : LV2UI_INVALID_PORT_INDEX;
}
@@ -424,13 +364,15 @@ void
jalv_ui_instantiate(Jalv* jalv, const char* native_ui_type, void* parent)
{
#if USE_SUIL
+ const 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};
@@ -466,273 +408,46 @@ jalv_ui_instantiate(Jalv* jalv, const char* native_ui_type, void* parent)
lilv_free(binary_path);
lilv_free(bundle_path);
+#else
+ (void)jalv;
+ (void)native_ui_type;
+ (void)parent;
#endif
}
-bool
-jalv_ui_is_resizable(Jalv* jalv)
-{
- if (!jalv->ui) {
- return false;
- }
-
- const LilvNode* s = lilv_ui_get_uri(jalv->ui);
- LilvNode* p = lilv_new_uri(jalv->world, LV2_CORE__optionalFeature);
- LilvNode* fs = lilv_new_uri(jalv->world, LV2_UI__fixedSize);
- LilvNode* nrs = lilv_new_uri(jalv->world, LV2_UI__noUserResize);
-
- LilvNodes* fs_matches = lilv_world_find_nodes(jalv->world, s, p, fs);
- LilvNodes* nrs_matches = lilv_world_find_nodes(jalv->world, s, p, nrs);
-
- lilv_nodes_free(nrs_matches);
- lilv_nodes_free(fs_matches);
- lilv_node_free(nrs);
- lilv_node_free(fs);
- lilv_node_free(p);
-
- return !fs_matches && !nrs_matches;
-}
-
-static void
-jalv_send_control_to_plugin(Jalv* const jalv,
- uint32_t port_index,
- uint32_t buffer_size,
- const void* buffer)
-{
- if (buffer_size != sizeof(float)) {
- jalv_log(JALV_LOG_ERR, "UI wrote invalid control size %u\n", buffer_size);
-
- } else {
- jalv_write_control(
- jalv, jalv->ui_to_plugin, port_index, *(const float*)buffer);
- }
-}
-
-static void
-jalv_send_event_to_plugin(Jalv* const jalv,
- uint32_t port_index,
- uint32_t buffer_size,
- const void* buffer)
-{
- const LV2_Atom* const atom = (const LV2_Atom*)buffer;
-
- if (buffer_size < sizeof(LV2_Atom)) {
- jalv_log(JALV_LOG_ERR, "UI wrote impossible atom size\n");
-
- } else if (sizeof(LV2_Atom) + atom->size != buffer_size) {
- jalv_log(JALV_LOG_ERR, "UI wrote corrupt atom size\n");
-
- } else {
- jalv_dump_atom(jalv, stdout, "UI => Plugin", atom, 36);
- jalv_write_event(
- jalv, jalv->ui_to_plugin, port_index, atom->size, atom->type, atom + 1U);
- }
-}
-
-void
-jalv_send_to_plugin(void* const jalv_handle,
- uint32_t port_index,
- uint32_t buffer_size,
- uint32_t protocol,
- const void* buffer)
-{
- Jalv* const jalv = (Jalv*)jalv_handle;
-
- if (port_index >= jalv->num_ports) {
- jalv_log(JALV_LOG_ERR, "UI wrote to invalid port index %u\n", port_index);
-
- } else if (protocol == 0U) {
- jalv_send_control_to_plugin(jalv, port_index, buffer_size, buffer);
-
- } else if (protocol == jalv->urids.atom_eventTransfer) {
- jalv_send_event_to_plugin(jalv, port_index, buffer_size, buffer);
-
- } else {
- jalv_log(JALV_LOG_ERR,
- "UI wrote with unsupported protocol %u (%s)\n",
- protocol,
- unmap_uri(jalv, protocol));
- }
-}
-
-void
-jalv_apply_ui_events(Jalv* jalv, uint32_t nframes)
-{
- if (!jalv->has_ui) {
- return;
- }
-
- ControlChange ev = {0U, 0U, 0U};
- const size_t space = zix_ring_read_space(jalv->ui_to_plugin);
- for (size_t i = 0; i < space; i += sizeof(ev) + ev.size) {
- if (zix_ring_read(jalv->ui_to_plugin, &ev, sizeof(ev)) != sizeof(ev)) {
- jalv_log(JALV_LOG_ERR, "Failed to read header from UI ring buffer\n");
- break;
- }
-
- struct {
- union {
- LV2_Atom atom;
- float control;
- } head;
- uint8_t body[MSG_BUFFER_SIZE];
- } buffer;
-
- if (zix_ring_read(jalv->ui_to_plugin, &buffer, ev.size) != ev.size) {
- jalv_log(JALV_LOG_ERR, "Failed to read from UI ring buffer\n");
- break;
- }
-
- assert(ev.index < jalv->num_ports);
- struct Port* const port = &jalv->ports[ev.index];
- if (ev.protocol == 0) {
- assert(ev.size == sizeof(float));
- port->control = buffer.head.control;
- } else if (ev.protocol == jalv->urids.atom_eventTransfer) {
- LV2_Evbuf_Iterator e = lv2_evbuf_end(port->evbuf);
- const LV2_Atom* const atom = &buffer.head.atom;
- lv2_evbuf_write(
- &e, nframes, 0, atom->type, atom->size, LV2_ATOM_BODY_CONST(atom));
- } else {
- jalv_log(
- JALV_LOG_ERR, "Unknown control change protocol %u\n", ev.protocol);
- }
- }
-}
-
void
jalv_init_ui(Jalv* jalv)
{
// Set initial control port values
for (uint32_t i = 0; i < jalv->num_ports; ++i) {
if (jalv->ports[i].type == TYPE_CONTROL) {
- jalv_ui_port_event(jalv, i, sizeof(float), 0, &jalv->ports[i].control);
+ jalv_frontend_port_event(
+ jalv, i, sizeof(float), 0, &jalv->process.controls_buf[i]);
}
}
- if (jalv->control_in != (uint32_t)-1) {
+ 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;
- uint8_t buf[MSG_BUFFER_SIZE];
- lv2_atom_forge_set_buffer(&forge, buf, sizeof(buf));
- lv2_atom_forge_object(&forge, &frame, 0, jalv->urids.patch_Get);
+ uint64_t buf[4U] = {0U, 0U, 0U, 0U};
+ 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);
}
}
static int
-jalv_write_control_change(Jalv* const jalv,
- ZixRing* const target,
- const void* const header,
- const uint32_t header_size,
- const void* const body,
- const uint32_t body_size)
+ring_error(const char* const message)
{
- ZixRingTransaction tx = zix_ring_begin_write(target);
- if (zix_ring_amend_write(target, &tx, header, header_size) ||
- zix_ring_amend_write(target, &tx, body, body_size)) {
- jalv_log(JALV_LOG_ERR,
- target == jalv->plugin_to_ui ? "Plugin => UI buffer overflow"
- : "UI => Plugin buffer overflow");
- return -1;
- }
-
- zix_ring_commit_write(target, &tx);
- return 0;
-}
-
-int
-jalv_write_event(Jalv* const jalv,
- ZixRing* const target,
- const uint32_t port_index,
- const uint32_t size,
- const LV2_URID type,
- const void* const body)
-{
- // TODO: Be more discriminate about what to send
-
- typedef struct {
- ControlChange change;
- LV2_Atom atom;
- } Header;
-
- const Header header = {
- {port_index, jalv->urids.atom_eventTransfer, sizeof(LV2_Atom) + size},
- {size, type}};
-
- return jalv_write_control_change(
- jalv, target, &header, sizeof(header), body, size);
-}
-
-int
-jalv_write_control(Jalv* const jalv,
- ZixRing* const target,
- const uint32_t port_index,
- const float value)
-{
- const ControlChange header = {port_index, 0, sizeof(value)};
-
- return jalv_write_control_change(
- jalv, target, &header, sizeof(header), &value, sizeof(value));
-}
-
-void
-jalv_dump_atom(Jalv* const jalv,
- FILE* const stream,
- const char* const label,
- const LV2_Atom* const atom,
- const int color)
-{
- if (jalv->opts.dump) {
- char* const str = sratom_to_turtle(jalv->sratom,
- &jalv->unmap,
- "jalv:",
- NULL,
- NULL,
- atom->type,
- atom->size,
- LV2_ATOM_BODY_CONST(atom));
-
- jalv_ansi_start(stream, color);
- fprintf(stream, "\n# %s (%u bytes):\n%s\n", label, atom->size, str);
- jalv_ansi_reset(stream);
- free(str);
- }
-}
-
-bool
-jalv_run(Jalv* jalv, uint32_t nframes)
-{
- // Read and apply control change events from UI
- jalv_apply_ui_events(jalv, nframes);
-
- // Run plugin for this cycle
- lilv_instance_run(jalv->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);
-
- // Check if it's time to send updates to the UI
- jalv->event_delta_t += nframes;
- bool send_ui_updates = false;
- uint32_t update_frames = (uint32_t)(jalv->sample_rate / jalv->ui_update_hz);
- if (jalv->has_ui && (jalv->event_delta_t > update_frames)) {
- send_ui_updates = true;
- jalv->event_delta_t = 0;
- }
-
- return send_ui_updates;
+ jalv_log(JALV_LOG_ERR, "%s", message);
+ return 1;
}
int
@@ -745,27 +460,36 @@ jalv_update(Jalv* jalv)
}
// Emit UI events
- ControlChange ev;
- const size_t space = zix_ring_read_space(jalv->plugin_to_ui);
- for (size_t i = 0; i + sizeof(ev) < space; i += sizeof(ev) + ev.size) {
- // Read event header to get the size
- zix_ring_read(jalv->plugin_to_ui, &ev, sizeof(ev));
-
- // Resize read buffer if necessary
- jalv->ui_event_buf = realloc(jalv->ui_event_buf, ev.size);
- void* const buf = jalv->ui_event_buf;
-
- // Read event body
- zix_ring_read(jalv->plugin_to_ui, buf, ev.size);
-
- if (ev.protocol == jalv->urids.atom_eventTransfer) {
- jalv_dump_atom(jalv, stdout, "Plugin => UI", (const LV2_Atom*)buf, 35);
+ 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) {
+ // Read message header (which includes the body size)
+ if (zix_ring_read(ring, &header, sizeof(header)) != sizeof(header)) {
+ return ring_error("Failed to read header from process ring\n");
}
- jalv_ui_port_event(jalv, ev.index, ev.size, ev.protocol, buf);
+ // Read message body
+ void* const body = jalv->ui_msg;
+ if (zix_ring_read(ring, body, header.size) != header.size) {
+ return ring_error("Failed to read message from process ring\n");
+ }
- if (ev.protocol == 0 && jalv->opts.print_controls) {
- jalv_print_control(jalv, &jalv->ports[ev.index], *(float*)buf);
+ if (header.type == CONTROL_PORT_CHANGE) {
+ const JalvControlChange* const msg = (const JalvControlChange*)body;
+ jalv_frontend_port_event(jalv, msg->port_index, sizeof(float), 0, body);
+ } else if (header.type == EVENT_TRANSFER) {
+ const JalvEventTransfer* const msg = (const JalvEventTransfer*)body;
+ jalv_dump_atom(jalv->dumper, stdout, "Plugin => UI", &msg->atom, 35);
+ jalv_frontend_port_event(jalv,
+ msg->port_index,
+ sizeof(LV2_Atom) + msg->atom.size,
+ jalv->urids.atom_eventTransfer,
+ &msg->atom);
+ } else if (header.type == LATENCY_CHANGE) {
+ jalv_backend_recompute_latencies(jalv->backend);
+ } else {
+ return ring_error("Unknown message type received from process ring\n");
}
}
@@ -775,14 +499,14 @@ jalv_update(Jalv* jalv)
static bool
jalv_apply_control_arg(Jalv* jalv, const char* s)
{
- char sym[256];
- float val = 0.0f;
- if (sscanf(s, "%[^=]=%f", sym, &val) != 2) {
+ char sym[256] = {'\0'};
+ float val = 0.0f;
+ if (sscanf(s, "%240[^=]=%f", sym, &val) != 2) {
jalv_log(JALV_LOG_WARNING, "Ignoring invalid value `%s'\n", s);
return false;
}
- ControlID* control = jalv_control_by_symbol(jalv, sym);
+ const ControlID* control = jalv_control_by_symbol(jalv, sym);
if (!control) {
jalv_log(
JALV_LOG_WARNING, "Ignoring value for unknown control `%s'\n", sym);
@@ -796,37 +520,12 @@ jalv_apply_control_arg(Jalv* jalv, const char* s)
}
static void
-signal_handler(int ZIX_UNUSED(sig))
-{
- zix_sem_post(exit_sem);
-}
-
-static void
init_feature(LV2_Feature* const dest, const char* const URI, void* data)
{
dest->URI = URI;
dest->data = data;
}
-static void
-setup_signals(Jalv* const jalv)
-{
- exit_sem = &jalv->done;
-
-#if !defined(_WIN32) && USE_SIGACTION
- struct sigaction action;
- sigemptyset(&action.sa_mask);
- action.sa_flags = 0;
- action.sa_handler = signal_handler;
- sigaction(SIGINT, &action, NULL);
- sigaction(SIGTERM, &action, NULL);
-#else
- // May not work in combination with fgets in the console interface
- signal(SIGINT, signal_handler);
- signal(SIGTERM, signal_handler);
-#endif
-}
-
static const LilvUI*
jalv_select_custom_ui(const Jalv* const jalv)
{
@@ -887,114 +586,17 @@ jalv_select_custom_ui(const Jalv* const jalv)
}
static void
-jalv_init_urids(Symap* const symap, JalvURIDs* const urids)
-{
-#define MAP_URI(uri) symap_map(symap, (uri))
-
- urids->atom_Float = MAP_URI(LV2_ATOM__Float);
- urids->atom_Int = MAP_URI(LV2_ATOM__Int);
- urids->atom_Object = MAP_URI(LV2_ATOM__Object);
- urids->atom_Path = MAP_URI(LV2_ATOM__Path);
- urids->atom_String = MAP_URI(LV2_ATOM__String);
- urids->atom_eventTransfer = MAP_URI(LV2_ATOM__eventTransfer);
- urids->bufsz_maxBlockLength = MAP_URI(LV2_BUF_SIZE__maxBlockLength);
- urids->bufsz_minBlockLength = MAP_URI(LV2_BUF_SIZE__minBlockLength);
- urids->bufsz_sequenceSize = MAP_URI(LV2_BUF_SIZE__sequenceSize);
- urids->log_Error = MAP_URI(LV2_LOG__Error);
- urids->log_Trace = MAP_URI(LV2_LOG__Trace);
- urids->log_Warning = MAP_URI(LV2_LOG__Warning);
- urids->midi_MidiEvent = MAP_URI(LV2_MIDI__MidiEvent);
- urids->param_sampleRate = MAP_URI(LV2_PARAMETERS__sampleRate);
- urids->patch_Get = MAP_URI(LV2_PATCH__Get);
- urids->patch_Put = MAP_URI(LV2_PATCH__Put);
- urids->patch_Set = MAP_URI(LV2_PATCH__Set);
- urids->patch_body = MAP_URI(LV2_PATCH__body);
- urids->patch_property = MAP_URI(LV2_PATCH__property);
- urids->patch_value = MAP_URI(LV2_PATCH__value);
- urids->time_Position = MAP_URI(LV2_TIME__Position);
- urids->time_bar = MAP_URI(LV2_TIME__bar);
- urids->time_barBeat = MAP_URI(LV2_TIME__barBeat);
- urids->time_beatUnit = MAP_URI(LV2_TIME__beatUnit);
- urids->time_beatsPerBar = MAP_URI(LV2_TIME__beatsPerBar);
- urids->time_beatsPerMinute = MAP_URI(LV2_TIME__beatsPerMinute);
- urids->time_frame = MAP_URI(LV2_TIME__frame);
- urids->time_speed = MAP_URI(LV2_TIME__speed);
- urids->ui_scaleFactor = MAP_URI(LV2_UI__scaleFactor);
- urids->ui_updateRate = MAP_URI(LV2_UI__updateRate);
-
-#undef MAP_URI
-}
-
-static void
-jalv_init_nodes(LilvWorld* const world, JalvNodes* const nodes)
-{
-#define MAP_NODE(uri) lilv_new_uri(world, (uri))
-
- nodes->atom_AtomPort = MAP_NODE(LV2_ATOM__AtomPort);
- nodes->atom_Chunk = MAP_NODE(LV2_ATOM__Chunk);
- nodes->atom_Float = MAP_NODE(LV2_ATOM__Float);
- nodes->atom_Path = MAP_NODE(LV2_ATOM__Path);
- nodes->atom_Sequence = MAP_NODE(LV2_ATOM__Sequence);
- nodes->lv2_AudioPort = MAP_NODE(LV2_CORE__AudioPort);
- nodes->lv2_CVPort = MAP_NODE(LV2_CORE__CVPort);
- nodes->lv2_ControlPort = MAP_NODE(LV2_CORE__ControlPort);
- nodes->lv2_InputPort = MAP_NODE(LV2_CORE__InputPort);
- nodes->lv2_OutputPort = MAP_NODE(LV2_CORE__OutputPort);
- nodes->lv2_connectionOptional = MAP_NODE(LV2_CORE__connectionOptional);
- nodes->lv2_control = MAP_NODE(LV2_CORE__control);
- nodes->lv2_default = MAP_NODE(LV2_CORE__default);
- nodes->lv2_enumeration = MAP_NODE(LV2_CORE__enumeration);
- nodes->lv2_extensionData = MAP_NODE(LV2_CORE__extensionData);
- nodes->lv2_integer = MAP_NODE(LV2_CORE__integer);
- nodes->lv2_maximum = MAP_NODE(LV2_CORE__maximum);
- nodes->lv2_minimum = MAP_NODE(LV2_CORE__minimum);
- nodes->lv2_name = MAP_NODE(LV2_CORE__name);
- nodes->lv2_reportsLatency = MAP_NODE(LV2_CORE__reportsLatency);
- nodes->lv2_sampleRate = MAP_NODE(LV2_CORE__sampleRate);
- nodes->lv2_symbol = MAP_NODE(LV2_CORE__symbol);
- nodes->lv2_toggled = MAP_NODE(LV2_CORE__toggled);
- nodes->midi_MidiEvent = MAP_NODE(LV2_MIDI__MidiEvent);
- nodes->pg_group = MAP_NODE(LV2_PORT_GROUPS__group);
- nodes->pprops_logarithmic = MAP_NODE(LV2_PORT_PROPS__logarithmic);
- nodes->pprops_notOnGUI = MAP_NODE(LV2_PORT_PROPS__notOnGUI);
- nodes->pprops_rangeSteps = MAP_NODE(LV2_PORT_PROPS__rangeSteps);
- nodes->pset_Preset = MAP_NODE(LV2_PRESETS__Preset);
- nodes->pset_bank = MAP_NODE(LV2_PRESETS__bank);
- nodes->rdfs_comment = MAP_NODE(LILV_NS_RDFS "comment");
- nodes->rdfs_label = MAP_NODE(LILV_NS_RDFS "label");
- nodes->rdfs_range = MAP_NODE(LILV_NS_RDFS "range");
- nodes->rsz_minimumSize = MAP_NODE(LV2_RESIZE_PORT__minimumSize);
- nodes->ui_showInterface = MAP_NODE(LV2_UI__showInterface);
- nodes->work_interface = MAP_NODE(LV2_WORKER__interface);
- nodes->work_schedule = MAP_NODE(LV2_WORKER__schedule);
- nodes->end = NULL;
-
-#undef MAP_NODE
-}
-
-static void
-jalv_init_env(SerdEnv* const env)
-{
- serd_env_set_prefix_from_strings(
- env, (const uint8_t*)"patch", (const uint8_t*)LV2_PATCH_PREFIX);
- serd_env_set_prefix_from_strings(
- env, (const uint8_t*)"time", (const uint8_t*)LV2_TIME_PREFIX);
- serd_env_set_prefix_from_strings(
- env, (const uint8_t*)"xsd", (const uint8_t*)NS_XSD);
-}
-
-static void
jalv_init_features(Jalv* const jalv)
{
// urid:map
- jalv->map.handle = jalv;
- jalv->map.map = map_uri;
- init_feature(&jalv->features.map_feature, LV2_URID__map, &jalv->map);
+ init_feature(&jalv->features.map_feature,
+ LV2_URID__map,
+ jalv_mapper_urid_map(jalv->mapper));
// urid:unmap
- jalv->unmap.handle = jalv;
- jalv->unmap.unmap = unmap_uri;
- init_feature(&jalv->features.unmap_feature, LV2_URID__unmap, &jalv->unmap);
+ init_feature(&jalv->features.unmap_feature,
+ LV2_URID__unmap,
+ jalv_mapper_urid_unmap(jalv->mapper));
// state:makePath
jalv->features.make_path.handle = jalv;
@@ -1034,80 +636,52 @@ jalv_init_features(Jalv* const jalv)
}
static void
-jalv_init_options(Jalv* const jalv)
+jalv_init_ui_settings(Jalv* const jalv)
{
- const LV2_Options_Option options[ARRAY_SIZE(jalv->features.options)] = {
- {LV2_OPTIONS_INSTANCE,
- 0,
- jalv->urids.param_sampleRate,
- sizeof(float),
- jalv->urids.atom_Float,
- &jalv->sample_rate},
- {LV2_OPTIONS_INSTANCE,
- 0,
- jalv->urids.bufsz_minBlockLength,
- sizeof(int32_t),
- jalv->urids.atom_Int,
- &jalv->block_length},
- {LV2_OPTIONS_INSTANCE,
- 0,
- jalv->urids.bufsz_maxBlockLength,
- sizeof(int32_t),
- jalv->urids.atom_Int,
- &jalv->block_length},
- {LV2_OPTIONS_INSTANCE,
- 0,
- jalv->urids.bufsz_sequenceSize,
- sizeof(int32_t),
- jalv->urids.atom_Int,
- &jalv->midi_buf_size},
- {LV2_OPTIONS_INSTANCE,
- 0,
- jalv->urids.ui_updateRate,
- sizeof(float),
- jalv->urids.atom_Float,
- &jalv->ui_update_hz},
- {LV2_OPTIONS_INSTANCE,
- 0,
- jalv->urids.ui_scaleFactor,
- sizeof(float),
- jalv->urids.atom_Float,
- &jalv->ui_scale_factor},
- {LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL}};
-
- memcpy(jalv->features.options, options, sizeof(jalv->features.options));
-
- init_feature(&jalv->features.options_feature,
- LV2_OPTIONS__options,
- (void*)jalv->features.options);
-}
+ const JalvOptions* const opts = &jalv->opts;
+ JalvSettings* const settings = &jalv->settings;
-static void
-jalv_init_display(Jalv* const jalv)
-{
- if (!jalv->opts.update_rate) {
+ if (!settings->ring_size) {
+ /* The UI ring is fed by plugin output ports (usually one), and the UI
+ updates roughly once per cycle. The ring size is a few times the size
+ of the MIDI output to give the UI a chance to keep up. */
+ settings->ring_size = settings->midi_buf_size * N_BUFFER_CYCLES;
+ }
+
+ if (opts->update_rate <= 0.0f) {
// Calculate a reasonable UI update frequency
- jalv->ui_update_hz = jalv_frontend_refresh_rate(jalv);
- } else {
- // Use user-specified UI update rate
- jalv->ui_update_hz = jalv->opts.update_rate;
- jalv->ui_update_hz = MAX(1.0f, jalv->ui_update_hz);
+ settings->ui_update_hz = jalv_frontend_refresh_rate(jalv);
}
- if (!jalv->opts.scale_factor) {
+ if (opts->scale_factor <= 0.0f) {
// Calculate the monitor's scale factor
- jalv->ui_scale_factor = jalv_frontend_scale_factor(jalv);
- } else {
- // Use user-specified UI scale factor
- jalv->ui_scale_factor = jalv->opts.scale_factor;
+ settings->ui_scale_factor = jalv_frontend_scale_factor(jalv);
}
// The UI can only go so fast, clamp to reasonable limits
- jalv->ui_update_hz = MIN(60, jalv->ui_update_hz);
- jalv->opts.buffer_size = MAX(4096, jalv->opts.buffer_size);
- jalv_log(JALV_LOG_INFO, "Comm buffers: %u bytes\n", jalv->opts.buffer_size);
- jalv_log(JALV_LOG_INFO, "Update rate: %.01f Hz\n", jalv->ui_update_hz);
- jalv_log(JALV_LOG_INFO, "Scale factor: %.01f\n", jalv->ui_scale_factor);
+ settings->ui_update_hz = MAX(1.0f, MIN(60.0f, settings->ui_update_hz));
+ settings->ring_size = MAX(4096, settings->ring_size);
+ jalv_log(JALV_LOG_INFO, "Comm buffers: %u bytes\n", settings->ring_size);
+ jalv_log(JALV_LOG_INFO, "Update rate: %.01f Hz\n", settings->ui_update_hz);
+ jalv_log(JALV_LOG_INFO, "Scale factor: %.01f\n", settings->ui_scale_factor);
+}
+
+static LilvState*
+initial_state(LilvWorld* const world,
+ LV2_URID_Map* const urid_map,
+ const char* const state_path)
+{
+ LilvState* state = NULL;
+ if (state_path) {
+ if (zix_file_type(state_path) == ZIX_FILE_TYPE_DIRECTORY) {
+ char* const path = zix_path_join(NULL, state_path, "state.ttl");
+ state = lilv_state_new_from_file(world, urid_map, NULL, path);
+ free(path);
+ } else {
+ state = lilv_state_new_from_file(world, urid_map, NULL, state_path);
+ }
+ }
+ return state;
}
int
@@ -1118,86 +692,70 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
#endif
// Parse command-line arguments
- int ret = 0;
- if ((ret = jalv_frontend_init(argc, argv, &jalv->opts))) {
- jalv_close(jalv);
+ JalvFrontendArgs args = {argc, argv};
+ const int ret = jalv_frontend_init(&args, &jalv->opts);
+ if (ret) {
return ret;
}
+ JalvSettings* const settings = &jalv->settings;
+
+ settings->block_length = 4096U;
+ settings->midi_buf_size = 1024U;
+ settings->ring_size = jalv->opts.ring_size;
+ settings->ui_update_hz = jalv->opts.update_rate;
+ settings->ui_scale_factor = jalv->opts.scale_factor;
+
// Load the LV2 world
LilvWorld* const world = lilv_world_new();
lilv_world_load_all(world);
- jalv->world = world;
- jalv->env = serd_env_new(NULL);
- jalv->symap = symap_new();
- jalv->block_length = 4096U;
- jalv->midi_buf_size = 1024U;
- jalv->play_state = JALV_PAUSED;
- jalv->bpm = 120.0f;
- jalv->control_in = (uint32_t)-1;
- jalv->log.urids = &jalv->urids;
- jalv->log.tracing = jalv->opts.trace;
-
- zix_sem_init(&jalv->symap_lock, 1);
+ 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);
+ LV2_URID_Unmap* const urid_unmap = jalv_mapper_urid_unmap(jalv->mapper);
+ if (jalv->opts.dump) {
+ jalv->dumper = jalv_dumper_new(urid_map, urid_unmap);
+ }
+
zix_sem_init(&jalv->work_lock, 1);
zix_sem_init(&jalv->done, 0);
- zix_sem_init(&jalv->paused, 0);
-
- jalv_init_env(jalv->env);
- jalv_init_urids(jalv->symap, &jalv->urids);
+ jalv_init_urids(jalv->mapper, &jalv->urids);
jalv_init_nodes(world, &jalv->nodes);
jalv_init_features(jalv);
- lv2_atom_forge_init(&jalv->forge, &jalv->map);
-
- // Set up atom reading and writing environment
- jalv->sratom = sratom_new(&jalv->map);
- sratom_set_env(jalv->sratom, jalv->env);
- jalv->ui_sratom = sratom_new(&jalv->map);
- sratom_set_env(jalv->ui_sratom, jalv->env);
+ lv2_atom_forge_init(&jalv->forge, urid_map);
// Create temporary directory for plugin state
-#ifdef _WIN32
- jalv->temp_dir = jalv_strdup("jalvXXXXXX");
- _mktemp(jalv->temp_dir);
-#else
- char* templ = jalv_strdup("/tmp/jalv-XXXXXX");
- jalv->temp_dir = jalv_strjoin(mkdtemp(templ), "/");
- free(templ);
-#endif
-
- // Get plugin URI from loaded state or command line
- LilvState* state = NULL;
- LilvNode* plugin_uri = NULL;
- if (jalv->opts.load) {
- struct stat info;
- stat(jalv->opts.load, &info);
- if ((info.st_mode & S_IFMT) == S_IFDIR) {
- char* path = jalv_strjoin(jalv->opts.load, "/state.ttl");
- state = lilv_state_new_from_file(jalv->world, &jalv->map, NULL, path);
- free(path);
- } else {
- state = lilv_state_new_from_file(
- jalv->world, &jalv->map, NULL, jalv->opts.load);
- }
- if (!state) {
- jalv_log(JALV_LOG_ERR, "Failed to load state from %s\n", jalv->opts.load);
- jalv_close(jalv);
- return -2;
- }
- plugin_uri = lilv_node_duplicate(lilv_state_get_plugin_uri(state));
- } else if (*argc > 1) {
- plugin_uri = lilv_new_uri(world, (*argv)[*argc - 1]);
+ jalv->temp_dir = zix_create_temporary_directory(NULL, "jalvXXXXXX");
+ if (!jalv->temp_dir) {
+ jalv_log(JALV_LOG_WARNING, "Failed to create temporary state directory\n");
}
- if (!plugin_uri) {
- plugin_uri = jalv_frontend_select_plugin(jalv);
+ // Load initial state given in options if any
+ LilvState* state = initial_state(world, urid_map, jalv->opts.load);
+ if (jalv->opts.load && !state) {
+ jalv_log(JALV_LOG_ERR, "Failed to load state from %s\n", jalv->opts.load);
+ return -2;
}
- if (!plugin_uri) {
- jalv_log(JALV_LOG_ERR, "Missing plugin URI, try lv2ls to list plugins\n");
- jalv_close(jalv);
- return -3;
+ // Get plugin URI from loaded state or command line
+ LilvNode* plugin_uri = NULL;
+ if (state) {
+ plugin_uri = lilv_node_duplicate(lilv_state_get_plugin_uri(state));
+ } else if (*args.argc == 0) {
+ if (!(plugin_uri = jalv_frontend_select_plugin(jalv))) {
+ jalv_log(JALV_LOG_ERR, "Missing plugin URI, try lv2ls to list plugins\n");
+ return -3;
+ }
+ } else if (*args.argc == 1) {
+ plugin_uri = lilv_new_uri(world, (*args.argv)[0]);
+ } else {
+ jalv_log(JALV_LOG_ERR, "Unexpected trailing arguments\n");
+ return -1;
}
// Find plugin
@@ -1208,18 +766,22 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
lilv_node_free(plugin_uri);
if (!jalv->plugin) {
jalv_log(JALV_LOG_ERR, "Failed to find plugin\n");
- jalv_close(jalv);
return -4;
}
+ if (!jalv->opts.name) {
+ jalv->opts.name =
+ jalv_strdup(lilv_node_as_string(lilv_plugin_get_name(jalv->plugin)));
+ }
+
// 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;
}
}
@@ -1228,28 +790,23 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
LilvNode* preset = lilv_new_uri(jalv->world, jalv->opts.preset);
jalv_load_presets(jalv, NULL, NULL);
- state = lilv_state_new_from_world(jalv->world, &jalv->map, preset);
+ state = lilv_state_new_from_world(jalv->world, urid_map, preset);
jalv->preset = state;
lilv_node_free(preset);
if (!state) {
jalv_log(JALV_LOG_ERR, "Failed to find preset <%s>\n", jalv->opts.preset);
- jalv_close(jalv);
return -5;
}
}
// Check for thread-safe state restore() method
- LilvNode* state_threadSafeRestore =
- lilv_new_uri(jalv->world, LV2_STATE__threadSafeRestore);
- if (lilv_plugin_has_feature(jalv->plugin, state_threadSafeRestore)) {
- jalv->safe_restore = true;
- }
- lilv_node_free(state_threadSafeRestore);
+ jalv->safe_restore =
+ lilv_plugin_has_feature(jalv->plugin, jalv->nodes.state_threadSafeRestore);
if (!state) {
// Not restoring state, load the plugin as a preset to get default
state = lilv_state_new_from_world(
- jalv->world, &jalv->map, lilv_plugin_get_uri(jalv->plugin));
+ jalv->world, urid_map, lilv_plugin_get_uri(jalv->plugin));
}
// Get a plugin UI
@@ -1270,46 +827,47 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
}
#endif
- if (jalv->ui) {
- jalv_log(JALV_LOG_INFO,
- "UI: %s\n",
- lilv_node_as_uri(lilv_ui_get_uri(jalv->ui)));
- }
+ jalv_log(JALV_LOG_INFO,
+ "UI: %s\n",
+ lilv_node_as_uri(lilv_ui_get_uri(jalv->ui)));
}
}
- // Create port and control structures
- jalv_create_ports(jalv);
+ // 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;
+ }
+
+ // Create input and output control structures
jalv_create_controls(jalv, true);
jalv_create_controls(jalv, false);
- if (!(jalv->backend = jalv_backend_init(jalv))) {
+ if (jalv_backend_open(jalv->backend,
+ &jalv->urids,
+ &jalv->settings,
+ &jalv->process,
+ &jalv->done,
+ jalv->opts.name,
+ jalv->opts.name_exact)) {
jalv_log(JALV_LOG_ERR, "Failed to connect to audio system\n");
- jalv_close(jalv);
return -6;
}
- jalv_log(JALV_LOG_INFO, "Sample rate: %u Hz\n", (uint32_t)jalv->sample_rate);
- jalv_log(JALV_LOG_INFO, "Block length: %u frames\n", jalv->block_length);
- jalv_log(JALV_LOG_INFO, "MIDI buffers: %zu bytes\n", jalv->midi_buf_size);
-
- if (jalv->opts.buffer_size == 0) {
- /* The UI ring is fed by plugin output ports (usually one), and the UI
- updates roughly once per cycle. The ring size is a few times the size
- of the MIDI output to give the UI a chance to keep up. The UI should be
- able to keep up with 4 cycles, and tests show this works for me, but
- this value might need increasing to avoid overflows. */
- jalv->opts.buffer_size = jalv->midi_buf_size * N_BUFFER_CYCLES;
- }
+ jalv_log(
+ JALV_LOG_INFO, "Sample rate: %u Hz\n", (uint32_t)settings->sample_rate);
+ jalv_log(JALV_LOG_INFO, "Block length: %u frames\n", settings->block_length);
+ jalv_log(JALV_LOG_INFO, "MIDI buffers: %zu bytes\n", settings->midi_buf_size);
- jalv_init_display(jalv);
- jalv_init_options(jalv);
+ jalv_init_ui_settings(jalv);
+ jalv_init_lv2_options(&jalv->features, &jalv->urids, settings);
- // Create Plugin <=> UI communication buffers
- jalv->ui_to_plugin = zix_ring_new(NULL, jalv->opts.buffer_size);
- jalv->plugin_to_ui = zix_ring_new(NULL, jalv->opts.buffer_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,
@@ -1326,7 +884,6 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
jalv->feature_list = (const LV2_Feature**)calloc(1, sizeof(features));
if (!jalv->feature_list) {
jalv_log(JALV_LOG_ERR, "Failed to allocate feature list\n");
- jalv_close(jalv);
return -7;
}
memcpy(jalv->feature_list, features, sizeof(features));
@@ -1337,38 +894,37 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
const char* uri = lilv_node_as_uri(lilv_nodes_get(req_feats, f));
if (!feature_is_supported(jalv, uri)) {
jalv_log(JALV_LOG_ERR, "Feature %s is not supported\n", uri);
- jalv_close(jalv);
return -8;
}
}
lilv_nodes_free(req_feats);
// Instantiate the plugin
- jalv->instance = lilv_plugin_instantiate(
- jalv->plugin, jalv->sample_rate, jalv->feature_list);
- if (!jalv->instance) {
+ LilvInstance* const instance = lilv_plugin_instantiate(
+ jalv->plugin, settings->sample_rate, jalv->feature_list);
+ if (!instance) {
jalv_log(JALV_LOG_ERR, "Failed to instantiate plugin\n");
- jalv_close(jalv);
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);
-
- jalv_worker_start(jalv->worker, worker_iface, jalv->instance->lv2_handle);
- jalv_worker_start(
- jalv->state_worker, worker_iface, jalv->instance->lv2_handle);
+ instance, LV2_WORKER__interface);
+ jalv_worker_attach(jalv->process.worker, worker_iface, instance->lv2_handle);
+ jalv_worker_attach(
+ jalv->process.state_worker, worker_iface, instance->lv2_handle);
jalv_log(JALV_LOG_INFO, "\n");
- if (!jalv->buf_size_set) {
- 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) {
@@ -1385,100 +941,100 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
// Create Jack ports and connect plugin ports to buffers
for (uint32_t i = 0; i < jalv->num_ports; ++i) {
- jalv_backend_activate_port(jalv, i);
+ jalv_backend_activate_port(jalv->backend, &jalv->process, i);
}
- // Print initial control values
- for (size_t i = 0; i < jalv->controls.n_controls; ++i) {
- ControlID* control = jalv->controls.controls[i];
- if (control->type == PORT && control->is_writable) {
- struct Port* port = &jalv->ports[control->index];
- jalv_print_control(jalv, port, port->control);
+ // Discover UI
+ jalv->process.has_ui = jalv_frontend_discover(jalv);
+ return 0;
+}
+
+int
+jalv_activate(Jalv* const jalv)
+{
+ jalv->process.run_state = JALV_RUNNING;
+
+ if (jalv->backend) {
+ if (jalv->process.worker) {
+ jalv_worker_launch(jalv->process.worker);
}
+ lilv_instance_activate(jalv->process.instance);
+ jalv_backend_activate(jalv->backend);
}
- // Activate plugin
- lilv_instance_activate(jalv->instance);
-
- // Discover UI
- jalv->has_ui = jalv_frontend_discover(jalv);
+ return 0;
+}
- // Activate audio backend
- jalv_backend_activate(jalv);
- jalv->play_state = JALV_RUNNING;
+int
+jalv_deactivate(Jalv* const jalv)
+{
+ if (jalv->backend) {
+ jalv_backend_deactivate(jalv->backend);
+ }
+ if (jalv->process.instance) {
+ lilv_instance_deactivate(jalv->process.instance);
+ }
+ if (jalv->process.worker) {
+ jalv_worker_exit(jalv->process.worker);
+ }
+ jalv->process.run_state = JALV_PAUSED;
return 0;
}
int
jalv_close(Jalv* const jalv)
{
- // Terminate the worker
- jalv_worker_exit(jalv->worker);
-
- // Deactivate audio
+ // Stop audio processing, free event port buffers, and close backend
+ jalv_deactivate(jalv);
+ jalv_process_deactivate(&jalv->process);
if (jalv->backend) {
- jalv_backend_deactivate(jalv);
- jalv_backend_close(jalv);
+ jalv_backend_close(jalv->backend);
}
- // Free event port buffers
- for (uint32_t i = 0; i < jalv->num_ports; ++i) {
- if (jalv->ports[i].evbuf) {
- lv2_evbuf_free(jalv->ports[i].evbuf);
- }
- }
-
- // Destroy the worker
- jalv_worker_free(jalv->worker);
- jalv_worker_free(jalv->state_worker);
-
- // Deactivate plugin
+ // Free UI and plugin instances
#if USE_SUIL
suil_instance_free(jalv->ui_instance);
#endif
- if (jalv->instance) {
- lilv_instance_deactivate(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);
- for (LilvNode** n = (LilvNode**)&jalv->nodes; *n; ++n) {
- lilv_node_free(*n);
- }
- symap_free(jalv->symap);
- zix_sem_destroy(&jalv->symap_lock);
+ jalv_process_cleanup(&jalv->process);
+ zix_aligned_free(NULL, jalv->ui_msg);
+ free(jalv->process.controls_buf);
+ jalv_free_nodes(&jalv->nodes);
#if USE_SUIL
suil_host_free(jalv->ui_host);
#endif
for (unsigned i = 0; i < jalv->controls.n_controls; ++i) {
- ControlID* const control = jalv->controls.controls[i];
- lilv_node_free(control->node);
- lilv_node_free(control->symbol);
- lilv_node_free(control->label);
- lilv_node_free(control->group);
- lilv_node_free(control->min);
- lilv_node_free(control->max);
- lilv_node_free(control->def);
- free(control);
+ free_control(jalv->controls.controls[i]);
}
free(jalv->controls.controls);
- sratom_free(jalv->sratom);
- sratom_free(jalv->ui_sratom);
- serd_env_free(jalv->env);
+ jalv_dumper_free(jalv->dumper);
lilv_uis_free(jalv->uis);
+ jalv_mapper_free(jalv->mapper);
lilv_world_free(jalv->world);
zix_sem_destroy(&jalv->done);
- remove(jalv->temp_dir);
- free(jalv->temp_dir);
- free(jalv->ui_event_buf);
+ if (jalv->temp_dir) {
+ // Remove temporary state directory
+ const ZixStatus zst = zix_remove(jalv->temp_dir);
+ if (zst) {
+ jalv_log(JALV_LOG_WARNING,
+ "Failed to remove temporary directory %s (%s)\n",
+ jalv->temp_dir,
+ zix_strerror(zst));
+ }
+ }
+
+ zix_free(NULL, jalv->temp_dir);
free(jalv->feature_list);
free(jalv->opts.name);
@@ -1487,25 +1043,3 @@ jalv_close(Jalv* const jalv)
return 0;
}
-
-int
-main(int argc, char** argv)
-{
- Jalv jalv;
- memset(&jalv, '\0', sizeof(Jalv));
-
- if (jalv_open(&jalv, &argc, &argv)) {
- return EXIT_FAILURE;
- }
-
- // Set up signal handlers
- setup_signals(&jalv);
-
- // Run UI (or prompt at console)
- jalv_frontend_open(&jalv);
-
- // Wait for finish signal from UI or signal handler
- zix_sem_wait(&jalv.done);
-
- return jalv_close(&jalv);
-}