Compare commits
No commits in common. "25bef2393303f7a6208c8ea46f0e45e2cc4e9be1" and "bbf34f1133a36bbab71349b87cdbddf94fdee742" have entirely different histories.
25bef23933
...
bbf34f1133
4 changed files with 27 additions and 150 deletions
117
src/image.zig
117
src/image.zig
|
@ -1,6 +1,12 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const lv2 = @import("lv2_helpers.zig");
|
const lv2 = @import("lv2_helpers.zig");
|
||||||
const c = lv2.c;
|
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("sndfile.h");
|
||||||
|
|
||||||
|
@cInclude("lilv/lilv.h");
|
||||||
|
@cInclude("lv2/core/lv2.h");
|
||||||
|
});
|
||||||
|
|
||||||
const plugins = @import("plugin.zig");
|
const plugins = @import("plugin.zig");
|
||||||
|
|
||||||
|
@ -8,8 +14,6 @@ pub const ImageError = error{
|
||||||
OpenFail,
|
OpenFail,
|
||||||
InvalidPlugin,
|
InvalidPlugin,
|
||||||
UnknownPlugin,
|
UnknownPlugin,
|
||||||
InvalidSymbol,
|
|
||||||
InstantiateFail,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Low level integration function with libsndfile.
|
/// Low level integration function with libsndfile.
|
||||||
|
@ -38,48 +42,6 @@ fn sopen(
|
||||||
return file.?;
|
return file.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn temporaryName(allocator: *std.mem.Allocator) ![]u8 {
|
|
||||||
const template_start = "/temp/temp_";
|
|
||||||
const template = "/tmp/temp_XXXXXX";
|
|
||||||
var nam = try allocator.alloc(u8, template.len);
|
|
||||||
std.mem.copy(u8, nam, template);
|
|
||||||
|
|
||||||
var r = std.rand.DefaultPrng.init(std.time.timestamp());
|
|
||||||
|
|
||||||
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));
|
|
||||||
var letter = u8(65) + idx;
|
|
||||||
fill[f_idx] = letter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we fail to access it, we assume it doesn't exist and return it.
|
|
||||||
std.fs.File.access(nam) catch |err| {
|
|
||||||
if (err == error.FileNotFound) {
|
|
||||||
return nam;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return error.TempGenFail;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mkSfInfo() c.SF_INFO {
|
|
||||||
return c.SF_INFO{
|
|
||||||
.frames = c_int(0),
|
|
||||||
.samplerate = c_int(44100),
|
|
||||||
.channels = c_int(1),
|
|
||||||
.format = c.SF_FORMAT_ULAW | c.SF_FORMAT_RAW | c.SF_ENDIAN_BIG,
|
|
||||||
.sections = c_int(0),
|
|
||||||
.seekable = c_int(0),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const Image = struct {
|
pub const Image = struct {
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
sndfile: *c.SNDFILE,
|
sndfile: *c.SNDFILE,
|
||||||
|
@ -87,7 +49,14 @@ pub const Image = struct {
|
||||||
|
|
||||||
/// Open a BMP file.
|
/// Open a BMP file.
|
||||||
pub fn open(allocator: *std.mem.Allocator, path: []const u8) !*Image {
|
pub fn open(allocator: *std.mem.Allocator, path: []const u8) !*Image {
|
||||||
var in_fmt = mkSfInfo();
|
var in_fmt = c.SF_INFO{
|
||||||
|
.frames = c_int(0),
|
||||||
|
.samplerate = c_int(44100),
|
||||||
|
.channels = c_int(1),
|
||||||
|
.format = c.SF_FORMAT_ULAW | c.SF_FORMAT_RAW | c.SF_ENDIAN_BIG,
|
||||||
|
.sections = c_int(0),
|
||||||
|
.seekable = c_int(0),
|
||||||
|
};
|
||||||
|
|
||||||
var sndfile = try sopen(allocator, path, c.SFM_READ, &in_fmt);
|
var sndfile = try sopen(allocator, path, c.SFM_READ, &in_fmt);
|
||||||
var image = try allocator.create(Image);
|
var image = try allocator.create(Image);
|
||||||
|
@ -128,63 +97,19 @@ pub const Image = struct {
|
||||||
return n_read == 1;
|
return n_read == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run a plugin over the image.
|
|
||||||
/// This setups a new lilv world/plugin among other things.
|
|
||||||
/// The internal SNDFILE pointer is modified to point to the output of the
|
|
||||||
/// plugin run.
|
|
||||||
pub fn runPlugin(
|
pub fn runPlugin(
|
||||||
self: *Image,
|
self: *Image,
|
||||||
plugin_uri: []const u8,
|
plugin_uri: []const u8,
|
||||||
pos: plugins.Position,
|
pos: plugins.Position,
|
||||||
params: plugins.ParamList,
|
params: plugins.ParamList,
|
||||||
) !void {
|
) !void {
|
||||||
var ctx = try plugins.makeContext(self.allocator, plugin_uri);
|
var context = try plugins.makeContext(self.allocator, plugin_uri);
|
||||||
std.debug.warn("\tworld: {}\n", ctx.world);
|
std.debug.warn("world: {}\n", context.world);
|
||||||
std.debug.warn("\tplugin: {}\n", ctx.plugin);
|
std.debug.warn("plugin: {}\n", context.plugin);
|
||||||
|
|
||||||
var ports = try lv2.setupPorts(&ctx);
|
var ports = try lv2.setupPorts(&context);
|
||||||
|
for (ports) |port| {
|
||||||
if (ctx.n_audio_in != 1) {
|
std.debug.warn("port: {}\n", port.*);
|
||||||
std.debug.warn("plugin <{}> does not accept mono input.\n", plugin_uri);
|
|
||||||
return ImageError.InvalidPlugin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// now, for each param for the plugin, we find its port, and set
|
|
||||||
// the value for the port there.
|
|
||||||
var it = params.iterator();
|
|
||||||
|
|
||||||
while (it.next()) |param| {
|
|
||||||
var sym_cstr = try std.cstr.addNullByte(self.allocator, param.sym);
|
|
||||||
defer self.allocator.free(sym_cstr);
|
|
||||||
|
|
||||||
var sym = c.lilv_new_string(ctx.world, sym_cstr.ptr);
|
|
||||||
const port = c.lilv_plugin_get_port_by_symbol(ctx.plugin, sym) orelse blk: {
|
|
||||||
std.debug.warn("assert fail: symbol not found on port");
|
|
||||||
return ImageError.InvalidSymbol;
|
|
||||||
};
|
|
||||||
|
|
||||||
c.lilv_node_free(sym);
|
|
||||||
|
|
||||||
var idx = c.lilv_port_get_index(ctx.plugin, port);
|
|
||||||
std.debug.warn(
|
|
||||||
"sym={}, idx={} to val={}\n",
|
|
||||||
param.sym,
|
|
||||||
idx,
|
|
||||||
param.value,
|
|
||||||
);
|
|
||||||
ports[idx].value = param.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// now we need to generate a temporary file and put the output of
|
|
||||||
// running the plugin on that file
|
|
||||||
var tmpnam = try temporaryName(self.allocator);
|
|
||||||
std.debug.warn("temporary name: {}\n", tmpnam);
|
|
||||||
|
|
||||||
var out_fmt = mkSfInfo();
|
|
||||||
var out_file = try sopen(self.allocator, tmpnam, c.SFM_WRITE, &out_fmt);
|
|
||||||
|
|
||||||
var rctx = try plugins.RunContext.init(self.allocator, ctx.plugin);
|
|
||||||
rctx.connectPorts(ports);
|
|
||||||
lv2.lilv_instance_activate(rctx.instance);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,6 @@ const std = @import("std");
|
||||||
const plugin = @import("plugin.zig");
|
const plugin = @import("plugin.zig");
|
||||||
|
|
||||||
pub const c = @cImport({
|
pub const c = @cImport({
|
||||||
@cInclude("sndfile.h");
|
|
||||||
@cInclude("lilv/lilv.h");
|
@cInclude("lilv/lilv.h");
|
||||||
@cInclude("lv2/core/lv2.h");
|
@cInclude("lv2/core/lv2.h");
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const lv2 = @import("lv2_helpers.zig");
|
const c = @import("lv2_helpers.zig").c;
|
||||||
const c = lv2.c;
|
|
||||||
|
|
||||||
const ImageError = @import("image.zig").ImageError;
|
const ImageError = @import("image.zig").ImageError;
|
||||||
|
|
||||||
|
@ -27,59 +26,13 @@ pub const Position = struct {
|
||||||
pub const Context = struct {
|
pub const Context = struct {
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
world: *c.LilvWorld,
|
world: *c.LilvWorld,
|
||||||
plugin: *const c.LilvPlugin,
|
plugin: ?*const c.LilvPlugin,
|
||||||
|
|
||||||
// they should both be 1.
|
// they should both be 1.
|
||||||
n_audio_in: usize = 0,
|
n_audio_in: usize = 0,
|
||||||
n_audio_out: usize = 0,
|
n_audio_out: usize = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represents the specific run context of plugin instantation.
|
|
||||||
pub const RunContext = struct {
|
|
||||||
in_buf: []f32,
|
|
||||||
out_buf: []f32,
|
|
||||||
instance: *c.LilvInstance,
|
|
||||||
|
|
||||||
pub fn init(
|
|
||||||
allocator: *std.mem.Allocator,
|
|
||||||
plugin: *const c.LilvPlugin,
|
|
||||||
) !RunContext {
|
|
||||||
var instance = c.lilv_plugin_instantiate(plugin, f64(44100), null);
|
|
||||||
if (instance == null) {
|
|
||||||
return ImageError.InstantiateFail;
|
|
||||||
}
|
|
||||||
|
|
||||||
return RunContext{
|
|
||||||
.in_buf = try allocator.alloc(f32, 1),
|
|
||||||
.out_buf = try allocator.alloc(f32, 1),
|
|
||||||
.instance = instance.?,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn connectPorts(self: *RunContext, ports: []*lv2.Port) void {
|
|
||||||
var i: usize = 0;
|
|
||||||
var o: usize = 0;
|
|
||||||
|
|
||||||
for (ports) |port, p_idx| {
|
|
||||||
var p = @intCast(u32, p_idx);
|
|
||||||
|
|
||||||
switch (port.ptype) {
|
|
||||||
.Control => lv2.lilv_instance_connect_port(self.instance, p, &port.value),
|
|
||||||
.Audio => blk: {
|
|
||||||
if (port.is_input) {
|
|
||||||
lv2.lilv_instance_connect_port(self.instance, p, &self.in_buf[i]);
|
|
||||||
i += 1;
|
|
||||||
} else {
|
|
||||||
lv2.lilv_instance_connect_port(self.instance, p, &self.out_buf[o]);
|
|
||||||
o += 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => lv2.lilv_instance_connect_port(self.instance, p, null),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn makeContext(allocator: *std.mem.Allocator, plugin_uri: []const u8) !Context {
|
pub fn makeContext(allocator: *std.mem.Allocator, plugin_uri: []const u8) !Context {
|
||||||
const cstr_plugin_uri = try std.cstr.addNullByte(allocator, plugin_uri);
|
const cstr_plugin_uri = try std.cstr.addNullByte(allocator, plugin_uri);
|
||||||
var world = c.lilv_world_new().?;
|
var world = c.lilv_world_new().?;
|
||||||
|
|
|
@ -143,14 +143,14 @@ pub const Runner = struct {
|
||||||
/// Run the http://lv2plug.in/plugins/eg-amp plugin over the file.
|
/// Run the http://lv2plug.in/plugins/eg-amp plugin over the file.
|
||||||
fn ampCmd(self: *Runner, split: usize, index: usize, gain: f32) !void {
|
fn ampCmd(self: *Runner, split: usize, index: usize, gain: f32) !void {
|
||||||
var image = try self.getImage();
|
var image = try self.getImage();
|
||||||
|
var param = try self.allocator.create(plugin.Param);
|
||||||
|
defer self.allocator.destroy(param);
|
||||||
|
|
||||||
|
param.* = plugin.Param{ .sym = "gain", .value = gain };
|
||||||
|
|
||||||
var params = plugin.ParamList.init(self.allocator);
|
var params = plugin.ParamList.init(self.allocator);
|
||||||
try params.append(plugin.Param{ .sym = "gain", .value = gain });
|
|
||||||
defer params.deinit();
|
defer params.deinit();
|
||||||
|
|
||||||
// TODO if we could detect that the next command is a quicksave then
|
|
||||||
// we don't need the temporary file in runPlugin and instead we
|
|
||||||
// dump it all on the output image for it.
|
|
||||||
try image.runPlugin(
|
try image.runPlugin(
|
||||||
"http://lv2plug.in/plugins/eg-amp",
|
"http://lv2plug.in/plugins/eg-amp",
|
||||||
plugin.Position{ .split = split, .index = index },
|
plugin.Position{ .split = split, .index = index },
|
||||||
|
|
Loading…
Reference in a new issue