aboutsummaryrefslogtreecommitdiffstats
path: root/src/portaudio.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/portaudio.c')
-rw-r--r--src/portaudio.c212
1 files changed, 129 insertions, 83 deletions
diff --git a/src/portaudio.c b/src/portaudio.c
index f9283dd..8ce1945 100644
--- a/src/portaudio.c
+++ b/src/portaudio.c
@@ -1,102 +1,138 @@
-// Copyright 2007-2022 David Robillard <d@drobilla.net>
+// Copyright 2007-2024 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
#include "backend.h"
+#include "comm.h"
+#include "log.h"
+#include "lv2_evbuf.h"
+#include "process.h"
+#include "settings.h"
+#include "types.h"
+#include "urids.h"
+
+#include <lilv/lilv.h>
+#include <zix/attributes.h>
+#include <zix/sem.h>
-#include "jalv_internal.h"
-#include "port.h"
-#include "worker.h"
-
-#include <math.h>
#include <portaudio.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
struct JalvBackendImpl {
PaStream* stream;
};
static int
-pa_process_cb(const void* inputs,
- void* outputs,
- unsigned long nframes,
- const PaStreamCallbackTimeInfo* time,
- PaStreamCallbackFlags flags,
- void* handle)
+process_silent(JalvProcess* const proc,
+ void* const outputs,
+ const unsigned long nframes)
+{
+ for (uint32_t i = 0; i < proc->num_ports; ++i) {
+ memset(((float**)outputs)[i], '\0', nframes * sizeof(float));
+ }
+
+ return jalv_bypass(proc, nframes);
+}
+
+static int
+process_cb(const void* inputs,
+ void* outputs,
+ unsigned long nframes,
+ const PaStreamCallbackTimeInfo* time,
+ PaStreamCallbackFlags flags,
+ void* handle)
{
- Jalv* jalv = (Jalv*)handle;
+ (void)time;
+ (void)flags;
+
+ JalvProcess* const proc = (JalvProcess*)handle;
+
+ // If execution is paused, emit silence and return
+ 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) {
- struct Port* 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++]);
- }
- } else if (port->type == TYPE_EVENT && port->flow == FLOW_INPUT) {
- lv2_evbuf_reset(port->evbuf, true);
-
- 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_Iterator iter = lv2_evbuf_begin(port->evbuf);
- lv2_evbuf_write(
- &iter, 0, 0, get.atom.type, get.atom.size, LV2_ATOM_BODY(&get));
+ proc->instance, i, ((float**)outputs)[out_index++]);
}
} else if (port->type == TYPE_EVENT) {
- // Clear event output for plugin to write to
- lv2_evbuf_reset(port->evbuf, false);
+ lv2_evbuf_reset(port->evbuf, port->flow == FLOW_INPUT);
}
}
- jalv->request_update = false;
// 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) {
- struct Port* 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);
i = lv2_evbuf_next(i)) {
// Get event from LV2 buffer
- uint32_t frames, subframes, type, size;
- void* body;
+ uint32_t frames = 0U;
+ uint32_t subframes = 0U;
+ uint32_t type = 0U;
+ uint32_t size = 0U;
+ 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, 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, jalv->plugin_to_ui, p, port->control);
+ jalv_write_control(proc->plugin_to_ui, p, proc->controls_buf[p]);
}
}
return paContinue;
}
-static JalvBackend*
-pa_error(const char* msg, PaError err)
+static int
+setup_error(const char* msg, PaError err)
{
jalv_log(JALV_LOG_ERR, "%s (%s)\n", msg, Pa_GetErrorText(err));
Pa_Terminate();
- return NULL;
+ return 1;
}
JalvBackend*
-jalv_backend_init(Jalv* jalv)
+jalv_backend_allocate(void)
+{
+ return (JalvBackend*)calloc(1, sizeof(JalvBackend));
+}
+
+void
+jalv_backend_free(JalvBackend* const backend)
+{
+ free(backend);
+}
+
+int
+jalv_backend_open(JalvBackend* const backend,
+ const JalvURIDs* const ZIX_UNUSED(urids),
+ JalvSettings* const settings,
+ JalvProcess* const proc,
+ ZixSem* const ZIX_UNUSED(done),
+ const char* const ZIX_UNUSED(name),
+ const bool ZIX_UNUSED(exact_name))
{
PaStreamParameters inputParameters;
PaStreamParameters outputParameters;
@@ -104,16 +140,18 @@ jalv_backend_init(Jalv* jalv)
PaError st = paNoError;
if ((st = Pa_Initialize())) {
- return pa_error("Failed to initialize audio system", st);
+ return setup_error("Failed to initialize audio system", st);
}
// Get default input and output devices
inputParameters.device = Pa_GetDefaultInputDevice();
outputParameters.device = Pa_GetDefaultOutputDevice();
if (inputParameters.device == paNoDevice) {
- return pa_error("No default input device", paDeviceUnavailable);
- } else if (outputParameters.device == paNoDevice) {
- return pa_error("No default output device", paDeviceUnavailable);
+ return setup_error("No default input device", paDeviceUnavailable);
+ }
+
+ if (outputParameters.device == paNoDevice) {
+ return setup_error("No default output device", paDeviceUnavailable);
}
const PaDeviceInfo* in_dev = Pa_GetDeviceInfo(inputParameters.device);
@@ -122,11 +160,11 @@ jalv_backend_init(Jalv* jalv)
// Count number of input and output audio ports/channels
inputParameters.channelCount = 0;
outputParameters.channelCount = 0;
- for (uint32_t i = 0; i < jalv->num_ports; ++i) {
- if (jalv->ports[i].type == TYPE_AUDIO) {
- if (jalv->ports[i].flow == FLOW_INPUT) {
+ for (uint32_t i = 0; i < proc->num_ports; ++i) {
+ if (proc->ports[i].type == TYPE_AUDIO) {
+ if (proc->ports[i].flow == FLOW_INPUT) {
++inputParameters.channelCount;
- } else if (jalv->ports[i].flow == FLOW_OUTPUT) {
+ } else if (proc->ports[i].flow == FLOW_OUTPUT) {
++outputParameters.channelCount;
}
}
@@ -148,59 +186,67 @@ jalv_backend_init(Jalv* jalv)
in_dev->defaultSampleRate,
paFramesPerBufferUnspecified,
0,
- pa_process_cb,
- jalv))) {
- return pa_error("Failed to open audio stream", st);
+ process_cb,
+ proc))) {
+ return setup_error("Failed to open audio stream", st);
}
// Set audio parameters
- jalv->sample_rate = in_dev->defaultSampleRate;
- // jalv->block_length = FIXME
- jalv->midi_buf_size = 4096;
-
- // Allocate and return opaque backend
- JalvBackend* backend = (JalvBackend*)calloc(1, sizeof(JalvBackend));
- backend->stream = stream;
- return backend;
+ settings->sample_rate = in_dev->defaultSampleRate;
+ // settings->block_length = FIXME
+ settings->midi_buf_size = 4096;
+
+ backend->stream = stream;
+ return 0;
}
void
-jalv_backend_close(Jalv* jalv)
+jalv_backend_close(JalvBackend* const backend)
{
- Pa_Terminate();
- free(jalv->backend);
- jalv->backend = NULL;
+ if (backend) {
+ PaError st = paNoError;
+ if (backend->stream && (st = Pa_CloseStream(backend->stream))) {
+ jalv_log(JALV_LOG_ERR, "Error closing audio (%s)\n", Pa_GetErrorText(st));
+ }
+
+ if ((st = Pa_Terminate())) {
+ jalv_log(
+ JALV_LOG_ERR, "Error terminating audio (%s)\n", Pa_GetErrorText(st));
+ }
+ }
}
void
-jalv_backend_activate(Jalv* jalv)
+jalv_backend_activate(JalvBackend* const backend)
{
- const int st = Pa_StartStream(jalv->backend->stream);
+ const PaError st = Pa_StartStream(backend->stream);
if (st != paNoError) {
- jalv_log(
- JALV_LOG_ERR, "Error starting audio stream (%s)\n", Pa_GetErrorText(st));
+ jalv_log(JALV_LOG_ERR, "Error starting audio (%s)\n", Pa_GetErrorText(st));
}
}
void
-jalv_backend_deactivate(Jalv* jalv)
+jalv_backend_deactivate(JalvBackend* const backend)
{
- const int st = Pa_CloseStream(jalv->backend->stream);
+ const PaError st = Pa_StopStream(backend->stream);
if (st != paNoError) {
- jalv_log(
- JALV_LOG_ERR, "Error closing audio stream (%s)\n", Pa_GetErrorText(st));
+ jalv_log(JALV_LOG_ERR, "Error stopping audio (%s)\n", Pa_GetErrorText(st));
}
}
void
-jalv_backend_activate_port(Jalv* jalv, uint32_t port_index)
+jalv_backend_activate_port(JalvBackend* const ZIX_UNUSED(backend),
+ JalvProcess* const proc,
+ const uint32_t port_index)
{
- struct Port* const port = &jalv->ports[port_index];
- switch (port->type) {
- case TYPE_CONTROL:
- lilv_instance_connect_port(jalv->instance, port_index, &port->control);
- break;
- default:
- break;
+ JalvProcessPort* const port = &proc->ports[port_index];
+
+ if (port->type == TYPE_CONTROL) {
+ lilv_instance_connect_port(
+ proc->instance, port_index, &proc->controls_buf[port_index]);
}
}
+
+void
+jalv_backend_recompute_latencies(JalvBackend* const ZIX_UNUSED(backend))
+{}