diff --git a/examples/middle_echo.scri b/examples/middle_echo.scri index 627c63a..45bc8d1 100644 --- a/examples/middle_echo.scri +++ b/examples/middle_echo.scri @@ -1,4 +1,4 @@ load :0; -amp 10 1; +amp 3 1 10; # echo 3 0 1 0.2; quicksave; diff --git a/src/image.zig b/src/image.zig index 0def9ba..d91a4a3 100644 --- a/src/image.zig +++ b/src/image.zig @@ -1,9 +1,18 @@ const std = @import("std"); const c = @cImport({ @cInclude("sndfile.h"); + + @cInclude("lilv/lilv.h"); + @cInclude("lv2/core/lv2.h"); }); -pub const ImageError = error{OpenFail}; +const plugins = @import("plugin.zig"); + +pub const ImageError = error{ + OpenFail, + InvalidPlugin, + UnknownPlugin, +}; /// Low level integration function with libsndfile. fn sopen( @@ -70,4 +79,30 @@ 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 { + const context = try plugins.makeContext(self.allocator, plugin_uri); + std.debug.warn("world: {}\n", context.world); + std.debug.warn("plugin: {}\n", context.plugin); + } }; diff --git a/src/lang.zig b/src/lang.zig index c558e22..ed5b402 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -3,6 +3,7 @@ const std = @import("std"); pub const ParseError = error{ NoCommandGiven, UnknownCommand, + ArgRequired, }; pub const CommandType = enum { @@ -20,13 +21,29 @@ pub const Command = struct { 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 = self.args.at(idx); + 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 = self.args.at(idx); + var arg = try self.argAt(idx); return try std.fmt.parseFloat(f32, arg); } }; diff --git a/src/lv2_helpers.zig b/src/lv2_helpers.zig new file mode 100644 index 0000000..be9d915 --- /dev/null +++ b/src/lv2_helpers.zig @@ -0,0 +1,39 @@ +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); + } +} diff --git a/src/main.zig b/src/main.zig index ddfeea5..2b2fcf6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -60,30 +60,6 @@ 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 index 4241df4..bfcc420 100644 --- a/src/plugin.zig +++ b/src/plugin.zig @@ -1,5 +1,12 @@ const std = @import("std"); +const c = @cImport({ + @cInclude("lilv/lilv.h"); + @cInclude("lv2/core/lv2.h"); +}); + +const ImageError = @import("image.zig").ImageError; + /// Control port pub const Param = struct { /// Port symbol @@ -9,9 +16,39 @@ pub const Param = struct { 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 { + world: *c.LilvWorld, + plugin: *const c.LilvPlugin, +}; + +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{ .world = world, .plugin = plugin }; +} diff --git a/src/runner.zig b/src/runner.zig index 740394e..0be0451 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -16,7 +16,9 @@ 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 { @@ -139,16 +141,16 @@ pub const Runner = struct { } /// Run the http://lv2plug.in/plugins/eg-amp plugin over the file. - fn ampCmd(self: *Runner, split: i32, index: i32, gain: f32) !void { + 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.* = Param{ .sym = "gain", .value = gain }; + param.* = plugin.Param{ .sym = "gain", .value = gain }; var params = plugin.ParamList.init(self.allocator); defer params.deinit(); - // TODO impl this try image.runPlugin( "http://lv2plug.in/plugins/eg-amp", plugin.Position{ .split = split, .index = index }, @@ -166,12 +168,12 @@ pub const Runner = struct { }, .Quicksave => try self.quicksaveCmd(), - // .Amp => blk: { - // const split = try cmd.intArgAt(0); - // const index = try cmd.intArgAt(1); - // const gain = try cmd.floatArgAt(2); - // try self.ampCmd(split, index, gain); - // } + .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);