From 16116371f567a869afbbcd2411a42cf13d25185d Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 13 Jul 2019 17:42:46 -0300 Subject: [PATCH 1/5] split in_buf/out_buf into RunBuffers struct - add basics of RandomNoise custom plugin --- src/custom.zig | 24 ++++++++++++++++++++++++ src/image.zig | 15 +++++++++++++-- src/lang.zig | 4 ++++ src/plugin.zig | 41 +++++++++++++++++++++++++++++++++++------ src/runner.zig | 11 +++++++++++ 5 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 src/custom.zig diff --git a/src/custom.zig b/src/custom.zig new file mode 100644 index 0000000..e0fadc3 --- /dev/null +++ b/src/custom.zig @@ -0,0 +1,24 @@ +// Custom plugins +const std = @import("std"); +const lv2 = @import("lv2_helpers.zig"); +const plugins = @import("plugin.zig"); + +const c = lv2.c; + +const RunContext = plugins.RunContext; + +pub const RandomNoise = struct { + allocator: *std.mem.Allocator, + + pub fn init( + allocator: *std.mem.Allocator, + ) RandomNoise { + return RandomNoise{ + .allocator = allocator, + }; + } + + pub fn run(self: *RandomNoise, rctx: *RunContext) void { + rctx.in_buf[0] = f32(2); + } +}; diff --git a/src/image.zig b/src/image.zig index 6fd37db..f6a2ae1 100644 --- a/src/image.zig +++ b/src/image.zig @@ -292,15 +292,18 @@ pub const Image = struct { var i: usize = seek_pos.start; std.debug.warn("\tseek pos start: {} end: {}\n", seek_pos.start, seek_pos.end); + var inbuf = rctx.buffers.in; + var outbuf = rctx.buffers.out; + while (i <= seek_pos.end) : (i += 1) { - const read_bytes = c.sf_readf_float(self.sndfile, rctx.in_buf.ptr, 1); + const read_bytes = c.sf_readf_float(self.sndfile, inbuf.ptr, 1); if (read_bytes == 0) { std.debug.warn("WARN! reached EOF at idx={}\n", i); break; } lv2.lilv_instance_run(rctx.instance, 1); - try swrite(out_file, rctx.out_buf.ptr, 1); + try swrite(out_file, outbuf.ptr, 1); } _ = c.sf_seek(self.sndfile, @intCast(i64, seek_pos.end), c.SEEK_SET); @@ -326,4 +329,12 @@ pub const Image = struct { std.debug.warn("saved to '{}'\n", out_path); try std.fs.copyFile(self.curpath, out_path); } + + pub fn runCustomPlugin( + self: *Image, + comptime Plugin: type, + pos: plugins.Position, + ) void { + var plugin = Plugin.init(self.allocator); + } }; diff --git a/src/lang.zig b/src/lang.zig index 0c38a93..20506fa 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -26,6 +26,7 @@ pub const CommandType = enum { Delay, Vinyl, RevDelay, + Noise, }; pub const Command = struct { @@ -143,6 +144,9 @@ pub const Lang = struct { _ = try self.keywords.put("delay", .Delay); _ = try self.keywords.put("vinyl", .Vinyl); _ = try self.keywords.put("revdelay", .RevDelay); + + // custom implementations (not lv2) + _ = try self.keywords.put("noise", .Noise); } pub fn parse(self: *Lang, data: []const u8) !CommandList { diff --git a/src/plugin.zig b/src/plugin.zig index abc22f5..a3e40d4 100644 --- a/src/plugin.zig +++ b/src/plugin.zig @@ -57,10 +57,36 @@ pub const Context = struct { } }; +/// Specific run context for non-plugins. +pub const RunBuffers = struct { + allocator: *std.mem.Allocator, + + in: []f32, + out: []f32, + + pub fn init(allocator: *std.mem.Allocator) !RunBuffers { + // we allocate []f32 with size 2 to account for stereo plugins, however + // we only use &in_buf[0] and &out_buf[0], and don't use the + // (supposedly) right side of neither input or output. + var in_buf = try allocator.alloc(f32, 2); + std.mem.secureZero(f32, in_buf); + + return RunBuffers{ + .allocator = allocator, + .in = in_buf, + .out = try allocator.alloc(f32, 2), + }; + } + + pub fn deinit(self: *RunBuffers) void { + self.allocator.free(self.in); + self.allocator.free(self.out); + } +}; + /// Represents the specific run context of plugin instantation. pub const RunContext = struct { - in_buf: []f32, - out_buf: []f32, + buffers: RunBuffers, instance: *c.LilvInstance, pub fn init( @@ -79,20 +105,23 @@ pub const RunContext = struct { std.mem.secureZero(f32, in_buf); return RunContext{ - .in_buf = in_buf, - .out_buf = try allocator.alloc(f32, 2), + .buffers = try RunBuffers.init(allocator), .instance = instance.?, }; } pub fn deinit(self: *RunContext) void { c.lilv_instance_free(self.instance); + self.buffers.deinit(); } pub fn connectPorts(self: *RunContext, ports: []*lv2.Port) void { var i: usize = 0; var o: usize = 0; + var in_buf = self.buffers.in; + var out_buf = self.buffers.out; + for (ports) |port, p_idx| { var p = @intCast(u32, p_idx); @@ -100,10 +129,10 @@ pub const RunContext = struct { .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]); + lv2.lilv_instance_connect_port(self.instance, p, &in_buf[i]); i += 1; } else { - lv2.lilv_instance_connect_port(self.instance, p, &self.out_buf[o]); + lv2.lilv_instance_connect_port(self.instance, p, &out_buf[o]); o += 1; } }, diff --git a/src/runner.zig b/src/runner.zig index 5632dca..4e54aaa 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -2,6 +2,7 @@ const std = @import("std"); const lang = @import("lang.zig"); const images = @import("image.zig"); const plugin = @import("plugin.zig"); +const custom = @import("custom.zig"); const Position = plugin.Position; const ParamList = plugin.ParamList; @@ -245,6 +246,11 @@ pub const Runner = struct { try image.runPlugin("http://plugin.org.uk/swh-plugins/revdelay", pos, params); } + fn noiseCmd(self: *Runner, pos: Position, params: ParamList) !void { + var image = try self.getImage(); + image.runCustomPlugin(custom.RandomNoise, pos); + } + fn runCommand(self: *Runner, cmd: *lang.Command) !void { var params = ParamList.init(self.allocator); defer params.deinit(); @@ -384,6 +390,11 @@ pub const Runner = struct { try self.revDelayCmd(pos, params); }, + .Noise => blk: { + const pos = try cmd.consumePosition(); + try self.noiseCmd(pos, params); + }, + else => blk: { std.debug.warn("Unsupported command: {}\n", cmd.command); break :blk RunError.UnknownCommand; -- 2.34.1 From 3edca782acbaa5eba631fd9469a95877e0e16ba3 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 13 Jul 2019 17:49:09 -0300 Subject: [PATCH 2/5] add basic rng init on RandomNoise plugin --- examples/noise.scri | 3 +++ src/custom.zig | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 examples/noise.scri diff --git a/examples/noise.scri b/examples/noise.scri new file mode 100644 index 0000000..171c934 --- /dev/null +++ b/examples/noise.scri @@ -0,0 +1,3 @@ +load :0; +noise 3 1; +quicksave; diff --git a/src/custom.zig b/src/custom.zig index e0fadc3..0e2b3e3 100644 --- a/src/custom.zig +++ b/src/custom.zig @@ -5,20 +5,24 @@ const plugins = @import("plugin.zig"); const c = lv2.c; -const RunContext = plugins.RunContext; +const RunBuffers = plugins.RunBuffers; pub const RandomNoise = struct { allocator: *std.mem.Allocator, + r: std.rand.DefaultPrng, pub fn init( allocator: *std.mem.Allocator, ) RandomNoise { + var r = std.rand.DefaultPrng.init(std.time.timestamp()); + return RandomNoise{ .allocator = allocator, + .r = r, }; } - pub fn run(self: *RandomNoise, rctx: *RunContext) void { - rctx.in_buf[0] = f32(2); + pub fn run(self: *RandomNoise, bufs: *RunBuffers) void { + bufs.out[0] = self.r.random.float(f32); } }; -- 2.34.1 From f85ff2037d5b4de798b295b03d8fec3c53ab6d3c Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 13 Jul 2019 18:17:44 -0300 Subject: [PATCH 3/5] finish custom plugins impl, finish noise cmd - add ParamMap --- examples/noise.scri | 4 ++- src/custom.zig | 40 +++++++++++++++++----- src/image.zig | 81 ++++++++++++++++++++++++++++++++++++++++++--- src/lang.zig | 10 ++++++ src/plugin.zig | 1 + src/runner.zig | 14 ++++++-- 6 files changed, 134 insertions(+), 16 deletions(-) diff --git a/examples/noise.scri b/examples/noise.scri index 171c934..a202f7e 100644 --- a/examples/noise.scri +++ b/examples/noise.scri @@ -1,3 +1,5 @@ load :0; -noise 3 1; +noise 5 1 3981 10000; +noise 5 2 3982 0; +noise 5 3 3982 5; quicksave; diff --git a/src/custom.zig b/src/custom.zig index 0e2b3e3..bd9d583 100644 --- a/src/custom.zig +++ b/src/custom.zig @@ -8,21 +8,45 @@ const c = lv2.c; const RunBuffers = plugins.RunBuffers; pub const RandomNoise = struct { - allocator: *std.mem.Allocator, r: std.rand.DefaultPrng, + rand_buf: ?[]f32, + cnt: usize = 0, pub fn init( allocator: *std.mem.Allocator, - ) RandomNoise { - var r = std.rand.DefaultPrng.init(std.time.timestamp()); + params: *plugins.ParamMap, + ) !RandomNoise { + const seed = @floatToInt(u64, params.get("seed").?.value); + const fillbytes = @floatToInt(usize, params.get("fill_bytes").?.value); - return RandomNoise{ - .allocator = allocator, - .r = r, - }; + var r = std.rand.DefaultPrng.init(seed); + + if (fillbytes > 0) { + var rand_buf = try allocator.alloc(f32, fillbytes); + + for (rand_buf) |_, idx| { + rand_buf[idx] = r.random.float(f32); + } + + return RandomNoise{ + .r = r, + .rand_buf = rand_buf, + }; + } else { + return RandomNoise{ + .r = r, + .rand_buf = null, + }; + } } pub fn run(self: *RandomNoise, bufs: *RunBuffers) void { - bufs.out[0] = self.r.random.float(f32); + if (self.rand_buf) |rand_buf| { + if (self.cnt >= rand_buf.len) self.cnt = 0; + bufs.out[0] = rand_buf[self.cnt]; + self.cnt += 1; + } else { + bufs.out[0] = self.r.random.float(f32); + } } }; diff --git a/src/image.zig b/src/image.zig index f6a2ae1..020d893 100644 --- a/src/image.zig +++ b/src/image.zig @@ -62,7 +62,7 @@ fn getEndPos(path: []const u8) !usize { fn temporaryName(allocator: *std.mem.Allocator) ![]u8 { const template_start = "/temp/temp_"; - const template = "/tmp/temp_XXXXXX"; + const template = "/tmp/temp_XXXXXXXX"; var nam = try allocator.alloc(u8, template.len); std.mem.copy(u8, nam, template); @@ -333,8 +333,81 @@ pub const Image = struct { pub fn runCustomPlugin( self: *Image, comptime Plugin: type, - pos: plugins.Position, - ) void { - var plugin = Plugin.init(self.allocator); + position: plugins.Position, + params: *plugins.ParamMap, + ) !void { + var plugin = try Plugin.init(self.allocator, params); + + // the code here is a copypaste of runPlugin() without the specific + // lilv things. + var tmpnam = try temporaryName(self.allocator); + std.debug.warn("\trunning CUSTOM plugin from '{}' to '{}'\n", self.curpath, tmpnam); + + var out_fmt = mkSfInfo(); + var out_file = try sopen(self.allocator, tmpnam, c.SFM_WRITE, &out_fmt); + + var bufs = try plugins.RunBuffers.init(self.allocator); + defer bufs.deinit(); + + const file_end = try getEndPos(self.curpath); + const seek_pos = position.seekPos(file_end); + std.debug.warn("\tstart {} end {}\n", seek_pos.start, seek_pos.end); + + // make sure we start from 0 + _ = c.sf_seek(self.sndfile, 0, c.SEEK_SET); + + // there are four main stages: + // - the bmp header copy + // - pre-plugin + // - CUSTOM plugin + // - post-plugin + + var file_copy_buf = try self.allocator.alloc(f32, BufferSize); + defer self.allocator.free(file_copy_buf); + + // pre-plugin copy, merged with bmp header copy + try self.copyBytes( + out_file, + file_copy_buf, + usize(0), + seek_pos.start + @mod(seek_pos.start, BufferSize), + ); + + _ = c.sf_seek(self.sndfile, @intCast(i64, seek_pos.start), c.SEEK_SET); + + var i: usize = seek_pos.start; + std.debug.warn("\tseek pos start: {} end: {}\n", seek_pos.start, seek_pos.end); + + var inbuf = bufs.in; + var outbuf = bufs.out; + + while (i <= seek_pos.end) : (i += 1) { + const read_bytes = c.sf_readf_float(self.sndfile, bufs.in.ptr, 1); + if (read_bytes == 0) { + std.debug.warn("WARN! reached EOF at idx={}\n", i); + break; + } + + plugin.run(&bufs); + try swrite(out_file, bufs.out.ptr, 1); + } + + _ = c.sf_seek(self.sndfile, @intCast(i64, seek_pos.end), c.SEEK_SET); + + // post-plugin copy + try self.copyBytes( + out_file, + file_copy_buf, + seek_pos.end + 1, + file_end + @mod(file_end, BufferSize), + ); + + c.sf_write_sync(out_file); + _ = c.sf_close(out_file); + _ = c.sf_close(self.sndfile); + + // reopen the file as SFM_READ so we can run plugin chains etc + self.sndfile = try sopen(self.allocator, tmpnam, c.SFM_READ, &out_fmt); + self.curpath = tmpnam; } }; diff --git a/src/lang.zig b/src/lang.zig index 20506fa..922e1dc 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -107,6 +107,16 @@ pub const Command = struct { .value = val, }); } + + pub fn appendParamMap( + self: *Command, + map: *plugin.ParamMap, + symbol: []const u8, + ) !void { + var val = try self.floatArgAt(self.cur_idx); + self.cur_idx += 1; + _ = try map.put(symbol, val); + } }; pub const CommandList = std.ArrayList(*Command); diff --git a/src/plugin.zig b/src/plugin.zig index a3e40d4..7794c9e 100644 --- a/src/plugin.zig +++ b/src/plugin.zig @@ -16,6 +16,7 @@ pub const Param = struct { /// List of parameters to be set to control ports. pub const ParamList = std.ArrayList(Param); +pub const ParamMap = std.AutoHashMap([]const u8, f32); /// Represents an absolute position in the image. pub const SeekPos = struct { diff --git a/src/runner.zig b/src/runner.zig index 4e54aaa..fde23ff 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -6,6 +6,7 @@ const custom = @import("custom.zig"); const Position = plugin.Position; const ParamList = plugin.ParamList; +const ParamMap = plugin.ParamMap; const Image = images.Image; @@ -246,15 +247,18 @@ pub const Runner = struct { try image.runPlugin("http://plugin.org.uk/swh-plugins/revdelay", pos, params); } - fn noiseCmd(self: *Runner, pos: Position, params: ParamList) !void { + fn noiseCmd(self: *Runner, pos: Position, map: *ParamMap) !void { var image = try self.getImage(); - image.runCustomPlugin(custom.RandomNoise, pos); + try image.runCustomPlugin(custom.RandomNoise, pos, map); } fn runCommand(self: *Runner, cmd: *lang.Command) !void { var params = ParamList.init(self.allocator); defer params.deinit(); + var map = ParamMap.init(self.allocator); + defer map.deinit(); + return switch (cmd.command) { .Noop => {}, .Load => blk: { @@ -392,7 +396,11 @@ pub const Runner = struct { .Noise => blk: { const pos = try cmd.consumePosition(); - try self.noiseCmd(pos, params); + + try cmd.appendParamMap(&map, "seed"); + try cmd.appendParamMap(&map, "fill_bytes"); + + try self.noiseCmd(pos, &map); }, else => blk: { -- 2.34.1 From a993c1dc0e3f73b45631b8fe348f545cb9e47e89 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 13 Jul 2019 18:23:28 -0300 Subject: [PATCH 4/5] add noise cmd to doc --- doc/README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/README.md b/doc/README.md index 7a34553..4678a48 100644 --- a/doc/README.md +++ b/doc/README.md @@ -138,14 +138,19 @@ Parameters: - `feedback`: Feedback, 0..1, default 0 - `xfade_samp`: Crossfade samples (int), 0..5000, default 1250 +## `noise split index seed repeat_bytes` + +Inject random noise on the image. + +Parameters: + - `seed`, Random seed + - `repeat_bytes`, Amount of bytes to preload with random data and repeat + throughout the image slice + ## TODO `echo split index delay` Run an echo filter on the given loaded file. -## TODO `noise split index seed` - -Inject white noise on the image. - ## `quicksave` Save the file on the same directory of the file specified by `load`, but -- 2.34.1 From a71ff830ac9e2aef51dee465e815bfb9b60ee577 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 13 Jul 2019 19:55:57 -0300 Subject: [PATCH 5/5] add wildnoise cmd --- doc/README.md | 4 +++- examples/noise.scri | 2 +- src/custom.zig | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/lang.zig | 2 ++ src/runner.zig | 14 ++++++++++++++ 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/doc/README.md b/doc/README.md index 4678a48..95995be 100644 --- a/doc/README.md +++ b/doc/README.md @@ -138,10 +138,12 @@ Parameters: - `feedback`: Feedback, 0..1, default 0 - `xfade_samp`: Crossfade samples (int), 0..5000, default 1250 -## `noise split index seed repeat_bytes` +## `wildnoise/noise split index seed repeat_bytes` Inject random noise on the image. +the `wildnoise` version gives a harder version than `noise`. + Parameters: - `seed`, Random seed - `repeat_bytes`, Amount of bytes to preload with random data and repeat diff --git a/examples/noise.scri b/examples/noise.scri index a202f7e..57ae984 100644 --- a/examples/noise.scri +++ b/examples/noise.scri @@ -1,5 +1,5 @@ load :0; noise 5 1 3981 10000; -noise 5 2 3982 0; +wildnoise 5 2 3982 0; noise 5 3 3982 5; quicksave; diff --git a/src/custom.zig b/src/custom.zig index bd9d583..d1e7269 100644 --- a/src/custom.zig +++ b/src/custom.zig @@ -50,3 +50,47 @@ pub const RandomNoise = struct { } } }; + +pub const WildNoise = struct { + r: std.rand.DefaultPrng, + rand_buf: ?[]f32, + cnt: usize = 0, + + pub fn init( + allocator: *std.mem.Allocator, + params: *plugins.ParamMap, + ) !WildNoise { + const seed = @floatToInt(u64, params.get("seed").?.value); + const fillbytes = @floatToInt(usize, params.get("fill_bytes").?.value); + + var r = std.rand.DefaultPrng.init(seed); + + if (fillbytes > 0) { + var rand_buf = try allocator.alloc(f32, fillbytes); + + for (rand_buf) |_, idx| { + rand_buf[idx] = @intToFloat(f32, r.random.int(u1)); + } + + return WildNoise{ + .r = r, + .rand_buf = rand_buf, + }; + } else { + return WildNoise{ + .r = r, + .rand_buf = null, + }; + } + } + + pub fn run(self: *WildNoise, bufs: *RunBuffers) void { + if (self.rand_buf) |rand_buf| { + if (self.cnt >= rand_buf.len) self.cnt = 0; + bufs.out[0] = rand_buf[self.cnt]; + self.cnt += 1; + } else { + bufs.out[0] = @intToFloat(f32, self.r.random.int(u1)); + } + } +}; diff --git a/src/lang.zig b/src/lang.zig index 922e1dc..6b6b980 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -27,6 +27,7 @@ pub const CommandType = enum { Vinyl, RevDelay, Noise, + WildNoise, }; pub const Command = struct { @@ -157,6 +158,7 @@ pub const Lang = struct { // custom implementations (not lv2) _ = try self.keywords.put("noise", .Noise); + _ = try self.keywords.put("wildnoise", .WildNoise); } pub fn parse(self: *Lang, data: []const u8) !CommandList { diff --git a/src/runner.zig b/src/runner.zig index fde23ff..c835ba9 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -252,6 +252,11 @@ pub const Runner = struct { try image.runCustomPlugin(custom.RandomNoise, pos, map); } + fn wildNoiseCmd(self: *Runner, pos: Position, map: *ParamMap) !void { + var image = try self.getImage(); + try image.runCustomPlugin(custom.WildNoise, pos, map); + } + fn runCommand(self: *Runner, cmd: *lang.Command) !void { var params = ParamList.init(self.allocator); defer params.deinit(); @@ -403,6 +408,15 @@ pub const Runner = struct { try self.noiseCmd(pos, &map); }, + .WildNoise => blk: { + const pos = try cmd.consumePosition(); + + try cmd.appendParamMap(&map, "seed"); + try cmd.appendParamMap(&map, "fill_bytes"); + + try self.wildNoiseCmd(pos, &map); + }, + else => blk: { std.debug.warn("Unsupported command: {}\n", cmd.command); break :blk RunError.UnknownCommand; -- 2.34.1