From 05fe41d8b4ed47c14cc5a807856e34c609627f91 Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 9 Jul 2019 13:55:52 -0300 Subject: [PATCH 1/3] add setting of parameters to the ports - fix ampCmd's param pass --- src/image.zig | 49 +++++++++++++++++++++++++++++++++------------ src/lv2_helpers.zig | 1 + src/runner.zig | 5 +---- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/image.zig b/src/image.zig index f165896..785ae91 100644 --- a/src/image.zig +++ b/src/image.zig @@ -1,12 +1,6 @@ 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 c = lv2.c; const plugins = @import("plugin.zig"); @@ -14,6 +8,7 @@ pub const ImageError = error{ OpenFail, InvalidPlugin, UnknownPlugin, + InvalidSymbol, }; /// Low level integration function with libsndfile. @@ -103,13 +98,41 @@ pub const Image = struct { 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 ctx = try plugins.makeContext(self.allocator, plugin_uri); + std.debug.warn("\tworld: {}\n", ctx.world); + std.debug.warn("\tplugin: {}\n", ctx.plugin); - var ports = try lv2.setupPorts(&context); - for (ports) |port| { - std.debug.warn("port: {}\n", port.*); + var ports = try lv2.setupPorts(&ctx); + + if (ctx.n_audio_in != 1) { + std.debug.warn("plugin <{}> does not accept mono input.\n", plugin_uri); + return ImageError.InvalidPlugin; + } + + // now, for each param for the plugin, we find its port, and set + // the value for the port there. + var it = params.iterator(); + + while (it.next()) |param| { + var sym_cstr = try std.cstr.addNullByte(self.allocator, param.sym); + defer self.allocator.free(sym_cstr); + + var sym = c.lilv_new_string(ctx.world, sym_cstr.ptr); + const port = c.lilv_plugin_get_port_by_symbol(ctx.plugin, sym) orelse blk: { + std.debug.warn("assert fail: symbol not found on port"); + return ImageError.InvalidSymbol; + }; + + c.lilv_node_free(sym); + + var idx = c.lilv_port_get_index(ctx.plugin, port); + std.debug.warn( + "sym={}, idx={} to val={}\n", + param.sym, + idx, + param.value, + ); + ports[idx].value = param.value; } } }; diff --git a/src/lv2_helpers.zig b/src/lv2_helpers.zig index 8a6dc57..834a813 100644 --- a/src/lv2_helpers.zig +++ b/src/lv2_helpers.zig @@ -2,6 +2,7 @@ const std = @import("std"); const plugin = @import("plugin.zig"); pub const c = @cImport({ + @cInclude("sndfile.h"); @cInclude("lilv/lilv.h"); @cInclude("lv2/core/lv2.h"); }); diff --git a/src/runner.zig b/src/runner.zig index 0be0451..4b47dbd 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -143,12 +143,9 @@ pub const Runner = struct { /// 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); + try params.append(plugin.Param{ .sym = "gain", .value = gain }); defer params.deinit(); try image.runPlugin( From fc69a3f62fad124981e14d2097b82d7e96f534ac Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 9 Jul 2019 14:29:54 -0300 Subject: [PATCH 2/3] add temporary file name generator --- src/image.zig | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/image.zig b/src/image.zig index 785ae91..55639a8 100644 --- a/src/image.zig +++ b/src/image.zig @@ -37,6 +37,37 @@ fn sopen( return file.?; } +fn temporaryName(allocator: *std.mem.Allocator) ![]u8 { + const template_start = "/temp/temp_"; + const template = "/tmp/temp_XXXXXX"; + var nam = try allocator.alloc(u8, template.len); + std.mem.copy(u8, nam, template); + + var r = std.rand.DefaultPrng.init(std.time.timestamp()); + + var fill = nam[template_start.len..nam.len]; + + var i: usize = 0; + while (i < 100) : (i += 1) { + + // generate a random uppercase letter, that is, 65 + random number. + for (fill) |_, f_idx| { + var idx = @intCast(u8, r.random.uintLessThan(u5, 24)); + var letter = u8(65) + idx; + fill[f_idx] = letter; + } + + // if we fail to access it, we assume it doesn't exist and return it. + std.fs.File.access(nam) catch |err| { + if (err == error.FileNotFound) { + return nam; + } + }; + } + + return error.TempGenFail; +} + pub const Image = struct { allocator: *std.mem.Allocator, sndfile: *c.SNDFILE, @@ -92,6 +123,10 @@ pub const Image = struct { return n_read == 1; } + /// Run a plugin over the image. + /// This setups a new lilv world/plugin among other things. + /// The internal SNDFILE pointer is modified to point to the output of the + /// plugin run. pub fn runPlugin( self: *Image, plugin_uri: []const u8, @@ -134,5 +169,10 @@ pub const Image = struct { ); ports[idx].value = param.value; } + + // now we need to generate a temporary file and put the output of + // running the plugin on that file + var tmpnam = try temporaryName(self.allocator); + std.debug.warn("temporary name: {}\n", tmpnam); } }; From 25bef2393303f7a6208c8ea46f0e45e2cc4e9be1 Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 9 Jul 2019 15:15:02 -0300 Subject: [PATCH 3/3] add basic lilv instantiation support --- src/image.zig | 28 +++++++++++++++++++-------- src/plugin.zig | 51 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/runner.zig | 3 +++ 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/src/image.zig b/src/image.zig index 55639a8..19d859b 100644 --- a/src/image.zig +++ b/src/image.zig @@ -9,6 +9,7 @@ pub const ImageError = error{ InvalidPlugin, UnknownPlugin, InvalidSymbol, + InstantiateFail, }; /// Low level integration function with libsndfile. @@ -68,6 +69,17 @@ fn temporaryName(allocator: *std.mem.Allocator) ![]u8 { return error.TempGenFail; } +fn mkSfInfo() c.SF_INFO { + return c.SF_INFO{ + .frames = c_int(0), + .samplerate = c_int(44100), + .channels = c_int(1), + .format = c.SF_FORMAT_ULAW | c.SF_FORMAT_RAW | c.SF_ENDIAN_BIG, + .sections = c_int(0), + .seekable = c_int(0), + }; +} + pub const Image = struct { allocator: *std.mem.Allocator, sndfile: *c.SNDFILE, @@ -75,14 +87,7 @@ pub const Image = struct { /// Open a BMP file. pub fn open(allocator: *std.mem.Allocator, path: []const u8) !*Image { - var in_fmt = c.SF_INFO{ - .frames = c_int(0), - .samplerate = c_int(44100), - .channels = c_int(1), - .format = c.SF_FORMAT_ULAW | c.SF_FORMAT_RAW | c.SF_ENDIAN_BIG, - .sections = c_int(0), - .seekable = c_int(0), - }; + var in_fmt = mkSfInfo(); var sndfile = try sopen(allocator, path, c.SFM_READ, &in_fmt); var image = try allocator.create(Image); @@ -174,5 +179,12 @@ pub const Image = struct { // running the plugin on that file var tmpnam = try temporaryName(self.allocator); std.debug.warn("temporary name: {}\n", tmpnam); + + var out_fmt = mkSfInfo(); + var out_file = try sopen(self.allocator, tmpnam, c.SFM_WRITE, &out_fmt); + + var rctx = try plugins.RunContext.init(self.allocator, ctx.plugin); + rctx.connectPorts(ports); + lv2.lilv_instance_activate(rctx.instance); } }; diff --git a/src/plugin.zig b/src/plugin.zig index a555b11..a96fe39 100644 --- a/src/plugin.zig +++ b/src/plugin.zig @@ -1,6 +1,7 @@ const std = @import("std"); -const c = @import("lv2_helpers.zig").c; +const lv2 = @import("lv2_helpers.zig"); +const c = lv2.c; const ImageError = @import("image.zig").ImageError; @@ -26,13 +27,59 @@ pub const Position = struct { pub const Context = struct { allocator: *std.mem.Allocator, world: *c.LilvWorld, - plugin: ?*const c.LilvPlugin, + plugin: *const c.LilvPlugin, // they should both be 1. n_audio_in: usize = 0, n_audio_out: usize = 0, }; +/// Represents the specific run context of plugin instantation. +pub const RunContext = struct { + in_buf: []f32, + out_buf: []f32, + instance: *c.LilvInstance, + + pub fn init( + allocator: *std.mem.Allocator, + plugin: *const c.LilvPlugin, + ) !RunContext { + var instance = c.lilv_plugin_instantiate(plugin, f64(44100), null); + if (instance == null) { + return ImageError.InstantiateFail; + } + + return RunContext{ + .in_buf = try allocator.alloc(f32, 1), + .out_buf = try allocator.alloc(f32, 1), + .instance = instance.?, + }; + } + + pub fn connectPorts(self: *RunContext, ports: []*lv2.Port) void { + var i: usize = 0; + var o: usize = 0; + + for (ports) |port, p_idx| { + var p = @intCast(u32, p_idx); + + switch (port.ptype) { + .Control => lv2.lilv_instance_connect_port(self.instance, p, &port.value), + .Audio => blk: { + if (port.is_input) { + lv2.lilv_instance_connect_port(self.instance, p, &self.in_buf[i]); + i += 1; + } else { + lv2.lilv_instance_connect_port(self.instance, p, &self.out_buf[o]); + o += 1; + } + }, + else => lv2.lilv_instance_connect_port(self.instance, p, null), + } + } + } +}; + 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().?; diff --git a/src/runner.zig b/src/runner.zig index 4b47dbd..f704ea1 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -148,6 +148,9 @@ pub const Runner = struct { try params.append(plugin.Param{ .sym = "gain", .value = gain }); defer params.deinit(); + // TODO if we could detect that the next command is a quicksave then + // we don't need the temporary file in runPlugin and instead we + // dump it all on the output image for it. try image.runPlugin( "http://lv2plug.in/plugins/eg-amp", plugin.Position{ .split = split, .index = index },