aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2024-11-17 08:20:17 -0500
committerDavid Robillard <d@drobilla.net>2024-11-24 19:04:07 -0500
commit660d0e3591e6a972f5d4adfd7d0d3bf6cef566f9 (patch)
tree046c2c571efb23cc24fbe90dd885c44dd0b52743
parent06bd42a00bd86f5d487727ff8f08797f9286b27f (diff)
downloadjalv-660d0e3591e6a972f5d4adfd7d0d3bf6cef566f9.tar.gz
jalv-660d0e3591e6a972f5d4adfd7d0d3bf6cef566f9.tar.bz2
jalv-660d0e3591e6a972f5d4adfd7d0d3bf6cef566f9.zip
Use message mechanism to pause plugin execution
-rw-r--r--src/comm.h12
-rw-r--r--src/jack.c19
-rw-r--r--src/jalv.c4
-rw-r--r--src/jalv_internal.h2
-rw-r--r--src/portaudio.c20
-rw-r--r--src/process.c21
-rw-r--r--src/process.h13
-rw-r--r--src/state.c25
-rw-r--r--src/types.h9
9 files changed, 98 insertions, 27 deletions
diff --git a/src/comm.h b/src/comm.h
index d570b3a..bd87e13 100644
--- a/src/comm.h
+++ b/src/comm.h
@@ -5,6 +5,7 @@
#define JALV_COMM_H
#include "attributes.h"
+#include "types.h"
#include "lv2/atom/atom.h"
#include "lv2/urid/urid.h"
@@ -24,6 +25,7 @@ typedef enum {
EVENT_TRANSFER, ///< Event transfer for a sequence port (atom)
LATENCY_CHANGE, ///< Change to plugin latency
STATE_REQUEST, ///< Request for a plugin state update (no payload)
+ RUN_STATE_CHANGE, ///< Request to pause or resume running
} JalvMessageType;
/**
@@ -71,6 +73,16 @@ typedef struct {
} JalvLatencyChange;
/**
+ The payload of a RUN_STATE_CHANGE message.
+
+ This message has a fixed sized, and is described in its entirety by this
+ struct.
+*/
+typedef struct {
+ JalvRunState state; ///< Run state to change to
+} JalvRunStateChange;
+
+/**
Write a message in two parts to a ring.
This is used to conveniently write a message with a fixed-size header and
diff --git a/src/jack.c b/src/jack.c
index 5ea9065..da9d45b 100644
--- a/src/jack.c
+++ b/src/jack.c
@@ -125,7 +125,7 @@ process_silent(Jalv* const jalv, const jack_nframes_t nframes)
}
}
- return 0;
+ return jalv_bypass(jalv, nframes);
}
static bool
@@ -158,6 +158,11 @@ jack_process_cb(jack_nframes_t nframes, void* data)
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);
+ }
+
// Get transport state and position
jack_position_t pos = {0U};
const jack_transport_state_t state = jack_transport_query(client, &pos);
@@ -170,18 +175,6 @@ jack_process_cb(jack_nframes_t nframes, void* data)
forge_position(&jalv->forge, &jalv->urids, state, pos);
}
- // Update play state and signal UI if necessary
- switch (jalv->play_state) {
- case JALV_RUNNING:
- break;
- case JALV_PAUSE_REQUESTED:
- jalv->play_state = JALV_PAUSED;
- zix_sem_post(&jalv->paused);
- break;
- case JALV_PAUSED:
- return process_silent(jalv, nframes);
- }
-
// Prepare port buffers
for (uint32_t p = 0; p < jalv->num_ports; ++p) {
JalvPort* const port = &jalv->ports[p];
diff --git a/src/jalv.c b/src/jalv.c
index 10dd8e8..5243d32 100644
--- a/src/jalv.c
+++ b/src/jalv.c
@@ -924,7 +924,7 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
jalv->block_length = 4096U;
jalv->midi_buf_size = 1024U;
jalv->msg_buf_size = 1024U;
- jalv->play_state = JALV_PAUSED;
+ jalv->run_state = JALV_PAUSED;
jalv->bpm = 120.0f;
jalv->control_in = UINT32_MAX;
jalv->log.urids = &jalv->urids;
@@ -1187,8 +1187,8 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
jalv->has_ui = jalv_frontend_discover(jalv);
// Activate audio backend
+ jalv->run_state = JALV_RUNNING;
jalv_backend_activate(jalv);
- jalv->play_state = JALV_RUNNING;
return 0;
}
diff --git a/src/jalv_internal.h b/src/jalv_internal.h
index db9ec68..4b76162 100644
--- a/src/jalv_internal.h
+++ b/src/jalv_internal.h
@@ -85,7 +85,7 @@ struct JalvImpl {
ZixSem work_lock; ///< Lock for plugin work() method
ZixSem done; ///< Exit semaphore
ZixSem paused; ///< Paused signal from process thread
- JalvPlayState play_state; ///< Current play state
+ 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)
diff --git a/src/portaudio.c b/src/portaudio.c
index d0c4aa3..516b1a8 100644
--- a/src/portaudio.c
+++ b/src/portaudio.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 "backend.h"
@@ -18,12 +18,25 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
struct JalvBackendImpl {
PaStream* stream;
};
static int
+process_silent(Jalv* const jalv,
+ void* const outputs,
+ const unsigned long nframes)
+{
+ for (uint32_t i = 0; i < jalv->num_ports; ++i) {
+ memset(((float**)outputs)[i], '\0', nframes * sizeof(float));
+ }
+
+ return jalv_bypass(jalv, nframes);
+}
+
+static int
pa_process_cb(const void* inputs,
void* outputs,
unsigned long nframes,
@@ -36,6 +49,11 @@ pa_process_cb(const void* inputs,
Jalv* jalv = (Jalv*)handle;
+ // If execution is paused, emit silence and return
+ if (jalv->run_state == JALV_PAUSED) {
+ return process_silent(jalv, outputs, nframes);
+ }
+
// Prepare port buffers
uint32_t in_index = 0;
uint32_t out_index = 0;
diff --git a/src/process.c b/src/process.c
index 49e5a1f..fd2aa31 100644
--- a/src/process.c
+++ b/src/process.c
@@ -14,6 +14,7 @@
#include "lv2/atom/atom.h"
#include "lv2/core/lv2.h"
#include "zix/ring.h"
+#include "zix/sem.h"
#include <assert.h>
#include <stddef.h>
@@ -81,6 +82,18 @@ apply_ui_events(Jalv* const jalv, const uint32_t nframes)
lv2_evbuf_write(
&iter, nframes, 0U, get.atom.type, get.atom.size, &get.body);
+ } else if (header.type == RUN_STATE_CHANGE) {
+ assert(header.size == sizeof(JalvRunStateChange));
+ JalvRunStateChange msg = {JALV_RUNNING};
+ if (zix_ring_read(ring, &msg, sizeof(msg)) != sizeof(msg)) {
+ return ring_error("Failed to read run state change from UI ring\n");
+ }
+
+ jalv->run_state = msg.state;
+ if (msg.state == JALV_PAUSED) {
+ zix_sem_post(&jalv->paused);
+ }
+
} else {
return ring_error("Unknown message type received from UI ring\n");
}
@@ -115,3 +128,11 @@ jalv_run(Jalv* const jalv, const uint32_t nframes)
return send_ui_updates;
}
+
+int
+jalv_bypass(Jalv* const jalv, const uint32_t nframes)
+{
+ // Read and apply control change events from UI
+ apply_ui_events(jalv, nframes);
+ return 0;
+}
diff --git a/src/process.h b/src/process.h
index 366d30e..da7470b 100644
--- a/src/process.h
+++ b/src/process.h
@@ -27,6 +27,19 @@ JALV_BEGIN_DECLS
bool
jalv_run(Jalv* jalv, uint32_t nframes);
+/**
+ Bypass the plugin for a block of frames.
+
+ 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 nframes Number of frames to bypass.
+ @return Zero.
+*/
+int
+jalv_bypass(Jalv* jalv, uint32_t nframes);
+
JALV_END_DECLS
#endif // JALV_PROCESS_H
diff --git a/src/state.c b/src/state.c
index 1bfe6ce..767db0a 100644
--- a/src/state.c
+++ b/src/state.c
@@ -1,4 +1,4 @@
-// Copyright 2007-2016 David Robillard <d@drobilla.net>
+// Copyright 2007-2024 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
#include "state.h"
@@ -150,7 +150,7 @@ set_port_value(const char* port_symbol,
}
ZixStatus st = ZIX_STATUS_SUCCESS;
- if (jalv->play_state != JALV_RUNNING) {
+ if (jalv->run_state != JALV_RUNNING) {
// Set value on port struct directly
port->control = fvalue;
} else {
@@ -172,10 +172,18 @@ set_port_value(const char* port_symbol,
void
jalv_apply_state(Jalv* jalv, const LilvState* state)
{
+ typedef struct {
+ JalvMessageHeader head;
+ JalvRunStateChange body;
+ } PauseMessage;
+
const bool must_pause =
- !jalv->safe_restore && jalv->play_state == JALV_RUNNING;
+ !jalv->safe_restore && jalv->run_state == JALV_RUNNING;
if (must_pause) {
- jalv->play_state = JALV_PAUSE_REQUESTED;
+ const PauseMessage pause_msg = {
+ {RUN_STATE_CHANGE, sizeof(JalvRunStateChange)}, {JALV_PAUSED}};
+ zix_ring_write(jalv->ui_to_plugin, &pause_msg, sizeof(pause_msg));
+
zix_sem_wait(&jalv->paused);
}
@@ -194,9 +202,12 @@ jalv_apply_state(Jalv* jalv, const LilvState* state)
state, jalv->instance, set_port_value, jalv, 0, state_features);
if (must_pause) {
- const JalvMessageHeader msg = {STATE_REQUEST, 0U};
- zix_ring_write(jalv->ui_to_plugin, &msg, sizeof(msg));
- jalv->play_state = JALV_RUNNING;
+ const JalvMessageHeader state_msg = {STATE_REQUEST, 0U};
+ zix_ring_write(jalv->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));
}
}
diff --git a/src/types.h b/src/types.h
index ed577f6..f56ee14 100644
--- a/src/types.h
+++ b/src/types.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2022 David Robillard <d@drobilla.net>
+// Copyright 2007-2024 David Robillard <d@drobilla.net>
// SPDX-License-Identifier: ISC
#ifndef JALV_TYPES_H
@@ -8,8 +8,11 @@
JALV_BEGIN_DECLS
-/// Backend playing state
-typedef enum { JALV_RUNNING, JALV_PAUSE_REQUESTED, JALV_PAUSED } JalvPlayState;
+/// Process thread running state
+typedef enum {
+ JALV_RUNNING, ///< Active and running the plugin
+ JALV_PAUSED, ///< Active but bypassing the plugin (silent)
+} JalvRunState;
/// "Global" application state
typedef struct JalvImpl Jalv;