diff options
Diffstat (limited to 'midimcast-server.c')
-rw-r--r-- | midimcast-server.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/midimcast-server.c b/midimcast-server.c new file mode 100644 index 0000000..a731668 --- /dev/null +++ b/midimcast-server.c @@ -0,0 +1,153 @@ +#include <alsa/asoundlib.h> +#include <arpa/inet.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> + +#include "midimcast.h" + +bool midimcast_debug; + +int snd_rawmidi_virtual_open(snd_rawmidi_t **inputp, + snd_rawmidi_t **outputp, + const char *name, + snd_seq_t *seq_handle, + int port, + int merge, + int mode); + +static void env_read_server(const char **const midi_in) +{ + *midi_in = getenv("MIDI_IN"); + if (!*midi_in) + err("`MIDI_IN` unset"); + debug("Using MIDI input %s", *midi_in); +} + +// TODO: Debug messages +// TODO: Const stuff +static void midi_open(const char *const midi_in, + snd_seq_t **seq, + snd_rawmidi_t **input) +{ + err_snd(snd_seq_open, seq, "default", SND_SEQ_OPEN_INPUT, 0); + err_snd(snd_seq_set_client_name, *seq, "MIDI Broadcaster"); + + const int port = snd_seq_create_simple_port(*seq, + "MIDI Broadcaster Input", + SND_SEQ_PORT_CAP_WRITE + | SND_SEQ_PORT_CAP_SYNC_WRITE + | SND_SEQ_PORT_CAP_SUBS_WRITE, + SND_SEQ_PORT_TYPE_MIDI_GENERIC); + + err_snd(snd_rawmidi_virtual_open, + input, + NULL, + "MIDI Broadcaster", + *seq, + port, + true, + SND_RAWMIDI_READ_STANDARD); + + snd_seq_client_info_t *client_info; + snd_seq_client_info_alloca(&client_info); + snd_seq_client_info_set_client(client_info, -1); + snd_seq_addr_t src, dst; + for (;;) { + if (snd_seq_query_next_client(*seq, client_info)) + err("Input '%s' absent", midi_in); + + if (strcmp(snd_seq_client_info_get_name(client_info), midi_in) == 0) { + src.client = snd_seq_client_info_get_client(client_info); + + snd_seq_port_info_t *port_info; + snd_seq_port_info_alloca(&port_info); + snd_seq_port_info_set_client(port_info, src.client); + snd_seq_port_info_set_port(port_info, -1); + if (snd_seq_query_next_port(*seq, port_info)) + err("Input '%s' has no ports", midi_in); + src.port = snd_seq_port_info_get_port(port_info); + + break; + } + } + + dst.client = snd_seq_client_id(*seq); + dst.port = port; + + snd_seq_port_subscribe_t *sub; + snd_seq_port_subscribe_alloca(&sub); + snd_seq_port_subscribe_set_sender(sub, &src); + snd_seq_port_subscribe_set_dest(sub, &dst); + err_snd(snd_seq_subscribe_port, *seq, sub); +} + +// TODO: Naming +static int sock_init(struct sockaddr_in *addr, + const char *const mcast_group, + const uint16_t mcast_port) +{ + memset(addr, 0, sizeof(*addr)); + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = inet_addr(mcast_group); + addr->sin_port = htons(mcast_port); + + return err_std_neg(socket, AF_INET, SOCK_DGRAM, 0); +} + +static void msg_write(struct msg *const msg, + const size_t midi_size, + const int out_sock, + struct sockaddr_in *addr) +{ + msg->ts = htole64(msg->ts); + const size_t header_size = (void *)&msg->midi - (void *)&msg->ts; + const size_t msg_size = header_size + midi_size; + err_std_neg(sendto, + out_sock, + msg, + msg_size, + 0, + (struct sockaddr *)addr, + sizeof(*addr)); + // TODO: Check enough written? +} + +int main() +{ + const char *mcast_group; + uint16_t mcast_port; + const char *midi_in; + env_read_common(&mcast_group, &mcast_port); + env_read_server(&midi_in); + + snd_seq_t *seq; + snd_rawmidi_t *input; + midi_open(midi_in, &seq, &input); + + struct sockaddr_in addr; + const int sock = sock_init(&addr, mcast_group, mcast_port); + + for (;;) { + struct msg msg; + + ssize_t nread = err_snd_neg(snd_rawmidi_read, + input, + &msg.midi, + sizeof(msg.midi)); + debug("Read %ld bytes", nread); + + msg.ts = now(); + msg_write(&msg, nread, sock, &addr); + } + + // TODO: Make these run + err_snd(snd_rawmidi_close, input); + err_snd(snd_seq_close, seq); + err_std(close, sock); + + return EXIT_SUCCESS; +} |