diff --git a/src/pipewire-jack.c b/src/pipewire-jack.c index 54014dcdc..2c66838f9 100644 --- a/src/pipewire-jack.c +++ b/src/pipewire-jack.c @@ -29,9 +29,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -121,6 +123,17 @@ struct midi_buffer { uint32_t lost_events; }; +#define MIDI_INLINE_MAX 4 + +struct midi_event { + uint16_t time; + uint16_t size; + union { + uint32_t byte_offset; + uint8_t inline_data[MIDI_INLINE_MAX]; + }; +}; + struct buffer { struct spa_list link; #define BUFFER_FLAG_OUT (1<<0) @@ -151,6 +164,10 @@ struct mix { struct io ios[MAX_IO]; struct spa_io_buffers *io; + struct spa_io_sequence *notify; + size_t notify_size; + struct spa_io_sequence *control; + size_t control_size; struct buffer buffers[MAX_BUFFERS]; uint32_t n_buffers; @@ -645,6 +662,41 @@ static void reuse_buffer(struct client *c, struct mix *mix, uint32_t id) } } + +static void convert_midi(void *midi, void *buffer, size_t size) +{ + struct spa_pod_builder b = { 0, }; + uint32_t i, count; + + count = jack_midi_get_event_count(midi); + + spa_pod_builder_init(&b, buffer, size); + spa_pod_builder_push_sequence(&b, 0); + + for (i = 0; i < count; i++) { + jack_midi_event_t ev; + jack_midi_event_get(&ev, midi, i); + spa_pod_builder_control_header(&b, ev.time, SPA_CONTROL_Midi); + spa_pod_builder_bytes(&b, ev.buffer, ev.size); + } + spa_pod_builder_pop(&b); +} + +static void process_tee(struct client *c) +{ + struct port *p; + + spa_list_for_each(p, &c->ports[SPA_DIRECTION_OUTPUT], link) { + struct mix *mix; + spa_list_for_each(mix, &p->mix, port_link) { + if (mix->notify == NULL) + continue; + convert_midi(p->empty, mix->notify, mix->notify_size); + break; + } + } +} + static void on_rtsocket_condition(void *data, int fd, enum spa_io mask) { @@ -695,7 +747,8 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) &c->jack_position, c->sync_arg); } - pw_log_trace("do process %d %d %d %"PRIi64, c->buffer_size, c->sample_rate, + pw_log_trace("do process %"PRIu64" %d %d %d %"PRIi64, c->quantum->nsec, + c->buffer_size, c->sample_rate, c->jack_position.frame, c->quantum->delay); if (c->process_callback) @@ -708,6 +761,7 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask) false, c->timebase_arg); } + process_tee(c); cmd = 1; write(c->writefd, &cmd, 8); @@ -969,6 +1023,36 @@ static int param_buffers(struct client *c, struct port *p, return 1; } +static int param_io(struct client *c, struct port *p, + struct spa_pod **param, struct spa_pod_builder *b) +{ + switch (p->object->port.type_id) { + case 0: + *param = spa_pod_builder_object(b, + SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, + SPA_PARAM_IO_id, &SPA_POD_Id(SPA_IO_Buffers), + SPA_PARAM_IO_size, &SPA_POD_Int(sizeof(struct spa_io_buffers)), + 0); + break; + case 1: + if (p->direction == SPA_DIRECTION_OUTPUT) { + *param = spa_pod_builder_object(b, + SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, + SPA_PARAM_IO_id, &SPA_POD_Id(SPA_IO_Notify), + SPA_PARAM_IO_size, &SPA_POD_Int(BUFFER_SIZE_MAX), + 0); + } else { + *param = spa_pod_builder_object(b, + SPA_TYPE_OBJECT_ParamIO, SPA_PARAM_IO, + SPA_PARAM_IO_id, &SPA_POD_Id(SPA_IO_Control), + SPA_PARAM_IO_size, &SPA_POD_Int(sizeof(struct spa_io_sequence)), + 0); + } + break; + } + return 1; +} + static int port_set_format(struct client *c, struct port *p, uint32_t flags, const struct spa_pod *param) { @@ -1268,8 +1352,21 @@ static void client_node_port_set_io(void *object, update_io(c, mix, id, mem_id); - if (id == SPA_IO_Buffers) + switch (id) { + case SPA_IO_Buffers: mix->io = ptr; + break; + case SPA_IO_Notify: + mix->notify = ptr; + mix->notify_size = size; + break; + case SPA_IO_Control: + mix->control = ptr; + mix->control_size = size; + break; + default: + break; + } pw_log_debug("port %p: set io id %u %u %u %u %p", p, id, mem_id, offset, size, ptr); @@ -1372,6 +1469,10 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, if (pw_properties_parse_bool(item->value)) flags |= JackPortIsTerminal; } + else if (!strcmp(item->key, "port.control")) { + if (pw_properties_parse_bool(item->value)) + type_id = 1; + } } o = NULL; @@ -1982,6 +2083,7 @@ jack_port_t * jack_port_register (jack_client_t *client, uint8_t buffer[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); struct spa_pod *params[4]; + uint32_t n_params = 0; struct port *p; int res; @@ -2008,16 +2110,20 @@ jack_port_t * jack_port_register (jack_client_t *client, spa_list_init(&p->mix); - port_info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | - SPA_PORT_INFO_FLAG_NO_REF; - port_info.props = &dict; dict = SPA_DICT_INIT(items, 0); items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.dsp", port_type); items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.name", port_name); - param_enum_format(c, p, ¶ms[0], &b); - param_buffers(c, p, ¶ms[1], &b); + if (type_id == 1) + port_info.flags = SPA_PORT_INFO_FLAG_NO_REF; + else + port_info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | + SPA_PORT_INFO_FLAG_NO_REF; + + param_enum_format(c, p, ¶ms[n_params++], &b); + param_buffers(c, p, ¶ms[n_params++], &b); + param_io(c, p, ¶ms[n_params++], &b); pw_thread_loop_lock(c->context.loop); @@ -2026,7 +2132,7 @@ jack_port_t * jack_port_register (jack_client_t *client, p->id, PW_CLIENT_NODE_PORT_UPDATE_PARAMS | PW_CLIENT_NODE_PORT_UPDATE_INFO , - 2, + n_params, (const struct spa_pod **) params, &port_info); @@ -2898,6 +3004,128 @@ void jack_set_thread_creator (jack_thread_creator_t creator) globals.creator = creator; } +static inline uint8_t * midi_event_data (void* port_buffer, + const struct midi_event* event) +{ + if (event->size <= MIDI_INLINE_MAX) + return (uint8_t *)event->inline_data; + else + return SPA_MEMBER(port_buffer, event->byte_offset, uint8_t); +} + +uint32_t jack_midi_get_event_count(void* port_buffer) +{ + struct midi_buffer *mb = port_buffer; + return mb->event_count; +} + +int jack_midi_event_get(jack_midi_event_t *event, + void *port_buffer, + uint32_t event_index) +{ + struct midi_buffer *mb = port_buffer; + struct midi_event *ev = SPA_MEMBER(mb, sizeof(*mb), struct midi_event); + ev += event_index; + event->time = ev->time; + event->size = ev->size; + event->buffer = midi_event_data (port_buffer, ev); + return 0; +} + +void jack_midi_clear_buffer(void *port_buffer) +{ + struct midi_buffer *mb = port_buffer; + mb->event_count = 0; + mb->write_pos = 0; + mb->lost_events = 0; +} + +void jack_midi_reset_buffer(void *port_buffer) +{ + jack_midi_clear_buffer(port_buffer); +} + +size_t jack_midi_max_event_size(void* port_buffer) +{ + struct midi_buffer *mb = port_buffer; + size_t buffer_size = mb->buffer_size; + + /* (event_count + 1) below accounts for jack_midi_port_internal_event_t + * which would be needed to store the next event */ + size_t used_size = sizeof(struct midi_buffer) + + mb->write_pos + + ((mb->event_count + 1) + * sizeof(struct midi_event)); + + if (used_size > buffer_size) { + return 0; + } else if ((buffer_size - used_size) < MIDI_INLINE_MAX) { + return MIDI_INLINE_MAX; + } else { + return buffer_size - used_size; + } +} + +jack_midi_data_t* jack_midi_event_reserve(void *port_buffer, + jack_nframes_t time, + size_t data_size) +{ + struct midi_buffer *mb = port_buffer; + struct midi_event *events = SPA_MEMBER(mb, sizeof(*mb), struct midi_event); + size_t buffer_size = mb->buffer_size; + + if (time < 0 || time >= mb->nframes) + goto failed; + + if (mb->event_count > 0 && time < events[mb->event_count - 1].time) + goto failed; + + /* Check if data_size is >0 and there is enough space in the buffer for the event. */ + if (data_size <= 0) { + goto failed; // return NULL? + } else if (jack_midi_max_event_size (port_buffer) < data_size) { + goto failed; + } else { + struct midi_event *ev = &events[mb->event_count]; + uint8_t *res; + + ev->time = time; + ev->size = data_size; + if (data_size <= MIDI_INLINE_MAX) { + res = ev->inline_data; + } else { + mb->write_pos += data_size; + ev->byte_offset = buffer_size - 1 - mb->write_pos; + res = SPA_MEMBER(mb, ev->byte_offset, uint8_t); + } + mb->event_count += 1; + return res; + } +failed: + mb->lost_events++; + return NULL; +} + +int jack_midi_event_write(void *port_buffer, + jack_nframes_t time, + const jack_midi_data_t *data, + size_t data_size) +{ + jack_midi_data_t *retbuf = jack_midi_event_reserve (port_buffer, time, data_size); + if (retbuf) { + memcpy (retbuf, data, data_size); + return 0; + } else { + return ENOBUFS; + } +} + +uint32_t jack_midi_get_lost_event_count(void *port_buffer) +{ + struct midi_buffer *mb = port_buffer; + return mb->lost_events; +} + static void reg(void) __attribute__ ((constructor)); static void reg(void) {