commit
2bf44bc581
7 changed files with 284 additions and 13 deletions
|
@ -138,14 +138,21 @@ Parameters:
|
||||||
- `feedback`: Feedback, 0..1, default 0
|
- `feedback`: Feedback, 0..1, default 0
|
||||||
- `xfade_samp`: Crossfade samples (int), 0..5000, default 1250
|
- `xfade_samp`: Crossfade samples (int), 0..5000, default 1250
|
||||||
|
|
||||||
|
## `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
|
||||||
|
throughout the image slice
|
||||||
|
|
||||||
## TODO `echo split index delay`
|
## TODO `echo split index delay`
|
||||||
|
|
||||||
Run an echo filter on the given loaded file.
|
Run an echo filter on the given loaded file.
|
||||||
|
|
||||||
## TODO `noise split index seed`
|
|
||||||
|
|
||||||
Inject white noise on the image.
|
|
||||||
|
|
||||||
## `quicksave`
|
## `quicksave`
|
||||||
|
|
||||||
Save the file on the same directory of the file specified by `load`, but
|
Save the file on the same directory of the file specified by `load`, but
|
||||||
|
|
5
examples/noise.scri
Normal file
5
examples/noise.scri
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
load :0;
|
||||||
|
noise 5 1 3981 10000;
|
||||||
|
wildnoise 5 2 3982 0;
|
||||||
|
noise 5 3 3982 5;
|
||||||
|
quicksave;
|
96
src/custom.zig
Normal file
96
src/custom.zig
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
// Custom plugins
|
||||||
|
const std = @import("std");
|
||||||
|
const lv2 = @import("lv2_helpers.zig");
|
||||||
|
const plugins = @import("plugin.zig");
|
||||||
|
|
||||||
|
const c = lv2.c;
|
||||||
|
|
||||||
|
const RunBuffers = plugins.RunBuffers;
|
||||||
|
|
||||||
|
pub const RandomNoise = struct {
|
||||||
|
r: std.rand.DefaultPrng,
|
||||||
|
rand_buf: ?[]f32,
|
||||||
|
cnt: usize = 0,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
allocator: *std.mem.Allocator,
|
||||||
|
params: *plugins.ParamMap,
|
||||||
|
) !RandomNoise {
|
||||||
|
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] = 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 {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -62,7 +62,7 @@ fn getEndPos(path: []const u8) !usize {
|
||||||
|
|
||||||
fn temporaryName(allocator: *std.mem.Allocator) ![]u8 {
|
fn temporaryName(allocator: *std.mem.Allocator) ![]u8 {
|
||||||
const template_start = "/temp/temp_";
|
const template_start = "/temp/temp_";
|
||||||
const template = "/tmp/temp_XXXXXX";
|
const template = "/tmp/temp_XXXXXXXX";
|
||||||
var nam = try allocator.alloc(u8, template.len);
|
var nam = try allocator.alloc(u8, template.len);
|
||||||
std.mem.copy(u8, nam, template);
|
std.mem.copy(u8, nam, template);
|
||||||
|
|
||||||
|
@ -292,15 +292,18 @@ pub const Image = struct {
|
||||||
var i: usize = seek_pos.start;
|
var i: usize = seek_pos.start;
|
||||||
std.debug.warn("\tseek pos start: {} end: {}\n", seek_pos.start, seek_pos.end);
|
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) {
|
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) {
|
if (read_bytes == 0) {
|
||||||
std.debug.warn("WARN! reached EOF at idx={}\n", i);
|
std.debug.warn("WARN! reached EOF at idx={}\n", i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
lv2.lilv_instance_run(rctx.instance, 1);
|
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);
|
_ = c.sf_seek(self.sndfile, @intCast(i64, seek_pos.end), c.SEEK_SET);
|
||||||
|
@ -326,4 +329,85 @@ pub const Image = struct {
|
||||||
std.debug.warn("saved to '{}'\n", out_path);
|
std.debug.warn("saved to '{}'\n", out_path);
|
||||||
try std.fs.copyFile(self.curpath, out_path);
|
try std.fs.copyFile(self.curpath, out_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn runCustomPlugin(
|
||||||
|
self: *Image,
|
||||||
|
comptime Plugin: type,
|
||||||
|
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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
16
src/lang.zig
16
src/lang.zig
|
@ -26,6 +26,8 @@ pub const CommandType = enum {
|
||||||
Delay,
|
Delay,
|
||||||
Vinyl,
|
Vinyl,
|
||||||
RevDelay,
|
RevDelay,
|
||||||
|
Noise,
|
||||||
|
WildNoise,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Command = struct {
|
pub const Command = struct {
|
||||||
|
@ -106,6 +108,16 @@ pub const Command = struct {
|
||||||
.value = val,
|
.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);
|
pub const CommandList = std.ArrayList(*Command);
|
||||||
|
@ -143,6 +155,10 @@ pub const Lang = struct {
|
||||||
_ = try self.keywords.put("delay", .Delay);
|
_ = try self.keywords.put("delay", .Delay);
|
||||||
_ = try self.keywords.put("vinyl", .Vinyl);
|
_ = try self.keywords.put("vinyl", .Vinyl);
|
||||||
_ = try self.keywords.put("revdelay", .RevDelay);
|
_ = try self.keywords.put("revdelay", .RevDelay);
|
||||||
|
|
||||||
|
// 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 {
|
pub fn parse(self: *Lang, data: []const u8) !CommandList {
|
||||||
|
|
|
@ -16,6 +16,7 @@ pub const Param = struct {
|
||||||
|
|
||||||
/// List of parameters to be set to control ports.
|
/// List of parameters to be set to control ports.
|
||||||
pub const ParamList = std.ArrayList(Param);
|
pub const ParamList = std.ArrayList(Param);
|
||||||
|
pub const ParamMap = std.AutoHashMap([]const u8, f32);
|
||||||
|
|
||||||
/// Represents an absolute position in the image.
|
/// Represents an absolute position in the image.
|
||||||
pub const SeekPos = struct {
|
pub const SeekPos = struct {
|
||||||
|
@ -57,10 +58,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.
|
/// Represents the specific run context of plugin instantation.
|
||||||
pub const RunContext = struct {
|
pub const RunContext = struct {
|
||||||
in_buf: []f32,
|
buffers: RunBuffers,
|
||||||
out_buf: []f32,
|
|
||||||
instance: *c.LilvInstance,
|
instance: *c.LilvInstance,
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
|
@ -79,20 +106,23 @@ pub const RunContext = struct {
|
||||||
std.mem.secureZero(f32, in_buf);
|
std.mem.secureZero(f32, in_buf);
|
||||||
|
|
||||||
return RunContext{
|
return RunContext{
|
||||||
.in_buf = in_buf,
|
.buffers = try RunBuffers.init(allocator),
|
||||||
.out_buf = try allocator.alloc(f32, 2),
|
|
||||||
.instance = instance.?,
|
.instance = instance.?,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *RunContext) void {
|
pub fn deinit(self: *RunContext) void {
|
||||||
c.lilv_instance_free(self.instance);
|
c.lilv_instance_free(self.instance);
|
||||||
|
self.buffers.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connectPorts(self: *RunContext, ports: []*lv2.Port) void {
|
pub fn connectPorts(self: *RunContext, ports: []*lv2.Port) void {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
var o: usize = 0;
|
var o: usize = 0;
|
||||||
|
|
||||||
|
var in_buf = self.buffers.in;
|
||||||
|
var out_buf = self.buffers.out;
|
||||||
|
|
||||||
for (ports) |port, p_idx| {
|
for (ports) |port, p_idx| {
|
||||||
var p = @intCast(u32, p_idx);
|
var p = @intCast(u32, p_idx);
|
||||||
|
|
||||||
|
@ -100,10 +130,10 @@ pub const RunContext = struct {
|
||||||
.Control => lv2.lilv_instance_connect_port(self.instance, p, &port.value),
|
.Control => lv2.lilv_instance_connect_port(self.instance, p, &port.value),
|
||||||
.Audio => blk: {
|
.Audio => blk: {
|
||||||
if (port.is_input) {
|
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;
|
i += 1;
|
||||||
} else {
|
} 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;
|
o += 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,9 +2,11 @@ const std = @import("std");
|
||||||
const lang = @import("lang.zig");
|
const lang = @import("lang.zig");
|
||||||
const images = @import("image.zig");
|
const images = @import("image.zig");
|
||||||
const plugin = @import("plugin.zig");
|
const plugin = @import("plugin.zig");
|
||||||
|
const custom = @import("custom.zig");
|
||||||
|
|
||||||
const Position = plugin.Position;
|
const Position = plugin.Position;
|
||||||
const ParamList = plugin.ParamList;
|
const ParamList = plugin.ParamList;
|
||||||
|
const ParamMap = plugin.ParamMap;
|
||||||
|
|
||||||
const Image = images.Image;
|
const Image = images.Image;
|
||||||
|
|
||||||
|
@ -245,10 +247,23 @@ pub const Runner = struct {
|
||||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/revdelay", pos, params);
|
try image.runPlugin("http://plugin.org.uk/swh-plugins/revdelay", pos, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn noiseCmd(self: *Runner, pos: Position, map: *ParamMap) !void {
|
||||||
|
var image = try self.getImage();
|
||||||
|
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 {
|
fn runCommand(self: *Runner, cmd: *lang.Command) !void {
|
||||||
var params = ParamList.init(self.allocator);
|
var params = ParamList.init(self.allocator);
|
||||||
defer params.deinit();
|
defer params.deinit();
|
||||||
|
|
||||||
|
var map = ParamMap.init(self.allocator);
|
||||||
|
defer map.deinit();
|
||||||
|
|
||||||
return switch (cmd.command) {
|
return switch (cmd.command) {
|
||||||
.Noop => {},
|
.Noop => {},
|
||||||
.Load => blk: {
|
.Load => blk: {
|
||||||
|
@ -384,6 +399,24 @@ pub const Runner = struct {
|
||||||
try self.revDelayCmd(pos, params);
|
try self.revDelayCmd(pos, params);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.Noise => blk: {
|
||||||
|
const pos = try cmd.consumePosition();
|
||||||
|
|
||||||
|
try cmd.appendParamMap(&map, "seed");
|
||||||
|
try cmd.appendParamMap(&map, "fill_bytes");
|
||||||
|
|
||||||
|
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: {
|
else => blk: {
|
||||||
std.debug.warn("Unsupported command: {}\n", cmd.command);
|
std.debug.warn("Unsupported command: {}\n", cmd.command);
|
||||||
break :blk RunError.UnknownCommand;
|
break :blk RunError.UnknownCommand;
|
||||||
|
|
Loading…
Reference in a new issue