Use comptime for fully declarative LV2 and Custom commands #14
6 changed files with 867 additions and 944 deletions
|
@ -16,15 +16,12 @@ pub const RandomNoise = struct {
|
|||
|
||||
pub fn init(
|
||||
allocator: *std.mem.Allocator,
|
||||
params: *plugins.ParamMap,
|
||||
params: var,
|
||||
) ?RandomNoise {
|
||||
const seed = @floatToInt(u64, params.get("seed").?.value);
|
||||
const fillbytes = @floatToInt(usize, params.get("fill_bytes").?.value);
|
||||
var r = std.rand.DefaultPrng.init(params.seed);
|
||||
|
||||
var r = std.rand.DefaultPrng.init(seed);
|
||||
|
||||
if (fillbytes > 0) {
|
||||
var rand_buf = allocator.alloc(f32, fillbytes) catch return null;
|
||||
if (params.fill_bytes > 0) {
|
||||
var rand_buf = allocator.alloc(f32, params.fill_bytes) catch return null;
|
||||
|
||||
for (rand_buf) |_, idx| {
|
||||
rand_buf[idx] = r.random.float(f32);
|
||||
|
@ -67,15 +64,12 @@ pub const WildNoise = struct {
|
|||
|
||||
pub fn init(
|
||||
allocator: *std.mem.Allocator,
|
||||
params: *plugins.ParamMap,
|
||||
params: var,
|
||||
) ?WildNoise {
|
||||
const seed = @floatToInt(u64, params.get("seed").?.value);
|
||||
const fillbytes = @floatToInt(usize, params.get("fill_bytes").?.value);
|
||||
var r = std.rand.DefaultPrng.init(params.seed);
|
||||
|
||||
var r = std.rand.DefaultPrng.init(seed);
|
||||
|
||||
if (fillbytes > 0) {
|
||||
var rand_buf = allocator.alloc(f32, fillbytes) catch return null;
|
||||
if (params.fill_bytes > 0) {
|
||||
var rand_buf = allocator.alloc(f32, params.fill_bytes) catch return null;
|
||||
|
||||
for (rand_buf) |_, idx| {
|
||||
rand_buf[idx] = @intToFloat(f32, r.random.int(u1));
|
||||
|
@ -118,11 +112,10 @@ pub const Write = struct {
|
|||
|
||||
pub fn init(
|
||||
allocator: *std.mem.Allocator,
|
||||
params: *plugins.ParamMap,
|
||||
params: var,
|
||||
) Write {
|
||||
const data = params.get("data").?;
|
||||
return Write{
|
||||
.data = data.value,
|
||||
.data = params.data,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -140,10 +133,10 @@ pub const Embed = struct {
|
|||
sndfile: *c.SNDFILE = undefined,
|
||||
buf: []f32 = undefined,
|
||||
|
||||
pub fn init(allocator: *std.mem.Allocator, filepath: []const u8) @This() {
|
||||
pub fn init(allocator: *std.mem.Allocator, params: var) @This() {
|
||||
return Embed{
|
||||
.allocator = allocator,
|
||||
.filepath = filepath,
|
||||
.filepath = params.path,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -75,18 +75,17 @@ pub fn sseek(file: *c.SNDFILE, offset: usize) void {
|
|||
/// Caller owns the returned memory.
|
||||
pub fn temporaryName(allocator: *std.mem.Allocator) ![]u8 {
|
||||
const template_start = "/temp/temp_";
|
||||
const template = "/tmp/temp_XXXXXXXXXXX";
|
||||
const template = "/tmp/temp_XXXXXXXXXXXXXXXXXXXXX";
|
||||
var nam = try allocator.alloc(u8, template.len);
|
||||
std.mem.copy(u8, nam, template);
|
||||
|
||||
const seed = @bitCast(u64, std.time.timestamp());
|
||||
const seed = @truncate(u64, @bitCast(u128, std.time.nanoTimestamp()));
|
||||
var r = std.rand.DefaultPrng.init(seed);
|
||||
|
||||
var fill = nam[template_start.len..nam.len];
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 100) : (i += 1) {
|
||||
|
||||
// generate a random uppercase letter, that is, 65 + random number.
|
||||
for (fill) |_, f_idx| {
|
||||
var idx = @intCast(u8, r.random.uintLessThan(u5, 24));
|
||||
|
@ -95,12 +94,16 @@ pub fn temporaryName(allocator: *std.mem.Allocator) ![]u8 {
|
|||
}
|
||||
|
||||
// if we fail to access it, we assume it doesn't exist and return it.
|
||||
|
||||
_ = std.fs.cwd().openFile(nam, .{ .read = true, .write = false }) catch |err| {
|
||||
if (err == error.FileNotFound) {
|
||||
return nam;
|
||||
}
|
||||
var tmp_file: std.fs.File = std.fs.cwd().openFile(
|
||||
nam,
|
||||
.{ .read = true, .write = false },
|
||||
) catch |err| blk: {
|
||||
if (err == error.FileNotFound) return nam else continue;
|
||||
};
|
||||
|
||||
// if we actually found someone, close the handle so that we don't
|
||||
// get EMFILE later on.
|
||||
tmp_file.close();
|
||||
}
|
||||
|
||||
return error.TempGenFail;
|
||||
|
@ -422,8 +425,7 @@ pub const Image = struct {
|
|||
self: *Image,
|
||||
comptime Plugin: type,
|
||||
position: plugins.Position,
|
||||
comptime ExtraType: type,
|
||||
extra: ExtraType,
|
||||
extra: var,
|
||||
) !void {
|
||||
var plugin_opt: ?Plugin = Plugin.init(self.allocator, extra);
|
||||
if (plugin_opt == null) {
|
||||
|
|
826
src/lang.zig
826
src/lang.zig
|
@ -1,166 +1,486 @@
|
|||
const std = @import("std");
|
||||
|
||||
const plugin = @import("plugin.zig");
|
||||
const custom = @import("custom.zig");
|
||||
|
||||
pub const ParseError = error{
|
||||
OutOfMemory,
|
||||
ArgRequired,
|
||||
ParseFail,
|
||||
};
|
||||
pub const ParseError = error{ParseFail};
|
||||
|
||||
pub const CommandType = enum {
|
||||
Noop,
|
||||
Load,
|
||||
Quicksave,
|
||||
RunQS,
|
||||
/// "LV2 Commands" are commands that receive split, index, and then receive
|
||||
/// any f64 arguments.
|
||||
lv2_command,
|
||||
|
||||
Amp,
|
||||
RFlanger,
|
||||
Eq,
|
||||
Phaser,
|
||||
Mbeq,
|
||||
Chorus,
|
||||
PitchScaler,
|
||||
Reverb,
|
||||
Highpass,
|
||||
Delay,
|
||||
Vinyl,
|
||||
RevDelay,
|
||||
Gate,
|
||||
Detune,
|
||||
Overdrive,
|
||||
Degrade,
|
||||
RePsycho,
|
||||
TalkBox,
|
||||
DynComp,
|
||||
ThruZero,
|
||||
Foverdrive,
|
||||
Gverb,
|
||||
Invert,
|
||||
TapeDelay,
|
||||
ModDelay,
|
||||
MultiChorus,
|
||||
Saturator,
|
||||
VintageDelay,
|
||||
|
||||
Noise,
|
||||
WildNoise,
|
||||
Write,
|
||||
Embed,
|
||||
|
||||
Rotate,
|
||||
custom_command,
|
||||
};
|
||||
|
||||
fn LV2Command(
|
||||
comptime tag: Command.Tag,
|
||||
comptime plugin_url: []const u8,
|
||||
comptime LV2Parameters: type,
|
||||
) type {
|
||||
return struct {
|
||||
pub const base_tag = tag;
|
||||
pub const command_type = CommandType.lv2_command;
|
||||
pub const lv2_url = plugin_url;
|
||||
|
||||
base: Command,
|
||||
split: usize,
|
||||
index: usize,
|
||||
parameters: LV2Parameters,
|
||||
};
|
||||
}
|
||||
|
||||
fn CustomCommand(
|
||||
comptime tag: Command.Tag,
|
||||
comptime Plugin: type,
|
||||
comptime PluginParameters: type,
|
||||
) type {
|
||||
return struct {
|
||||
pub const base_tag = tag;
|
||||
pub const command_type = CommandType.custom_command;
|
||||
pub const plugin_type = Plugin;
|
||||
|
||||
base: Command,
|
||||
split: usize,
|
||||
index: usize,
|
||||
parameters: PluginParameters,
|
||||
};
|
||||
}
|
||||
|
||||
pub const Command = struct {
|
||||
command: CommandType,
|
||||
args: ArgList,
|
||||
cur_idx: usize = 0,
|
||||
tag: Tag,
|
||||
|
||||
pub fn print(self: Command) void {
|
||||
std.debug.warn("cmd:{}\n", .{self.command});
|
||||
}
|
||||
pub const Tag = enum {
|
||||
noop,
|
||||
load,
|
||||
quicksave,
|
||||
runqs,
|
||||
|
||||
pub fn argAt(self: Command, idx: usize) ![]const u8 {
|
||||
std.debug.warn("{} {}", .{ idx, self.args.items.len });
|
||||
amp,
|
||||
rflanger,
|
||||
eq,
|
||||
phaser,
|
||||
mbeq,
|
||||
chorus,
|
||||
pitchscaler,
|
||||
reverb,
|
||||
highpass,
|
||||
delay,
|
||||
vinyl,
|
||||
revdelay,
|
||||
gate,
|
||||
detune,
|
||||
overdrive,
|
||||
degrade,
|
||||
repsycho,
|
||||
talkbox,
|
||||
dyncomp,
|
||||
thruzero,
|
||||
foverdrive,
|
||||
gverb,
|
||||
invert,
|
||||
tapedelay,
|
||||
moddelay,
|
||||
multichorus,
|
||||
saturator,
|
||||
vintagedelay,
|
||||
|
||||
if (idx > (self.args.items.len - 1)) {
|
||||
std.debug.warn("Expected argument at index {}\n", .{idx});
|
||||
return ParseError.ArgRequired;
|
||||
}
|
||||
noise,
|
||||
wildnoise,
|
||||
write,
|
||||
embed,
|
||||
|
||||
return self.args.items[idx];
|
||||
}
|
||||
rotate,
|
||||
};
|
||||
|
||||
pub fn usizeArgAt(self: Command, idx: usize) !usize {
|
||||
var arg = try self.argAt(idx);
|
||||
return try std.fmt.parseInt(usize, arg, 10);
|
||||
}
|
||||
pub fn tagToType(tag: Tag) type {
|
||||
return switch (tag) {
|
||||
.noop => Noop,
|
||||
.load => Load,
|
||||
.quicksave => Quicksave,
|
||||
.runqs => RunQS,
|
||||
|
||||
pub fn consumePosition(self: *Command) !plugin.Position {
|
||||
self.cur_idx = 2;
|
||||
return plugin.Position{
|
||||
.split = try self.usizeArgAt(0),
|
||||
.index = try self.usizeArgAt(1),
|
||||
.amp => Amp,
|
||||
.rflanger => RFlanger,
|
||||
.eq => Eq,
|
||||
.phaser => Phaser,
|
||||
.mbeq => Mbeq,
|
||||
.chorus => Chorus,
|
||||
.pitchscaler => Pitchscaler,
|
||||
.reverb => Reverb,
|
||||
.highpass => Highpass,
|
||||
.delay => Delay,
|
||||
.vinyl => Vinyl,
|
||||
.revdelay => Revdelay,
|
||||
.gate => Gate,
|
||||
.detune => Detune,
|
||||
.overdrive => Overdrive,
|
||||
.degrade => Degrade,
|
||||
.repsycho => Repsycho,
|
||||
.talkbox => Talkbox,
|
||||
.dyncomp => Dyncomp,
|
||||
.thruzero => Thruzero,
|
||||
.foverdrive => Foverdrive,
|
||||
.gverb => Gverb,
|
||||
.invert => Invert,
|
||||
.tapedelay => Tapedelay,
|
||||
.moddelay => Moddelay,
|
||||
.multichorus => Multichorus,
|
||||
.saturator => Saturator,
|
||||
.vintagedelay => Vintagedelay,
|
||||
|
||||
.noise => Noise,
|
||||
.wildnoise => Wildnoise,
|
||||
.write => Write,
|
||||
.embed => Embed,
|
||||
|
||||
.rotate => Rotate,
|
||||
|
||||
else => @panic("TODO"),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn intArgAt(self: Command, idx: usize) !i32 {
|
||||
var arg = try self.argAt(idx);
|
||||
return try std.fmt.parseInt(i32, arg, 10);
|
||||
pub fn cast(base: *const @This(), comptime T: type) ?*const T {
|
||||
if (base.tag != T.base_tag)
|
||||
return null;
|
||||
|
||||
return @fieldParentPtr(T, "base", base);
|
||||
}
|
||||
|
||||
pub fn floatArgAt(self: Command, idx: usize) !f32 {
|
||||
var arg = try self.argAt(idx);
|
||||
return try std.fmt.parseFloat(f32, arg);
|
||||
pub fn print(base: *const @This()) void {
|
||||
std.debug.warn("tag: {}\n", .{base.tag});
|
||||
}
|
||||
|
||||
pub fn floatArgMany(
|
||||
self: Command,
|
||||
allocator: *std.mem.Allocator,
|
||||
start_index: usize,
|
||||
elements: usize,
|
||||
default: f32,
|
||||
) ![]const f32 {
|
||||
var i: usize = start_index;
|
||||
var arr = std.ArrayList(f32).init(allocator);
|
||||
|
||||
while (i < elements) : (i += 1) {
|
||||
var value: f32 = self.floatArgAt(i) catch |err| blk: {
|
||||
std.debug.warn("\tdoing default on arg {}\n", .{i});
|
||||
break :blk default;
|
||||
pub const Noop = struct {
|
||||
pub const base_tag = Tag.noop;
|
||||
base: Command,
|
||||
};
|
||||
|
||||
try arr.append(value);
|
||||
}
|
||||
pub const Load = struct {
|
||||
pub const base_tag = Tag.load;
|
||||
base: Command,
|
||||
path: []u8,
|
||||
};
|
||||
|
||||
return arr.items;
|
||||
}
|
||||
pub const Quicksave = struct {
|
||||
pub const base_tag = Tag.quicksave;
|
||||
base: Command,
|
||||
};
|
||||
|
||||
pub fn appendParam(
|
||||
self: *Command,
|
||||
params: *plugin.ParamList,
|
||||
symbol: []const u8,
|
||||
) !void {
|
||||
var val = try self.floatArgAt(self.cur_idx);
|
||||
self.cur_idx += 1;
|
||||
pub const RunQS = struct {
|
||||
pub const base_tag = Tag.runqs;
|
||||
base: Command,
|
||||
program: []const u8,
|
||||
};
|
||||
|
||||
try params.append(plugin.Param{
|
||||
.sym = symbol,
|
||||
.value = val,
|
||||
pub const Noise = CustomCommand(Tag.noise, custom.RandomNoise, struct {
|
||||
seed: u64,
|
||||
fill_bytes: usize,
|
||||
});
|
||||
}
|
||||
|
||||
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 Wildnoise = CustomCommand(Tag.wildnoise, custom.WildNoise, struct {
|
||||
seed: u64,
|
||||
fill_bytes: usize,
|
||||
});
|
||||
|
||||
pub fn copy(self: Command, allocator: *std.mem.Allocator) !*Command {
|
||||
var cmd = try allocator.create(Command);
|
||||
cmd.* = Command{
|
||||
.command = self.command,
|
||||
.args = self.args,
|
||||
.cur_idx = self.cur_idx,
|
||||
pub const Write = CustomCommand(Tag.write, custom.Write, struct {
|
||||
data: f32,
|
||||
});
|
||||
|
||||
pub const Embed = CustomCommand(Tag.write, custom.Embed, struct {
|
||||
path: []const u8,
|
||||
});
|
||||
|
||||
pub const Rotate = struct {
|
||||
pub const base_tag = Tag.rotate;
|
||||
base: Command,
|
||||
deg: f32,
|
||||
bgfill: []const u8,
|
||||
};
|
||||
|
||||
return cmd;
|
||||
}
|
||||
pub const Amp = LV2Command(
|
||||
.amp,
|
||||
"http://lv2plug.in/plugins/eg-amp",
|
||||
struct {
|
||||
gain: f32
|
||||
},
|
||||
);
|
||||
|
||||
pub const RFlanger = LV2Command(
|
||||
.rflanger,
|
||||
"http://plugin.org.uk/swh-plugins/retroFlange",
|
||||
struct {
|
||||
delay_depth_avg: f32, law_freq: f32
|
||||
},
|
||||
);
|
||||
|
||||
pub const Eq = LV2Command(
|
||||
.rflanger,
|
||||
"http://plugin.org.uk/swh-plugins/dj_eq_mono",
|
||||
struct {
|
||||
lo: f32, mid: f32, hi: f32
|
||||
},
|
||||
);
|
||||
|
||||
pub const Phaser = LV2Command(
|
||||
.phaser,
|
||||
"http://plugin.org.uk/swh-plugins/lfoPhaser",
|
||||
struct {
|
||||
lfo_rate: f32, lfo_depth: f32, fb: f32, spread: f32
|
||||
},
|
||||
);
|
||||
|
||||
pub const Mbeq = LV2Command(
|
||||
.mbeq,
|
||||
"http://plugin.org.uk/swh-plugins/mbeq",
|
||||
struct {
|
||||
band_1: f32,
|
||||
band_2: f32,
|
||||
band_3: f32,
|
||||
band_4: f32,
|
||||
band_5: f32,
|
||||
band_6: f32,
|
||||
band_7: f32,
|
||||
band_8: f32,
|
||||
band_9: f32,
|
||||
band_10: f32,
|
||||
band_11: f32,
|
||||
band_12: f32,
|
||||
band_13: f32,
|
||||
band_14: f32,
|
||||
band_15: f32,
|
||||
},
|
||||
);
|
||||
|
||||
pub const Chorus = LV2Command(
|
||||
.chorus,
|
||||
"http://plugin.org.uk/swh-plugins/multivoiceChorus",
|
||||
struct {
|
||||
voices: f32,
|
||||
delay_base: f32,
|
||||
voice_spread: f32,
|
||||
detune: f32,
|
||||
law_freq: f32,
|
||||
attendb: f32,
|
||||
},
|
||||
);
|
||||
|
||||
pub const Pitchscaler = LV2Command(
|
||||
.pitchscaler,
|
||||
"http://plugin.org.uk/swh-plugins/pitchScaleHQ",
|
||||
struct { mult: f32 },
|
||||
);
|
||||
|
||||
pub const Reverb = LV2Command(
|
||||
.reverb,
|
||||
"http://invadarecords.com/plugins/lv2/erreverb/mono",
|
||||
struct {
|
||||
roomLength: f32,
|
||||
roomWidth: f32,
|
||||
roomHeight: f32,
|
||||
sourceLR: f32,
|
||||
sourceFB: f32,
|
||||
listLR: f32,
|
||||
listFB: f32,
|
||||
hpf: f32,
|
||||
warmth: f32,
|
||||
diffusion: f32,
|
||||
},
|
||||
);
|
||||
|
||||
pub const Highpass = LV2Command(.highpass, "http://invadarecords.com/plugins/lv2/filter/hpf/mono", struct {
|
||||
freq: f32,
|
||||
gain: f32,
|
||||
noClip: f32,
|
||||
});
|
||||
|
||||
pub const Delay = LV2Command(.delay, "http://plugin.org.uk/swh-plugins/delayorama", struct {
|
||||
seed: f32,
|
||||
gain: f32,
|
||||
feedback_pc: f32,
|
||||
tap_count: f32,
|
||||
first_delay: f32,
|
||||
delay_range: f32,
|
||||
delay_scale: f32,
|
||||
delay_rand_pc: f32,
|
||||
gain_scale: f32,
|
||||
wet: f32,
|
||||
});
|
||||
|
||||
pub const Vinyl = LV2Command(.vinyl, "http://plugin.org.uk/swh-plugins/vynil", struct {
|
||||
year: f32,
|
||||
rpm: f32,
|
||||
warp: f32,
|
||||
click: f32,
|
||||
wear: f32,
|
||||
});
|
||||
|
||||
pub const Revdelay = LV2Command(.revdelay, "http://plugin.org.uk/swh-plugins/revdelay", struct {
|
||||
delay_time: f32,
|
||||
dry_level: f32,
|
||||
wet_level: f32,
|
||||
feedback: f32,
|
||||
xfade_samp: f32,
|
||||
});
|
||||
// pub const Noise= LV2Command(.,,struct{});
|
||||
pub const Gate = LV2Command(.gate, "http://hippie.lt/lv2/gate", struct {
|
||||
@"switch": f32,
|
||||
threshold: f32,
|
||||
attack: f32,
|
||||
hold: f32,
|
||||
decay: f32,
|
||||
gaterange: f32,
|
||||
});
|
||||
pub const Detune = LV2Command(.detune, "http://drobilla.net/plugins/mda/Detune", struct {
|
||||
detune: f32,
|
||||
mix: f32,
|
||||
output: f32,
|
||||
latency: f32,
|
||||
});
|
||||
pub const Overdrive = LV2Command(.overdrive, "http://drobilla.net/plugins/mda/Overdrive", struct {
|
||||
drive: f32,
|
||||
muffle: f32,
|
||||
output: f32,
|
||||
});
|
||||
pub const Degrade = LV2Command(.degrade, "http://drobilla.net/plugins/mda/Degrade", struct {
|
||||
headroom: f32,
|
||||
quant: f32,
|
||||
rate: f32,
|
||||
post_filt: f32,
|
||||
non_lin: f32,
|
||||
output: f32,
|
||||
});
|
||||
pub const Repsycho = LV2Command(.repsycho, "http://drobilla.net/plugins/mda/RePsycho", struct {
|
||||
tune: f32,
|
||||
fine: f32,
|
||||
decay: f32,
|
||||
thresh: f32,
|
||||
hold: f32,
|
||||
mix: f32,
|
||||
quality: f32,
|
||||
});
|
||||
pub const Talkbox = LV2Command(.talkbox, "http://drobilla.net/plugins/mda/TalkBox", struct {
|
||||
wet: f32,
|
||||
dry: f32,
|
||||
carrier: f32,
|
||||
quality: f32,
|
||||
});
|
||||
pub const Dyncomp = LV2Command(.dyncomp, "http://gareus.org/oss/lv2/darc#mono", struct {
|
||||
enable: f32,
|
||||
hold: f32,
|
||||
inputgain: f32,
|
||||
threshold: f32,
|
||||
Ratio: f32,
|
||||
attack: f32,
|
||||
release: f32,
|
||||
gain_min: f32,
|
||||
gain_max: f32,
|
||||
rms: f32,
|
||||
});
|
||||
pub const Foverdrive = LV2Command(.foverdrive, "http://plugin.org.uk/swh-plugins/foverdrive", struct {
|
||||
drive: f32,
|
||||
});
|
||||
pub const Thruzero = LV2Command(.thruzero, "http://drobilla.net/plugins/mda/ThruZero", struct {
|
||||
rate: f32, mix: f32, feedback: f32, depth_mod: f32
|
||||
});
|
||||
|
||||
pub const Gverb = LV2Command(.gverb, "http://plugin.org.uk/swh-plugins/gverb", struct {
|
||||
roomsize: f32,
|
||||
revtime: f32,
|
||||
damping: f32,
|
||||
inputbandwidth: f32,
|
||||
drylevel: f32,
|
||||
earlylevel: f32,
|
||||
taillevel: f32,
|
||||
});
|
||||
pub const Invert = LV2Command(.invert, "http://plugin.org.uk/swh-plugins/inv", struct {});
|
||||
pub const Tapedelay = LV2Command(.tapedelay, "http://plugin.org.uk/swh-plugins/tapeDelay", struct {
|
||||
speed: f32,
|
||||
da_db: f32,
|
||||
|
||||
t1d: f32,
|
||||
t1a_db: f32,
|
||||
|
||||
t2d: f32,
|
||||
t2a_db: f32,
|
||||
|
||||
t3d: f32,
|
||||
t3a_db: f32,
|
||||
|
||||
t4d: f32,
|
||||
t4a_db: f32,
|
||||
});
|
||||
|
||||
pub const Moddelay = LV2Command(
|
||||
.moddelay,
|
||||
"http://plugin.org.uk/swh-plugins/modDelay",
|
||||
struct {
|
||||
base: f32,
|
||||
},
|
||||
);
|
||||
|
||||
pub const Multichorus = LV2Command(.multichorus, "http://calf.sourceforge.net/plugins/MultiChorus", struct {
|
||||
min_delay: f32,
|
||||
mod_depth: f32,
|
||||
mod_rate: f32,
|
||||
stereo: f32,
|
||||
voices: f32,
|
||||
vphase: f32,
|
||||
amount: f32,
|
||||
dry: f32,
|
||||
freq: f32,
|
||||
freq2: f32,
|
||||
q: f32,
|
||||
overlap: f32,
|
||||
level_in: f32,
|
||||
level_out: f32,
|
||||
lfo: f32,
|
||||
});
|
||||
pub const Saturator = LV2Command(.saturator, "http://calf.sourceforge.net/plugins/Saturator", struct {
|
||||
bypass: f32,
|
||||
level_in: f32,
|
||||
level_out: f32,
|
||||
mix: f32,
|
||||
drive: f32,
|
||||
blend: f32,
|
||||
lp_pre_freq: f32,
|
||||
hp_pre_freq: f32,
|
||||
lp_post_freq: f32,
|
||||
hp_post_freq: f32,
|
||||
p_freq: f32,
|
||||
p_level: f32,
|
||||
p_q: f32,
|
||||
pre: f32,
|
||||
post: f32,
|
||||
});
|
||||
pub const Vintagedelay = LV2Command(.vintagedelay, "http://calf.sourceforge.net/plugins/VintageDelay", struct {
|
||||
level_in: f32,
|
||||
level_out: f32,
|
||||
subdiv: f32,
|
||||
time_l: f32,
|
||||
time_r: f32,
|
||||
feedback: f32,
|
||||
amount: f32,
|
||||
mix_mode: f32,
|
||||
medium: f32,
|
||||
dry: f32,
|
||||
width: f32,
|
||||
fragmentation: f32,
|
||||
pbeats: f32,
|
||||
pfrag: f32,
|
||||
timing: f32,
|
||||
bpm: f32,
|
||||
ms: f32,
|
||||
hz: f32,
|
||||
bpm_host: f32,
|
||||
});
|
||||
};
|
||||
|
||||
pub const CommandList = std.ArrayList(Command);
|
||||
pub const ArgList = std.ArrayList([]const u8);
|
||||
|
||||
pub const KeywordMap = std.StringHashMap(CommandType);
|
||||
pub const CommandList = std.ArrayList(*Command);
|
||||
|
||||
/// A parser.
|
||||
pub const Lang = struct {
|
||||
allocator: *std.mem.Allocator,
|
||||
keywords: KeywordMap,
|
||||
|
||||
has_error: bool = false,
|
||||
line: usize = 0,
|
||||
|
@ -168,137 +488,108 @@ pub const Lang = struct {
|
|||
pub fn init(allocator: *std.mem.Allocator) Lang {
|
||||
return Lang{
|
||||
.allocator = allocator,
|
||||
.keywords = KeywordMap.init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Lang) void {
|
||||
self.keywords.deinit();
|
||||
}
|
||||
pub fn deinit(self: *Lang) void {}
|
||||
|
||||
pub fn reset(self: *Lang) void {
|
||||
self.has_error = false;
|
||||
self.line = 0;
|
||||
}
|
||||
|
||||
fn fillKeywords(self: *Lang) !void {
|
||||
_ = try self.keywords.put("noop", .Noop);
|
||||
_ = try self.keywords.put("load", .Load);
|
||||
_ = try self.keywords.put("quicksave", .Quicksave);
|
||||
_ = try self.keywords.put("runqs", .RunQS);
|
||||
|
||||
_ = try self.keywords.put("amp", .Amp);
|
||||
_ = try self.keywords.put("rflanger", .RFlanger);
|
||||
_ = try self.keywords.put("eq", .Eq);
|
||||
_ = try self.keywords.put("mbeq", .Mbeq);
|
||||
_ = try self.keywords.put("phaser", .Phaser);
|
||||
_ = try self.keywords.put("chorus", .Chorus);
|
||||
_ = try self.keywords.put("pitchscaler", .PitchScaler);
|
||||
_ = try self.keywords.put("reverb", .Reverb);
|
||||
_ = try self.keywords.put("highpass", .Highpass);
|
||||
_ = try self.keywords.put("delay", .Delay);
|
||||
_ = try self.keywords.put("vinyl", .Vinyl);
|
||||
_ = try self.keywords.put("revdelay", .RevDelay);
|
||||
_ = try self.keywords.put("gate", .Gate);
|
||||
_ = try self.keywords.put("detune", .Detune);
|
||||
_ = try self.keywords.put("overdrive", .Overdrive);
|
||||
_ = try self.keywords.put("talkbox", .TalkBox);
|
||||
_ = try self.keywords.put("thruzero", .ThruZero);
|
||||
_ = try self.keywords.put("foverdrive", .Foverdrive);
|
||||
_ = try self.keywords.put("gverb", .Gverb);
|
||||
_ = try self.keywords.put("invert", .Invert);
|
||||
_ = try self.keywords.put("tapedelay", .TapeDelay);
|
||||
_ = try self.keywords.put("moddelay", .ModDelay);
|
||||
_ = try self.keywords.put("multichorus", .MultiChorus);
|
||||
_ = try self.keywords.put("saturator", .Saturator);
|
||||
_ = try self.keywords.put("vintagedelay", .VintageDelay);
|
||||
|
||||
// custom implementations (not lv2)
|
||||
_ = try self.keywords.put("noise", .Noise);
|
||||
_ = try self.keywords.put("wildnoise", .WildNoise);
|
||||
_ = try self.keywords.put("write", .Write);
|
||||
_ = try self.keywords.put("embed", .Embed);
|
||||
_ = try self.keywords.put("degrade", .Degrade);
|
||||
_ = try self.keywords.put("repsycho", .RePsycho);
|
||||
_ = try self.keywords.put("dyncomp", .RePsycho);
|
||||
|
||||
// even more custom
|
||||
_ = try self.keywords.put("rotate", .Rotate);
|
||||
}
|
||||
|
||||
pub fn getCommand(self: *Lang, stmt: []const u8) ?CommandType {
|
||||
var kv_opt = self.keywords.get(stmt);
|
||||
|
||||
if (kv_opt) |kv| {
|
||||
return kv.value;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
fn expectAny(self: *Lang, count: usize, args: ArgList) !void {
|
||||
if (args.items.len != count) {
|
||||
self.doError("expected {} arguments, found {}", .{ count, args.items.len });
|
||||
return error.ArgRequired;
|
||||
}
|
||||
}
|
||||
|
||||
fn expectSingle(self: *Lang, args: ArgList) !void {
|
||||
return try self.expectAny(1, args);
|
||||
}
|
||||
|
||||
fn expectFloat(self: *Lang, count: usize, args: ArgList) !void {
|
||||
var i: usize = 0;
|
||||
|
||||
if (args.items.len != count) {
|
||||
self.doError("expected {} arguments, found {}", .{ count, args.items.len });
|
||||
return error.ArgRequired;
|
||||
}
|
||||
|
||||
while (i < count) : (i += 1) {
|
||||
var arg = args.items[i];
|
||||
_ = std.fmt.parseFloat(f32, arg) catch |err| {
|
||||
std.debug.warn("failed to parse f32: {}\n", .{err});
|
||||
return error.FloatParseFail;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn validateCommand(self: *Lang, cmd: Command) !void {
|
||||
switch (cmd.command) {
|
||||
.Quicksave, .Noop => {},
|
||||
.Load, .RunQS => try self.expectSingle(cmd.args),
|
||||
.Amp => try self.expectFloat(3, cmd.args),
|
||||
.RFlanger => try self.expectFloat(4, cmd.args),
|
||||
.Eq => try self.expectFloat(5, cmd.args),
|
||||
.Phaser => try self.expectFloat(6, cmd.args),
|
||||
.Mbeq => try self.expectFloat(17, cmd.args),
|
||||
.Chorus => try self.expectFloat(8, cmd.args),
|
||||
.PitchScaler => try self.expectFloat(3, cmd.args),
|
||||
.Reverb => try self.expectFloat(12, cmd.args),
|
||||
.Highpass => try self.expectFloat(5, cmd.args),
|
||||
.Delay => try self.expectFloat(12, cmd.args),
|
||||
.Vinyl => try self.expectFloat(7, cmd.args),
|
||||
.RevDelay => try self.expectFloat(7, cmd.args),
|
||||
.Noise => try self.expectFloat(4, cmd.args),
|
||||
.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}),
|
||||
}
|
||||
}
|
||||
|
||||
fn doError(self: *Lang, comptime fmt: []const u8, args: var) void {
|
||||
std.debug.warn("error at line {}: ", .{self.line});
|
||||
std.debug.warn("ERROR! at line {}: ", .{self.line});
|
||||
std.debug.warn(fmt, args);
|
||||
std.debug.warn("\n", .{});
|
||||
self.has_error = true;
|
||||
}
|
||||
|
||||
pub fn parse(self: *Lang, data: []const u8) ParseError!CommandList {
|
||||
fn parseCommandArguments(
|
||||
self: *@This(),
|
||||
comptime command_struct: type,
|
||||
tok_it: *std.mem.TokenIterator,
|
||||
commands: *CommandList,
|
||||
) !void {
|
||||
// Based on the command struct fields, we can parse the arguments.
|
||||
var cmd = try self.allocator.create(command_struct);
|
||||
const is_lv2_command = switch (command_struct.base_tag) {
|
||||
.noop, .load, .quicksave, .runqs, .rotate => false,
|
||||
else => true,
|
||||
};
|
||||
|
||||
// TODO: crash when no arguments are left but we still need
|
||||
// arguments...
|
||||
|
||||
if (is_lv2_command) {
|
||||
const split = tok_it.next();
|
||||
if (split == null) {
|
||||
self.doError("Expected split parameter, got EOL", .{});
|
||||
return;
|
||||
}
|
||||
|
||||
const index = tok_it.next();
|
||||
if (index == null) {
|
||||
self.doError("Expected index parameter, got EOL", .{});
|
||||
return;
|
||||
}
|
||||
|
||||
cmd.split = try std.fmt.parseInt(usize, split.?, 10);
|
||||
cmd.index = try std.fmt.parseInt(usize, index.?, 10);
|
||||
|
||||
inline for (@typeInfo(@TypeOf(cmd.parameters)).Struct.fields) |cmd_field| {
|
||||
const maybe_arg = tok_it.next();
|
||||
if (maybe_arg == null) {
|
||||
self.doError("Expected parameter for {}, got nothing", .{cmd_field.name});
|
||||
return;
|
||||
}
|
||||
|
||||
const arg = maybe_arg.?;
|
||||
const arg_value = switch (cmd_field.field_type) {
|
||||
f32 => try std.fmt.parseFloat(f32, arg),
|
||||
u64 => try std.fmt.parseInt(u64, arg, 10),
|
||||
usize => try std.fmt.parseInt(usize, arg, 10),
|
||||
[]const u8 => try self.allocator.dupe(u8, arg),
|
||||
else => @compileError("parameter struct has unsupported type " ++ @typeName(cmd_field.field_type)),
|
||||
};
|
||||
|
||||
@field(cmd.parameters, cmd_field.name) = arg_value;
|
||||
}
|
||||
} else {
|
||||
inline for (@typeInfo(command_struct).Struct.fields) |cmd_field| {
|
||||
comptime {
|
||||
if (std.mem.eql(u8, cmd_field.name, "base")) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const arg = tok_it.next().?;
|
||||
const argument_value = switch (cmd_field.field_type) {
|
||||
usize => try std.fmt.parseInt(usize, arg, 10),
|
||||
i32 => try std.fmt.parseInt(i32, arg, 10),
|
||||
f32 => try std.fmt.parseFloat(f32, arg),
|
||||
[]u8 => try self.allocator.dupe(u8, arg),
|
||||
[]const u8 => try self.allocator.dupe(u8, arg),
|
||||
else => @compileError("Invalid parameter type (" ++ @typeName(cmd_field.field_type) ++ ") left on command struct " ++ @typeName(command_struct) ++ "."),
|
||||
};
|
||||
|
||||
std.debug.warn("parsing {}, arg of type {} => {}\n", .{
|
||||
@typeName(command_struct),
|
||||
@typeName(@TypeOf(argument_value)),
|
||||
argument_value,
|
||||
});
|
||||
|
||||
@field(cmd, cmd_field.name) = argument_value;
|
||||
}
|
||||
}
|
||||
|
||||
cmd.base.tag = command_struct.base_tag;
|
||||
const command = cmd.base.cast(command_struct).?;
|
||||
std.debug.warn("cmd: {}\n", .{command});
|
||||
|
||||
try commands.append(&cmd.base);
|
||||
}
|
||||
|
||||
pub fn parse(self: *Lang, data: []const u8) !CommandList {
|
||||
var splitted_it = std.mem.split(data, ";");
|
||||
try self.fillKeywords();
|
||||
var cmds = CommandList.init(self.allocator);
|
||||
|
||||
while (splitted_it.next()) |stmt_orig| {
|
||||
|
@ -309,7 +600,7 @@ pub const Lang = struct {
|
|||
if (stmt.len == 0) continue;
|
||||
if (std.mem.startsWith(u8, stmt, "#")) continue;
|
||||
|
||||
// TODO better tokenizer instead of just tokenize(" ");
|
||||
// TODO better tokenizer instead of just tokenize(" ")...maybe????
|
||||
var tok_it = std.mem.tokenize(stmt, " ");
|
||||
|
||||
var cmd_opt = tok_it.next();
|
||||
|
@ -317,32 +608,45 @@ pub const Lang = struct {
|
|||
self.doError("No command given", .{});
|
||||
continue;
|
||||
}
|
||||
var command = cmd_opt.?;
|
||||
const command_string = cmd_opt.?;
|
||||
|
||||
var ctype_opt = self.getCommand(command);
|
||||
var ctype: CommandType = undefined;
|
||||
if (ctype_opt) |ctype_val| {
|
||||
ctype = ctype_val;
|
||||
} else {
|
||||
self.doError("Unknown command '{}' ({})", .{ command, command.len });
|
||||
continue;
|
||||
var found: bool = false;
|
||||
|
||||
inline for (@typeInfo(Command).Struct.decls) |cmd_struct_decl| {
|
||||
switch (cmd_struct_decl.data) {
|
||||
.Type => |typ| switch (@typeInfo(typ)) {
|
||||
.Struct => {},
|
||||
else => continue,
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
|
||||
var args = ArgList.init(self.allocator);
|
||||
errdefer args.deinit();
|
||||
|
||||
while (tok_it.next()) |arg| {
|
||||
try args.append(arg);
|
||||
const struct_name = cmd_struct_decl.name;
|
||||
comptime var lowered_command_name = [_]u8{0} ** struct_name.len;
|
||||
comptime {
|
||||
for (struct_name) |c, i| {
|
||||
lowered_command_name[i] = std.ascii.toLower(c);
|
||||
}
|
||||
}
|
||||
|
||||
// construct final Command based on command
|
||||
var cmd = Command{ .command = ctype, .args = args };
|
||||
self.validateCommand(cmd) catch |err| {
|
||||
//self.doError("error validating command '{}': {}", command, err);
|
||||
continue;
|
||||
};
|
||||
// if we have a match, we know the proper struct type
|
||||
// to use. this actually works compared to storing command_struct
|
||||
// in a variable because then that variable must be comptime.
|
||||
|
||||
try cmds.append(cmd);
|
||||
// the drawback of this approach is that our emitted code is basically linear
|
||||
// because we don't use the hashmap anymore. maybe #5359 can help.
|
||||
|
||||
if (std.mem.eql(u8, &lowered_command_name, command_string)) {
|
||||
found = true;
|
||||
const cmd_struct_type = cmd_struct_decl.data.Type;
|
||||
try self.parseCommandArguments(cmd_struct_type, &tok_it, &cmds);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
self.doError("Unknown command '{}' ({})", .{ command_string, command_string.len });
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (self.has_error) return ParseError.ParseFail;
|
||||
|
|
97
src/main.zig
97
src/main.zig
|
@ -16,12 +16,26 @@ const readline = @cImport({
|
|||
@cInclude("readline/history.h");
|
||||
});
|
||||
|
||||
fn wrapInCmdList(allocator: *std.mem.Allocator, cmd: langs.Command) !langs.CommandList {
|
||||
fn wrapInCmdList(allocator: *std.mem.Allocator, cmd: *langs.Command) !langs.CommandList {
|
||||
var cmds = langs.CommandList.init(allocator);
|
||||
try cmds.append(cmd);
|
||||
return cmds;
|
||||
}
|
||||
|
||||
fn copyCommandToHeap(allocator: *std.mem.Allocator, command: langs.Command, comptime tag: langs.Command.Tag) !*langs.Command {
|
||||
const CommandStruct = langs.Command.tagToType(tag);
|
||||
const casted = command.cast(CommandStruct).?;
|
||||
var heap_cmd = try allocator.create(CommandStruct);
|
||||
|
||||
@memcpy(
|
||||
@ptrCast([*]u8, &heap_cmd),
|
||||
@ptrCast([*]const u8, &casted),
|
||||
@sizeOf(CommandStruct),
|
||||
);
|
||||
|
||||
return &heap_cmd.base;
|
||||
}
|
||||
|
||||
pub fn doRepl(allocator: *std.mem.Allocator, args_it: var) !void {
|
||||
var stdout_file = std.io.getStdOut();
|
||||
const stdout = &stdout_file.outStream();
|
||||
|
@ -57,13 +71,16 @@ pub fn doRepl(allocator: *std.mem.Allocator, args_it: var) !void {
|
|||
} else {
|
||||
// if there isn't any commands on the file, we load our default
|
||||
// 'load :0' command
|
||||
var loadargs = langs.ArgList.init(allocator);
|
||||
try loadargs.append(":0");
|
||||
|
||||
try cmds.append(langs.Command{
|
||||
.command = .Load,
|
||||
.args = loadargs,
|
||||
});
|
||||
// TODO: deliberate memleak here. we only allocate this
|
||||
// command once, for the start of the file, so.
|
||||
var load_cmd = try allocator.create(langs.Command.Load);
|
||||
std.mem.copy(u8, load_cmd.path, ":0");
|
||||
load_cmd.base.tag = langs.Command.Tag.load;
|
||||
|
||||
// taking address is fine, because load_cmd lives in the lifetime
|
||||
// of the allocator.
|
||||
try cmds.append(&load_cmd.base);
|
||||
}
|
||||
|
||||
if (file_read_opt) |file_read| {
|
||||
|
@ -101,11 +118,12 @@ pub fn doRepl(allocator: *std.mem.Allocator, args_it: var) !void {
|
|||
// run the load command
|
||||
try runner.runCommands(cmds, true);
|
||||
|
||||
var runqs_args = langs.ArgList.init(allocator);
|
||||
defer runqs_args.deinit();
|
||||
|
||||
const wanted_runner: []const u8 = std.os.getenv("SCRITCHER_RUNNER") orelse "ristretto";
|
||||
try runqs_args.append(wanted_runner);
|
||||
|
||||
var runqs_cmd = langs.Command.RunQS{
|
||||
.base = langs.Command{ .tag = langs.Command.Tag.runqs },
|
||||
.program = wanted_runner,
|
||||
};
|
||||
|
||||
while (true) {
|
||||
lang.reset();
|
||||
|
@ -121,11 +139,51 @@ pub fn doRepl(allocator: *std.mem.Allocator, args_it: var) !void {
|
|||
var line = rd_line[0..std.mem.len(rd_line)];
|
||||
|
||||
if (std.mem.eql(u8, line, "push")) {
|
||||
try cmds.append(current);
|
||||
const heap_cmd = switch (current.tag) {
|
||||
.noop => try copyCommandToHeap(allocator, current, .noop),
|
||||
.load => try copyCommandToHeap(allocator, current, .load),
|
||||
.quicksave => try copyCommandToHeap(allocator, current, .quicksave),
|
||||
.runqs => try copyCommandToHeap(allocator, current, .runqs),
|
||||
.amp => try copyCommandToHeap(allocator, current, .amp),
|
||||
.rflanger => try copyCommandToHeap(allocator, current, .rflanger),
|
||||
.eq => try copyCommandToHeap(allocator, current, .eq),
|
||||
.phaser => try copyCommandToHeap(allocator, current, .phaser),
|
||||
.mbeq => try copyCommandToHeap(allocator, current, .mbeq),
|
||||
.chorus => try copyCommandToHeap(allocator, current, .chorus),
|
||||
.pitchscaler => try copyCommandToHeap(allocator, current, .pitchscaler),
|
||||
.reverb => try copyCommandToHeap(allocator, current, .reverb),
|
||||
.highpass => try copyCommandToHeap(allocator, current, .highpass),
|
||||
.delay => try copyCommandToHeap(allocator, current, .delay),
|
||||
.vinyl => try copyCommandToHeap(allocator, current, .vinyl),
|
||||
.revdelay => try copyCommandToHeap(allocator, current, .revdelay),
|
||||
.gate => try copyCommandToHeap(allocator, current, .gate),
|
||||
.detune => try copyCommandToHeap(allocator, current, .detune),
|
||||
.overdrive => try copyCommandToHeap(allocator, current, .overdrive),
|
||||
.degrade => try copyCommandToHeap(allocator, current, .degrade),
|
||||
.repsycho => try copyCommandToHeap(allocator, current, .repsycho),
|
||||
.talkbox => try copyCommandToHeap(allocator, current, .talkbox),
|
||||
.dyncomp => try copyCommandToHeap(allocator, current, .dyncomp),
|
||||
.thruzero => try copyCommandToHeap(allocator, current, .thruzero),
|
||||
.foverdrive => try copyCommandToHeap(allocator, current, .foverdrive),
|
||||
.gverb => try copyCommandToHeap(allocator, current, .gverb),
|
||||
.invert => try copyCommandToHeap(allocator, current, .invert),
|
||||
.tapedelay => try copyCommandToHeap(allocator, current, .tapedelay),
|
||||
.moddelay => try copyCommandToHeap(allocator, current, .moddelay),
|
||||
.multichorus => try copyCommandToHeap(allocator, current, .multichorus),
|
||||
.saturator => try copyCommandToHeap(allocator, current, .saturator),
|
||||
.vintagedelay => try copyCommandToHeap(allocator, current, .vintagedelay),
|
||||
.noise => try copyCommandToHeap(allocator, current, .noise),
|
||||
.wildnoise => try copyCommandToHeap(allocator, current, .wildnoise),
|
||||
.write => try copyCommandToHeap(allocator, current, .write),
|
||||
.embed => try copyCommandToHeap(allocator, current, .embed),
|
||||
.rotate => try copyCommandToHeap(allocator, current, .rotate),
|
||||
};
|
||||
|
||||
try cmds.append(heap_cmd);
|
||||
|
||||
// run the current added command to main cmds list
|
||||
// with the main parent runner
|
||||
var cmds_wrapped = try wrapInCmdList(allocator, current);
|
||||
var cmds_wrapped = try wrapInCmdList(allocator, heap_cmd);
|
||||
defer cmds_wrapped.deinit();
|
||||
try runner.runCommands(cmds_wrapped, true);
|
||||
|
||||
|
@ -150,7 +208,11 @@ pub fn doRepl(allocator: *std.mem.Allocator, args_it: var) !void {
|
|||
std.debug.warn("repl: error while parsing: {}\n", .{err});
|
||||
continue;
|
||||
};
|
||||
current = cmds_parsed.items[0];
|
||||
|
||||
// no command? ignore!
|
||||
if (cmds_parsed.items.len == 0) continue;
|
||||
|
||||
current = cmds_parsed.items[0].*;
|
||||
|
||||
// by cloning the parent runner, we can iteratively write
|
||||
// whatever command we want and only commit the good results
|
||||
|
@ -158,10 +220,9 @@ pub fn doRepl(allocator: *std.mem.Allocator, args_it: var) !void {
|
|||
var runner_clone = try runner.clone();
|
||||
defer runner_clone.deinit();
|
||||
|
||||
try cmds_parsed.append(langs.Command{
|
||||
.command = .RunQS,
|
||||
.args = runqs_args,
|
||||
});
|
||||
// taking address is fine, because runqs_cmd lives in the lifetime
|
||||
// of this function.
|
||||
try cmds_parsed.append(&runqs_cmd.base);
|
||||
|
||||
try runner_clone.runCommands(cmds_parsed, true);
|
||||
_ = try stdout.write("\n");
|
||||
|
|
125
src/printer.zig
125
src/printer.zig
|
@ -1,54 +1,89 @@
|
|||
const std = @import("std");
|
||||
const langs = @import("lang.zig");
|
||||
|
||||
fn printCommandWithParams(stream: var, command: var) !void {
|
||||
const Parameters = @TypeOf(command.parameters);
|
||||
try stream.print(" {} {}", .{ command.split, command.index });
|
||||
inline for (@typeInfo(Parameters).Struct.fields) |field| {
|
||||
if (field.field_type == f32 or field.field_type == f64) {
|
||||
try stream.print(" {d}", .{@field(command.parameters, field.name)});
|
||||
} else {
|
||||
try stream.print(" {}", .{@field(command.parameters, field.name)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn printCommand(stream: var, cmd: *langs.Command, comptime tag: langs.Command.Tag) !void {
|
||||
const CommandStruct = langs.Command.tagToType(tag);
|
||||
const casted = cmd.cast(CommandStruct).?;
|
||||
|
||||
// TODO move this to Tag method?
|
||||
const is_typed = switch (tag) {
|
||||
.noop, .load, .quicksave, .runqs, .rotate => false,
|
||||
else => true,
|
||||
};
|
||||
|
||||
const ctype = CommandStruct.command_type;
|
||||
switch (ctype) {
|
||||
.lv2_command => try printCommandWithParams(stream, casted),
|
||||
.custom_command => try printCommandWithParams(stream, casted),
|
||||
else => @panic("TODO support command type"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn printList(list: langs.CommandList, stream: var) !void {
|
||||
for (list.items) |cmd| {
|
||||
var command = switch (cmd.command) {
|
||||
.Noop => "noop",
|
||||
.Load => "load",
|
||||
.Quicksave => "quicksave",
|
||||
.RunQS => "runqs",
|
||||
|
||||
.Amp => "amp",
|
||||
.RFlanger => "rflanger",
|
||||
.Eq => "eq",
|
||||
.Phaser => "phaser",
|
||||
.Mbeq => "mbeq",
|
||||
.Chorus => "chorus",
|
||||
.PitchScaler => "pitchscaler",
|
||||
.Reverb => "reverb",
|
||||
.Highpass => "highpass",
|
||||
.Delay => "delay",
|
||||
.Vinyl => "vinyl",
|
||||
.RevDelay => "revdelay",
|
||||
.Gate => "gate",
|
||||
.Detune => "detune",
|
||||
.Overdrive => "overdrive",
|
||||
.Degrade => "Degrade",
|
||||
.RePsycho => "repsycho",
|
||||
.TalkBox => "talkbox",
|
||||
.DynComp => "dyncomp",
|
||||
.ThruZero => "thruzero",
|
||||
.Foverdrive => "foverdrive",
|
||||
.Gverb => "gverb",
|
||||
.Invert => "invert",
|
||||
.TapeDelay => "tapedelay",
|
||||
.ModDelay => "moddelay",
|
||||
.MultiChorus => "multichorus",
|
||||
.Saturator => "saturator",
|
||||
.VintageDelay => "vintagedelay",
|
||||
|
||||
.Noise => "noise",
|
||||
.WildNoise => "wildnoise",
|
||||
.Write => "write",
|
||||
.Embed => "embed",
|
||||
|
||||
.Rotate => "rotate",
|
||||
};
|
||||
|
||||
const command = @tagName(cmd.tag);
|
||||
try stream.print("{}", .{command});
|
||||
|
||||
for (cmd.args.items) |arg| {
|
||||
try stream.print(" {}", .{arg});
|
||||
switch (cmd.tag) {
|
||||
.load => {
|
||||
const load = cmd.cast(langs.Command.Load).?;
|
||||
try stream.print(" {}", .{load.path});
|
||||
},
|
||||
.runqs => {
|
||||
const runqs = cmd.cast(langs.Command.RunQS).?;
|
||||
try stream.print(" {}", .{runqs.program});
|
||||
},
|
||||
.noop, .quicksave => {},
|
||||
.rotate => {
|
||||
const rotate = cmd.cast(langs.Command.Rotate).?;
|
||||
try stream.print(" {d} {}", .{ rotate.deg, rotate.bgfill });
|
||||
},
|
||||
|
||||
.amp => try printCommand(stream, cmd, .amp),
|
||||
.rflanger => try printCommand(stream, cmd, .rflanger),
|
||||
.eq => try printCommand(stream, cmd, .eq),
|
||||
.phaser => try printCommand(stream, cmd, .phaser),
|
||||
.mbeq => try printCommand(stream, cmd, .mbeq),
|
||||
.chorus => try printCommand(stream, cmd, .chorus),
|
||||
.pitchscaler => try printCommand(stream, cmd, .pitchscaler),
|
||||
.reverb => try printCommand(stream, cmd, .reverb),
|
||||
.highpass => try printCommand(stream, cmd, .highpass),
|
||||
.delay => try printCommand(stream, cmd, .delay),
|
||||
.vinyl => try printCommand(stream, cmd, .vinyl),
|
||||
.revdelay => try printCommand(stream, cmd, .revdelay),
|
||||
.gate => try printCommand(stream, cmd, .gate),
|
||||
.detune => try printCommand(stream, cmd, .detune),
|
||||
.overdrive => try printCommand(stream, cmd, .overdrive),
|
||||
.degrade => try printCommand(stream, cmd, .degrade),
|
||||
.repsycho => try printCommand(stream, cmd, .repsycho),
|
||||
.talkbox => try printCommand(stream, cmd, .talkbox),
|
||||
.dyncomp => try printCommand(stream, cmd, .dyncomp),
|
||||
.thruzero => try printCommand(stream, cmd, .thruzero),
|
||||
.foverdrive => try printCommand(stream, cmd, .foverdrive),
|
||||
.gverb => try printCommand(stream, cmd, .gverb),
|
||||
.invert => try printCommand(stream, cmd, .invert),
|
||||
.tapedelay => try printCommand(stream, cmd, .tapedelay),
|
||||
.moddelay => try printCommand(stream, cmd, .moddelay),
|
||||
.multichorus => try printCommand(stream, cmd, .multichorus),
|
||||
.saturator => try printCommand(stream, cmd, .saturator),
|
||||
.vintagedelay => try printCommand(stream, cmd, .vintagedelay),
|
||||
|
||||
.noise => try printCommand(stream, cmd, .noise),
|
||||
.wildnoise => try printCommand(stream, cmd, .wildnoise),
|
||||
.write => try printCommand(stream, cmd, .write),
|
||||
.embed => try printCommand(stream, cmd, .embed),
|
||||
}
|
||||
|
||||
_ = try stream.write(";\n");
|
||||
|
|
692
src/runner.zig
692
src/runner.zig
|
@ -24,12 +24,15 @@ pub const Runner = struct {
|
|||
image: ?*Image = null,
|
||||
|
||||
/// If the runner is in REPL mode
|
||||
repl: bool = false,
|
||||
repl: bool,
|
||||
|
||||
args: [][]u8,
|
||||
|
||||
pub fn init(allocator: *std.mem.Allocator, repl: bool) Runner {
|
||||
return Runner{
|
||||
.allocator = allocator,
|
||||
.repl = repl,
|
||||
.args = std.process.argsAlloc(allocator) catch unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -41,7 +44,12 @@ pub const Runner = struct {
|
|||
|
||||
pub fn clone(self: *Runner) !Runner {
|
||||
var cloned_image = if (self.image) |image| try image.clone() else null;
|
||||
return Runner{ .allocator = self.allocator, .image = cloned_image };
|
||||
return Runner{
|
||||
.allocator = self.allocator,
|
||||
.image = cloned_image,
|
||||
.repl = self.repl,
|
||||
.args = self.args,
|
||||
};
|
||||
}
|
||||
|
||||
fn resolveArg(self: *Runner, load_path: []const u8) ![]const u8 {
|
||||
|
@ -49,20 +57,20 @@ pub const Runner = struct {
|
|||
// parse the index from 1 to end
|
||||
var index = try std.fmt.parseInt(usize, load_path[1..], 10);
|
||||
|
||||
// don't care about the 'repl' being prepended when we're in repl
|
||||
if (self.repl) index += 1;
|
||||
// if it isn't in the repl, args look like this:
|
||||
// 'scritcher ./script ./image'
|
||||
// if it is, it looks like this
|
||||
// 'scritcher repl ./script ./image'
|
||||
|
||||
var args_it = std.process.args();
|
||||
_ = args_it.skip();
|
||||
// ':0' should ALWAYS point to the image.
|
||||
if (self.repl) index += 3 else index += 2;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i <= index) : (i += 1) {
|
||||
_ = args_it.skip();
|
||||
std.debug.warn("ARGS!! {} \n", .{self.args.len});
|
||||
for (self.args) |arg, idx| {
|
||||
std.debug.warn("arg{} = {}\n", .{ idx, arg });
|
||||
}
|
||||
|
||||
const arg = try (args_it.next(self.allocator) orelse @panic("expected argument"));
|
||||
|
||||
return arg;
|
||||
std.debug.warn("fetch arg idx={}, val={}\n", .{ index, self.args[index] });
|
||||
return self.args[index];
|
||||
} else {
|
||||
return load_path;
|
||||
}
|
||||
|
@ -90,7 +98,7 @@ pub const Runner = struct {
|
|||
// krita/gimp and make it export a bmp and while in the program you can
|
||||
// apply filters, etc.
|
||||
if (!std.mem.endsWith(u8, load_path, ".bmp") and !std.mem.endsWith(u8, load_path, ".ppm")) {
|
||||
std.debug.warn("Only BMP files are allowed to be loaded.\n", .{});
|
||||
std.debug.warn("Only BMP files are allowed to be loaded. Got path '{}'\n", .{load_path});
|
||||
return RunError.NoBMP;
|
||||
}
|
||||
|
||||
|
@ -175,614 +183,134 @@ pub const Runner = struct {
|
|||
try image.saveTo(out_path);
|
||||
}
|
||||
|
||||
fn runQSCmd(self: *Runner, program: []const u8) !void {
|
||||
fn runQSCmd(self: *Runner, cmd: lang.Command) !void {
|
||||
const runqs = cmd.cast(lang.Command.RunQS).?;
|
||||
var image = try self.getImage();
|
||||
const out_path = try self.makeGlitchedPath();
|
||||
try image.saveTo(out_path);
|
||||
|
||||
var proc = try std.ChildProcess.init(
|
||||
&[_][]const u8{ program, out_path },
|
||||
&[_][]const u8{ runqs.program, out_path },
|
||||
self.allocator,
|
||||
);
|
||||
defer proc.deinit();
|
||||
|
||||
std.debug.warn("running '{} {}'\n", .{ program, out_path });
|
||||
std.debug.warn("running '{} {}'\n", .{ runqs.program, out_path });
|
||||
_ = try proc.spawnAndWait();
|
||||
}
|
||||
|
||||
/// Run the http://lv2plug.in/plugins/eg-amp plugin over the file.
|
||||
fn ampCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
fn rotateCmd(self: *Runner, cmd: lang.Command) !void {
|
||||
const rotate_cmd = cmd.cast(lang.Command.Rotate).?;
|
||||
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://lv2plug.in/plugins/eg-amp", pos, params);
|
||||
var c_bgfill = try std.cstr.addNullByte(self.allocator, rotate_cmd.bgfill);
|
||||
defer self.allocator.free(c_bgfill);
|
||||
|
||||
try magick.runRotate(image, rotate_cmd.deg, c_bgfill);
|
||||
}
|
||||
|
||||
fn rFlangerCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/retroFlange", pos, params);
|
||||
}
|
||||
fn executeLV2Command(self: *@This(), command: var) !void {
|
||||
const pos = plugin.Position{
|
||||
.split = command.split,
|
||||
.index = command.index,
|
||||
};
|
||||
|
||||
fn eqCmd(self: *Runner, position: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/dj_eq_mono", position, params);
|
||||
}
|
||||
|
||||
fn phaserCmd(self: *Runner, position: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/lfoPhaser", position, params);
|
||||
}
|
||||
|
||||
fn mbeqCmd(self: *Runner, position: Position, bands: []const f32) !void {
|
||||
var image = try self.getImage();
|
||||
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});
|
||||
const typ = @TypeOf(command);
|
||||
|
||||
inline for (@typeInfo(@TypeOf(command.parameters)).Struct.fields) |cmd_field| {
|
||||
try params.append(plugin.Param{
|
||||
.sym = sym,
|
||||
.value = band_value,
|
||||
.sym = cmd_field.name,
|
||||
.value = @field(command.parameters, cmd_field.name),
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
try image.runPlugin(typ.lv2_url, pos, params);
|
||||
}
|
||||
|
||||
fn pitchScalerCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
fn executeCustomCommand(self: *@This(), command: var) !void {
|
||||
const pos = plugin.Position{
|
||||
.split = command.split,
|
||||
.index = command.index,
|
||||
};
|
||||
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/pitchScaleHQ", pos, params);
|
||||
try image.runCustomPlugin(@TypeOf(command).plugin_type, pos, command.parameters);
|
||||
}
|
||||
|
||||
fn reverbCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://invadarecords.com/plugins/lv2/erreverb/mono", pos, params);
|
||||
}
|
||||
|
||||
fn highpassCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://invadarecords.com/plugins/lv2/filter/hpf/mono", pos, params);
|
||||
}
|
||||
|
||||
fn delayCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/delayorama", pos, params);
|
||||
}
|
||||
|
||||
fn vinylCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/vynil", pos, params);
|
||||
}
|
||||
|
||||
fn revDelayCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
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, *ParamMap, map);
|
||||
}
|
||||
|
||||
fn wildNoiseCmd(self: *Runner, pos: Position, map: *ParamMap) !void {
|
||||
var image = try self.getImage();
|
||||
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, *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(
|
||||
self: *Runner,
|
||||
deg: f32,
|
||||
bgfill: []const u8,
|
||||
fn runSingleCommand(
|
||||
self: *@This(),
|
||||
cmd: lang.Command,
|
||||
comptime tag: lang.Command.Tag,
|
||||
) !void {
|
||||
var image = try self.getImage();
|
||||
var c_bgfill = try std.cstr.addNullByte(self.allocator, bgfill);
|
||||
defer self.allocator.free(c_bgfill);
|
||||
|
||||
try magick.runRotate(image, deg, c_bgfill);
|
||||
comptime const typ = lang.Command.tagToType(tag);
|
||||
const command = cmd.cast(typ).?;
|
||||
const ctype = typ.command_type;
|
||||
switch (ctype) {
|
||||
.lv2_command => try self.executeLV2Command(command.*),
|
||||
.custom_command => try self.executeCustomCommand(command.*),
|
||||
else => @panic("TODO support command type"),
|
||||
}
|
||||
}
|
||||
|
||||
fn gateCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://hippie.lt/lv2/gate", pos, params);
|
||||
fn runCommand(self: *@This(), cmd: lang.Command) !void {
|
||||
switch (cmd.tag) {
|
||||
.noop => {},
|
||||
.load => {
|
||||
const command = cmd.cast(lang.Command.Load).?;
|
||||
try self.loadCmd(command.path);
|
||||
},
|
||||
.quicksave => try self.quicksaveCmd(),
|
||||
.rotate => try self.rotateCmd(cmd),
|
||||
.runqs => try self.runQSCmd(cmd),
|
||||
|
||||
.amp => try self.runSingleCommand(cmd, .amp),
|
||||
.rflanger => try self.runSingleCommand(cmd, .rflanger),
|
||||
.eq => try self.runSingleCommand(cmd, .eq),
|
||||
.phaser => try self.runSingleCommand(cmd, .phaser),
|
||||
.mbeq => try self.runSingleCommand(cmd, .mbeq),
|
||||
.chorus => try self.runSingleCommand(cmd, .chorus),
|
||||
.pitchscaler => try self.runSingleCommand(cmd, .pitchscaler),
|
||||
.reverb => try self.runSingleCommand(cmd, .reverb),
|
||||
.highpass => try self.runSingleCommand(cmd, .highpass),
|
||||
.delay => try self.runSingleCommand(cmd, .delay),
|
||||
.vinyl => try self.runSingleCommand(cmd, .vinyl),
|
||||
.revdelay => try self.runSingleCommand(cmd, .revdelay),
|
||||
.gate => try self.runSingleCommand(cmd, .gate),
|
||||
.detune => try self.runSingleCommand(cmd, .detune),
|
||||
.overdrive => try self.runSingleCommand(cmd, .overdrive),
|
||||
.degrade => try self.runSingleCommand(cmd, .degrade),
|
||||
.repsycho => try self.runSingleCommand(cmd, .repsycho),
|
||||
.talkbox => try self.runSingleCommand(cmd, .talkbox),
|
||||
.dyncomp => try self.runSingleCommand(cmd, .dyncomp),
|
||||
.thruzero => try self.runSingleCommand(cmd, .thruzero),
|
||||
.foverdrive => try self.runSingleCommand(cmd, .foverdrive),
|
||||
.gverb => try self.runSingleCommand(cmd, .gverb),
|
||||
.invert => try self.runSingleCommand(cmd, .invert),
|
||||
.tapedelay => try self.runSingleCommand(cmd, .tapedelay),
|
||||
.moddelay => try self.runSingleCommand(cmd, .moddelay),
|
||||
.multichorus => try self.runSingleCommand(cmd, .multichorus),
|
||||
.saturator => try self.runSingleCommand(cmd, .saturator),
|
||||
.vintagedelay => try self.runSingleCommand(cmd, .vintagedelay),
|
||||
|
||||
.noise => try self.runSingleCommand(cmd, .noise),
|
||||
.wildnoise => try self.runSingleCommand(cmd, .wildnoise),
|
||||
.write => try self.runSingleCommand(cmd, .write),
|
||||
.embed => try self.runSingleCommand(cmd, .embed),
|
||||
}
|
||||
|
||||
fn detuneCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://drobilla.net/plugins/mda/Detune", pos, params);
|
||||
}
|
||||
|
||||
fn overdriveCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://drobilla.net/plugins/mda/Overdrive", pos, params);
|
||||
}
|
||||
|
||||
fn degradeCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://drobilla.net/plugins/mda/Degrade", pos, params);
|
||||
}
|
||||
|
||||
fn repsychoCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://drobilla.net/plugins/mda/RePsycho", pos, params);
|
||||
}
|
||||
|
||||
fn talkboxCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://drobilla.net/plugins/mda/TalkBox", pos, params);
|
||||
}
|
||||
|
||||
fn dynCompCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://gareus.org/oss/lv2/darc#mono", pos, params);
|
||||
}
|
||||
|
||||
fn foverdriveCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/foverdrive", pos, params);
|
||||
}
|
||||
|
||||
fn thruZeroCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://drobilla.net/plugins/mda/ThruZero", pos, params);
|
||||
}
|
||||
|
||||
fn gverbCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/gverb", pos, params);
|
||||
}
|
||||
|
||||
fn invertCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/inv", pos, params);
|
||||
}
|
||||
|
||||
fn tapedelayCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/tapeDelay", pos, params);
|
||||
}
|
||||
|
||||
fn moddelayCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/modDelay", pos, params);
|
||||
}
|
||||
|
||||
fn multichorusCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://calf.sourceforge.net/plugins/MultiChorus", pos, params);
|
||||
}
|
||||
|
||||
fn saturatorCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://calf.sourceforge.net/plugins/Saturator", pos, params);
|
||||
}
|
||||
|
||||
fn vintagedelayCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
||||
var image = try self.getImage();
|
||||
try image.runPlugin("http://calf.sourceforge.net/plugins/VintageDelay", pos, params);
|
||||
}
|
||||
|
||||
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: {
|
||||
var path = cmd.args.items[0];
|
||||
try self.loadCmd(path);
|
||||
|
||||
// TODO is this needed?
|
||||
break :blk;
|
||||
},
|
||||
.Quicksave => try self.quicksaveCmd(),
|
||||
.RunQS => try self.runQSCmd(cmd.args.items[0]),
|
||||
|
||||
.Amp => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "gain");
|
||||
try self.ampCmd(pos, params);
|
||||
},
|
||||
|
||||
.RFlanger => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "delay_depth_avg");
|
||||
try cmd.appendParam(¶ms, "law_freq");
|
||||
try self.rFlangerCmd(pos, params);
|
||||
},
|
||||
|
||||
.Eq => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "lo");
|
||||
try cmd.appendParam(¶ms, "mid");
|
||||
try cmd.appendParam(¶ms, "hi");
|
||||
|
||||
try self.eqCmd(pos, params);
|
||||
},
|
||||
|
||||
.Phaser => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
|
||||
try cmd.appendParam(¶ms, "lfo_rate");
|
||||
try cmd.appendParam(¶ms, "lfo_depth");
|
||||
try cmd.appendParam(¶ms, "fb");
|
||||
try cmd.appendParam(¶ms, "spread");
|
||||
|
||||
try self.phaserCmd(pos, params);
|
||||
},
|
||||
|
||||
.Mbeq => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
const bands = try cmd.floatArgMany(self.allocator, 2, 15, @as(f32, 0));
|
||||
defer self.allocator.free(bands);
|
||||
|
||||
try self.mbeqCmd(pos, bands);
|
||||
},
|
||||
|
||||
.Chorus => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
|
||||
try cmd.appendParam(¶ms, "voices");
|
||||
try cmd.appendParam(¶ms, "delay_base");
|
||||
try cmd.appendParam(¶ms, "voice_spread");
|
||||
try cmd.appendParam(¶ms, "detune");
|
||||
try cmd.appendParam(¶ms, "law_freq");
|
||||
try cmd.appendParam(¶ms, "attendb");
|
||||
|
||||
try self.chorusCmd(pos, params);
|
||||
},
|
||||
|
||||
.PitchScaler => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "mult");
|
||||
try self.pitchScalerCmd(pos, params);
|
||||
},
|
||||
|
||||
.Reverb => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
|
||||
try cmd.appendParam(¶ms, "roomLength");
|
||||
try cmd.appendParam(¶ms, "roomWidth");
|
||||
try cmd.appendParam(¶ms, "roomHeight");
|
||||
try cmd.appendParam(¶ms, "sourceLR");
|
||||
try cmd.appendParam(¶ms, "sourceFB");
|
||||
try cmd.appendParam(¶ms, "listLR");
|
||||
try cmd.appendParam(¶ms, "listFB");
|
||||
try cmd.appendParam(¶ms, "hpf");
|
||||
try cmd.appendParam(¶ms, "warmth");
|
||||
try cmd.appendParam(¶ms, "diffusion");
|
||||
|
||||
try self.reverbCmd(pos, params);
|
||||
},
|
||||
|
||||
.Highpass => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
|
||||
try cmd.appendParam(¶ms, "freq");
|
||||
try cmd.appendParam(¶ms, "gain");
|
||||
try cmd.appendParam(¶ms, "noClip");
|
||||
|
||||
try self.highpassCmd(pos, params);
|
||||
},
|
||||
|
||||
.Delay => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
|
||||
try cmd.appendParam(¶ms, "seed");
|
||||
try cmd.appendParam(¶ms, "gain");
|
||||
try cmd.appendParam(¶ms, "feedback_pc");
|
||||
try cmd.appendParam(¶ms, "tap_count");
|
||||
try cmd.appendParam(¶ms, "first_delay");
|
||||
try cmd.appendParam(¶ms, "delay_range");
|
||||
try cmd.appendParam(¶ms, "delay_scale");
|
||||
try cmd.appendParam(¶ms, "delay_rand_pc");
|
||||
try cmd.appendParam(¶ms, "gain_scale");
|
||||
try cmd.appendParam(¶ms, "wet");
|
||||
|
||||
try self.delayCmd(pos, params);
|
||||
},
|
||||
|
||||
.Vinyl => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
|
||||
try cmd.appendParam(¶ms, "year");
|
||||
try cmd.appendParam(¶ms, "rpm");
|
||||
try cmd.appendParam(¶ms, "warp");
|
||||
try cmd.appendParam(¶ms, "click");
|
||||
try cmd.appendParam(¶ms, "wear");
|
||||
|
||||
try self.vinylCmd(pos, params);
|
||||
},
|
||||
|
||||
.RevDelay => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
|
||||
try cmd.appendParam(¶ms, "delay_time");
|
||||
try cmd.appendParam(¶ms, "dry_level");
|
||||
try cmd.appendParam(¶ms, "wet_level");
|
||||
try cmd.appendParam(¶ms, "feedback");
|
||||
try cmd.appendParam(¶ms, "xfade_samp");
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
.Write => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParamMap(&map, "data");
|
||||
try self.writeCmd(pos, &map);
|
||||
},
|
||||
|
||||
.Embed => blk: {
|
||||
const pos = try cmd.consumePosition();
|
||||
const path = cmd.args.items[2];
|
||||
try self.embedCmd(pos, path);
|
||||
},
|
||||
|
||||
.Rotate => blk: {
|
||||
const deg = try cmd.floatArgAt(0);
|
||||
const bgfill = try cmd.argAt(1);
|
||||
try self.rotateCmd(deg, bgfill);
|
||||
},
|
||||
|
||||
.Gate => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "switch");
|
||||
try cmd.appendParam(¶ms, "threshold");
|
||||
try cmd.appendParam(¶ms, "attack");
|
||||
try cmd.appendParam(¶ms, "hold");
|
||||
try cmd.appendParam(¶ms, "decay");
|
||||
try cmd.appendParam(¶ms, "gaterange");
|
||||
try self.gateCmd(pos, params);
|
||||
},
|
||||
|
||||
.Detune => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "detune");
|
||||
try cmd.appendParam(¶ms, "mix");
|
||||
try cmd.appendParam(¶ms, "output");
|
||||
try cmd.appendParam(¶ms, "latency");
|
||||
try self.detuneCmd(pos, params);
|
||||
},
|
||||
|
||||
.Overdrive => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "drive");
|
||||
try cmd.appendParam(¶ms, "muffle");
|
||||
try cmd.appendParam(¶ms, "output");
|
||||
try self.overdriveCmd(pos, params);
|
||||
},
|
||||
|
||||
.Degrade => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "headroom");
|
||||
try cmd.appendParam(¶ms, "quant");
|
||||
try cmd.appendParam(¶ms, "rate");
|
||||
try cmd.appendParam(¶ms, "post_filt");
|
||||
try cmd.appendParam(¶ms, "non_lin");
|
||||
try cmd.appendParam(¶ms, "output");
|
||||
try self.degradeCmd(pos, params);
|
||||
},
|
||||
|
||||
.RePsycho => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "tune");
|
||||
try cmd.appendParam(¶ms, "fine");
|
||||
try cmd.appendParam(¶ms, "decay");
|
||||
try cmd.appendParam(¶ms, "thresh");
|
||||
try cmd.appendParam(¶ms, "hold");
|
||||
try cmd.appendParam(¶ms, "mix");
|
||||
try cmd.appendParam(¶ms, "quality");
|
||||
try self.repsychoCmd(pos, params);
|
||||
},
|
||||
|
||||
.TalkBox => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "wet");
|
||||
try cmd.appendParam(¶ms, "dry");
|
||||
try cmd.appendParam(¶ms, "carrier");
|
||||
try cmd.appendParam(¶ms, "quality");
|
||||
try self.talkboxCmd(pos, params);
|
||||
},
|
||||
|
||||
.DynComp => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "enable");
|
||||
try cmd.appendParam(¶ms, "hold");
|
||||
try cmd.appendParam(¶ms, "inputgain");
|
||||
try cmd.appendParam(¶ms, "threshold");
|
||||
try cmd.appendParam(¶ms, "ratio");
|
||||
try cmd.appendParam(¶ms, "attack");
|
||||
try cmd.appendParam(¶ms, "release");
|
||||
try cmd.appendParam(¶ms, "gain_min");
|
||||
try cmd.appendParam(¶ms, "gain_max");
|
||||
try cmd.appendParam(¶ms, "rms");
|
||||
try self.dynCompCmd(pos, params);
|
||||
},
|
||||
|
||||
.ThruZero => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "rate");
|
||||
try cmd.appendParam(¶ms, "mix");
|
||||
try cmd.appendParam(¶ms, "feedback");
|
||||
try cmd.appendParam(¶ms, "depth_mod");
|
||||
try self.thruZeroCmd(pos, params);
|
||||
},
|
||||
|
||||
.Foverdrive => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "drive");
|
||||
try self.foverdriveCmd(pos, params);
|
||||
},
|
||||
|
||||
.Gverb => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "roomsize");
|
||||
try cmd.appendParam(¶ms, "revtime");
|
||||
try cmd.appendParam(¶ms, "damping");
|
||||
try cmd.appendParam(¶ms, "inputbandwidth");
|
||||
try cmd.appendParam(¶ms, "drylevel");
|
||||
try cmd.appendParam(¶ms, "earlylevel");
|
||||
try cmd.appendParam(¶ms, "taillevel");
|
||||
try self.gverbCmd(pos, params);
|
||||
},
|
||||
|
||||
.Invert => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try self.gverbCmd(pos, params);
|
||||
},
|
||||
|
||||
.TapeDelay => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "speed");
|
||||
try cmd.appendParam(¶ms, "da_db");
|
||||
|
||||
try cmd.appendParam(¶ms, "t1d");
|
||||
try cmd.appendParam(¶ms, "t1a_db");
|
||||
|
||||
try cmd.appendParam(¶ms, "t2d");
|
||||
try cmd.appendParam(¶ms, "t2a_db");
|
||||
|
||||
try cmd.appendParam(¶ms, "t3d");
|
||||
try cmd.appendParam(¶ms, "t3a_db");
|
||||
|
||||
try cmd.appendParam(¶ms, "t4d");
|
||||
try cmd.appendParam(¶ms, "t4a_db");
|
||||
|
||||
try self.tapedelayCmd(pos, params);
|
||||
},
|
||||
|
||||
.ModDelay => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "base");
|
||||
try self.moddelayCmd(pos, params);
|
||||
},
|
||||
|
||||
.MultiChorus => {
|
||||
const pos = try cmd.consumePosition();
|
||||
try cmd.appendParam(¶ms, "min_delay");
|
||||
try cmd.appendParam(¶ms, "mod_depth");
|
||||
try cmd.appendParam(¶ms, "mod_rate");
|
||||
try cmd.appendParam(¶ms, "stereo");
|
||||
try cmd.appendParam(¶ms, "voices");
|
||||
try cmd.appendParam(¶ms, "vphase");
|
||||
try cmd.appendParam(¶ms, "amount");
|
||||
try cmd.appendParam(¶ms, "dry");
|
||||
try cmd.appendParam(¶ms, "freq");
|
||||
try cmd.appendParam(¶ms, "freq2");
|
||||
try cmd.appendParam(¶ms, "q");
|
||||
try cmd.appendParam(¶ms, "overlap");
|
||||
try cmd.appendParam(¶ms, "level_in");
|
||||
try cmd.appendParam(¶ms, "level_out");
|
||||
try cmd.appendParam(¶ms, "lfo");
|
||||
try self.multichorusCmd(pos, params);
|
||||
},
|
||||
|
||||
.Saturator => {
|
||||
const pos = try cmd.consumePosition();
|
||||
|
||||
try cmd.appendParam(¶ms, "bypass");
|
||||
try cmd.appendParam(¶ms, "level_in");
|
||||
try cmd.appendParam(¶ms, "level_out");
|
||||
try cmd.appendParam(¶ms, "mix");
|
||||
try cmd.appendParam(¶ms, "drive");
|
||||
try cmd.appendParam(¶ms, "blend");
|
||||
try cmd.appendParam(¶ms, "lp_pre_freq");
|
||||
try cmd.appendParam(¶ms, "hp_pre_freq");
|
||||
try cmd.appendParam(¶ms, "lp_post_freq");
|
||||
try cmd.appendParam(¶ms, "hp_post_freq");
|
||||
try cmd.appendParam(¶ms, "p_freq");
|
||||
try cmd.appendParam(¶ms, "p_level");
|
||||
try cmd.appendParam(¶ms, "p_q");
|
||||
try cmd.appendParam(¶ms, "pre");
|
||||
try cmd.appendParam(¶ms, "post");
|
||||
try self.saturatorCmd(pos, params);
|
||||
},
|
||||
|
||||
.VintageDelay => {
|
||||
const pos = try cmd.consumePosition();
|
||||
const PARAMS = [_][]const u8{
|
||||
"level_in",
|
||||
"level_out",
|
||||
"subdiv",
|
||||
"time_l",
|
||||
"time_r",
|
||||
"feedback",
|
||||
"amount",
|
||||
"mix_mode",
|
||||
"medium",
|
||||
"dry",
|
||||
"width",
|
||||
"fragmentation",
|
||||
"pbeats",
|
||||
"pfrag",
|
||||
"timing",
|
||||
"bpm",
|
||||
"ms",
|
||||
"hz",
|
||||
"bpm_host",
|
||||
};
|
||||
|
||||
inline for (PARAMS) |param| {
|
||||
try cmd.appendParam(¶ms, param);
|
||||
}
|
||||
|
||||
try self.vintagedelayCmd(pos, params);
|
||||
},
|
||||
|
||||
else => blk: {
|
||||
std.debug.warn("Unsupported command: {}\n", .{cmd.command});
|
||||
break :blk RunError.UnknownCommand;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Run a list of commands.
|
||||
pub fn runCommands(
|
||||
self: *Runner,
|
||||
cmds: lang.CommandList,
|
||||
debug_flag: bool,
|
||||
) !void {
|
||||
for (cmds.items) |const_cmd| {
|
||||
if (debug_flag) const_cmd.print();
|
||||
|
||||
// copy the command so we own its memory
|
||||
var cmd = try const_cmd.copy(self.allocator);
|
||||
defer self.allocator.destroy(cmd);
|
||||
|
||||
try self.runCommand(cmd);
|
||||
for (cmds.items) |cmd| {
|
||||
cmd.print();
|
||||
try self.runCommand(cmd.*);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue