ref: 18e8675003170f4a2251872b3f6188c2a7c2e9da
parent: 070d9a990d8249ae105f838f4ab03427aefd2e9b
author: cancel <cancel@cancel.fm>
date: Sat Dec 8 13:17:24 EST 2018
Add start of OSC output
--- /dev/null
+++ b/osc_out.c
@@ -1,0 +1,103 @@
+#include "osc_out.h"
+
+//#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+struct Oosc_dev {
+ int fd;
+ struct sockaddr_in addr;
+};
+
+Oosc_udp_create_error oosc_dev_create_udp(Oosc_dev** out_ptr, U16 port) {
+ int udpfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (udpfd < 0) {
+ fprintf(stderr, "Failed to open UDP socket, error number: %d\n", errno);
+ return Oosc_udp_create_error_couldnt_open_socket;
+ }
+ struct sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr.sin_port = htons((U16)port);
+ Oosc_dev* dev = malloc(sizeof(Oosc_dev));
+ dev->fd = udpfd;
+ dev->addr = addr;
+ *out_ptr = dev;
+ return Oosc_udp_create_error_ok;
+}
+
+void oosc_dev_destroy(Oosc_dev* dev) {
+ close(dev->fd);
+ free(dev);
+}
+
+void oosc_send_datagram(Oosc_dev* dev, char const* data, Usz size) {
+ ssize_t res = sendto(dev->fd, data, size, 0, (struct sockaddr*)&dev->addr,
+ sizeof(dev->addr));
+ if (res < 0) {
+ fprintf(stderr, "UDP message send failed\n");
+ exit(1);
+ }
+}
+
+static bool oosc_write_strn(char* restrict buffer, Usz buffer_size,
+ Usz* buffer_pos, char const* restrict in_str,
+ Usz in_str_len) {
+ // no overflow check, should be fine
+ Usz in_plus_null = in_str_len + 1;
+ Usz null_pad = in_plus_null % 4;
+ Usz needed = in_plus_null + null_pad;
+ Usz cur_pos = *buffer_pos;
+ if (cur_pos + needed >= buffer_size)
+ return false;
+ for (Usz i = 0; i < in_str_len; ++i) {
+ buffer[cur_pos + i] = in_str[i];
+ }
+ buffer[cur_pos + in_str_len] = 0;
+ cur_pos += in_str_len;
+ for (Usz i = 0; i < null_pad; ++i) {
+ buffer[cur_pos + i] = 0;
+ }
+ *buffer_pos = cur_pos + null_pad;
+ return true;
+}
+
+void oosc_send_int32s(Oosc_dev* dev, char const* osc_address, I32 const* vals,
+ Usz count) {
+ char buffer[2048];
+ Usz buf_pos = 0;
+ if (!oosc_write_strn(buffer, sizeof(buffer), &buf_pos, osc_address,
+ strlen(osc_address)))
+ return;
+ Usz typetag_str_size = 1 + count + 1; // comma, 'i'... , null
+ Usz typetag_str_null_pad = typetag_str_size % 4;
+ if (buf_pos + typetag_str_size + typetag_str_null_pad > sizeof(buffer))
+ return;
+ buffer[buf_pos] = ',';
+ ++buf_pos;
+ for (Usz i = 0; i < count; ++i) {
+ buffer[buf_pos + i] = 'i';
+ }
+ buffer[buf_pos + count] = 0;
+ buf_pos += count + 1;
+ for (Usz i = 0; i < typetag_str_null_pad; ++i) {
+ buffer[buf_pos + i] = 0;
+ }
+ buf_pos += typetag_str_null_pad;
+ Usz ints_size = count * sizeof(I32);
+ if (buf_pos + ints_size > sizeof(buffer))
+ return;
+ for (Usz i = 0; i < count; ++i) {
+ union {
+ I32 i;
+ U32 u;
+ } pun;
+ pun.i = vals[i];
+ U32 u_ne = htonl(pun.u);
+ memcpy(buffer + buf_pos, &u_ne, sizeof(u_ne));
+ }
+ buf_pos += count;
+ oosc_send_datagram(dev, buffer, buf_pos);
+}
--- /dev/null
+++ b/osc_out.h
@@ -1,0 +1,15 @@
+#include "base.h"
+
+typedef struct Oosc_dev Oosc_dev;
+
+typedef enum {
+ Oosc_udp_create_error_ok = 0,
+ Oosc_udp_create_error_couldnt_open_socket = 1,
+} Oosc_udp_create_error;
+
+Oosc_udp_create_error oosc_dev_create_udp(Oosc_dev** out_dev_ptr, U16 port);
+void oosc_dev_destroy(Oosc_dev* dev);
+// raw UDP datagram
+void oosc_send_datagram(Oosc_dev* dev, char const* data, Usz size);
+void oosc_send_int32s(Oosc_dev* dev, char const* osc_address, I32 const* vals,
+ Usz count);
--- a/tool
+++ b/tool
@@ -226,7 +226,7 @@
out_exe=cli
;;
orca|tui)
- add source_files tui_main.c
+ add source_files osc_out.c tui_main.c
add cc_flags -D_XOPEN_SOURCE_EXTENDED=1
# thirdparty headers (like sokol_time.h) should get -isystem for their
# include dir so that any warnings they generate with our warning flags
--- a/tui_main.c
+++ b/tui_main.c
@@ -3,6 +3,7 @@
#include "field.h"
#include "gbuffer.h"
#include "mark.h"
+#include "osc_out.h"
#include "sim.h"
#include <getopt.h>
#include <locale.h>
@@ -18,10 +19,14 @@
// clang-format off
fprintf(stderr,
"Usage: orca [options] [file]\n\n"
- "Options:\n"
+ "General options:\n"
" --margins <number> Set cosmetic margins.\n"
" Default: 2\n"
" -h or --help Print this message and exit.\n"
+ "\n"
+ "OSC options:\n"
+ " --osc-port <number> UDP Port to send OSC messages to\n"
+ " Default: none\n"
);
// clang-format on
}
@@ -471,6 +476,7 @@
Usz bpm;
double accum_secs;
char const* filename;
+ Oosc_dev* oosc_dev;
bool needs_remarking;
bool is_draw_dirty;
bool is_playing;
@@ -494,6 +500,7 @@
a->bpm = 120;
a->accum_secs = 0.0;
a->filename = NULL;
+ a->oosc_dev = NULL;
a->needs_remarking = true;
a->is_draw_dirty = false;
a->is_playing = false;
@@ -508,6 +515,9 @@
undo_history_deinit(&a->undo_hist);
oevent_list_deinit(&a->oevent_list);
oevent_list_deinit(&a->scratch_oevent_list);
+ if (a->oosc_dev) {
+ oosc_dev_destroy(a->oosc_dev);
+ }
}
bool app_is_draw_dirty(App_state* a) {
@@ -532,6 +542,18 @@
}
}
+bool app_set_osc_udp_port(App_state* a, U16 port) {
+ if (a->oosc_dev) {
+ oosc_dev_destroy(a->oosc_dev);
+ a->oosc_dev = NULL;
+ }
+ Oosc_udp_create_error err = oosc_dev_create_udp(&a->oosc_dev, port);
+ if (err) {
+ return false;
+ }
+ return true;
+}
+
void app_do_stuff(App_state* a) {
double secs_span = 60.0 / (double)a->bpm / 4.0;
while (a->accum_secs > secs_span) {
@@ -743,15 +765,20 @@
}
}
-enum { Argopt_margins = UCHAR_MAX + 1 };
+enum {
+ Argopt_margins = UCHAR_MAX + 1,
+ Argopt_osc_port,
+};
int main(int argc, char** argv) {
static struct option tui_options[] = {
{"margins", required_argument, 0, Argopt_margins},
{"help", no_argument, 0, 'h'},
+ {"osc-port", required_argument, 0, Argopt_osc_port},
{NULL, 0, NULL, 0}};
char* input_file = NULL;
int margin_thickness = 2;
+ U16 osc_port = 0;
for (;;) {
int c = getopt_long(argc, argv, "h", tui_options, NULL);
if (c == -1)
@@ -760,7 +787,7 @@
case 'h':
usage();
return 0;
- case Argopt_margins:
+ case Argopt_margins: {
margin_thickness = atoi(optarg);
if (margin_thickness == 0 && strcmp(optarg, "0")) {
fprintf(stderr,
@@ -769,7 +796,18 @@
optarg);
return 1;
}
- break;
+ } break;
+ case Argopt_osc_port: {
+ int osc_port0 = atoi(optarg);
+ if (osc_port0 <= 0) {
+ fprintf(stderr, "OSC port must be greater than 0.\n");
+ return 1;
+ } else if (osc_port0 > UINT16_MAX) {
+ fprintf(stderr, "OSC port must be <= %d\n", (int)UINT16_MAX);
+ return 1;
+ }
+ osc_port = (U16)osc_port0;
+ } break;
case '?':
usage();
return 1;
@@ -791,6 +829,13 @@
App_state app_state;
app_init(&app_state);
+
+ if (osc_port != 0) {
+ if (!app_set_osc_udp_port(&app_state, osc_port)) {
+ fprintf(stderr, "Failed to open OSC UDP port %d\n", (int)osc_port);
+ exit(1);
+ }
+ }
if (input_file) {
Field_load_error fle = field_load_file(input_file, &app_state.field);