commit cd0d1ab8fe35d6bc30a7e6c770b9b93267e77b86 Author: Michael Sippel Date: Sat Dec 7 02:42:48 2024 +0100 initial pipewire setup - setup audio in/out and midi in ports - dump incoming midi messages diff --git a/guitfx.c b/guitfx.c new file mode 100644 index 0000000..351eb99 --- /dev/null +++ b/guitfx.c @@ -0,0 +1,188 @@ +#include "pipewire/keys.h" +#include "pipewire/port.h" +#include "spa/pod/iter.h" +#include "spa/utils/defs.h" +#include +#include + +#include +#include +#include +#include + +#include +#include + +struct data; + +struct port { + struct data *data; +}; + +struct data { + struct pw_main_loop *loop; + struct pw_filter *filter; + struct port *in_port; + struct port *midi_in_port; + struct port *out_port; + + //! elapsed time in number of samples + uint64_t time; +}; + +static void on_process(void *userdata, struct spa_io_position *position) +{ + struct data *data = userdata; + float *in, *out; + uint32_t n_samples = position->clock.duration; + uint64_t frame = data->time; + data->time += n_samples; + + //printf("do process (%d samples) at %u\n", n_samples, frame); + + struct pw_buffer * b = pw_filter_dequeue_buffer(data->midi_in_port); + if( b == NULL ) { + fprintf(stderr, "on_process(): no buffer for midi_in_port\n"); + return; + } + + struct spa_buffer * buf = b->buffer; + spa_assert(buf->n_datas == 1); + struct spa_data * d = &buf->datas[0]; + + if( d->data ) { + struct spa_pod * pod = + spa_pod_from_data( + d->data, + d->maxsize, + d->chunk->offset, + d->chunk->size + ); + + if( pod ) { + if( spa_pod_is_sequence(pod) ) { + struct spa_pod_sequence * pod_seq = (struct spa_pod_sequence*) pod; + struct spa_pod_control * c; + SPA_POD_SEQUENCE_FOREACH(pod_seq, c) { + if( c->type == SPA_CONTROL_Midi ) { + unsigned sec = + (frame + c->offset) + / (float) position->clock.rate.denom; + char * data = SPA_POD_BODY(&c->value); + unsigned size = SPA_POD_BODY_SIZE(&c->value); + + printf("[%d] MIDI message (%d bytes) : %x, %x, %x\n", sec, size, data[0], data[1], data[2]); + } else { + printf("on_process(): non midi-control\n"); + } + } + } else { + fprintf(stderr, "on_process(): unexpected POD that is not a sequence (midi_in_port)\n"); + } + } else { + fprintf(stderr, "on_process(): pod is NULL\n"); + } + } else { + fprintf(stderr, "on_process(): no data in buffer of midi_in_port\n"); + } + + pw_filter_queue_buffer(data->midi_in_port, b); + + + in = pw_filter_get_dsp_buffer(data->in_port, n_samples); + out = pw_filter_get_dsp_buffer(data->out_port, n_samples); + + if( in && out ) { + memcpy(out, in, n_samples * sizeof(float)); + } +} + +static const struct pw_filter_events filter_events = { + PW_VERSION_FILTER_EVENTS, + .process = on_process, +}; + +static void do_quit(void *userdata, int signal_number) +{ + struct data *data = userdata; + pw_main_loop_quit(data->loop); +} + +int main(int argc, char *argv[]) +{ + struct data data = { 0, }; + const struct spa_pod *params[1]; + uint8_t buffer[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + + pw_init(&argc, &argv); + + data.loop = pw_main_loop_new(NULL); + + pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGINT, do_quit, &data); + pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGTERM, do_quit, &data); + + data.filter = pw_filter_new_simple( + pw_main_loop_get_loop(data.loop), + "Guitar FX", + pw_properties_new( + PW_KEY_MEDIA_TYPE, "Audio", + PW_KEY_MEDIA_CATEGORY, "Filter", + PW_KEY_MEDIA_ROLE, "DSP", + NULL), + &filter_events, + &data); + + data.midi_in_port = pw_filter_add_port(data.filter, + PW_DIRECTION_INPUT, + PW_FILTER_PORT_FLAG_MAP_BUFFERS, + sizeof(struct port), + pw_properties_new( + PW_KEY_FORMAT_DSP, "8 bit raw midi", + PW_KEY_PORT_NAME, "midi-input", + NULL + ), + NULL, 0 + ); + + data.in_port = pw_filter_add_port(data.filter, + PW_DIRECTION_INPUT, + PW_FILTER_PORT_FLAG_MAP_BUFFERS, + sizeof(struct port), + pw_properties_new( + PW_KEY_FORMAT_DSP, "32 bit float mono audio", + PW_KEY_PORT_NAME, "guitar input", + NULL), + NULL, 0); + + data.out_port = pw_filter_add_port(data.filter, + PW_DIRECTION_OUTPUT, + PW_FILTER_PORT_FLAG_MAP_BUFFERS, + sizeof(struct port), + pw_properties_new( + PW_KEY_FORMAT_DSP, "32 bit float mono audio", + PW_KEY_PORT_NAME, "guitar output", + NULL), + NULL, 0); + + params[0] = spa_process_latency_build(&b, + SPA_PARAM_ProcessLatency, + &SPA_PROCESS_LATENCY_INFO_INIT( + .ns = 10 * SPA_NSEC_PER_MSEC + )); + + if (pw_filter_connect(data.filter, + PW_FILTER_FLAG_RT_PROCESS, + params, 1) < 0) { + fprintf(stderr, "can't connect\n"); + return -1; + } + + pw_main_loop_run(data.loop); + + pw_filter_destroy(data.filter); + pw_main_loop_destroy(data.loop); + pw_deinit(); + + return 0; +} diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..0840c97 --- /dev/null +++ b/meson.build @@ -0,0 +1,7 @@ +project('guitfx', 'c') +pipewire_dep = dependency('libpipewire-0.3') +executable( + 'guitfx', + 'guitfx.c', + dependencies : [pipewire_dep], +)