diff --git a/examples/middle_echo.scri b/examples/middle_echo.scri index 45bc8d1..18e29f6 100644 --- a/examples/middle_echo.scri +++ b/examples/middle_echo.scri @@ -1,4 +1,3 @@ load :0; -amp 3 1 10; -# echo 3 0 1 0.2; +echo 3 0 1 0.2; quicksave; diff --git a/src/image.zig b/src/image.zig index f165896..0def9ba 100644 --- a/src/image.zig +++ b/src/image.zig @@ -1,20 +1,9 @@ const std = @import("std"); -const lv2 = @import("lv2_helpers.zig"); - const c = @cImport({ @cInclude("sndfile.h"); - - @cInclude("lilv/lilv.h"); - @cInclude("lv2/core/lv2.h"); }); -const plugins = @import("plugin.zig"); - -pub const ImageError = error{ - OpenFail, - InvalidPlugin, - UnknownPlugin, -}; +pub const ImageError = error{OpenFail}; /// Low level integration function with libsndfile. fn sopen( @@ -81,35 +70,4 @@ pub const Image = struct { ); } } - - pub fn read(self: *Image, file_chans: c_int, buf: []f32) bool { - var file = file_opt.?; - - const n_read: c.sf_count_t = c.sf_readf_float(file, buf.ptr, 1); - const buf_chans = @intCast(c_int, buf.len); - - var i = file_chans - 1; - while (i < buf_chans) : (i += 1) { - //buf[@intCast(usize, i)] = buf[i % file_chans]; - buf[@intCast(usize, i)] = buf[@intCast(usize, @mod(i, file_chans))]; - } - - return n_read == 1; - } - - pub fn runPlugin( - self: *Image, - plugin_uri: []const u8, - pos: plugins.Position, - params: plugins.ParamList, - ) !void { - var context = try plugins.makeContext(self.allocator, plugin_uri); - std.debug.warn("world: {}\n", context.world); - std.debug.warn("plugin: {}\n", context.plugin); - - var ports = try lv2.setupPorts(&context); - for (ports) |port| { - std.debug.warn("port: {}\n", port.*); - } - } }; diff --git a/src/lang.zig b/src/lang.zig index ed5b402..8b91e8f 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -3,7 +3,6 @@ const std = @import("std"); pub const ParseError = error{ NoCommandGiven, UnknownCommand, - ArgRequired, }; pub const CommandType = enum { @@ -20,32 +19,6 @@ pub const Command = struct { pub fn print(self: *const Command) void { std.debug.warn("cmd:{}\n", self.command); } - - pub fn argAt(self: *const Command, idx: usize) ![]const u8 { - const args = self.args.toSliceConst(); - - if (idx > (args.len - 1)) { - std.debug.warn("Expected argument at index {}\n", idx); - return ParseError.ArgRequired; - } - - return args[idx]; - } - - pub fn usizeArgAt(self: *const Command, idx: usize) !usize { - var arg = try self.argAt(idx); - return try std.fmt.parseInt(usize, arg, 10); - } - - pub fn intArgAt(self: *const Command, idx: usize) !i32 { - var arg = try self.argAt(idx); - return try std.fmt.parseInt(i32, arg, 10); - } - - pub fn floatArgAt(self: *const Command, idx: usize) !f32 { - var arg = try self.argAt(idx); - return try std.fmt.parseFloat(f32, arg); - } }; pub const CommandList = std.ArrayList(*Command); @@ -82,7 +55,6 @@ pub const Lang = struct { stmt = std.mem.trimLeft(u8, stmt, "\n"); if (stmt.len == 0) continue; - if (stmt[0] == '#') continue; // TODO better tokenizer instead of just tokenize(" "); var tok_it = std.mem.tokenize(stmt, " "); diff --git a/src/lv2_helpers.zig b/src/lv2_helpers.zig deleted file mode 100644 index 8a6dc57..0000000 --- a/src/lv2_helpers.zig +++ /dev/null @@ -1,150 +0,0 @@ -const std = @import("std"); -const plugin = @import("plugin.zig"); - -pub const c = @cImport({ - @cInclude("lilv/lilv.h"); - @cInclude("lv2/core/lv2.h"); -}); - -const LV2_CORE_URI = "http://lv2plug.in/ns/lv2core"; - -pub fn Lv2Core(ns: []const u8) ![]const u8 { - var allocator = std.heap.direct_allocator; - - return try std.cstr.addNullByte( - allocator, - try std.fmt.allocPrint(allocator, "{}{}", LV2_CORE_URI, ns), - ); -} - -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); - } -} - -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. -pub fn setupPorts(ctx: *plugin.Context) ![]*Port { - var world = ctx.world; - const n_ports: u32 = c.lilv_plugin_get_num_ports(ctx.plugin); - - var ports = try ctx.allocator.alloc(*Port, n_ports); - - for (ports) |port_ptr, idx| { - var port = try ctx.allocator.create(Port); - port.* = Port{ - .lilv_port = null, - .ptype = .Control, - .index = f32(0), - .value = f32(0), - .is_input = false, - .optional = false, - }; - - ports[idx] = port; - } - - 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); - - // bad solution, but it really do be like that - const LV2_CORE__InputPort = try Lv2Core("#InputPort"); - const LV2_CORE__OutputPort = try Lv2Core("#OutputPort"); - const LV2_CORE__AudioPort = try Lv2Core("#AudioPort"); - const LV2_CORE__ControlPort = try Lv2Core("#ControlPort"); - const LV2_CORE__connectionOptional = try Lv2Core("#connectionOptional"); - - 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) { - var port: *Port = ports[i]; - - 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])) { - port.value = f32(0); - } 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) { - std.debug.warn("Port {} is neither input or output\n", i); - 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) { - std.debug.warn("Port {} has unsupported type\n", i); - return error.UnsupportedPortType; - } - } - - return ports; -} diff --git a/src/main.zig b/src/main.zig index 2b2fcf6..ddfeea5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -60,6 +60,30 @@ const Param = struct { value: f32, }; +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); + } +} + const ParamList = std.ArrayList(Param); const PortType = enum { diff --git a/src/plugin.zig b/src/plugin.zig deleted file mode 100644 index a555b11..0000000 --- a/src/plugin.zig +++ /dev/null @@ -1,56 +0,0 @@ -const std = @import("std"); - -const c = @import("lv2_helpers.zig").c; - -const ImageError = @import("image.zig").ImageError; - -/// Control port -pub const Param = struct { - /// Port symbol - sym: []const u8, - - /// Control value - value: f32, -}; - -/// List of parameters to be set to control ports. -pub const ParamList = std.ArrayList(Param); - -/// Represents a relative position in the image -pub const Position = struct { - split: usize, - index: usize, -}; - -/// Represents the starting context for a single plugin run. -pub const Context = struct { - allocator: *std.mem.Allocator, - world: *c.LilvWorld, - plugin: ?*const c.LilvPlugin, - - // they should both be 1. - n_audio_in: usize = 0, - n_audio_out: usize = 0, -}; - -pub fn makeContext(allocator: *std.mem.Allocator, plugin_uri: []const u8) !Context { - const cstr_plugin_uri = try std.cstr.addNullByte(allocator, plugin_uri); - var world = c.lilv_world_new().?; - - c.lilv_world_load_all(world); - var uri: *c.LilvNode = c.lilv_new_uri(world, cstr_plugin_uri.ptr) orelse blk: { - std.debug.warn("Invalid plugin URI <{}>\n", plugin_uri); - return ImageError.InvalidPlugin; - }; - - const plugins: *const c.LilvPlugins = c.lilv_world_get_all_plugins(world); - - var plugin: *const c.LilvPlugin = c.lilv_plugins_get_by_uri(plugins, uri) orelse blk: { - std.debug.warn("Plugin <{}> not found\n", plugin_uri); - return ImageError.UnknownPlugin; - }; - - c.lilv_node_free(uri); - - return Context{ .allocator = allocator, .world = world, .plugin = plugin }; -} diff --git a/src/runner.zig b/src/runner.zig index 0be0451..90ccd08 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -1,7 +1,6 @@ const std = @import("std"); const lang = @import("lang.zig"); const images = @import("image.zig"); -const plugin = @import("plugin.zig"); const Image = images.Image; @@ -16,9 +15,7 @@ pub const Runner = struct { image: ?*Image = null, pub fn init(allocator: *std.mem.Allocator) Runner { - return Runner{ - .allocator = allocator, - }; + return Runner{ .allocator = allocator }; } pub fn deinit(self: *Runner) void { @@ -73,8 +70,6 @@ pub const Runner = struct { return RunError.NoBMP; } - // TODO, copy load_path into a temporary file, then use that - // file to work on things. self.image = try Image.open(self.allocator, load_path); } @@ -140,24 +135,6 @@ pub const Runner = struct { std.debug.warn("out path: {}\n", out_path); } - /// Run the http://lv2plug.in/plugins/eg-amp plugin over the file. - fn ampCmd(self: *Runner, split: usize, index: usize, gain: f32) !void { - var image = try self.getImage(); - var param = try self.allocator.create(plugin.Param); - defer self.allocator.destroy(param); - - param.* = plugin.Param{ .sym = "gain", .value = gain }; - - var params = plugin.ParamList.init(self.allocator); - defer params.deinit(); - - try image.runPlugin( - "http://lv2plug.in/plugins/eg-amp", - plugin.Position{ .split = split, .index = index }, - params, - ); - } - fn runCommand(self: *Runner, cmd: *lang.Command) !void { return switch (cmd.command) { .Noop => {}, @@ -166,15 +143,8 @@ pub const Runner = struct { try self.loadCmd(path); break :blk; }, + .Quicksave => try self.quicksaveCmd(), - - .Amp => blk: { - const split = try cmd.usizeArgAt(0); - const index = try cmd.usizeArgAt(1); - const gain = try cmd.floatArgAt(2); - try self.ampCmd(split, index, gain); - }, - else => blk: { std.debug.warn("Unknown command: {}\n", cmd.command); break :blk RunError.UnknownCommand;