2019-07-09 16:21:07 +00:00
|
|
|
const std = @import("std");
|
|
|
|
const plugin = @import("plugin.zig");
|
|
|
|
|
|
|
|
pub const c = @cImport({
|
2019-07-09 16:55:52 +00:00
|
|
|
@cInclude("sndfile.h");
|
2019-07-09 03:04:01 +00:00
|
|
|
@cInclude("lilv/lilv.h");
|
2020-06-03 01:24:43 +00:00
|
|
|
@cInclude("lv2.h");
|
2019-07-09 03:04:01 +00:00
|
|
|
});
|
|
|
|
|
2019-08-10 17:36:51 +00:00
|
|
|
pub fn Lv2Core(comptime ns: []const u8) []const u8 {
|
|
|
|
const LV2_CORE_URI = "http://lv2plug.in/ns/lv2core";
|
|
|
|
return LV2_CORE_URI ++ ns ++ [_]u8{0};
|
2019-07-09 03:04:01 +00:00
|
|
|
}
|
|
|
|
|
2019-08-10 17:36:51 +00:00
|
|
|
const LV2_CORE__InputPort = Lv2Core("#InputPort");
|
|
|
|
const LV2_CORE__OutputPort = Lv2Core("#OutputPort");
|
|
|
|
const LV2_CORE__AudioPort = Lv2Core("#AudioPort");
|
|
|
|
const LV2_CORE__ControlPort = Lv2Core("#ControlPort");
|
|
|
|
const LV2_CORE__connectionOptional = Lv2Core("#connectionOptional");
|
|
|
|
|
2019-07-09 03:04:01 +00:00
|
|
|
pub fn lilv_instance_connect_port(
|
|
|
|
instance: [*c]c.LilvInstance,
|
|
|
|
port_index: u32,
|
|
|
|
data_location: ?*c_void,
|
|
|
|
) void {
|
|
|
|
instance.?.*.lv2_descriptor.?.*.connect_port.?(instance.?.*.lv2_handle, port_index, data_location);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn lilv_instance_activate(instance: [*c]c.LilvInstance) void {
|
|
|
|
if (instance.?.*.lv2_descriptor.?.*.activate != null) {
|
|
|
|
instance.?.*.lv2_descriptor.?.*.activate.?(instance.?.*.lv2_handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn lilv_instance_run(instance: [*c]c.LilvInstance, sample_count: u32) void {
|
|
|
|
instance.?.*.lv2_descriptor.?.*.run.?(instance.?.*.lv2_handle, sample_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn lilv_instance_deactivate(instance: [*c]c.LilvInstance) void {
|
|
|
|
if (instance.?.*.lv2_descriptor.?.*.deactivate != null) {
|
|
|
|
instance.?.*.lv2_descriptor.?.*.deactivate.?(instance.?.*.lv2_handle);
|
|
|
|
}
|
|
|
|
}
|
2019-07-09 16:21:07 +00:00
|
|
|
|
|
|
|
pub const PortType = enum {
|
|
|
|
Control,
|
|
|
|
Audio,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub const Port = struct {
|
|
|
|
lilv_port: ?*const c.LilvPort,
|
|
|
|
ptype: PortType,
|
|
|
|
index: u32,
|
|
|
|
value: f32,
|
|
|
|
is_input: bool,
|
|
|
|
optional: bool,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Setup ports for a given plugin. Gives an array to pointers of Port structs.
|
|
|
|
/// This setup is required so we link the plugin to the ports later on, and
|
|
|
|
/// also link our buffers, and control values.
|
2019-08-15 01:35:22 +00:00
|
|
|
pub fn setupPorts(ctx: *plugin.Context) ![]Port {
|
2019-07-09 16:21:07 +00:00
|
|
|
var world = ctx.world;
|
|
|
|
const n_ports: u32 = c.lilv_plugin_get_num_ports(ctx.plugin);
|
|
|
|
|
2019-08-15 01:35:22 +00:00
|
|
|
var ports = try ctx.allocator.alloc(Port, n_ports);
|
2019-07-09 16:21:07 +00:00
|
|
|
|
2019-08-15 01:35:22 +00:00
|
|
|
for (ports) |_, idx| {
|
|
|
|
var port: *Port = &ports[idx];
|
2019-07-09 16:21:07 +00:00
|
|
|
port.* = Port{
|
|
|
|
.lilv_port = null,
|
|
|
|
.ptype = .Control,
|
2019-11-10 16:37:59 +00:00
|
|
|
.index = @as(f32, 0),
|
|
|
|
.value = @as(f32, 0),
|
2019-07-09 16:21:07 +00:00
|
|
|
.is_input = false,
|
|
|
|
.optional = false,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
var values: []f32 = try ctx.allocator.alloc(f32, n_ports);
|
|
|
|
defer ctx.allocator.free(values);
|
|
|
|
|
|
|
|
c.lilv_plugin_get_port_ranges_float(ctx.plugin, null, null, values.ptr);
|
|
|
|
var lv2_InputPort = c.lilv_new_uri(world, LV2_CORE__InputPort.ptr);
|
|
|
|
defer std.heap.c_allocator.destroy(lv2_InputPort);
|
|
|
|
|
|
|
|
var lv2_OutputPort = c.lilv_new_uri(world, LV2_CORE__OutputPort.ptr);
|
|
|
|
defer std.heap.c_allocator.destroy(lv2_OutputPort);
|
|
|
|
|
|
|
|
var lv2_AudioPort = c.lilv_new_uri(world, LV2_CORE__AudioPort.ptr);
|
|
|
|
defer std.heap.c_allocator.destroy(lv2_AudioPort);
|
|
|
|
|
|
|
|
var lv2_ControlPort = c.lilv_new_uri(world, LV2_CORE__ControlPort.ptr);
|
|
|
|
defer std.heap.c_allocator.destroy(lv2_ControlPort);
|
|
|
|
|
|
|
|
var lv2_connectionOptional = c.lilv_new_uri(world, LV2_CORE__connectionOptional.ptr);
|
|
|
|
defer std.heap.c_allocator.destroy(lv2_connectionOptional);
|
|
|
|
|
|
|
|
var i: u32 = 0;
|
|
|
|
while (i < n_ports) : (i += 1) {
|
2019-08-15 01:35:22 +00:00
|
|
|
var port: *Port = &ports[i];
|
2019-07-09 16:21:07 +00:00
|
|
|
|
|
|
|
const lport = c.lilv_plugin_get_port_by_index(ctx.plugin, i).?;
|
|
|
|
|
|
|
|
port.lilv_port = lport;
|
|
|
|
port.index = i;
|
|
|
|
|
|
|
|
if (std.math.isNan(values[i])) {
|
2019-11-10 16:37:59 +00:00
|
|
|
port.value = @as(f32, 0);
|
2019-07-09 16:21:07 +00:00
|
|
|
} else {
|
|
|
|
port.value = values[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
port.optional = c.lilv_port_has_property(ctx.plugin, lport, lv2_connectionOptional);
|
|
|
|
|
|
|
|
if (c.lilv_port_is_a(ctx.plugin, lport, lv2_InputPort)) {
|
|
|
|
port.is_input = true;
|
|
|
|
} else if (!c.lilv_port_is_a(ctx.plugin, lport, lv2_OutputPort) and !port.optional) {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("Port {} is neither input or output\n", .{i});
|
2019-07-09 16:21:07 +00:00
|
|
|
return error.UnassignedIOPort;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if port is an audio or control port
|
|
|
|
if (c.lilv_port_is_a(ctx.plugin, lport, lv2_ControlPort)) {
|
|
|
|
port.ptype = .Control;
|
|
|
|
} else if (c.lilv_port_is_a(ctx.plugin, lport, lv2_AudioPort)) {
|
|
|
|
port.ptype = .Audio;
|
|
|
|
|
|
|
|
if (port.is_input) {
|
|
|
|
ctx.n_audio_in += 1;
|
|
|
|
} else {
|
|
|
|
ctx.n_audio_out += 1;
|
|
|
|
}
|
|
|
|
} else if (!port.optional) {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("Port {} has unsupported type\n", .{i});
|
2019-07-09 16:21:07 +00:00
|
|
|
return error.UnsupportedPortType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ports;
|
|
|
|
}
|