diff --git a/.gitignore b/.gitignore index 3cef7be..b43912f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ zig-cache/ +*.mp3 +*.wav diff --git a/examples/embed.scri b/examples/embed.scri new file mode 100644 index 0000000..b78f7c9 --- /dev/null +++ b/examples/embed.scri @@ -0,0 +1,3 @@ +load :0; +embed 3 1 ./file.wav; +quicksave; diff --git a/src/custom.zig b/src/custom.zig index 3f57a4b..0727113 100644 --- a/src/custom.zig +++ b/src/custom.zig @@ -2,6 +2,7 @@ const std = @import("std"); const lv2 = @import("lv2_helpers.zig"); const plugins = @import("plugin.zig"); +const image = @import("image.zig"); const c = lv2.c; @@ -131,3 +132,63 @@ pub const Write = struct { bufs.out[0] = self.data; } }; + +pub const Embed = struct { + allocator: *std.mem.Allocator, + filepath: []const u8, + + sndfile: *c.SNDFILE = undefined, + buf: []f32 = undefined, + + pub fn init(allocator: *std.mem.Allocator, filepath: []const u8) @This() { + return Embed{ + .allocator = allocator, + .filepath = filepath, + }; + } + + pub fn setup(self: *@This()) !void { + var in_fmt = c.SF_INFO{ + .frames = c_int(0), + .samplerate = c_int(0), + .channels = c_int(0), + .format = c_int(0), + .sections = c_int(0), + .seekable = c_int(0), + }; + + self.sndfile = try image.sopen( + self.allocator, + self.filepath, + c.SFM_READ, + &in_fmt, + ); + + image.sseek(self.sndfile, 0); + + self.buf = try self.allocator.alloc(f32, @intCast(usize, in_fmt.channels)); + } + + pub fn deinit(self: *@This()) void {} + + pub fn run(self: *@This(), bufs: *RunBuffers) void { + const read_bytes = c.sf_readf_float(self.sndfile, self.buf.ptr, 1); + + if (read_bytes == 0) { + bufs.out[0] = bufs.in[0]; + return; + } + + if (read_bytes < 0) { + const st: i32 = c.sf_error(self.sndfile); + std.debug.warn( + "Failed to read {} ({})\n", + self.filepath, + c.sf_error_number(st), + ); + return; + } + + bufs.out[0] = bufs.in[0] + self.buf[0]; + } +}; diff --git a/src/image.zig b/src/image.zig index 2b2a38a..9748bc9 100644 --- a/src/image.zig +++ b/src/image.zig @@ -1,7 +1,6 @@ const std = @import("std"); const lv2 = @import("lv2_helpers.zig"); const c = lv2.c; -const custom = @import("custom.zig"); const bmp = @import("bmp_valid.zig"); const plugins = @import("plugin.zig"); @@ -23,7 +22,7 @@ pub const ImageError = error{ }; /// Low level integration function with libsndfile. -fn sopen( +pub fn sopen( allocator: *std.mem.Allocator, path: []const u8, mode: i32, @@ -57,7 +56,7 @@ fn sopen( return file.?; } -fn swrite(file: *c.SNDFILE, buf: [*]f32, frames: i64) !void { +pub fn swrite(file: *c.SNDFILE, buf: [*]f32, frames: i64) !void { const count = c.sf_writef_float(file, buf, frames); if (count != frames) { @@ -66,7 +65,7 @@ fn swrite(file: *c.SNDFILE, buf: [*]f32, frames: i64) !void { } } -fn sseek(file: *c.SNDFILE, offset: usize) void { +pub fn sseek(file: *c.SNDFILE, offset: usize) void { const offset_i64 = @intCast(i64, offset); const frames = c.sf_seek(file, offset_i64, c.SEEK_SET); const frames_current = c.sf_seek(file, 0, c.SEEK_CUR); @@ -109,7 +108,7 @@ pub fn temporaryName(allocator: *std.mem.Allocator) ![]u8 { return error.TempGenFail; } -fn mkSfInfo() c.SF_INFO { +pub fn mkSfInfo() c.SF_INFO { return c.SF_INFO{ .frames = c_int(0), .samplerate = c_int(44100), @@ -422,9 +421,10 @@ pub const Image = struct { self: *Image, comptime Plugin: type, position: plugins.Position, - params: *plugins.ParamMap, + comptime ExtraType: type, + extra: ExtraType, ) !void { - var plugin_opt: ?Plugin = Plugin.init(self.allocator, params); + var plugin_opt: ?Plugin = Plugin.init(self.allocator, extra); if (plugin_opt == null) { return ImageError.PluginLoadFail; } @@ -432,6 +432,13 @@ pub const Image = struct { var plugin = plugin_opt.?; defer plugin.deinit(); + const decls = comptime std.meta.declarations(Plugin); + inline for (decls) |decl| { + if (comptime std.mem.eql(u8, decl.name, "setup")) { + try plugin.setup(); + } + } + // the code here is a copypaste of runPlugin() without the specific // lilv things. var tmpnam = try temporaryName(self.allocator); diff --git a/src/lang.zig b/src/lang.zig index a75193b..02dbf3b 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -30,6 +30,7 @@ pub const CommandType = enum { Noise, WildNoise, Write, + Embed, Rotate, }; @@ -187,6 +188,7 @@ pub const Lang = struct { _ = try self.keywords.put("noise", .Noise); _ = try self.keywords.put("wildnoise", .WildNoise); _ = try self.keywords.put("write", .Write); + _ = try self.keywords.put("embed", .Embed); // even more custom _ = try self.keywords.put("rotate", .Rotate); @@ -225,6 +227,7 @@ pub const Lang = struct { "noise", "wildnoise", "write", + "embed", "rotate", }; @@ -250,6 +253,7 @@ pub const Lang = struct { .Noise, .WildNoise, .Write, + .Embed, .Rotate, }; @@ -312,6 +316,7 @@ pub const Lang = struct { .WildNoise => try self.expectFloat(4, cmd.args), .Write => try self.expectFloat(3, cmd.args), .Rotate => try self.expectAny(2, cmd.args), + .Embed => try self.expectAny(3, cmd.args), else => std.debug.warn("WARN unchecked command {}\n", cmd.command), } } diff --git a/src/printer.zig b/src/printer.zig index 2a30f8e..f198579 100644 --- a/src/printer.zig +++ b/src/printer.zig @@ -24,6 +24,7 @@ pub fn printList(list: langs.CommandList, stream: var) !void { .Noise => "noise", .WildNoise => "wildnoise", .Write => "write", + .Embed => "embed", .Rotate => "rotate", }; diff --git a/src/runner.zig b/src/runner.zig index ede7b3b..5ae666c 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -266,17 +266,22 @@ pub const Runner = struct { fn noiseCmd(self: *Runner, pos: Position, map: *ParamMap) !void { var image = try self.getImage(); - try image.runCustomPlugin(custom.RandomNoise, pos, map); + try image.runCustomPlugin(custom.RandomNoise, pos, *ParamMap, map); } fn wildNoiseCmd(self: *Runner, pos: Position, map: *ParamMap) !void { var image = try self.getImage(); - try image.runCustomPlugin(custom.WildNoise, pos, map); + try image.runCustomPlugin(custom.WildNoise, pos, *ParamMap, map); } fn writeCmd(self: *Runner, pos: Position, map: *ParamMap) !void { var image = try self.getImage(); - try image.runCustomPlugin(custom.Write, pos, map); + try image.runCustomPlugin(custom.Write, pos, *ParamMap, map); + } + + fn embedCmd(self: *Runner, pos: Position, path: []const u8) !void { + var image = try self.getImage(); + try image.runCustomPlugin(custom.Embed, pos, []const u8, path); } fn rotateCmd( @@ -303,6 +308,8 @@ pub const Runner = struct { .Load => blk: { var path = cmd.args.at(0); try self.loadCmd(path); + + // TODO is this needed? break :blk; }, .Quicksave => try self.quicksaveCmd(), @@ -460,6 +467,12 @@ pub const Runner = struct { try self.writeCmd(pos, &map); }, + .Embed => blk: { + const pos = try cmd.consumePosition(); + const path = cmd.args.at(2); + try self.embedCmd(pos, path); + }, + .Rotate => blk: { const deg = try cmd.floatArgAt(0); const bgfill = try cmd.argAt(1);