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(
|
pub fn init(
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
params: *plugins.ParamMap,
|
params: var,
|
||||||
) ?RandomNoise {
|
) ?RandomNoise {
|
||||||
const seed = @floatToInt(u64, params.get("seed").?.value);
|
var r = std.rand.DefaultPrng.init(params.seed);
|
||||||
const fillbytes = @floatToInt(usize, params.get("fill_bytes").?.value);
|
|
||||||
|
|
||||||
var r = std.rand.DefaultPrng.init(seed);
|
if (params.fill_bytes > 0) {
|
||||||
|
var rand_buf = allocator.alloc(f32, params.fill_bytes) catch return null;
|
||||||
if (fillbytes > 0) {
|
|
||||||
var rand_buf = allocator.alloc(f32, fillbytes) catch return null;
|
|
||||||
|
|
||||||
for (rand_buf) |_, idx| {
|
for (rand_buf) |_, idx| {
|
||||||
rand_buf[idx] = r.random.float(f32);
|
rand_buf[idx] = r.random.float(f32);
|
||||||
|
@ -67,15 +64,12 @@ pub const WildNoise = struct {
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
params: *plugins.ParamMap,
|
params: var,
|
||||||
) ?WildNoise {
|
) ?WildNoise {
|
||||||
const seed = @floatToInt(u64, params.get("seed").?.value);
|
var r = std.rand.DefaultPrng.init(params.seed);
|
||||||
const fillbytes = @floatToInt(usize, params.get("fill_bytes").?.value);
|
|
||||||
|
|
||||||
var r = std.rand.DefaultPrng.init(seed);
|
if (params.fill_bytes > 0) {
|
||||||
|
var rand_buf = allocator.alloc(f32, params.fill_bytes) catch return null;
|
||||||
if (fillbytes > 0) {
|
|
||||||
var rand_buf = allocator.alloc(f32, fillbytes) catch return null;
|
|
||||||
|
|
||||||
for (rand_buf) |_, idx| {
|
for (rand_buf) |_, idx| {
|
||||||
rand_buf[idx] = @intToFloat(f32, r.random.int(u1));
|
rand_buf[idx] = @intToFloat(f32, r.random.int(u1));
|
||||||
|
@ -118,11 +112,10 @@ pub const Write = struct {
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
params: *plugins.ParamMap,
|
params: var,
|
||||||
) Write {
|
) Write {
|
||||||
const data = params.get("data").?;
|
|
||||||
return Write{
|
return Write{
|
||||||
.data = data.value,
|
.data = params.data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,10 +133,10 @@ pub const Embed = struct {
|
||||||
sndfile: *c.SNDFILE = undefined,
|
sndfile: *c.SNDFILE = undefined,
|
||||||
buf: []f32 = 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{
|
return Embed{
|
||||||
.allocator = allocator,
|
.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.
|
/// Caller owns the returned memory.
|
||||||
pub fn temporaryName(allocator: *std.mem.Allocator) ![]u8 {
|
pub fn temporaryName(allocator: *std.mem.Allocator) ![]u8 {
|
||||||
const template_start = "/temp/temp_";
|
const template_start = "/temp/temp_";
|
||||||
const template = "/tmp/temp_XXXXXXXXXXX";
|
const template = "/tmp/temp_XXXXXXXXXXXXXXXXXXXXX";
|
||||||
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);
|
||||||
|
|
||||||
const seed = @bitCast(u64, std.time.timestamp());
|
const seed = @truncate(u64, @bitCast(u128, std.time.nanoTimestamp()));
|
||||||
var r = std.rand.DefaultPrng.init(seed);
|
var r = std.rand.DefaultPrng.init(seed);
|
||||||
|
|
||||||
var fill = nam[template_start.len..nam.len];
|
var fill = nam[template_start.len..nam.len];
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < 100) : (i += 1) {
|
while (i < 100) : (i += 1) {
|
||||||
|
|
||||||
// generate a random uppercase letter, that is, 65 + random number.
|
// generate a random uppercase letter, that is, 65 + random number.
|
||||||
for (fill) |_, f_idx| {
|
for (fill) |_, f_idx| {
|
||||||
var idx = @intCast(u8, r.random.uintLessThan(u5, 24));
|
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.
|
// if we fail to access it, we assume it doesn't exist and return it.
|
||||||
|
var tmp_file: std.fs.File = std.fs.cwd().openFile(
|
||||||
_ = std.fs.cwd().openFile(nam, .{ .read = true, .write = false }) catch |err| {
|
nam,
|
||||||
if (err == error.FileNotFound) {
|
.{ .read = true, .write = false },
|
||||||
return nam;
|
) 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;
|
return error.TempGenFail;
|
||||||
|
@ -422,8 +425,7 @@ pub const Image = struct {
|
||||||
self: *Image,
|
self: *Image,
|
||||||
comptime Plugin: type,
|
comptime Plugin: type,
|
||||||
position: plugins.Position,
|
position: plugins.Position,
|
||||||
comptime ExtraType: type,
|
extra: var,
|
||||||
extra: ExtraType,
|
|
||||||
) !void {
|
) !void {
|
||||||
var plugin_opt: ?Plugin = Plugin.init(self.allocator, extra);
|
var plugin_opt: ?Plugin = Plugin.init(self.allocator, extra);
|
||||||
if (plugin_opt == null) {
|
if (plugin_opt == null) {
|
||||||
|
|
834
src/lang.zig
834
src/lang.zig
|
@ -1,166 +1,486 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const plugin = @import("plugin.zig");
|
const plugin = @import("plugin.zig");
|
||||||
|
const custom = @import("custom.zig");
|
||||||
|
|
||||||
pub const ParseError = error{
|
pub const ParseError = error{ParseFail};
|
||||||
OutOfMemory,
|
|
||||||
ArgRequired,
|
|
||||||
ParseFail,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const CommandType = enum {
|
pub const CommandType = enum {
|
||||||
Noop,
|
/// "LV2 Commands" are commands that receive split, index, and then receive
|
||||||
Load,
|
/// any f64 arguments.
|
||||||
Quicksave,
|
lv2_command,
|
||||||
RunQS,
|
|
||||||
|
|
||||||
Amp,
|
custom_command,
|
||||||
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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 {
|
pub const Command = struct {
|
||||||
command: CommandType,
|
tag: Tag,
|
||||||
args: ArgList,
|
|
||||||
cur_idx: usize = 0,
|
|
||||||
|
|
||||||
pub fn print(self: Command) void {
|
pub const Tag = enum {
|
||||||
std.debug.warn("cmd:{}\n", .{self.command});
|
noop,
|
||||||
}
|
load,
|
||||||
|
quicksave,
|
||||||
|
runqs,
|
||||||
|
|
||||||
pub fn argAt(self: Command, idx: usize) ![]const u8 {
|
amp,
|
||||||
std.debug.warn("{} {}", .{ idx, self.args.items.len });
|
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)) {
|
noise,
|
||||||
std.debug.warn("Expected argument at index {}\n", .{idx});
|
wildnoise,
|
||||||
return ParseError.ArgRequired;
|
write,
|
||||||
}
|
embed,
|
||||||
|
|
||||||
return self.args.items[idx];
|
rotate,
|
||||||
}
|
};
|
||||||
|
|
||||||
pub fn usizeArgAt(self: Command, idx: usize) !usize {
|
pub fn tagToType(tag: Tag) type {
|
||||||
var arg = try self.argAt(idx);
|
return switch (tag) {
|
||||||
return try std.fmt.parseInt(usize, arg, 10);
|
.noop => Noop,
|
||||||
}
|
.load => Load,
|
||||||
|
.quicksave => Quicksave,
|
||||||
|
.runqs => RunQS,
|
||||||
|
|
||||||
pub fn consumePosition(self: *Command) !plugin.Position {
|
.amp => Amp,
|
||||||
self.cur_idx = 2;
|
.rflanger => RFlanger,
|
||||||
return plugin.Position{
|
.eq => Eq,
|
||||||
.split = try self.usizeArgAt(0),
|
.phaser => Phaser,
|
||||||
.index = try self.usizeArgAt(1),
|
.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 {
|
pub fn cast(base: *const @This(), comptime T: type) ?*const T {
|
||||||
var arg = try self.argAt(idx);
|
if (base.tag != T.base_tag)
|
||||||
return try std.fmt.parseInt(i32, arg, 10);
|
return null;
|
||||||
|
|
||||||
|
return @fieldParentPtr(T, "base", base);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn floatArgAt(self: Command, idx: usize) !f32 {
|
pub fn print(base: *const @This()) void {
|
||||||
var arg = try self.argAt(idx);
|
std.debug.warn("tag: {}\n", .{base.tag});
|
||||||
return try std.fmt.parseFloat(f32, arg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn floatArgMany(
|
pub const Noop = struct {
|
||||||
self: Command,
|
pub const base_tag = Tag.noop;
|
||||||
allocator: *std.mem.Allocator,
|
base: Command,
|
||||||
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) {
|
pub const Load = struct {
|
||||||
var value: f32 = self.floatArgAt(i) catch |err| blk: {
|
pub const base_tag = Tag.load;
|
||||||
std.debug.warn("\tdoing default on arg {}\n", .{i});
|
base: Command,
|
||||||
break :blk default;
|
path: []u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
try arr.append(value);
|
pub const Quicksave = struct {
|
||||||
}
|
pub const base_tag = Tag.quicksave;
|
||||||
|
base: Command,
|
||||||
|
};
|
||||||
|
|
||||||
return arr.items;
|
pub const RunQS = struct {
|
||||||
}
|
pub const base_tag = Tag.runqs;
|
||||||
|
base: Command,
|
||||||
|
program: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn appendParam(
|
pub const Noise = CustomCommand(Tag.noise, custom.RandomNoise, struct {
|
||||||
self: *Command,
|
seed: u64,
|
||||||
params: *plugin.ParamList,
|
fill_bytes: usize,
|
||||||
symbol: []const u8,
|
});
|
||||||
) !void {
|
|
||||||
var val = try self.floatArgAt(self.cur_idx);
|
|
||||||
self.cur_idx += 1;
|
|
||||||
|
|
||||||
try params.append(plugin.Param{
|
pub const Wildnoise = CustomCommand(Tag.wildnoise, custom.WildNoise, struct {
|
||||||
.sym = symbol,
|
seed: u64,
|
||||||
.value = val,
|
fill_bytes: usize,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
pub fn appendParamMap(
|
pub const Write = CustomCommand(Tag.write, custom.Write, struct {
|
||||||
self: *Command,
|
data: f32,
|
||||||
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 fn copy(self: Command, allocator: *std.mem.Allocator) !*Command {
|
pub const Embed = CustomCommand(Tag.write, custom.Embed, struct {
|
||||||
var cmd = try allocator.create(Command);
|
path: []const u8,
|
||||||
cmd.* = Command{
|
});
|
||||||
.command = self.command,
|
|
||||||
.args = self.args,
|
|
||||||
.cur_idx = self.cur_idx,
|
|
||||||
};
|
|
||||||
|
|
||||||
return cmd;
|
pub const Rotate = struct {
|
||||||
}
|
pub const base_tag = Tag.rotate;
|
||||||
|
base: Command,
|
||||||
|
deg: f32,
|
||||||
|
bgfill: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 CommandList = std.ArrayList(*Command);
|
||||||
pub const ArgList = std.ArrayList([]const u8);
|
|
||||||
|
|
||||||
pub const KeywordMap = std.StringHashMap(CommandType);
|
|
||||||
|
|
||||||
/// A parser.
|
/// A parser.
|
||||||
pub const Lang = struct {
|
pub const Lang = struct {
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
keywords: KeywordMap,
|
|
||||||
|
|
||||||
has_error: bool = false,
|
has_error: bool = false,
|
||||||
line: usize = 0,
|
line: usize = 0,
|
||||||
|
@ -168,137 +488,108 @@ pub const Lang = struct {
|
||||||
pub fn init(allocator: *std.mem.Allocator) Lang {
|
pub fn init(allocator: *std.mem.Allocator) Lang {
|
||||||
return Lang{
|
return Lang{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.keywords = KeywordMap.init(allocator),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Lang) void {
|
pub fn deinit(self: *Lang) void {}
|
||||||
self.keywords.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(self: *Lang) void {
|
pub fn reset(self: *Lang) void {
|
||||||
self.has_error = false;
|
self.has_error = false;
|
||||||
self.line = 0;
|
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 {
|
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(fmt, args);
|
||||||
std.debug.warn("\n", .{});
|
std.debug.warn("\n", .{});
|
||||||
self.has_error = true;
|
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, ";");
|
var splitted_it = std.mem.split(data, ";");
|
||||||
try self.fillKeywords();
|
|
||||||
var cmds = CommandList.init(self.allocator);
|
var cmds = CommandList.init(self.allocator);
|
||||||
|
|
||||||
while (splitted_it.next()) |stmt_orig| {
|
while (splitted_it.next()) |stmt_orig| {
|
||||||
|
@ -309,7 +600,7 @@ pub const Lang = struct {
|
||||||
if (stmt.len == 0) continue;
|
if (stmt.len == 0) continue;
|
||||||
if (std.mem.startsWith(u8, stmt, "#")) 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 tok_it = std.mem.tokenize(stmt, " ");
|
||||||
|
|
||||||
var cmd_opt = tok_it.next();
|
var cmd_opt = tok_it.next();
|
||||||
|
@ -317,32 +608,45 @@ pub const Lang = struct {
|
||||||
self.doError("No command given", .{});
|
self.doError("No command given", .{});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var command = cmd_opt.?;
|
const command_string = cmd_opt.?;
|
||||||
|
|
||||||
var ctype_opt = self.getCommand(command);
|
var found: bool = false;
|
||||||
var ctype: CommandType = undefined;
|
|
||||||
if (ctype_opt) |ctype_val| {
|
inline for (@typeInfo(Command).Struct.decls) |cmd_struct_decl| {
|
||||||
ctype = ctype_val;
|
switch (cmd_struct_decl.data) {
|
||||||
} else {
|
.Type => |typ| switch (@typeInfo(typ)) {
|
||||||
self.doError("Unknown command '{}' ({})", .{ command, command.len });
|
.Struct => {},
|
||||||
continue;
|
else => continue,
|
||||||
|
},
|
||||||
|
else => continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var args = ArgList.init(self.allocator);
|
if (!found) {
|
||||||
errdefer args.deinit();
|
self.doError("Unknown command '{}' ({})", .{ command_string, command_string.len });
|
||||||
|
|
||||||
while (tok_it.next()) |arg| {
|
|
||||||
try args.append(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
continue;
|
||||||
};
|
}
|
||||||
|
|
||||||
try cmds.append(cmd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.has_error) return ParseError.ParseFail;
|
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");
|
@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);
|
var cmds = langs.CommandList.init(allocator);
|
||||||
try cmds.append(cmd);
|
try cmds.append(cmd);
|
||||||
return cmds;
|
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 {
|
pub fn doRepl(allocator: *std.mem.Allocator, args_it: var) !void {
|
||||||
var stdout_file = std.io.getStdOut();
|
var stdout_file = std.io.getStdOut();
|
||||||
const stdout = &stdout_file.outStream();
|
const stdout = &stdout_file.outStream();
|
||||||
|
@ -57,13 +71,16 @@ pub fn doRepl(allocator: *std.mem.Allocator, args_it: var) !void {
|
||||||
} else {
|
} else {
|
||||||
// if there isn't any commands on the file, we load our default
|
// if there isn't any commands on the file, we load our default
|
||||||
// 'load :0' command
|
// 'load :0' command
|
||||||
var loadargs = langs.ArgList.init(allocator);
|
|
||||||
try loadargs.append(":0");
|
|
||||||
|
|
||||||
try cmds.append(langs.Command{
|
// TODO: deliberate memleak here. we only allocate this
|
||||||
.command = .Load,
|
// command once, for the start of the file, so.
|
||||||
.args = loadargs,
|
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| {
|
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
|
// run the load command
|
||||||
try runner.runCommands(cmds, true);
|
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";
|
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) {
|
while (true) {
|
||||||
lang.reset();
|
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)];
|
var line = rd_line[0..std.mem.len(rd_line)];
|
||||||
|
|
||||||
if (std.mem.eql(u8, line, "push")) {
|
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
|
// run the current added command to main cmds list
|
||||||
// with the main parent runner
|
// with the main parent runner
|
||||||
var cmds_wrapped = try wrapInCmdList(allocator, current);
|
var cmds_wrapped = try wrapInCmdList(allocator, heap_cmd);
|
||||||
defer cmds_wrapped.deinit();
|
defer cmds_wrapped.deinit();
|
||||||
try runner.runCommands(cmds_wrapped, true);
|
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});
|
std.debug.warn("repl: error while parsing: {}\n", .{err});
|
||||||
continue;
|
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
|
// by cloning the parent runner, we can iteratively write
|
||||||
// whatever command we want and only commit the good results
|
// 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();
|
var runner_clone = try runner.clone();
|
||||||
defer runner_clone.deinit();
|
defer runner_clone.deinit();
|
||||||
|
|
||||||
try cmds_parsed.append(langs.Command{
|
// taking address is fine, because runqs_cmd lives in the lifetime
|
||||||
.command = .RunQS,
|
// of this function.
|
||||||
.args = runqs_args,
|
try cmds_parsed.append(&runqs_cmd.base);
|
||||||
});
|
|
||||||
|
|
||||||
try runner_clone.runCommands(cmds_parsed, true);
|
try runner_clone.runCommands(cmds_parsed, true);
|
||||||
_ = try stdout.write("\n");
|
_ = 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");
|
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 {
|
pub fn printList(list: langs.CommandList, stream: var) !void {
|
||||||
for (list.items) |cmd| {
|
for (list.items) |cmd| {
|
||||||
var command = switch (cmd.command) {
|
const command = @tagName(cmd.tag);
|
||||||
.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",
|
|
||||||
};
|
|
||||||
|
|
||||||
try stream.print("{}", .{command});
|
try stream.print("{}", .{command});
|
||||||
|
|
||||||
for (cmd.args.items) |arg| {
|
switch (cmd.tag) {
|
||||||
try stream.print(" {}", .{arg});
|
.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");
|
_ = try stream.write(";\n");
|
||||||
|
|
702
src/runner.zig
702
src/runner.zig
|
@ -24,12 +24,15 @@ pub const Runner = struct {
|
||||||
image: ?*Image = null,
|
image: ?*Image = null,
|
||||||
|
|
||||||
/// If the runner is in REPL mode
|
/// If the runner is in REPL mode
|
||||||
repl: bool = false,
|
repl: bool,
|
||||||
|
|
||||||
|
args: [][]u8,
|
||||||
|
|
||||||
pub fn init(allocator: *std.mem.Allocator, repl: bool) Runner {
|
pub fn init(allocator: *std.mem.Allocator, repl: bool) Runner {
|
||||||
return Runner{
|
return Runner{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.repl = repl,
|
.repl = repl,
|
||||||
|
.args = std.process.argsAlloc(allocator) catch unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +44,12 @@ pub const Runner = struct {
|
||||||
|
|
||||||
pub fn clone(self: *Runner) !Runner {
|
pub fn clone(self: *Runner) !Runner {
|
||||||
var cloned_image = if (self.image) |image| try image.clone() else null;
|
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 {
|
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
|
// parse the index from 1 to end
|
||||||
var index = try std.fmt.parseInt(usize, load_path[1..], 10);
|
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 it isn't in the repl, args look like this:
|
||||||
if (self.repl) index += 1;
|
// 'scritcher ./script ./image'
|
||||||
|
// if it is, it looks like this
|
||||||
|
// 'scritcher repl ./script ./image'
|
||||||
|
|
||||||
var args_it = std.process.args();
|
// ':0' should ALWAYS point to the image.
|
||||||
_ = args_it.skip();
|
if (self.repl) index += 3 else index += 2;
|
||||||
|
|
||||||
var i: usize = 0;
|
std.debug.warn("ARGS!! {} \n", .{self.args.len});
|
||||||
while (i <= index) : (i += 1) {
|
for (self.args) |arg, idx| {
|
||||||
_ = args_it.skip();
|
std.debug.warn("arg{} = {}\n", .{ idx, arg });
|
||||||
}
|
}
|
||||||
|
std.debug.warn("fetch arg idx={}, val={}\n", .{ index, self.args[index] });
|
||||||
const arg = try (args_it.next(self.allocator) orelse @panic("expected argument"));
|
return self.args[index];
|
||||||
|
|
||||||
return arg;
|
|
||||||
} else {
|
} else {
|
||||||
return load_path;
|
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
|
// krita/gimp and make it export a bmp and while in the program you can
|
||||||
// apply filters, etc.
|
// apply filters, etc.
|
||||||
if (!std.mem.endsWith(u8, load_path, ".bmp") and !std.mem.endsWith(u8, load_path, ".ppm")) {
|
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;
|
return RunError.NoBMP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,614 +183,134 @@ pub const Runner = struct {
|
||||||
try image.saveTo(out_path);
|
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();
|
var image = try self.getImage();
|
||||||
const out_path = try self.makeGlitchedPath();
|
const out_path = try self.makeGlitchedPath();
|
||||||
try image.saveTo(out_path);
|
try image.saveTo(out_path);
|
||||||
|
|
||||||
var proc = try std.ChildProcess.init(
|
var proc = try std.ChildProcess.init(
|
||||||
&[_][]const u8{ program, out_path },
|
&[_][]const u8{ runqs.program, out_path },
|
||||||
self.allocator,
|
self.allocator,
|
||||||
);
|
);
|
||||||
defer proc.deinit();
|
defer proc.deinit();
|
||||||
|
|
||||||
std.debug.warn("running '{} {}'\n", .{ program, out_path });
|
std.debug.warn("running '{} {}'\n", .{ runqs.program, out_path });
|
||||||
_ = try proc.spawnAndWait();
|
_ = try proc.spawnAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the http://lv2plug.in/plugins/eg-amp plugin over the file.
|
fn rotateCmd(self: *Runner, cmd: lang.Command) !void {
|
||||||
fn ampCmd(self: *Runner, pos: Position, params: ParamList) !void {
|
const rotate_cmd = cmd.cast(lang.Command.Rotate).?;
|
||||||
|
|
||||||
var image = try self.getImage();
|
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 {
|
fn executeLV2Command(self: *@This(), command: var) !void {
|
||||||
var image = try self.getImage();
|
const pos = plugin.Position{
|
||||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/retroFlange", pos, params);
|
.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);
|
var params = ParamList.init(self.allocator);
|
||||||
defer params.deinit();
|
defer params.deinit();
|
||||||
|
|
||||||
for (bands) |band_value, idx| {
|
const typ = @TypeOf(command);
|
||||||
var sym = try std.fmt.allocPrint(self.allocator, "band_{}", .{idx + 1});
|
|
||||||
|
inline for (@typeInfo(@TypeOf(command.parameters)).Struct.fields) |cmd_field| {
|
||||||
try params.append(plugin.Param{
|
try params.append(plugin.Param{
|
||||||
.sym = sym,
|
.sym = cmd_field.name,
|
||||||
.value = band_value,
|
.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();
|
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 {
|
||||||
var image = try self.getImage();
|
const pos = plugin.Position{
|
||||||
try image.runPlugin("http://plugin.org.uk/swh-plugins/pitchScaleHQ", pos, params);
|
.split = command.split,
|
||||||
}
|
.index = command.index,
|
||||||
|
|
||||||
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,
|
|
||||||
) !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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var image = try self.getImage();
|
||||||
|
try image.runCustomPlugin(@TypeOf(command).plugin_type, pos, command.parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn runSingleCommand(
|
||||||
|
self: *@This(),
|
||||||
|
cmd: lang.Command,
|
||||||
|
comptime tag: lang.Command.Tag,
|
||||||
|
) !void {
|
||||||
|
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 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Run a list of commands.
|
/// Run a list of commands.
|
||||||
pub fn runCommands(
|
pub fn runCommands(
|
||||||
self: *Runner,
|
self: *Runner,
|
||||||
cmds: lang.CommandList,
|
cmds: lang.CommandList,
|
||||||
debug_flag: bool,
|
debug_flag: bool,
|
||||||
) !void {
|
) !void {
|
||||||
for (cmds.items) |const_cmd| {
|
for (cmds.items) |cmd| {
|
||||||
if (debug_flag) const_cmd.print();
|
cmd.print();
|
||||||
|
try self.runCommand(cmd.*);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue