aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--src/jalv.c120
-rw-r--r--src/jalv_internal.h3
3 files changed, 74 insertions, 51 deletions
diff --git a/NEWS b/NEWS
index 82b1099..76c7dde 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,8 @@ jalv (9999) unstable;
to handle the handle the maximum message rate the plugin can send.
* Support lv2:sampleRate control ports.
* Tolerate loading presets with port values that aren't xsd:decimal
+ * Notify plugins of Jack transport changes by sending events (an atom:Blank
+ with properties from the LV2 time extension)
-- David Robillard <d@drobilla.net>
diff --git a/src/jalv.c b/src/jalv.c
index 2ec0146..fb41e15 100644
--- a/src/jalv.c
+++ b/src/jalv.c
@@ -325,41 +325,53 @@ jack_process_cb(jack_nframes_t nframes, void* data)
{
Jalv* const host = (Jalv*)data;
-#if 0
+ /* Get Jack transport position */
jack_position_t pos;
- double speed = 0.0;
- if (jack_transport_query(host->jack_client, &pos) == JackTransportRolling) {
- speed = 1.0;
- }
-
- if (pos.valid & JackPositionBBT) {
- uint8_t buf[1024];
- lv2_atom_forge_set_buffer(&host->forge, buf, sizeof(buf));
+ const bool rolling = (jack_transport_query(host->jack_client, &pos)
+ == JackTransportRolling);
+
+ /* If transport state is not as expected, then something has changed */
+ const bool xport_changed = (rolling != host->rolling ||
+ pos.frame != host->position);
+
+ 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(&host->forge, pos_buf, sizeof(pos_buf));
LV2_Atom_Forge* forge = &host->forge;
LV2_Atom_Forge_Frame frame;
lv2_atom_forge_blank(forge, &frame, 1, host->urids.time_Position);
- lv2_atom_forge_property_head(forge, host->urids.time_barBeat, 0);
- lv2_atom_forge_float(forge, pos.beat - 1 + (pos.tick / (float)pos.ticks_per_beat));
- lv2_atom_forge_property_head(forge, host->urids.time_bar, 0);
- lv2_atom_forge_float(forge, pos.bar - 1);
- lv2_atom_forge_property_head(forge, host->urids.time_beatUnit, 0);
- lv2_atom_forge_float(forge, pos.beat_type);
- lv2_atom_forge_property_head(forge, host->urids.time_beatsPerBar, 0);
- lv2_atom_forge_float(forge, pos.beats_per_bar);
- lv2_atom_forge_property_head(forge, host->urids.time_beatsPerMinute, 0);
- lv2_atom_forge_float(forge, pos.beats_per_minute);
lv2_atom_forge_property_head(forge, host->urids.time_frame, 0);
- lv2_atom_forge_int64(forge, pos.frame);
+ lv2_atom_forge_long(forge, pos.frame);
lv2_atom_forge_property_head(forge, host->urids.time_speed, 0);
- lv2_atom_forge_float(forge, speed);
+ lv2_atom_forge_float(forge, rolling ? 1.0 : 0.0);
+ if (pos.valid & JackPositionBBT) {
+ lv2_atom_forge_property_head(forge, host->urids.time_barBeat, 0);
+ lv2_atom_forge_float(
+ forge, pos.beat - 1 + (pos.tick / pos.ticks_per_beat));
+ lv2_atom_forge_property_head(forge, host->urids.time_bar, 0);
+ lv2_atom_forge_float(forge, pos.bar - 1);
+ lv2_atom_forge_property_head(forge, host->urids.time_beatUnit, 0);
+ lv2_atom_forge_float(forge, pos.beat_type);
+ lv2_atom_forge_property_head(forge, host->urids.time_beatsPerBar, 0);
+ lv2_atom_forge_float(forge, pos.beats_per_bar);
+ lv2_atom_forge_property_head(forge, host->urids.time_beatsPerMinute, 0);
+ lv2_atom_forge_float(forge, pos.beats_per_minute);
+ }
- SerdNode s = serd_node_from_string(SERD_BLANK, USTR("pos"));
- SerdNode p = serd_node_from_string(SERD_URI, USTR(NS_RDF "value"));
- char* str = atom_to_turtle(&host->unmap, &s, &p, (LV2_Atom*)frame.ref);
- printf("\n## Position\n%s\n", str);
- free(str);
+ if (host->opts.dump) {
+ char* str = sratom_to_turtle(
+ host->sratom, &host->unmap, "time:", NULL, NULL,
+ lv2_pos->type, lv2_pos->size, LV2_ATOM_BODY(lv2_pos));
+ printf("\n## Position\n%s\n", str);
+ free(str);
+ }
}
-#endif
+
+ /* Update transport state to expected values for next cycle */
+ host->position = rolling ? pos.frame + nframes : pos.frame;
+ host->rolling = rolling;
switch (host->play_state) {
case JALV_PAUSE_REQUESTED:
@@ -385,35 +397,40 @@ jack_process_cb(jack_nframes_t nframes, void* data)
/* Prepare port buffers */
for (uint32_t p = 0; p < host->num_ports; ++p) {
- if (!host->ports[p].jack_port)
+ struct Port* port = &host->ports[p];
+ if (!port->jack_port)
continue;
- if (host->ports[p].type == TYPE_AUDIO) {
- /* Connect plugin port directly to Jack port buffer. */
+ if (port->type == TYPE_AUDIO) {
+ /* Connect plugin port directly to Jack port buffer */
lilv_instance_connect_port(
host->instance, p,
- jack_port_get_buffer(host->ports[p].jack_port, nframes));
-
- } else if (host->ports[p].type == TYPE_EVENT) {
- /* Prepare event ports. */
- if (host->ports[p].flow == FLOW_INPUT) {
- lv2_evbuf_reset(host->ports[p].evbuf, true);
-
- void* buf = jack_port_get_buffer(host->ports[p].jack_port,
- nframes);
-
- LV2_Evbuf_Iterator iter = lv2_evbuf_begin(host->ports[p].evbuf);
- 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,
- host->midi_event_id,
- ev.size, ev.buffer);
- }
- } else {
- lv2_evbuf_reset(host->ports[p].evbuf, false);
+ jack_port_get_buffer(port->jack_port, nframes));
+
+ } 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));
+ }
+
+ /* Write Jack MIDI input */
+ void* buf = jack_port_get_buffer(port->jack_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,
+ host->midi_event_id,
+ ev.size, ev.buffer);
}
+ } else if (port->type == TYPE_EVENT) {
+ /* Clear event output for plugin to write to */
+ lv2_evbuf_reset(port->evbuf, false);
}
}
@@ -787,6 +804,7 @@ main(int argc, char** argv)
host.nodes.rdfs_label = lilv_new_uri(world, LILV_NS_RDFS "label");
host.nodes.work_interface = lilv_new_uri(world, LV2_WORKER__interface);
host.nodes.work_schedule = lilv_new_uri(world, LV2_WORKER__schedule);
+ host.nodes.end = NULL;
/* Get plugin URI from loaded state or command line */
LilvState* state = NULL;
diff --git a/src/jalv_internal.h b/src/jalv_internal.h
index 9a58d1d..e4bea12 100644
--- a/src/jalv_internal.h
+++ b/src/jalv_internal.h
@@ -119,6 +119,7 @@ typedef struct {
LilvNode* rdfs_label;
LilvNode* work_interface;
LilvNode* work_schedule;
+ LilvNode* end; ///< NULL terminator for easy freeing of entire structure
} JalvNodes;
typedef enum {
@@ -171,6 +172,8 @@ typedef struct {
jack_nframes_t sample_rate; ///< Sample rate
jack_nframes_t event_delta_t; ///< Frames since last update sent to UI
uint32_t midi_event_id; ///< MIDI event class ID in event context
+ jack_nframes_t position; ///< Transport position in frames
+ bool rolling; ///< Transport speed (0=stop, 1=play)
bool buf_size_set; ///< True iff buffer size callback fired
bool exit; ///< True iff execution is finished
bool has_ui; ///< True iff a control UI is present