const std = @import("std"); const lv2 = @import("lv2_helpers.zig"); const c = lv2.c; const plugins = @import("plugin.zig"); pub const ImageError = error{ OpenFail, InvalidPlugin, UnknownPlugin, InvalidSymbol, }; /// Low level integration function with libsndfile. fn sopen( allocator: *std.mem.Allocator, path: []const u8, mode: i32, fmt: *c.SF_INFO, ) !*c.SNDFILE { var cstr_path = try std.cstr.addNullByte(allocator, path); defer allocator.free(cstr_path); var file = c.sf_open(cstr_path.ptr, mode, fmt); const st: i32 = c.sf_error(file); if (st != 0) { std.debug.warn( "Failed to open {} ({})\n", path, c.sf_error_number(st), ); return ImageError.OpenFail; } return file.?; } pub const Image = struct { allocator: *std.mem.Allocator, sndfile: *c.SNDFILE, path: []const u8, /// 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 sndfile = try sopen(allocator, path, c.SFM_READ, &in_fmt); var image = try allocator.create(Image); image.* = Image{ .allocator = allocator, .sndfile = sndfile, .path = path, }; return image; } pub fn close(self: *Image) void { var st: i32 = c.sf_close(self.sndfile); if (st != 0) { std.debug.warn( "Failed to close {} ({})\n", self.path, c.sf_error_number(st), ); } } 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 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(&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; } } };