diff --git a/src/image.zig b/src/image.zig index 19d859b..f165896 100644 --- a/src/image.zig +++ b/src/image.zig @@ -1,6 +1,12 @@ const std = @import("std"); const lv2 = @import("lv2_helpers.zig"); -const c = lv2.c; + +const c = @cImport({ + @cInclude("sndfile.h"); + + @cInclude("lilv/lilv.h"); + @cInclude("lv2/core/lv2.h"); +}); const plugins = @import("plugin.zig"); @@ -8,8 +14,6 @@ pub const ImageError = error{ OpenFail, InvalidPlugin, UnknownPlugin, - InvalidSymbol, - InstantiateFail, }; /// Low level integration function with libsndfile. @@ -38,48 +42,6 @@ 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; -} - -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, @@ -87,7 +49,14 @@ pub const Image = struct { /// Open a BMP file. pub fn open(allocator: *std.mem.Allocator, path: []const u8) !*Image { - var in_fmt = mkSfInfo(); + 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 sndfile = try sopen(allocator, path, c.SFM_READ, &in_fmt); var image = try allocator.create(Image); @@ -128,63 +97,19 @@ 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, pos: plugins.Position, params: plugins.ParamList, ) !void { - var ctx = try plugins.makeContext(self.allocator, plugin_uri); - std.debug.warn("\tworld: {}\n", ctx.world); - std.debug.warn("\tplugin: {}\n", ctx.plugin); + 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(&ctx); - - if (ctx.n_audio_in != 1) { - std.debug.warn("plugin <{}> does not accept mono input.\n", plugin_uri); - return ImageError.InvalidPlugin; + var ports = try lv2.setupPorts(&context); + for (ports) |port| { + std.debug.warn("port: {}\n", port.*); } - - // 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; - } - - // 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); - - 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/lv2_helpers.zig b/src/lv2_helpers.zig index 834a813..8a6dc57 100644 --- a/src/lv2_helpers.zig +++ b/src/lv2_helpers.zig @@ -2,7 +2,6 @@ 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/plugin.zig b/src/plugin.zig index a96fe39..a555b11 100644 --- a/src/plugin.zig +++ b/src/plugin.zig @@ -1,7 +1,6 @@ const std = @import("std"); -const lv2 = @import("lv2_helpers.zig"); -const c = lv2.c; +const c = @import("lv2_helpers.zig").c; const ImageError = @import("image.zig").ImageError; @@ -27,59 +26,13 @@ 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 f704ea1..0be0451 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -143,14 +143,14 @@ 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(); - // 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 },