mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-05 13:30:02 -05:00
jack: implement midi to control conversion
This commit is contained in:
parent
7e0b889a11
commit
ea3310bc6d
1 changed files with 236 additions and 8 deletions
|
|
@ -29,9 +29,11 @@
|
||||||
#include <jack/jack.h>
|
#include <jack/jack.h>
|
||||||
#include <jack/session.h>
|
#include <jack/session.h>
|
||||||
#include <jack/thread.h>
|
#include <jack/thread.h>
|
||||||
|
#include <jack/midiport.h>
|
||||||
|
|
||||||
#include <spa/param/audio/format-utils.h>
|
#include <spa/param/audio/format-utils.h>
|
||||||
#include <spa/debug/types.h>
|
#include <spa/debug/types.h>
|
||||||
|
#include <spa/debug/pod.h>
|
||||||
|
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
#include <pipewire/private.h>
|
#include <pipewire/private.h>
|
||||||
|
|
@ -121,6 +123,17 @@ struct midi_buffer {
|
||||||
uint32_t lost_events;
|
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 buffer {
|
||||||
struct spa_list link;
|
struct spa_list link;
|
||||||
#define BUFFER_FLAG_OUT (1<<0)
|
#define BUFFER_FLAG_OUT (1<<0)
|
||||||
|
|
@ -151,6 +164,10 @@ struct mix {
|
||||||
struct io ios[MAX_IO];
|
struct io ios[MAX_IO];
|
||||||
|
|
||||||
struct spa_io_buffers *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];
|
struct buffer buffers[MAX_BUFFERS];
|
||||||
uint32_t n_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
|
static void
|
||||||
on_rtsocket_condition(void *data, int fd, enum spa_io mask)
|
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);
|
&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);
|
c->jack_position.frame, c->quantum->delay);
|
||||||
|
|
||||||
if (c->process_callback)
|
if (c->process_callback)
|
||||||
|
|
@ -708,6 +761,7 @@ on_rtsocket_condition(void *data, int fd, enum spa_io mask)
|
||||||
false,
|
false,
|
||||||
c->timebase_arg);
|
c->timebase_arg);
|
||||||
}
|
}
|
||||||
|
process_tee(c);
|
||||||
|
|
||||||
cmd = 1;
|
cmd = 1;
|
||||||
write(c->writefd, &cmd, 8);
|
write(c->writefd, &cmd, 8);
|
||||||
|
|
@ -969,6 +1023,36 @@ static int param_buffers(struct client *c, struct port *p,
|
||||||
return 1;
|
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,
|
static int port_set_format(struct client *c, struct port *p,
|
||||||
uint32_t flags, const struct spa_pod *param)
|
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);
|
update_io(c, mix, id, mem_id);
|
||||||
|
|
||||||
if (id == SPA_IO_Buffers)
|
switch (id) {
|
||||||
|
case SPA_IO_Buffers:
|
||||||
mix->io = ptr;
|
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);
|
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))
|
if (pw_properties_parse_bool(item->value))
|
||||||
flags |= JackPortIsTerminal;
|
flags |= JackPortIsTerminal;
|
||||||
}
|
}
|
||||||
|
else if (!strcmp(item->key, "port.control")) {
|
||||||
|
if (pw_properties_parse_bool(item->value))
|
||||||
|
type_id = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
o = NULL;
|
o = NULL;
|
||||||
|
|
@ -1982,6 +2083,7 @@ jack_port_t * jack_port_register (jack_client_t *client,
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||||
struct spa_pod *params[4];
|
struct spa_pod *params[4];
|
||||||
|
uint32_t n_params = 0;
|
||||||
struct port *p;
|
struct port *p;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
|
@ -2008,16 +2110,20 @@ jack_port_t * jack_port_register (jack_client_t *client,
|
||||||
|
|
||||||
spa_list_init(&p->mix);
|
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;
|
port_info.props = &dict;
|
||||||
dict = SPA_DICT_INIT(items, 0);
|
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.dsp", port_type);
|
||||||
items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.name", port_name);
|
items[dict.n_items++] = SPA_DICT_ITEM_INIT("port.name", port_name);
|
||||||
|
|
||||||
param_enum_format(c, p, ¶ms[0], &b);
|
if (type_id == 1)
|
||||||
param_buffers(c, p, ¶ms[1], &b);
|
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);
|
pw_thread_loop_lock(c->context.loop);
|
||||||
|
|
||||||
|
|
@ -2026,7 +2132,7 @@ jack_port_t * jack_port_register (jack_client_t *client,
|
||||||
p->id,
|
p->id,
|
||||||
PW_CLIENT_NODE_PORT_UPDATE_PARAMS |
|
PW_CLIENT_NODE_PORT_UPDATE_PARAMS |
|
||||||
PW_CLIENT_NODE_PORT_UPDATE_INFO ,
|
PW_CLIENT_NODE_PORT_UPDATE_INFO ,
|
||||||
2,
|
n_params,
|
||||||
(const struct spa_pod **) params,
|
(const struct spa_pod **) params,
|
||||||
&port_info);
|
&port_info);
|
||||||
|
|
||||||
|
|
@ -2898,6 +3004,128 @@ void jack_set_thread_creator (jack_thread_creator_t creator)
|
||||||
globals.creator = 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) __attribute__ ((constructor));
|
||||||
static void reg(void)
|
static void reg(void)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue