You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

193 lines
7.7 KiB
Zig

const c = @import("c.zig");
const gl = @import("gl.zig");
const std = @import("std");
const cfg = @import("config.zig");
fn verify_args(expected: u8, got: []const u8) !void {
for (got) |typ| {
if (typ != expected) {
std.debug.print("expected '{c}' but got '{c}' element (expected {s})\n", .{ expected, typ, got });
return error.typeMismatch;
}
}
}
fn set_array(comptime T: type, dest: []T, argc: c_int, argv: [*c][*c]c.lo_arg, types: []const u8) !void {
if (argc != dest.len)
return error.sizeMismatch;
switch (T) {
f32 => {
try verify_args('f', types);
for (dest) |*v, i|
v.* = argv[i].*.f;
},
f64 => {
try verify_args('d', types);
for (dest) |*v, i|
v.* = argv[i].*.d;
},
i32 => {
try verify_args('i', types);
for (dest) |*v, i|
v.* = argv[i].*.i;
},
u32 => {
try verify_args('i', types);
for (dest) |*v, i| {
const val = argv[i].*.i;
if (val < 0)
return error.signDisallowed;
v.* = @intCast(u32, argv[i].*.i);
}
},
else => return error.invalidType,
}
}
pub const ControlServer = struct {
server: c.lo_server,
cache: *gl.UniformCache,
pub fn init(
allocator: std.mem.Allocator,
config: cfg.OSCConfig,
cache: *gl.UniformCache,
) !*ControlServer {
var self: *ControlServer = try allocator.create(ControlServer);
self.cache = cache;
switch (config) {
.Manual => |conf| {
var port = [_]u8{0} ** 6;
_ = std.fmt.formatIntBuf(port[0..], conf.port, 10, .lower, std.fmt.FormatOptions{});
const proto: c_int = switch (conf.protocol) {
.udp => c.LO_UDP,
.tcp => c.LO_TCP,
.unix => c.LO_UNIX,
};
std.debug.print(
"listening for OSC messages on {} port {}\n",
.{ conf.protocol, conf.port },
);
self.server = c.lo_server_new_with_proto(port[0..], proto, handle_error);
},
.URL => |url| {
std.debug.print("listening for OSC messages at {s}\n", .{url});
self.server = c.lo_server_new_from_url(url[0..].ptr, handle_error);
},
}
if (self.server == null)
return error.serverInitializationError;
_ = c.lo_server_add_method(self.server, null, null, handle_method, @ptrCast(*anyopaque, self));
return self;
}
pub fn update(self: *ControlServer) void {
while (c.lo_server_recv_noblock(self.server, 0) > 0) {}
}
pub fn destroy(self: ControlServer) void {
c.lo_server_free(self.server);
}
fn handle_error(num: c_int, msg: [*c]const u8, where: [*c]const u8) callconv(.C) void {
std.debug.print(
"OSC error {} @ {s}: {s}\n",
.{ num, @ptrCast([*:0]const u8, where), @ptrCast([*:0]const u8, msg) },
);
}
fn set_uniform(
self: *ControlServer,
uniform_name: []const u8,
argv: [*c][*c]c.lo_arg,
argc: c_int,
types: []const u8,
) !void {
var buffer: [256]u8 = undefined;
std.mem.copy(u8, buffer[0..], uniform_name);
buffer[uniform_name.len] = 0;
const name = buffer[0..uniform_name.len :0];
const uniform = (try self.cache.get(name)) orelse return error.notFound;
try switch (uniform.value) {
.FLOAT => |*val| set_array(f32, @ptrCast([*]f32, val)[0..1], argc, argv, types),
.DOUBLE => |*val| set_array(f64, @ptrCast([*]f64, val)[0..1], argc, argv, types),
.INT => |*val| set_array(i32, @ptrCast([*]i32, val)[0..1], argc, argv, types),
.UNSIGNED_INT => |*val| set_array(u32, @ptrCast([*]u32, val)[0..1], argc, argv, types),
.BOOL => |*val| set_array(u32, @ptrCast([*]u32, val)[0..1], argc, argv, types),
.FLOAT_VEC2 => |*val| set_array(f32, val[0..], argc, argv, types),
.FLOAT_VEC3 => |*val| set_array(f32, val[0..], argc, argv, types),
.FLOAT_VEC4 => |*val| set_array(f32, val[0..], argc, argv, types),
.DOUBLE_VEC2 => |*val| set_array(f64, val[0..], argc, argv, types),
.DOUBLE_VEC3 => |*val| set_array(f64, val[0..], argc, argv, types),
.DOUBLE_VEC4 => |*val| set_array(f64, val[0..], argc, argv, types),
.INT_VEC2 => |*val| set_array(i32, val[0..], argc, argv, types),
.INT_VEC3 => |*val| set_array(i32, val[0..], argc, argv, types),
.INT_VEC4 => |*val| set_array(i32, val[0..], argc, argv, types),
.UNSIGNED_INT_VEC2 => |*val| set_array(u32, val[0..], argc, argv, types),
.UNSIGNED_INT_VEC3 => |*val| set_array(u32, val[0..], argc, argv, types),
.UNSIGNED_INT_VEC4 => |*val| set_array(u32, val[0..], argc, argv, types),
.BOOL_VEC2 => |*val| set_array(u32, val[0..], argc, argv, types),
.BOOL_VEC3 => |*val| set_array(u32, val[0..], argc, argv, types),
.BOOL_VEC4 => |*val| set_array(u32, val[0..], argc, argv, types),
.FLOAT_MAT2 => |*val| set_array(f32, val[0..], argc, argv, types),
.FLOAT_MAT3 => |*val| set_array(f32, val[0..], argc, argv, types),
.FLOAT_MAT4 => |*val| set_array(f32, val[0..], argc, argv, types),
.FLOAT_MAT2x3 => |*val| set_array(f32, val[0..], argc, argv, types),
.FLOAT_MAT2x4 => |*val| set_array(f32, val[0..], argc, argv, types),
.FLOAT_MAT3x2 => |*val| set_array(f32, val[0..], argc, argv, types),
.FLOAT_MAT3x4 => |*val| set_array(f32, val[0..], argc, argv, types),
.FLOAT_MAT4x2 => |*val| set_array(f32, val[0..], argc, argv, types),
.FLOAT_MAT4x3 => |*val| set_array(f32, val[0..], argc, argv, types),
.DOUBLE_MAT2 => |*val| set_array(f64, val[0..], argc, argv, types),
.DOUBLE_MAT3 => |*val| set_array(f64, val[0..], argc, argv, types),
.DOUBLE_MAT4 => |*val| set_array(f64, val[0..], argc, argv, types),
.DOUBLE_MAT2x3 => |*val| set_array(f64, val[0..], argc, argv, types),
.DOUBLE_MAT2x4 => |*val| set_array(f64, val[0..], argc, argv, types),
.DOUBLE_MAT3x2 => |*val| set_array(f64, val[0..], argc, argv, types),
.DOUBLE_MAT3x4 => |*val| set_array(f64, val[0..], argc, argv, types),
.DOUBLE_MAT4x2 => |*val| set_array(f64, val[0..], argc, argv, types),
.DOUBLE_MAT4x3 => |*val| set_array(f64, val[0..], argc, argv, types),
else => error.uniformNotSupported,
};
uniform.setShaderValue(self.cache.shader.*);
}
fn handle_method(
_path: [*c]const u8,
_types: [*c]const u8,
argv: [*c][*c]c.lo_arg,
argc: c_int,
_: c.lo_message,
userdata: ?*anyopaque,
) callconv(.C) c_int {
const self = @ptrCast(*ControlServer, @alignCast(@alignOf(ControlServer), userdata.?));
const path = _path[0..c.strlen(_path) :0];
const types = _types[0..@intCast(u32, argc)];
if (!std.mem.startsWith(u8, path, "/")) {
std.debug.print("invalid OSC message {s} ({s})\n", .{ path, types });
return 1;
}
self.set_uniform(path[1..], argv, argc, types) catch |err| {
std.debug.print("{} while processing {s}\n", .{ err, path });
};
return 0;
}
fn handle_bundle_start(_: c.lo_timetag, _: ?*anyopaque) callconv(.C) c_int {
return 1;
}
fn handle_bundle_end(_: ?*anyopaque) callconv(.C) c_int {
return 1;
}
};