diff --git a/doc/README.md b/doc/README.md index 746b6f1..3182f7d 100644 --- a/doc/README.md +++ b/doc/README.md @@ -62,6 +62,8 @@ Parameters: ## `mbeq split index band_1 band_2 band_3 band_4 band_5 band_6 band_7 band_8 band_9 band_10 band_11 band_12 band_13 band_14 band_15` +Multiband EQ from the SWH plugins. + In respective order, the band arugments represent the: 50Hz, 100Hz, 156Hz, 220Hz, 311Hz, 440Hz, 622Hz, 880Hz 1250Hz, 1750Hz, 2500Hz, 3500Hz, 5000Hz, 10000Hz and 20000Hz frequencies. @@ -69,6 +71,18 @@ In respective order, the band arugments represent the: All of them represent the band's gain in dB. The range is -70 to +30dB, default is 0. +## `chorus split index voices delay_base voice_spread detune law_freq attendb` + +Multivoice Chrorus from the SWH plugins. + +Parameters: + - `voices`: Number of voices (int), 1..8, default 1 + - `delay_base`: Delay base (ms), 10..40, default 10 + - `voice_spread`: Voice separation (ms), 0-2, default 0.5 + - `detune`: Detune (%), 0..5, default 1 + - `law_freq`: LFO frequency (Hz), 2..30, default 9 + - `attendb`: Output attenuation (dB), -20..0, default 0 + ## TODO `echo split index delay` Run an echo filter on the given loaded file. diff --git a/examples/chorus.scri b/examples/chorus.scri new file mode 100644 index 0000000..bf30a1b --- /dev/null +++ b/examples/chorus.scri @@ -0,0 +1,3 @@ +load :0; +chorus 3 1 3 10 0.01 0.1 2 0; +quicksave; diff --git a/src/lang.zig b/src/lang.zig index 2ad9e9f..802ba82 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -18,6 +18,7 @@ pub const CommandType = enum { Eq, Phaser, Mbeq, + Chorus, }; pub const Command = struct { @@ -82,6 +83,19 @@ pub const Command = struct { return arr.toSliceConst(); } + + pub fn appendParam( + self: *const Command, + params: *plugin.ParamList, + symbol: []const u8, + idx: usize, + ) !void { + var val = try self.floatArgAt(idx); + try params.append(plugin.Param{ + .sym = symbol, + .value = val, + }); + } }; pub const CommandList = std.ArrayList(*Command); @@ -111,6 +125,7 @@ pub const Lang = struct { _ = try self.keywords.put("eq", .Eq); _ = try self.keywords.put("mbeq", .Mbeq); _ = try self.keywords.put("phaser", .Phaser); + _ = try self.keywords.put("chorus", .Chorus); } pub fn parse(self: *Lang, data: []const u8) !CommandList { diff --git a/src/runner.zig b/src/runner.zig index ab0cefd..b0d7c33 100644 --- a/src/runner.zig +++ b/src/runner.zig @@ -3,6 +3,9 @@ const lang = @import("lang.zig"); const images = @import("image.zig"); const plugin = @import("plugin.zig"); +const Position = plugin.Position; +const ParamList = plugin.ParamList; + const Image = images.Image; pub const RunError = error{ @@ -11,10 +14,6 @@ pub const RunError = error{ ImageRequired, }; -fn appendParam(params: *plugin.ParamList, sym: []const u8, value: f32) !void { - try params.append(plugin.Param{ .sym = sym, .value = value }); -} - pub const Runner = struct { allocator: *std.mem.Allocator, image: ?*Image = null, @@ -154,101 +153,59 @@ 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 { + fn ampCmd(self: *Runner, pos: Position, params: ParamList) !void { var image = try self.getImage(); - - var params = plugin.ParamList.init(self.allocator); - defer params.deinit(); - - try params.append(plugin.Param{ .sym = "gain", .value = gain }); - - // 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 }, - params, - ); + try image.runPlugin("http://lv2plug.in/plugins/eg-amp", pos, params); } - fn rFlangerCmd( - self: *Runner, - split: usize, - index: usize, - delay_depth_avg: f32, - law_freq: f32, - ) !void { + fn rFlangerCmd(self: *Runner, pos: Position, params: ParamList) !void { var image = try self.getImage(); - var params = plugin.ParamList.init(self.allocator); - defer params.deinit(); - - try params.append(plugin.Param{ .sym = "delay_depth_avg", .value = delay_depth_avg }); - try params.append(plugin.Param{ .sym = "law_freq", .value = law_freq }); - - try image.runPlugin( - "http://plugin.org.uk/swh-plugins/retroFlange", - plugin.Position{ .split = split, .index = index }, - params, - ); + try image.runPlugin("http://plugin.org.uk/swh-plugins/retroFlange", pos, params); } - fn eqCmd( - self: *Runner, - position: plugin.Position, - lo: f32, - mid: f32, - high: f32, - ) !void { + fn eqCmd(self: *Runner, position: Position, params: ParamList) !void { var image = try self.getImage(); - var params = plugin.ParamList.init(self.allocator); - defer params.deinit(); - - try appendParam(¶ms, "lo", lo); - try appendParam(¶ms, "mid", mid); - try appendParam(¶ms, "hi", high); - try image.runPlugin("http://plugin.org.uk/swh-plugins/dj_eq_mono", position, params); } - fn phaserCmd( - self: *Runner, - position: plugin.Position, - lfo_rate: f32, - lfo_depth: f32, - fb: f32, - spread: f32, - ) !void { + fn phaserCmd(self: *Runner, position: Position, params: ParamList) !void { var image = try self.getImage(); - var params = plugin.ParamList.init(self.allocator); - defer params.deinit(); - - try appendParam(¶ms, "lfo_rate", lfo_rate); - try appendParam(¶ms, "lfo_depth", lfo_depth); - try appendParam(¶ms, "fb", fb); - try appendParam(¶ms, "spread", spread); - try image.runPlugin("http://plugin.org.uk/swh-plugins/lfoPhaser", position, params); } fn mbeqCmd( self: *Runner, - position: plugin.Position, + position: Position, bands: []const f32, ) !void { var image = try self.getImage(); - var params = plugin.ParamList.init(self.allocator); + var params = ParamList.init(self.allocator); defer params.deinit(); for (bands) |band_value, idx| { var sym = try std.fmt.allocPrint(self.allocator, "band_{}", idx + 1); - try appendParam(¶ms, sym, band_value); + try params.append(plugin.Param{ + .sym = sym, + .value = band_value, + }); } try image.runPlugin("http://plugin.org.uk/swh-plugins/mbeq", position, params); } + fn chorusCmd( + self: *Runner, + pos: Position, + params: ParamList, + ) !void { + var image = try self.getImage(); + try image.runPlugin("http://plugin.org.uk/swh-plugins/multivoiceChorus", pos, params); + } + fn runCommand(self: *Runner, cmd: *lang.Command) !void { + var params = ParamList.init(self.allocator); + defer params.deinit(); + return switch (cmd.command) { .Noop => {}, .Load => blk: { @@ -259,40 +216,36 @@ pub const Runner = struct { .Quicksave => try self.quicksaveCmd(), .Amp => blk: { - const split = try cmd.usizeArgAt(0); - const index = try cmd.usizeArgAt(1); - const gain = try cmd.floatArgAt(2); - try self.ampCmd(split, index, gain); + const pos = try cmd.consumePosition(); + try cmd.appendParam(¶ms, "gain", 2); + try self.ampCmd(pos, params); }, .RFlanger => blk: { - const split = try cmd.usizeArgAt(0); - const index = try cmd.usizeArgAt(1); - const delay_depth_avg = try cmd.floatArgAt(2); - const law_freq = try cmd.floatArgAt(3); - - try self.rFlangerCmd(split, index, delay_depth_avg, law_freq); + const pos = try cmd.consumePosition(); + try cmd.appendParam(¶ms, "delay_depth_avg", 2); + try cmd.appendParam(¶ms, "law_freq", 3); + try self.rFlangerCmd(pos, params); }, .Eq => blk: { const pos = try cmd.consumePosition(); + try cmd.appendParam(¶ms, "lo", 2); + try cmd.appendParam(¶ms, "mid", 3); + try cmd.appendParam(¶ms, "hi", 4); - const lo = try cmd.floatArgAt(2); - const mid = try cmd.floatArgAt(3); - const high = try cmd.floatArgAt(4); - - try self.eqCmd(pos, lo, mid, high); + try self.eqCmd(pos, params); }, .Phaser => blk: { const pos = try cmd.consumePosition(); - const lfo_rate = try cmd.floatArgAt(2); - const lfo_depth = try cmd.floatArgAt(3); - const fb = try cmd.floatArgAt(4); - const spread = try cmd.floatArgAt(5); + try cmd.appendParam(¶ms, "lfo_rate", 2); + try cmd.appendParam(¶ms, "lfo_depth", 3); + try cmd.appendParam(¶ms, "fb", 4); + try cmd.appendParam(¶ms, "spread", 5); - try self.phaserCmd(pos, lfo_rate, lfo_depth, fb, spread); + try self.phaserCmd(pos, params); }, .Mbeq => blk: { @@ -301,8 +254,21 @@ pub const Runner = struct { try self.mbeqCmd(pos, bands); }, + .Chorus => blk: { + const pos = try cmd.consumePosition(); + + try cmd.appendParam(¶ms, "voices", 2); + try cmd.appendParam(¶ms, "delay_base", 3); + try cmd.appendParam(¶ms, "voice_spread", 4); + try cmd.appendParam(¶ms, "detune", 5); + try cmd.appendParam(¶ms, "law_freq", 6); + try cmd.appendParam(¶ms, "attendb", 7); + + try self.chorusCmd(pos, params); + }, + else => blk: { - std.debug.warn("Unknown command: {}\n", cmd.command); + std.debug.warn("Unsupported command: {}\n", cmd.command); break :blk RunError.UnknownCommand; }, };