diff options
Diffstat (limited to 'src/jack.c')
-rw-r--r-- | src/jack.c | 959 |
1 files changed, 460 insertions, 499 deletions
@@ -1,577 +1,538 @@ -/* - Copyright 2007-2016 David Robillard <http://drobilla.net> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ +// Copyright 2007-2022 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC +#include "backend.h" + +#include "frontend.h" +#include "jalv_config.h" #include "jalv_internal.h" -#include "worker.h" +#include "log.h" +#include "lv2_evbuf.h" +#include "port.h" +#include "types.h" + +#include "lilv/lilv.h" +#include "lv2/atom/atom.h" +#include "lv2/atom/forge.h" +#include "lv2/urid/urid.h" +#include "zix/sem.h" #include <jack/jack.h> #include <jack/midiport.h> -#ifdef JALV_JACK_SESSION -# include <jack/session.h> -#endif -#ifdef HAVE_JACK_METADATA -# include <jack/metadata.h> +#include <jack/transport.h> +#include <jack/types.h> + +#if USE_JACK_METADATA +# include <jack/metadata.h> #endif #include <ctype.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef __clang__ +# define REALTIME __attribute__((annotate("realtime"))) +#else +# define REALTIME +#endif -struct JalvBackend { - jack_client_t* client; ///< Jack client - bool is_internal_client; ///< Running inside jackd +struct JalvBackendImpl { + jack_client_t* client; ///< Jack client + bool is_internal_client; ///< Running inside jackd }; -/** Internal Jack client initialization entry point */ -int jack_initialize(jack_client_t* client, const char* load_init); +/// Internal Jack client initialization entry point +int +jack_initialize(jack_client_t* client, const char* load_init); -/** Internal Jack client finalization entry point */ -void jack_finish(void* arg); +/// Internal Jack client finalization entry point +void +jack_finish(void* arg); -/** Jack buffer size callback. */ +/// Jack buffer size callback static int jack_buffer_size_cb(jack_nframes_t nframes, void* data) { - Jalv* const jalv = (Jalv*)data; - jalv->block_length = nframes; - jalv->buf_size_set = true; -#ifdef HAVE_JACK_PORT_TYPE_GET_BUFFER_SIZE - jalv->midi_buf_size = jack_port_type_get_buffer_size( - jalv->backend->client, JACK_DEFAULT_MIDI_TYPE); + Jalv* const jalv = (Jalv*)data; + jalv->block_length = nframes; + jalv->buf_size_set = true; +#if USE_JACK_PORT_TYPE_GET_BUFFER_SIZE + jalv->midi_buf_size = jack_port_type_get_buffer_size(jalv->backend->client, + JACK_DEFAULT_MIDI_TYPE); #endif - jalv_allocate_port_buffers(jalv); - return 0; + jalv_allocate_port_buffers(jalv); + return 0; } -/** Jack shutdown callback. */ +/// Jack shutdown callback static void jack_shutdown_cb(void* data) { - Jalv* const jalv = (Jalv*)data; - jalv_close_ui(jalv); - zix_sem_post(&jalv->done); + Jalv* const jalv = (Jalv*)data; + jalv_frontend_close(jalv); + zix_sem_post(&jalv->done); } -/** Jack process callback. */ +/// Jack process callback static REALTIME int jack_process_cb(jack_nframes_t nframes, void* data) { - Jalv* const jalv = (Jalv*)data; - jack_client_t* client = jalv->backend->client; - - /* Get Jack transport position */ - jack_position_t pos; - const bool rolling = (jack_transport_query(client, &pos) - == JackTransportRolling); - - /* If transport state is not as expected, then something has changed */ - const bool xport_changed = (rolling != jalv->rolling || - pos.frame != jalv->position || - pos.beats_per_minute != jalv->bpm); - - uint8_t pos_buf[256]; - LV2_Atom* lv2_pos = (LV2_Atom*)pos_buf; - if (xport_changed) { - /* Build an LV2 position object to report change to plugin */ - lv2_atom_forge_set_buffer(&jalv->forge, pos_buf, sizeof(pos_buf)); - LV2_Atom_Forge* forge = &jalv->forge; - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_object(forge, &frame, 0, jalv->urids.time_Position); - lv2_atom_forge_key(forge, jalv->urids.time_frame); - lv2_atom_forge_long(forge, pos.frame); - lv2_atom_forge_key(forge, jalv->urids.time_speed); - lv2_atom_forge_float(forge, rolling ? 1.0 : 0.0); - if (pos.valid & JackPositionBBT) { - lv2_atom_forge_key(forge, jalv->urids.time_barBeat); - lv2_atom_forge_float( - forge, pos.beat - 1 + (pos.tick / pos.ticks_per_beat)); - lv2_atom_forge_key(forge, jalv->urids.time_bar); - lv2_atom_forge_long(forge, pos.bar - 1); - lv2_atom_forge_key(forge, jalv->urids.time_beatUnit); - lv2_atom_forge_int(forge, pos.beat_type); - lv2_atom_forge_key(forge, jalv->urids.time_beatsPerBar); - lv2_atom_forge_float(forge, pos.beats_per_bar); - lv2_atom_forge_key(forge, jalv->urids.time_beatsPerMinute); - lv2_atom_forge_float(forge, pos.beats_per_minute); - } - - if (jalv->opts.dump) { - char* str = sratom_to_turtle( - jalv->sratom, &jalv->unmap, "time:", NULL, NULL, - lv2_pos->type, lv2_pos->size, LV2_ATOM_BODY(lv2_pos)); - jalv_ansi_start(stdout, 36); - printf("\n## Position ##\n%s\n", str); - jalv_ansi_reset(stdout); - free(str); - } - } - - /* Update transport state to expected values for next cycle */ - jalv->position = rolling ? pos.frame + nframes : pos.frame; - jalv->bpm = pos.beats_per_minute; - jalv->rolling = rolling; - - switch (jalv->play_state) { - case JALV_PAUSE_REQUESTED: - jalv->play_state = JALV_PAUSED; - zix_sem_post(&jalv->paused); - break; - case JALV_PAUSED: - for (uint32_t p = 0; p < jalv->num_ports; ++p) { - jack_port_t* jport = jalv->ports[p].sys_port; - if (jport && jalv->ports[p].flow == FLOW_OUTPUT) { - void* buf = jack_port_get_buffer(jport, nframes); - if (jalv->ports[p].type == TYPE_EVENT) { - jack_midi_clear_buffer(buf); - } else { - memset(buf, '\0', nframes * sizeof(float)); - } - } - } - return 0; - default: - break; - } - - /* Prepare port buffers */ - for (uint32_t p = 0; p < jalv->num_ports; ++p) { - struct Port* port = &jalv->ports[p]; - if (port->type == TYPE_AUDIO && port->sys_port) { - /* Connect plugin port directly to Jack port buffer */ - lilv_instance_connect_port( - jalv->instance, p, - jack_port_get_buffer(port->sys_port, nframes)); -#ifdef HAVE_JACK_METADATA - } else if (port->type == TYPE_CV && port->sys_port) { - /* Connect plugin port directly to Jack port buffer */ - lilv_instance_connect_port( - jalv->instance, p, - jack_port_get_buffer(port->sys_port, nframes)); + Jalv* const jalv = (Jalv*)data; + jack_client_t* client = jalv->backend->client; + + // Get Jack transport position + jack_position_t pos; + const bool rolling = + (jack_transport_query(client, &pos) == JackTransportRolling); + + // If transport state is not as expected, then something has changed + 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)); + + uint8_t pos_buf[256]; + LV2_Atom* lv2_pos = (LV2_Atom*)pos_buf; + if (xport_changed) { + // Build an LV2 position object to report change to plugin + lv2_atom_forge_set_buffer(&jalv->forge, pos_buf, sizeof(pos_buf)); + LV2_Atom_Forge* forge = &jalv->forge; + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_object(forge, &frame, 0, jalv->urids.time_Position); + lv2_atom_forge_key(forge, jalv->urids.time_frame); + lv2_atom_forge_long(forge, pos.frame); + lv2_atom_forge_key(forge, jalv->urids.time_speed); + lv2_atom_forge_float(forge, rolling ? 1.0 : 0.0); + if (has_bbt) { + lv2_atom_forge_key(forge, jalv->urids.time_barBeat); + lv2_atom_forge_float(forge, + pos.beat - 1 + (pos.tick / pos.ticks_per_beat)); + lv2_atom_forge_key(forge, jalv->urids.time_bar); + lv2_atom_forge_long(forge, pos.bar - 1); + lv2_atom_forge_key(forge, jalv->urids.time_beatUnit); + lv2_atom_forge_int(forge, pos.beat_type); + lv2_atom_forge_key(forge, jalv->urids.time_beatsPerBar); + lv2_atom_forge_float(forge, pos.beats_per_bar); + lv2_atom_forge_key(forge, jalv->urids.time_beatsPerMinute); + lv2_atom_forge_float(forge, pos.beats_per_minute); + } + + jalv_dump_atom(jalv, stdout, "Position", lv2_pos, 32); + } + + // 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; + + switch (jalv->play_state) { + case JALV_PAUSE_REQUESTED: + jalv->play_state = JALV_PAUSED; + zix_sem_post(&jalv->paused); + break; + case JALV_PAUSED: + for (uint32_t p = 0; p < jalv->num_ports; ++p) { + jack_port_t* jport = jalv->ports[p].sys_port; + if (jport && jalv->ports[p].flow == FLOW_OUTPUT) { + void* buf = jack_port_get_buffer(jport, nframes); + if (jalv->ports[p].type == TYPE_EVENT) { + jack_midi_clear_buffer(buf); + } else { + memset(buf, '\0', nframes * sizeof(float)); + } + } + } + return 0; + default: + break; + } + + // Prepare port buffers + for (uint32_t p = 0; p < jalv->num_ports; ++p) { + struct Port* port = &jalv->ports[p]; + if (port->type == TYPE_AUDIO && port->sys_port) { + // Connect plugin port directly to Jack port buffer + lilv_instance_connect_port( + jalv->instance, p, jack_port_get_buffer(port->sys_port, nframes)); +#if USE_JACK_METADATA + } else if (port->type == TYPE_CV && port->sys_port) { + // Connect plugin port directly to Jack port buffer + lilv_instance_connect_port( + jalv->instance, p, jack_port_get_buffer(port->sys_port, nframes)); #endif - } else if (port->type == TYPE_EVENT && port->flow == FLOW_INPUT) { - lv2_evbuf_reset(port->evbuf, true); - - /* Write transport change event if applicable */ - LV2_Evbuf_Iterator iter = lv2_evbuf_begin(port->evbuf); - if (xport_changed) { - lv2_evbuf_write(&iter, 0, 0, - lv2_pos->type, lv2_pos->size, - (const uint8_t*)LV2_ATOM_BODY(lv2_pos)); - } - - if (jalv->request_update) { - /* Plugin state has changed, request an update */ - const LV2_Atom_Object get = { - { sizeof(LV2_Atom_Object_Body), jalv->urids.atom_Object }, - { 0, jalv->urids.patch_Get } }; - lv2_evbuf_write(&iter, 0, 0, - get.atom.type, get.atom.size, - (const uint8_t*)LV2_ATOM_BODY(&get)); - } - - if (port->sys_port) { - /* Write Jack MIDI input */ - void* buf = jack_port_get_buffer(port->sys_port, nframes); - for (uint32_t i = 0; i < jack_midi_get_event_count(buf); ++i) { - jack_midi_event_t ev; - jack_midi_event_get(&ev, buf, i); - lv2_evbuf_write(&iter, - ev.time, 0, - jalv->urids.midi_MidiEvent, - ev.size, ev.buffer); - } - } - } else if (port->type == TYPE_EVENT) { - /* Clear event output for plugin to write to */ - lv2_evbuf_reset(port->evbuf, false); - } - } - jalv->request_update = false; - - /* Run plugin for this cycle */ - const bool send_ui_updates = jalv_run(jalv, nframes); - - /* Deliver MIDI output and UI events */ - for (uint32_t p = 0; p < jalv->num_ports; ++p) { - struct Port* const port = &jalv->ports[p]; - if (port->flow == FLOW_OUTPUT && port->type == TYPE_CONTROL && - lilv_port_has_property(jalv->plugin, port->lilv_port, - jalv->nodes.lv2_reportsLatency)) { - if (jalv->plugin_latency != port->control) { - jalv->plugin_latency = port->control; - jack_recompute_total_latencies(client); - } - } else if (port->flow == FLOW_OUTPUT && port->type == TYPE_EVENT) { - void* buf = NULL; - if (port->sys_port) { - buf = jack_port_get_buffer(port->sys_port, nframes); - jack_midi_clear_buffer(buf); - } - - for (LV2_Evbuf_Iterator i = lv2_evbuf_begin(port->evbuf); - lv2_evbuf_is_valid(i); - i = lv2_evbuf_next(i)) { - // Get event from LV2 buffer - uint32_t frames, subframes, type, size; - uint8_t* body; - lv2_evbuf_get(i, &frames, &subframes, &type, &size, &body); - - if (buf && type == jalv->urids.midi_MidiEvent) { - // Write MIDI event to Jack output - jack_midi_event_write(buf, frames, body, size); - } - - if (jalv->has_ui) { - // Forward event to UI - jalv_send_to_ui(jalv, p, type, size, body); - } - } - } else if (send_ui_updates && - port->flow == FLOW_OUTPUT && port->type == TYPE_CONTROL) { - char buf[sizeof(ControlChange) + sizeof(float)]; - ControlChange* ev = (ControlChange*)buf; - ev->index = p; - ev->protocol = 0; - ev->size = sizeof(float); - *(float*)ev->body = port->control; - if (zix_ring_write(jalv->plugin_events, buf, sizeof(buf)) - < sizeof(buf)) { - fprintf(stderr, "Plugin => UI buffer overflow!\n"); - } - } - } - - return 0; + } else if (port->type == TYPE_EVENT && port->flow == FLOW_INPUT) { + lv2_evbuf_reset(port->evbuf, true); + + // Write transport change event if applicable + LV2_Evbuf_Iterator iter = lv2_evbuf_begin(port->evbuf); + if (xport_changed) { + lv2_evbuf_write( + &iter, 0, 0, lv2_pos->type, lv2_pos->size, LV2_ATOM_BODY(lv2_pos)); + } + + if (jalv->request_update) { + // Plugin state has changed, request an update + const LV2_Atom_Object get = { + {sizeof(LV2_Atom_Object_Body), jalv->urids.atom_Object}, + {0, jalv->urids.patch_Get}}; + lv2_evbuf_write( + &iter, 0, 0, get.atom.type, get.atom.size, LV2_ATOM_BODY_CONST(&get)); + } + + if (port->sys_port) { + // Write Jack MIDI input + void* buf = jack_port_get_buffer(port->sys_port, nframes); + for (uint32_t i = 0; i < jack_midi_get_event_count(buf); ++i) { + jack_midi_event_t ev; + jack_midi_event_get(&ev, buf, i); + lv2_evbuf_write( + &iter, ev.time, 0, jalv->urids.midi_MidiEvent, ev.size, ev.buffer); + } + } + } else if (port->type == TYPE_EVENT) { + // Clear event output for plugin to write to + lv2_evbuf_reset(port->evbuf, false); + } + } + jalv->request_update = false; + + // Run plugin for this cycle + const bool send_ui_updates = jalv_run(jalv, nframes); + + // Deliver MIDI output and UI events + for (uint32_t p = 0; p < jalv->num_ports; ++p) { + struct Port* const port = &jalv->ports[p]; + if (port->flow == FLOW_OUTPUT && port->type == TYPE_CONTROL && + lilv_port_has_property( + jalv->plugin, port->lilv_port, jalv->nodes.lv2_reportsLatency)) { + if (jalv->plugin_latency != port->control) { + jalv->plugin_latency = port->control; + jack_recompute_total_latencies(client); + } + } else if (port->flow == FLOW_OUTPUT && port->type == TYPE_EVENT) { + void* buf = NULL; + if (port->sys_port) { + buf = jack_port_get_buffer(port->sys_port, nframes); + jack_midi_clear_buffer(buf); + } + + for (LV2_Evbuf_Iterator i = lv2_evbuf_begin(port->evbuf); + lv2_evbuf_is_valid(i); + i = lv2_evbuf_next(i)) { + // Get event from LV2 buffer + uint32_t frames = 0; + uint32_t subframes = 0; + LV2_URID type = 0; + uint32_t size = 0; + void* body = NULL; + lv2_evbuf_get(i, &frames, &subframes, &type, &size, &body); + + if (buf && type == jalv->urids.midi_MidiEvent) { + // Write MIDI event to Jack output + jack_midi_event_write(buf, frames, body, size); + } + + if (jalv->has_ui) { + // Forward event to UI + jalv_write_event(jalv, jalv->plugin_to_ui, p, size, type, body); + } + } + } else if (send_ui_updates && port->flow == FLOW_OUTPUT && + port->type == TYPE_CONTROL) { + jalv_write_control(jalv, jalv->plugin_to_ui, p, port->control); + } + } + + return 0; } -/** Calculate latency assuming all ports depend on each other. */ +/// Calculate latency assuming all ports depend on each other static void jack_latency_cb(jack_latency_callback_mode_t mode, void* data) { - Jalv* const jalv = (Jalv*)data; - const enum 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) { - struct Port* port = &jalv->ports[p]; - if (port->sys_port && port->flow == flow) { - jack_latency_range_t r; - jack_port_get_latency_range(port->sys_port, mode, &r); - if (r.min < range.min) { range.min = r.min; } - if (r.max > range.max) { range.max = r.max; } - ++ports_found; - } - } - - if (ports_found == 0) { - range.min = 0; - } - - /* Add the plugin's own latency */ - range.min += jalv->plugin_latency; - range.max += jalv->plugin_latency; - - /* Tell Jack about it */ - for (uint32_t p = 0; p < jalv->num_ports; ++p) { - struct Port* port = &jalv->ports[p]; - if (port->sys_port && port->flow == flow) { - jack_port_set_latency_range(port->sys_port, mode, &range); - } - } + Jalv* const jalv = (Jalv*)data; + const enum 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) { + struct Port* port = &jalv->ports[p]; + if (port->sys_port && port->flow == flow) { + jack_latency_range_t r; + jack_port_get_latency_range(port->sys_port, mode, &r); + if (r.min < range.min) { + range.min = r.min; + } + if (r.max > range.max) { + range.max = r.max; + } + ++ports_found; + } + } + + if (ports_found == 0) { + range.min = 0; + } + + // Add the plugin's own latency + range.min += jalv->plugin_latency; + range.max += jalv->plugin_latency; + + // Tell Jack about it + for (uint32_t p = 0; p < jalv->num_ports; ++p) { + struct Port* port = &jalv->ports[p]; + if (port->sys_port && port->flow == flow) { + jack_port_set_latency_range(port->sys_port, mode, &range); + } + } } -#ifdef JALV_JACK_SESSION -static void -jack_session_cb(jack_session_event_t* event, void* arg) -{ - Jalv* const jalv = (Jalv*)arg; - - #define MAX_CMD_LEN 256 - event->command_line = (char*)malloc(MAX_CMD_LEN); - snprintf(event->command_line, MAX_CMD_LEN, "%s -u %s -l \"${SESSION_DIR}\"", - jalv->prog_name, - event->client_uuid); - - switch (event->type) { - case JackSessionSave: - case JackSessionSaveTemplate: - jalv_save(jalv, event->session_dir); - break; - case JackSessionSaveAndQuit: - jalv_save(jalv, event->session_dir); - jalv_close_ui(jalv); - break; - } - - jack_session_reply(jalv->backend->client, event); - jack_session_event_free(event); -} -#endif /* JALV_JACK_SESSION */ - static jack_client_t* jack_create_client(Jalv* jalv) { - jack_client_t* client = NULL; - - /* Determine the name of the JACK client */ - char* jack_name = NULL; - if (jalv->opts.name) { - /* Name given on command line */ - jack_name = jalv_strdup(jalv->opts.name); - } else { - /* Use plugin name */ - LilvNode* name = lilv_plugin_get_name(jalv->plugin); - jack_name = jalv_strdup(lilv_node_as_string(name)); - lilv_node_free(name); - } - - /* Truncate client name to suit JACK if necessary */ - if (strlen(jack_name) >= (unsigned)jack_client_name_size() - 1) { - jack_name[jack_client_name_size() - 1] = '\0'; - } - - /* Connect to JACK */ -#ifdef JALV_JACK_SESSION - if (jalv->opts.uuid) { - client = jack_client_open( - jack_name, - (jack_options_t)(JackSessionID | - (jalv->opts.name_exact ? JackUseExactName : 0)), - NULL, - jalv->opts.uuid); - } -#endif - - if (!client) { - client = jack_client_open( - jack_name, - (jalv->opts.name_exact ? JackUseExactName : JackNullOption), - NULL); - } - - free(jack_name); - - return client; + jack_client_t* client = NULL; + + // Determine the name of the JACK client + char* jack_name = NULL; + if (jalv->opts.name) { + // Name given on command line + jack_name = jalv_strdup(jalv->opts.name); + } else { + // Use plugin name + LilvNode* name = lilv_plugin_get_name(jalv->plugin); + jack_name = jalv_strdup(lilv_node_as_string(name)); + lilv_node_free(name); + } + + // Truncate client name to suit JACK if necessary + if (strlen(jack_name) >= (unsigned)jack_client_name_size() - 1) { + jack_name[jack_client_name_size() - 1] = '\0'; + } + + // Connect to JACK + if (!client) { + client = jack_client_open( + jack_name, + (jalv->opts.name_exact ? JackUseExactName : JackNullOption), + NULL); + } + + free(jack_name); + + return client; } JalvBackend* jalv_backend_init(Jalv* jalv) { - jack_client_t* const client = - jalv->backend ? jalv->backend->client : jack_create_client(jalv); - - if (!client) { - return NULL; - } - - printf("JACK Name: %s\n", jack_get_client_name(client)); - - /* Set audio engine properties */ - jalv->sample_rate = (float)jack_get_sample_rate(client); - jalv->block_length = jack_get_buffer_size(client); - jalv->midi_buf_size = 4096; -#ifdef HAVE_JACK_PORT_TYPE_GET_BUFFER_SIZE - jalv->midi_buf_size = jack_port_type_get_buffer_size( - client, JACK_DEFAULT_MIDI_TYPE); -#endif - - /* Set JACK callbacks */ - void* const arg = (void*)jalv; - jack_set_process_callback(client, &jack_process_cb, arg); - jack_set_buffer_size_callback(client, &jack_buffer_size_cb, arg); - jack_on_shutdown(client, &jack_shutdown_cb, arg); - jack_set_latency_callback(client, &jack_latency_cb, arg); -#ifdef JALV_JACK_SESSION - jack_set_session_callback(client, &jack_session_cb, arg); + jack_client_t* const client = + jalv->backend ? jalv->backend->client : jack_create_client(jalv); + + if (!client) { + return NULL; + } + + jalv_log(JALV_LOG_INFO, "JACK Name: %s\n", jack_get_client_name(client)); + + // Set audio engine properties + jalv->sample_rate = (float)jack_get_sample_rate(client); + jalv->block_length = jack_get_buffer_size(client); + jalv->midi_buf_size = 4096; +#if USE_JACK_PORT_TYPE_GET_BUFFER_SIZE + jalv->midi_buf_size = + jack_port_type_get_buffer_size(client, JACK_DEFAULT_MIDI_TYPE); #endif - if (jalv->backend) { - /* Internal JACK client, jalv->backend->is_internal_client was already - set in jack_initialize() when allocating the backend */ - return jalv->backend; - } - - /* External JACK client, allocate and return opaque backend */ - JalvBackend* backend = (JalvBackend*)calloc(1, sizeof(JalvBackend)); - backend->client = client; - backend->is_internal_client = false; - return backend; + // Set JACK callbacks + void* const arg = (void*)jalv; + jack_set_process_callback(client, &jack_process_cb, arg); + jack_set_buffer_size_callback(client, &jack_buffer_size_cb, arg); + jack_on_shutdown(client, &jack_shutdown_cb, arg); + jack_set_latency_callback(client, &jack_latency_cb, arg); + + if (jalv->backend) { + /* Internal JACK client, jalv->backend->is_internal_client was already set + in jack_initialize() when allocating the backend. */ + return jalv->backend; + } + + // External JACK client, allocate and return opaque backend + JalvBackend* backend = (JalvBackend*)calloc(1, sizeof(JalvBackend)); + backend->client = client; + backend->is_internal_client = false; + return backend; } void jalv_backend_close(Jalv* jalv) { - if (jalv->backend) { - if (!jalv->backend->is_internal_client) { - jack_client_close(jalv->backend->client); - } - - free(jalv->backend); - jalv->backend = NULL; - } + if (jalv->backend) { + if (!jalv->backend->is_internal_client) { + jack_client_close(jalv->backend->client); + } + + free(jalv->backend); + jalv->backend = NULL; + } } void jalv_backend_activate(Jalv* jalv) { - jack_activate(jalv->backend->client); + jack_activate(jalv->backend->client); } void jalv_backend_deactivate(Jalv* jalv) { - if (jalv->backend && !jalv->backend->is_internal_client) { - jack_deactivate(jalv->backend->client); - } + if (jalv->backend && !jalv->backend->is_internal_client) { + jack_deactivate(jalv->backend->client); + } } void jalv_backend_activate_port(Jalv* jalv, uint32_t port_index) { - jack_client_t* client = jalv->backend->client; - struct Port* const port = &jalv->ports[port_index]; - - const LilvNode* sym = lilv_port_get_symbol(jalv->plugin, port->lilv_port); - - /* 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); - return; - } - - /* Build Jack flags for port */ - enum JackPortFlags jack_flags = (port->flow == FLOW_INPUT) - ? JackPortIsInput - : JackPortIsOutput; - - /* Connect the port based on its type */ - switch (port->type) { - case TYPE_CONTROL: - lilv_instance_connect_port(jalv->instance, port_index, &port->control); - break; - case TYPE_AUDIO: - port->sys_port = jack_port_register( - client, lilv_node_as_string(sym), - JACK_DEFAULT_AUDIO_TYPE, jack_flags, 0); - break; -#ifdef HAVE_JACK_METADATA - case TYPE_CV: - port->sys_port = jack_port_register( - client, lilv_node_as_string(sym), - JACK_DEFAULT_AUDIO_TYPE, jack_flags, 0); - if (port->sys_port) { - jack_set_property(client, jack_port_uuid(port->sys_port), - "http://jackaudio.org/metadata/signal-type", "CV", - "text/plain"); - } - break; + jack_client_t* client = jalv->backend->client; + struct Port* const port = &jalv->ports[port_index]; + + const LilvNode* sym = lilv_port_get_symbol(jalv->plugin, port->lilv_port); + + // 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); + return; + } + + // Build Jack flags for port + enum JackPortFlags jack_flags = + (port->flow == FLOW_INPUT) ? JackPortIsInput : JackPortIsOutput; + + // Connect the port based on its type + switch (port->type) { + case TYPE_CONTROL: + lilv_instance_connect_port(jalv->instance, port_index, &port->control); + break; + case TYPE_AUDIO: + port->sys_port = jack_port_register( + client, lilv_node_as_string(sym), 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); + if (port->sys_port) { + jack_set_property(client, + jack_port_uuid(port->sys_port), + "http://jackaudio.org/metadata/signal-type", + "CV", + "text/plain"); + } + 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); - } - break; - default: - break; - } - -#ifdef HAVE_JACK_METADATA - if (port->sys_port) { - // Set port order to index - char index_str[16]; - snprintf(index_str, sizeof(index_str), "%d", port_index); - jack_set_property(client, jack_port_uuid(port->sys_port), - "http://jackaudio.org/metadata/order", index_str, - "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); - } + 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); + } + break; + default: + break; + } + +#if USE_JACK_METADATA + if (port->sys_port) { + // Set port order to index + char index_str[16]; + snprintf(index_str, sizeof(index_str), "%u", port_index); + jack_set_property(client, + jack_port_uuid(port->sys_port), + "http://jackaudio.org/metadata/order", + index_str, + "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); + } #endif } int jack_initialize(jack_client_t* const client, const char* const load_init) { - const size_t args_len = strlen(load_init); - if (args_len > JACK_LOAD_INIT_LIMIT) { - fprintf(stderr, "error: Too many arguments given\n"); - return -1; - } - - Jalv* const jalv = (Jalv*)calloc(1, sizeof(Jalv)); - if (!jalv) { - return -1; - } - - if (!(jalv->backend = (JalvBackend*)calloc(1, sizeof(JalvBackend)))) { - free(jalv); - return -1; - } - - jalv->backend->client = client; - jalv->backend->is_internal_client = true; - - /* Build full command line with "program" name for building argv */ - const size_t cmd_len = strlen("jalv ") + args_len; - char* const cmd = (char*)calloc(cmd_len + 1, 1); - strcat(cmd, "jalv "); - strcat(cmd, load_init); - - /* Build argv */ - int argc = 0; - char** argv = NULL; - char* tok = cmd; - for (size_t i = 0; i <= cmd_len; ++i) { - if (isspace(cmd[i]) || !cmd[i]) { - argv = (char**)realloc(argv, sizeof(char*) * ++argc); - cmd[i] = '\0'; - argv[argc - 1] = tok; - tok = cmd + i + 1; - } - } - - const int err = jalv_open(jalv, &argc, &argv); - if (err) { - jalv_backend_close(jalv); - free(jalv); - } - - free(argv); - free(cmd); - return err; + const size_t args_len = strlen(load_init); + if (args_len > JACK_LOAD_INIT_LIMIT) { + jalv_log(JALV_LOG_ERR, "Too many arguments given\n"); + return -1; + } + + Jalv* const jalv = (Jalv*)calloc(1, sizeof(Jalv)); + if (!jalv) { + return -1; + } + + if (!(jalv->backend = (JalvBackend*)calloc(1, sizeof(JalvBackend)))) { + free(jalv); + return -1; + } + + jalv->backend->client = client; + jalv->backend->is_internal_client = true; + + // Build full command line with "program" name for building argv + const size_t cmd_len = strlen("jalv ") + args_len; + char* const cmd = (char*)calloc(cmd_len + 1, 1); + memcpy(cmd, "jalv ", strlen("jalv ") + 1); + memcpy(cmd + 5, load_init, args_len + 1); + + // Build argv + int argc = 0; + char** argv = NULL; + char* tok = cmd; + for (size_t i = 0; i <= cmd_len; ++i) { + if (isspace(cmd[i]) || !cmd[i]) { + argv = (char**)realloc(argv, sizeof(char*) * ++argc); + cmd[i] = '\0'; + argv[argc - 1] = tok; + tok = cmd + i + 1; + } + } + + const int err = jalv_open(jalv, &argc, &argv); + if (err) { + jalv_backend_close(jalv); + free(jalv); + } + + free(argv); + free(cmd); + return err; } void jack_finish(void* const arg) { - Jalv* const jalv = (Jalv*)arg; - if (jalv) { - if (jalv_close(jalv)) { - fprintf(stderr, "Failed to close Jalv\n"); - } - - free(jalv); - } + Jalv* const jalv = (Jalv*)arg; + if (jalv) { + if (jalv_close(jalv)) { + jalv_log(JALV_LOG_ERR, "Failed to close Jalv\n"); + } + + free(jalv); + } } |