2019-07-08 17:43:58 +00:00
|
|
|
const std = @import("std");
|
2019-07-09 16:21:07 +00:00
|
|
|
const lv2 = @import("lv2_helpers.zig");
|
2019-07-09 16:55:52 +00:00
|
|
|
const c = lv2.c;
|
2019-09-08 14:55:17 +00:00
|
|
|
const bmp = @import("bmp_valid.zig");
|
2019-07-08 17:43:58 +00:00
|
|
|
|
2019-07-09 03:04:01 +00:00
|
|
|
const plugins = @import("plugin.zig");
|
|
|
|
|
2019-08-09 17:03:32 +00:00
|
|
|
/// Buffer size for main image copying.
|
|
|
|
pub const BufferSize: usize = 300000;
|
2019-07-11 01:28:34 +00:00
|
|
|
|
2019-07-09 03:04:01 +00:00
|
|
|
pub const ImageError = error{
|
|
|
|
OpenFail,
|
|
|
|
InvalidPlugin,
|
|
|
|
UnknownPlugin,
|
2019-07-09 16:55:52 +00:00
|
|
|
InvalidSymbol,
|
2019-07-09 18:15:02 +00:00
|
|
|
InstantiateFail,
|
2019-07-10 14:47:35 +00:00
|
|
|
WriteFail,
|
2019-07-29 14:51:40 +00:00
|
|
|
PluginLoadFail,
|
2019-07-09 03:04:01 +00:00
|
|
|
};
|
2019-07-08 17:43:58 +00:00
|
|
|
|
|
|
|
/// Low level integration function with libsndfile.
|
2019-10-22 21:16:15 +00:00
|
|
|
pub fn sopen(
|
2019-07-08 17:43:58 +00:00
|
|
|
allocator: *std.mem.Allocator,
|
|
|
|
path: []const u8,
|
|
|
|
mode: i32,
|
|
|
|
fmt: *c.SF_INFO,
|
|
|
|
) !*c.SNDFILE {
|
|
|
|
var cstr_path = try std.cstr.addNullByte(allocator, path);
|
|
|
|
defer allocator.free(cstr_path);
|
|
|
|
|
2019-10-22 22:08:38 +00:00
|
|
|
var file = c.sf_open(cstr_path.ptr, mode, fmt);
|
2019-07-08 17:43:58 +00:00
|
|
|
const st: i32 = c.sf_error(file);
|
|
|
|
|
|
|
|
if (st != 0) {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("Failed to open {} ({})\n", .{
|
2019-07-08 17:43:58 +00:00
|
|
|
path,
|
2019-10-22 22:08:38 +00:00
|
|
|
c.sf_error_number(st),
|
2020-01-15 01:31:20 +00:00
|
|
|
});
|
2019-07-08 17:43:58 +00:00
|
|
|
|
|
|
|
return ImageError.OpenFail;
|
|
|
|
}
|
|
|
|
|
2019-10-06 00:42:16 +00:00
|
|
|
const frames_on_end = c.sf_seek(file, 0, c.SEEK_END);
|
|
|
|
_ = c.sf_seek(file, 0, c.SEEK_SET);
|
|
|
|
std.testing.expectEqual(fmt.frames, frames_on_end);
|
|
|
|
|
|
|
|
const frames_on_end_by_end = c.sf_seek(file, frames_on_end, c.SEEK_SET);
|
|
|
|
std.testing.expectEqual(frames_on_end, frames_on_end_by_end);
|
|
|
|
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("frames on end: {}, frame on end (2): {}\n", .{ frames_on_end, frames_on_end_by_end });
|
2019-10-06 00:42:16 +00:00
|
|
|
|
2019-07-08 17:43:58 +00:00
|
|
|
return file.?;
|
|
|
|
}
|
|
|
|
|
2019-10-22 21:16:15 +00:00
|
|
|
pub fn swrite(file: *c.SNDFILE, buf: [*]f32, frames: i64) !void {
|
2019-07-10 14:47:35 +00:00
|
|
|
const count = c.sf_writef_float(file, buf, frames);
|
2019-07-09 21:34:05 +00:00
|
|
|
|
2019-07-10 14:47:35 +00:00
|
|
|
if (count != frames) {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("Wanted to write {}, got {}\n", .{ frames, count });
|
2019-07-10 14:47:35 +00:00
|
|
|
return ImageError.WriteFail;
|
2019-07-09 21:34:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-22 22:16:09 +00:00
|
|
|
pub fn sseek(file: *c.SNDFILE, offset: usize) void {
|
2019-10-06 00:42:16 +00:00
|
|
|
const offset_i64 = @intCast(i64, offset);
|
|
|
|
const frames = c.sf_seek(file, offset_i64, c.SEEK_SET);
|
|
|
|
const frames_current = c.sf_seek(file, 0, c.SEEK_CUR);
|
|
|
|
std.testing.expectEqual(frames, frames_current);
|
2019-07-15 13:53:23 +00:00
|
|
|
|
2019-10-06 00:42:16 +00:00
|
|
|
if (frames != offset_i64) {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("failed to seek to {} (seeked {} frames, offset_i64={})\n", .{ offset, frames, offset_i64 });
|
2019-10-06 00:42:16 +00:00
|
|
|
}
|
2019-07-15 13:53:23 +00:00
|
|
|
}
|
|
|
|
|
2019-08-14 12:10:09 +00:00
|
|
|
/// Caller owns the returned memory.
|
2019-07-22 23:32:50 +00:00
|
|
|
pub fn temporaryName(allocator: *std.mem.Allocator) ![]u8 {
|
2019-07-09 17:29:54 +00:00
|
|
|
const template_start = "/temp/temp_";
|
2019-08-09 21:40:11 +00:00
|
|
|
const template = "/tmp/temp_XXXXXXXXXXX";
|
2019-07-09 17:29:54 +00:00
|
|
|
var nam = try allocator.alloc(u8, template.len);
|
|
|
|
std.mem.copy(u8, nam, template);
|
|
|
|
|
2020-05-30 18:56:58 +00:00
|
|
|
const seed = @bitCast(u64, std.time.timestamp());
|
|
|
|
var r = std.rand.DefaultPrng.init(seed);
|
2019-07-09 17:29:54 +00:00
|
|
|
|
|
|
|
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));
|
2019-11-10 16:37:59 +00:00
|
|
|
var letter = @as(u8, 65) + idx;
|
2019-07-09 17:29:54 +00:00
|
|
|
fill[f_idx] = letter;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we fail to access it, we assume it doesn't exist and return it.
|
2020-03-26 19:35:58 +00:00
|
|
|
|
|
|
|
_ = std.fs.cwd().openFile(nam, .{ .read = true, .write = false }) catch |err| {
|
2019-07-09 17:29:54 +00:00
|
|
|
if (err == error.FileNotFound) {
|
|
|
|
return nam;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return error.TempGenFail;
|
|
|
|
}
|
|
|
|
|
2019-10-22 21:16:15 +00:00
|
|
|
pub fn mkSfInfo() c.SF_INFO {
|
2019-07-09 18:15:02 +00:00
|
|
|
return c.SF_INFO{
|
2019-11-10 16:37:59 +00:00
|
|
|
.frames = @as(c_int, 0),
|
|
|
|
.samplerate = @as(c_int, 44100),
|
|
|
|
.channels = @as(c_int, 1),
|
2019-07-09 18:15:02 +00:00
|
|
|
.format = c.SF_FORMAT_ULAW | c.SF_FORMAT_RAW | c.SF_ENDIAN_BIG,
|
2019-11-10 16:37:59 +00:00
|
|
|
.sections = @as(c_int, 0),
|
|
|
|
.seekable = @as(c_int, 0),
|
2019-07-09 18:15:02 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-07-08 17:43:58 +00:00
|
|
|
pub const Image = struct {
|
|
|
|
allocator: *std.mem.Allocator,
|
2019-07-10 02:04:05 +00:00
|
|
|
|
|
|
|
/// Pointer to the underlying libsndfile's SNDFILE struct.
|
2019-07-08 17:43:58 +00:00
|
|
|
sndfile: *c.SNDFILE,
|
2019-07-10 02:04:05 +00:00
|
|
|
|
2019-07-14 21:54:48 +00:00
|
|
|
/// Current sound file's framecount.
|
|
|
|
frames: usize,
|
|
|
|
|
2019-07-10 02:04:05 +00:00
|
|
|
/// The original image file path.
|
2019-07-08 17:43:58 +00:00
|
|
|
path: []const u8,
|
|
|
|
|
2019-07-10 02:04:05 +00:00
|
|
|
/// Represents the current path being worked on.
|
|
|
|
curpath: []const u8,
|
|
|
|
|
2019-07-14 21:54:48 +00:00
|
|
|
/// Open a BMP image for later.
|
2019-07-08 17:43:58 +00:00
|
|
|
pub fn open(allocator: *std.mem.Allocator, path: []const u8) !*Image {
|
2019-07-09 18:15:02 +00:00
|
|
|
var in_fmt = mkSfInfo();
|
2019-07-08 17:43:58 +00:00
|
|
|
var sndfile = try sopen(allocator, path, c.SFM_READ, &in_fmt);
|
2019-10-06 00:42:16 +00:00
|
|
|
|
2019-07-08 17:43:58 +00:00
|
|
|
var image = try allocator.create(Image);
|
|
|
|
|
2019-11-10 16:37:59 +00:00
|
|
|
std.debug.assert(in_fmt.frames > @as(i64, 0));
|
|
|
|
std.debug.assert(in_fmt.seekable == @as(i32, 1));
|
2019-07-14 21:54:48 +00:00
|
|
|
|
2019-07-08 17:43:58 +00:00
|
|
|
image.* = Image{
|
|
|
|
.allocator = allocator,
|
|
|
|
.sndfile = sndfile,
|
|
|
|
.path = path,
|
2019-07-10 02:04:05 +00:00
|
|
|
.curpath = path,
|
2019-07-14 21:54:48 +00:00
|
|
|
.frames = @intCast(usize, in_fmt.frames),
|
2019-07-08 17:43:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
return image;
|
|
|
|
}
|
|
|
|
|
2019-09-11 00:31:23 +00:00
|
|
|
pub fn clone(self: *Image) !*Image {
|
|
|
|
var in_fmt = mkSfInfo();
|
|
|
|
// clone sndfile
|
2019-09-11 00:35:22 +00:00
|
|
|
var sndfile = try sopen(self.allocator, self.curpath, c.SFM_READ, &in_fmt);
|
2019-10-06 00:42:16 +00:00
|
|
|
std.testing.expectEqual(self.frames, @intCast(usize, in_fmt.frames));
|
|
|
|
|
2019-09-11 01:00:07 +00:00
|
|
|
var image = try self.allocator.create(Image);
|
2019-09-11 00:31:23 +00:00
|
|
|
|
2019-11-10 16:37:59 +00:00
|
|
|
std.debug.assert(in_fmt.frames > @as(i64, 0));
|
|
|
|
std.debug.assert(in_fmt.seekable == @as(i32, 1));
|
2019-09-11 00:31:23 +00:00
|
|
|
|
|
|
|
image.* = Image{
|
|
|
|
.allocator = self.allocator,
|
|
|
|
.sndfile = sndfile,
|
|
|
|
.path = self.path,
|
|
|
|
.curpath = self.curpath,
|
|
|
|
.frames = @intCast(usize, in_fmt.frames),
|
|
|
|
};
|
|
|
|
|
|
|
|
return image;
|
|
|
|
}
|
|
|
|
|
2019-07-08 17:43:58 +00:00
|
|
|
pub fn close(self: *Image) void {
|
2019-07-22 23:32:50 +00:00
|
|
|
//self.allocator.free(self.path);
|
|
|
|
//self.allocator.free(self.curpath);
|
2019-07-08 17:43:58 +00:00
|
|
|
var st: i32 = c.sf_close(self.sndfile);
|
|
|
|
|
|
|
|
if (st != 0) {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("Failed to close {} ({})\n", .{
|
2019-07-08 17:43:58 +00:00
|
|
|
self.path,
|
|
|
|
c.sf_error_number(st),
|
2020-01-15 01:31:20 +00:00
|
|
|
});
|
2019-07-08 17:43:58 +00:00
|
|
|
}
|
|
|
|
}
|
2019-07-09 03:04:01 +00:00
|
|
|
|
|
|
|
pub fn read(self: *Image, file_chans: c_int, buf: []f32) bool {
|
|
|
|
var file = file_opt.?;
|
|
|
|
|
|
|
|
const n_read: c.sf_count_t = c.sf_readf_float(file, buf.ptr, 1);
|
|
|
|
const buf_chans = @intCast(c_int, buf.len);
|
|
|
|
|
|
|
|
var i = file_chans - 1;
|
|
|
|
while (i < buf_chans) : (i += 1) {
|
|
|
|
//buf[@intCast(usize, i)] = buf[i % file_chans];
|
|
|
|
buf[@intCast(usize, i)] = buf[@intCast(usize, @mod(i, file_chans))];
|
|
|
|
}
|
|
|
|
|
|
|
|
return n_read == 1;
|
|
|
|
}
|
|
|
|
|
2019-08-09 17:03:32 +00:00
|
|
|
/// Copy bytes from the current file to out_file.
|
2019-07-11 01:28:34 +00:00
|
|
|
fn copyBytes(
|
|
|
|
self: *Image,
|
|
|
|
out_file: *c.SNDFILE,
|
|
|
|
start: usize,
|
|
|
|
end: usize,
|
|
|
|
) !void {
|
2019-07-14 22:01:50 +00:00
|
|
|
var buf = try self.allocator.alloc(f32, BufferSize);
|
|
|
|
defer self.allocator.free(buf);
|
|
|
|
|
2019-07-11 01:28:34 +00:00
|
|
|
var i: usize = start;
|
|
|
|
|
2019-07-11 12:10:21 +00:00
|
|
|
// we do sf_seek() calls to make sure we are actually on the start
|
|
|
|
// and actually end at the end position for the file.
|
2019-07-15 13:58:45 +00:00
|
|
|
sseek(self.sndfile, start);
|
|
|
|
sseek(out_file, start);
|
2019-07-11 12:10:21 +00:00
|
|
|
|
|
|
|
while (i <= end) : (i += buf.len) {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("\t\ti={}, buf.len={}, end={}\n", .{ i, buf.len, end });
|
2019-07-15 14:26:41 +00:00
|
|
|
sseek(self.sndfile, i);
|
|
|
|
sseek(out_file, i);
|
|
|
|
|
|
|
|
const bytes_until_end = end - i;
|
2019-07-11 12:10:21 +00:00
|
|
|
|
|
|
|
var read_bytes: i64 = undefined;
|
2019-07-11 01:28:34 +00:00
|
|
|
var view: []f32 = buf[0..buf.len];
|
2019-07-11 12:10:21 +00:00
|
|
|
|
2019-07-15 14:26:41 +00:00
|
|
|
if (bytes_until_end < buf.len) {
|
|
|
|
read_bytes = c.sf_readf_float(self.sndfile, buf.ptr, @intCast(i64, bytes_until_end));
|
|
|
|
view = buf[0..bytes_until_end];
|
2019-07-11 12:10:21 +00:00
|
|
|
} else {
|
|
|
|
read_bytes = c.sf_readf_float(self.sndfile, buf.ptr, @intCast(i64, buf.len));
|
2019-07-11 01:28:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
try swrite(out_file, view.ptr, @intCast(i64, view.len));
|
|
|
|
}
|
2019-07-11 12:10:21 +00:00
|
|
|
|
2019-07-15 13:58:45 +00:00
|
|
|
sseek(self.sndfile, end);
|
|
|
|
sseek(out_file, end);
|
2019-07-11 01:28:34 +00:00
|
|
|
}
|
|
|
|
|
2019-07-14 21:54:48 +00:00
|
|
|
fn getSeekPos(self: *Image, position: plugins.Position) plugins.SeekPos {
|
|
|
|
const file_end = self.frames;
|
|
|
|
var seek_pos = position.seekPos(file_end);
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("\tstart {} end {}\n", .{ seek_pos.start, seek_pos.end });
|
2019-07-14 21:54:48 +00:00
|
|
|
return seek_pos;
|
|
|
|
}
|
|
|
|
|
2019-07-23 01:48:37 +00:00
|
|
|
pub fn reopen(self: *Image, path: []const u8) !void {
|
|
|
|
var in_fmt = mkSfInfo();
|
|
|
|
|
|
|
|
self.sndfile = try sopen(self.allocator, path, c.SFM_READ, &in_fmt);
|
2019-11-10 16:46:48 +00:00
|
|
|
// std.testing.expectEqual(self.frames, @intCast(usize, in_fmt.frames));
|
2019-10-06 00:42:16 +00:00
|
|
|
|
2019-07-23 01:48:37 +00:00
|
|
|
self.curpath = path;
|
|
|
|
self.frames = @intCast(usize, in_fmt.frames);
|
|
|
|
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("\timage: reopened on '{}' (frames={}, fmt.frames={})\n", .{
|
2019-10-06 00:42:16 +00:00
|
|
|
self.curpath,
|
|
|
|
self.frames,
|
|
|
|
in_fmt.frames,
|
2020-01-15 01:31:20 +00:00
|
|
|
});
|
2019-07-23 01:48:37 +00:00
|
|
|
}
|
|
|
|
|
2019-09-08 14:55:17 +00:00
|
|
|
pub fn checkValid(self: *Image) !void {
|
2020-03-26 19:35:58 +00:00
|
|
|
var file = try std.fs.cwd().openFile(self.path, .{ .read = true });
|
2019-09-08 14:55:17 +00:00
|
|
|
defer file.close();
|
|
|
|
|
|
|
|
// main bmp header:
|
|
|
|
// 2 bytes for magic header
|
|
|
|
// 4 bytes for size in bytes
|
|
|
|
// 2 bytes ?
|
|
|
|
// 2 bytes ?
|
|
|
|
// 4 bytes for pixel array offset
|
|
|
|
var magic = [2]u8{ 0, 0 };
|
|
|
|
_ = try file.read(&magic);
|
|
|
|
|
2019-11-02 00:43:45 +00:00
|
|
|
if (std.mem.endsWith(u8, self.path, ".bmp"))
|
|
|
|
try bmp.magicValid(&magic);
|
2019-09-08 14:55:17 +00:00
|
|
|
}
|
|
|
|
|
2019-07-09 17:29:54 +00:00
|
|
|
/// 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.
|
2019-07-09 03:04:01 +00:00
|
|
|
pub fn runPlugin(
|
|
|
|
self: *Image,
|
|
|
|
plugin_uri: []const u8,
|
2019-07-09 19:52:46 +00:00
|
|
|
position: plugins.Position,
|
2019-07-09 03:04:01 +00:00
|
|
|
params: plugins.ParamList,
|
|
|
|
) !void {
|
2019-08-07 02:34:11 +00:00
|
|
|
var timer = try std.time.Timer.start();
|
|
|
|
|
2019-07-09 16:55:52 +00:00
|
|
|
var ctx = try plugins.makeContext(self.allocator, plugin_uri);
|
2019-07-10 14:47:35 +00:00
|
|
|
defer ctx.deinit();
|
2019-07-09 16:55:52 +00:00
|
|
|
|
|
|
|
var ports = try lv2.setupPorts(&ctx);
|
|
|
|
|
2019-07-13 19:48:53 +00:00
|
|
|
if (ctx.n_audio_in > 2) {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("plugin <{}> has more than two inputs.\n", .{plugin_uri});
|
2019-07-09 16:55:52 +00:00
|
|
|
return ImageError.InvalidPlugin;
|
|
|
|
}
|
|
|
|
|
2019-08-06 22:25:31 +00:00
|
|
|
if (ctx.n_audio_out > 2) {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("plugin <{}> has more than two outputs.\n", .{plugin_uri});
|
2019-08-06 22:25:31 +00:00
|
|
|
return ImageError.InvalidPlugin;
|
|
|
|
}
|
2019-07-10 14:47:35 +00:00
|
|
|
|
2019-07-09 16:55:52 +00:00
|
|
|
// now, for each param for the plugin, we find its port, and set
|
|
|
|
// the value for the port there.
|
2020-05-12 20:26:33 +00:00
|
|
|
for (params.items) |param| {
|
2019-07-09 16:55:52 +00:00
|
|
|
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: {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("assert fail: symbol {} not found on port\n", .{param.sym});
|
2019-07-09 16:55:52 +00:00
|
|
|
return ImageError.InvalidSymbol;
|
|
|
|
};
|
|
|
|
|
|
|
|
c.lilv_node_free(sym);
|
|
|
|
|
|
|
|
var idx = c.lilv_port_get_index(ctx.plugin, port);
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("\tset sym={}, idx={} to val={}\n", .{
|
2019-07-09 16:55:52 +00:00
|
|
|
param.sym,
|
|
|
|
idx,
|
|
|
|
param.value,
|
2020-01-15 01:31:20 +00:00
|
|
|
});
|
2019-08-15 01:35:22 +00:00
|
|
|
(&ports[idx]).value = param.value;
|
2019-07-09 16:21:07 +00:00
|
|
|
}
|
2019-07-09 17:29:54 +00:00
|
|
|
|
|
|
|
// 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);
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("\trunning plugin from '{}' to '{}'\n", .{ self.curpath, tmpnam });
|
2019-07-09 18:15:02 +00:00
|
|
|
|
|
|
|
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);
|
2019-07-10 14:47:35 +00:00
|
|
|
defer rctx.deinit();
|
|
|
|
|
2019-07-09 18:15:02 +00:00
|
|
|
rctx.connectPorts(ports);
|
|
|
|
lv2.lilv_instance_activate(rctx.instance);
|
2019-07-09 19:52:46 +00:00
|
|
|
|
|
|
|
// now that we have everything setup, we need to make the part where we
|
|
|
|
// just copy the original image and the part where we run the plugin
|
|
|
|
// over the image.
|
|
|
|
|
2019-07-14 21:54:48 +00:00
|
|
|
const seek_pos = self.getSeekPos(position);
|
2019-07-09 21:34:05 +00:00
|
|
|
|
2019-07-11 01:28:34 +00:00
|
|
|
// there are four main stages:
|
|
|
|
// - the bmp header copy
|
|
|
|
// - pre-plugin
|
|
|
|
// - plugin
|
|
|
|
// - post-plugin
|
|
|
|
|
2019-07-11 12:10:21 +00:00
|
|
|
// pre-plugin copy, merged with bmp header copy
|
2019-07-11 01:28:34 +00:00
|
|
|
try self.copyBytes(
|
|
|
|
out_file,
|
2019-11-10 16:37:59 +00:00
|
|
|
@as(usize, 0),
|
2019-07-14 21:54:48 +00:00
|
|
|
seek_pos.start,
|
2019-07-11 01:28:34 +00:00
|
|
|
);
|
2019-07-09 21:34:05 +00:00
|
|
|
|
2019-07-15 13:58:45 +00:00
|
|
|
sseek(self.sndfile, seek_pos.start);
|
2019-07-11 13:51:00 +00:00
|
|
|
|
2019-07-11 01:28:34 +00:00
|
|
|
var i: usize = seek_pos.start;
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("\tseek pos start: {} end: {}\n", .{ seek_pos.start, seek_pos.end });
|
2019-07-09 21:34:05 +00:00
|
|
|
|
2020-01-26 01:46:00 +00:00
|
|
|
var inbuf = &rctx.buffers.in;
|
|
|
|
var outbuf = &rctx.buffers.out;
|
2019-07-13 20:42:46 +00:00
|
|
|
|
2019-07-11 12:10:21 +00:00
|
|
|
while (i <= seek_pos.end) : (i += 1) {
|
2020-01-26 01:46:00 +00:00
|
|
|
inbuf[0] = 0;
|
|
|
|
inbuf[1] = 0;
|
|
|
|
|
|
|
|
const read_bytes = c.sf_readf_float(self.sndfile, inbuf, 1);
|
2019-07-10 14:47:35 +00:00
|
|
|
if (read_bytes == 0) {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("WARN! reached EOF at idx={}\n", .{i});
|
2019-07-10 14:47:35 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-07-09 21:34:05 +00:00
|
|
|
|
2020-01-26 01:46:00 +00:00
|
|
|
// trick plugins into having correct stereo signal from
|
|
|
|
// my mono input
|
|
|
|
inbuf[1] = inbuf[0];
|
|
|
|
|
2019-07-11 01:28:34 +00:00
|
|
|
lv2.lilv_instance_run(rctx.instance, 1);
|
2020-01-26 01:46:00 +00:00
|
|
|
try swrite(out_file, outbuf, 1);
|
2019-07-09 21:34:05 +00:00
|
|
|
}
|
|
|
|
|
2019-07-15 13:58:45 +00:00
|
|
|
sseek(self.sndfile, seek_pos.end);
|
2019-07-11 13:57:39 +00:00
|
|
|
|
2019-07-11 01:28:34 +00:00
|
|
|
// post-plugin copy
|
|
|
|
try self.copyBytes(
|
|
|
|
out_file,
|
2019-07-11 12:10:21 +00:00
|
|
|
seek_pos.end + 1,
|
2019-07-14 21:54:48 +00:00
|
|
|
self.frames,
|
2019-07-11 01:28:34 +00:00
|
|
|
);
|
|
|
|
|
2019-07-10 14:47:35 +00:00
|
|
|
c.sf_write_sync(out_file);
|
|
|
|
_ = c.sf_close(out_file);
|
2019-07-10 02:04:05 +00:00
|
|
|
_ = c.sf_close(self.sndfile);
|
2019-07-10 14:47:35 +00:00
|
|
|
|
2019-07-23 01:48:37 +00:00
|
|
|
try self.reopen(tmpnam);
|
2019-09-08 14:55:17 +00:00
|
|
|
try self.checkValid();
|
2019-08-07 02:34:11 +00:00
|
|
|
|
|
|
|
var time_taken = timer.read();
|
2020-05-30 18:56:58 +00:00
|
|
|
std.debug.warn("\ttook {d:.2}ms running plugin\n", .{time_taken / std.time.us_per_ms});
|
2019-07-10 02:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn saveTo(self: *Image, out_path: []const u8) !void {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("\timg: copy from '{}' to '{}'\n", .{ self.curpath, out_path });
|
2020-03-26 19:35:58 +00:00
|
|
|
try std.fs.copyFileAbsolute(self.curpath, out_path, .{});
|
2019-07-09 03:04:01 +00:00
|
|
|
}
|
2019-07-13 20:42:46 +00:00
|
|
|
|
|
|
|
pub fn runCustomPlugin(
|
|
|
|
self: *Image,
|
|
|
|
comptime Plugin: type,
|
2019-07-13 21:17:44 +00:00
|
|
|
position: plugins.Position,
|
2019-10-22 21:16:15 +00:00
|
|
|
comptime ExtraType: type,
|
|
|
|
extra: ExtraType,
|
2019-07-13 21:17:44 +00:00
|
|
|
) !void {
|
2019-10-22 21:16:15 +00:00
|
|
|
var plugin_opt: ?Plugin = Plugin.init(self.allocator, extra);
|
2019-07-29 14:51:40 +00:00
|
|
|
if (plugin_opt == null) {
|
|
|
|
return ImageError.PluginLoadFail;
|
|
|
|
}
|
|
|
|
|
|
|
|
var plugin = plugin_opt.?;
|
2019-08-14 12:10:54 +00:00
|
|
|
defer plugin.deinit();
|
2019-07-13 21:17:44 +00:00
|
|
|
|
2019-10-22 21:16:15 +00:00
|
|
|
const decls = comptime std.meta.declarations(Plugin);
|
|
|
|
inline for (decls) |decl| {
|
|
|
|
if (comptime std.mem.eql(u8, decl.name, "setup")) {
|
|
|
|
try plugin.setup();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-13 21:17:44 +00:00
|
|
|
// the code here is a copypaste of runPlugin() without the specific
|
|
|
|
// lilv things.
|
|
|
|
var tmpnam = try temporaryName(self.allocator);
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("\trunning CUSTOM plugin from '{}' to '{}'\n", .{ self.curpath, tmpnam });
|
2019-07-13 21:17:44 +00:00
|
|
|
|
|
|
|
var out_fmt = mkSfInfo();
|
|
|
|
var out_file = try sopen(self.allocator, tmpnam, c.SFM_WRITE, &out_fmt);
|
|
|
|
|
2020-01-26 01:46:00 +00:00
|
|
|
var bufs = plugins.RunBuffers{};
|
2019-07-14 21:54:48 +00:00
|
|
|
const seek_pos = self.getSeekPos(position);
|
2019-07-13 21:17:44 +00:00
|
|
|
|
|
|
|
// make sure we start from 0
|
2019-07-15 13:58:45 +00:00
|
|
|
sseek(self.sndfile, 0);
|
2019-07-13 21:17:44 +00:00
|
|
|
|
|
|
|
// there are four main stages:
|
|
|
|
// - the bmp header copy
|
|
|
|
// - pre-plugin
|
|
|
|
// - CUSTOM plugin
|
|
|
|
// - post-plugin
|
|
|
|
|
|
|
|
// pre-plugin copy, merged with bmp header copy
|
|
|
|
try self.copyBytes(
|
|
|
|
out_file,
|
2019-11-10 16:37:59 +00:00
|
|
|
@as(usize, 0),
|
2019-07-14 21:54:48 +00:00
|
|
|
seek_pos.start,
|
2019-07-13 21:17:44 +00:00
|
|
|
);
|
|
|
|
|
2019-07-15 13:58:45 +00:00
|
|
|
sseek(self.sndfile, seek_pos.start);
|
2019-07-13 21:17:44 +00:00
|
|
|
|
|
|
|
var i: usize = seek_pos.start;
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("\tseek pos start: {} end: {}\n", .{ seek_pos.start, seek_pos.end });
|
2019-07-13 21:17:44 +00:00
|
|
|
|
2020-01-26 01:46:00 +00:00
|
|
|
var inbuf = &bufs.in;
|
|
|
|
var outbuf = &bufs.out;
|
2019-07-13 21:17:44 +00:00
|
|
|
|
|
|
|
while (i <= seek_pos.end) : (i += 1) {
|
2020-01-26 01:46:00 +00:00
|
|
|
const read_bytes = c.sf_readf_float(self.sndfile, inbuf, 1);
|
2019-07-13 21:17:44 +00:00
|
|
|
if (read_bytes == 0) {
|
2020-01-15 01:31:20 +00:00
|
|
|
std.debug.warn("WARN! reached EOF at idx={}\n", .{i});
|
2019-07-13 21:17:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
plugin.run(&bufs);
|
2020-01-26 01:46:00 +00:00
|
|
|
try swrite(out_file, outbuf, 1);
|
2019-07-13 21:17:44 +00:00
|
|
|
}
|
|
|
|
|
2019-07-15 13:58:45 +00:00
|
|
|
sseek(self.sndfile, seek_pos.end);
|
2019-07-13 21:17:44 +00:00
|
|
|
|
|
|
|
// post-plugin copy
|
|
|
|
try self.copyBytes(
|
|
|
|
out_file,
|
|
|
|
seek_pos.end + 1,
|
2019-07-14 21:54:48 +00:00
|
|
|
self.frames,
|
2019-07-13 21:17:44 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
c.sf_write_sync(out_file);
|
|
|
|
_ = c.sf_close(out_file);
|
|
|
|
_ = c.sf_close(self.sndfile);
|
|
|
|
|
|
|
|
// reopen the file as SFM_READ so we can run plugin chains etc
|
2019-07-23 01:48:37 +00:00
|
|
|
try self.reopen(tmpnam);
|
2019-09-08 14:55:17 +00:00
|
|
|
try self.checkValid();
|
2019-07-13 20:42:46 +00:00
|
|
|
}
|
2019-07-08 17:43:58 +00:00
|
|
|
};
|