aboutsummaryrefslogtreecommitdiff
path: root/midimcast-server.c
diff options
context:
space:
mode:
authorrcerc <88944439+rcerc@users.noreply.github.com>2025-06-19 16:39:47 -0400
committerrcerc <88944439+rcerc@users.noreply.github.com>2025-06-19 18:20:38 -0400
commit5179ace697f702d7103c9aa6e26478363e0f6a4d (patch)
tree92c3b9393f3ea2d197400368cf106ffa89f14c6b /midimcast-server.c
Rudimentary streaming of raw MIDI over multicast UDP
Diffstat (limited to 'midimcast-server.c')
-rw-r--r--midimcast-server.c153
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;
+}