remove oldMain, add LICENSE

This commit is contained in:
Luna 2019-07-10 12:10:09 -03:00
parent 4c9d94d7e5
commit 16a082ce0a
2 changed files with 20 additions and 478 deletions

20
LICENSE Normal file
View file

@ -0,0 +1,20 @@
Copyright 2019 Luna Mendes <https://l4.pm>
Copyright 2007-2016 David Robillard <http://drobilla.net>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,282 +1,7 @@
// Copyright 2007-2016 David Robillard <http://drobilla.net>
// Copyright 2019 Luna Mendes <https://l4.pm>
//
// the main script is a fork/port of lv2apply, licensed under the ISC license.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
const std = @import("std");
const langs = @import("lang.zig");
const runners = @import("runner.zig");
const c = @cImport({
@cInclude("assert.h");
@cInclude("math.h");
@cInclude("stdarg.h");
@cInclude("stdio.h");
@cInclude("stdlib.h");
@cInclude("string.h");
@cInclude("sndfile.h");
@cInclude("lilv/lilv.h");
@cInclude("lv2/core/lv2.h");
@cDefine("LILV_VERSION", "0.24.4");
});
const LV2_CORE_URI = "http://lv2plug.in/ns/lv2core";
fn Lv2Core(ns: []const u8) ![]const u8 {
var allocator = std.heap.direct_allocator;
return try std.cstr.addNullByte(
allocator,
try std.fmt.allocPrint(allocator, "{}{}", LV2_CORE_URI, ns),
);
}
fn makeCStr(data: []const u8) ![]u8 {
var allocator = std.heap.direct_allocator;
return std.cstr.addNullByte(allocator, data);
}
/// Control port value set from the command line
const Param = struct {
/// Port symbol
sym: []const u8,
/// Control value
value: f32,
};
const ParamList = std.ArrayList(Param);
const PortType = enum {
Control,
Audio,
};
/// Runtime port information.
const Port = struct {
lilv_port: ?*const c.LilvPort,
ptype: PortType,
index: u32,
value: f32,
is_input: bool,
optional: bool,
};
/// Application state
const LV2Apply = struct {
allocator: *std.mem.Allocator,
world: ?*c.LilvWorld = null,
plugin: ?*const c.LilvPlugin = null,
instance: ?*c.LilvInstance = null,
in_path: ?[]u8 = undefined,
out_path: ?[]u8 = undefined,
in_file: ?*c.SNDFILE = null,
out_file: ?*c.SNDFILE = null,
n_params: u32,
params: ParamList,
n_audio_in: u32,
n_audio_out: u32,
ports: []*Port = undefined,
pub fn deinit(self: *LV2Apply) void {
sclose(self.in_path, self.in_file);
sclose(self.out_path, self.out_file);
if (self.instance) |instance| {
c.lilv_instance_free(self.instance);
}
if (self.world) |world| {
c.lilv_world_free(self.world);
}
self.allocator.free(self.ports);
self.params.deinit();
}
pub fn makeCStr(self: *LV2Apply, data: []const u8) ![]u8 {
return std.cstr.addNullByte(self.allocator, data);
}
pub fn createPorts(self: *LV2Apply) !i32 {
var world = self.world;
const n_ports: u32 = c.lilv_plugin_get_num_ports(self.plugin);
self.ports = try self.allocator.realloc(self.ports, n_ports);
for (self.ports) |port_ptr, idx| {
var port = try self.allocator.create(Port);
port.* = Port{
.lilv_port = null,
.ptype = .Control,
.index = f32(0),
.value = f32(0),
.is_input = false,
.optional = false,
};
self.ports[idx] = port;
}
var values: []f32 = try self.allocator.alloc(f32, n_ports);
defer self.allocator.free(values);
c.lilv_plugin_get_port_ranges_float(self.plugin, null, null, values.ptr);
// bad solution, but it really do be like that
const LV2_CORE__InputPort = try Lv2Core("#InputPort");
const LV2_CORE__OutputPort = try Lv2Core("#OutputPort");
const LV2_CORE__AudioPort = try Lv2Core("#AudioPort");
const LV2_CORE__ControlPort = try Lv2Core("#ControlPort");
const LV2_CORE__connectionOptional = try Lv2Core("#connectionOptional");
var lv2_InputPort = c.lilv_new_uri(world, LV2_CORE__InputPort.ptr);
defer std.heap.c_allocator.destroy(lv2_InputPort);
var lv2_OutputPort = c.lilv_new_uri(world, LV2_CORE__OutputPort.ptr);
defer std.heap.c_allocator.destroy(lv2_OutputPort);
var lv2_AudioPort = c.lilv_new_uri(world, LV2_CORE__AudioPort.ptr);
defer std.heap.c_allocator.destroy(lv2_AudioPort);
var lv2_ControlPort = c.lilv_new_uri(world, LV2_CORE__ControlPort.ptr);
defer std.heap.c_allocator.destroy(lv2_ControlPort);
var lv2_connectionOptional = c.lilv_new_uri(world, LV2_CORE__connectionOptional.ptr);
defer std.heap.c_allocator.destroy(lv2_connectionOptional);
var i: u32 = 0;
while (i < n_ports) : (i += 1) {
var port: *Port = self.ports[i];
const lport = c.lilv_plugin_get_port_by_index(self.plugin, i).?;
port.lilv_port = lport;
port.index = i;
if (std.math.isNan(values[i])) {
port.value = f32(0);
} else {
port.value = values[i];
}
port.optional = c.lilv_port_has_property(self.plugin, lport, lv2_connectionOptional);
if (c.lilv_port_is_a(self.plugin, lport, lv2_InputPort)) {
port.is_input = true;
} else if (!c.lilv_port_is_a(self.plugin, lport, lv2_OutputPort) and !port.optional) {
std.debug.warn("Port {} is neither input or output\n", i);
return error.UnassignedIOPort;
}
// check if port is an audio or control port
if (c.lilv_port_is_a(self.plugin, lport, lv2_ControlPort)) {
port.ptype = .Control;
} else if (c.lilv_port_is_a(self.plugin, lport, lv2_AudioPort)) {
port.ptype = .Audio;
if (port.is_input) {
self.n_audio_in += 1;
} else {
self.n_audio_out += 1;
}
} else if (!port.optional) {
std.debug.warn("Port {} has unsupported type\n", i);
return error.UnsupportedPortType;
}
}
return 0;
}
};
fn sopen(path_opt: ?[]const u8, mode: i32, fmt: *c.SF_INFO) ?*c.SNDFILE {
if (path_opt == null) return null;
var path = path_opt.?;
var file = c.sf_open(path.ptr, mode, fmt);
const st: i32 = c.sf_error(file);
if (st != 0) {
std.debug.warn("Failed to open {} ({})\n", path, c.sf_error_number(st));
return null;
}
return file;
}
fn sclose(path_opt: ?[]const u8, file_opt: ?*c.SNDFILE) void {
if (path_opt == null) return;
if (file_opt == null) return;
var path = path_opt.?;
var file = file_opt.?;
var st: i32 = c.sf_close(file);
if (st != 0) {
std.debug.warn(
"Failed to close {} ({})\n",
path,
c.sf_error_number(st),
);
}
}
///Read a single frame from a file into an interleaved buffer.
///If more channels are required than are available in the file, the remaining
///channels are distributed in a round-robin fashion (LRLRL).
fn sread(file_opt: ?*c.SNDFILE, 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;
}
fn print_version() !void {
const stdout_file = try std.io.getStdOut();
try stdout_file.write("lv2apply (lilv) 0.24.4\n");
try stdout_file.write("Copyright 2007-2016 David Robillard <http://drobilla.net>\n");
try stdout_file.write("Copyright 2019 Luna Mendes <http://l4.pm>\n");
}
fn print_usage() !void {
const stdout_file = try std.io.getStdOut();
try stdout_file.write("Usage: lv2apply [OPTION]... PLUGIN_URI\n");
try stdout_file.write("\t-i IN_FILE Input file\n");
try stdout_file.write("\t-o OUT_FILE Output file\n");
try stdout_file.write("\t-c SYM VAL Control value\n");
try stdout_file.write("\t--help print help\n");
try stdout_file.write("\t--version display version\n");
}
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
defer arena.deinit();
@ -309,206 +34,3 @@ pub fn main() !void {
var cmds = try lang.parse(data);
try runner.runCommands(cmds, true);
}
pub fn oldMain() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
const allocator = &arena.allocator;
var in_path: ?[]u8 = null;
var out_path: ?[]u8 = null;
var self = LV2Apply{
.allocator = allocator,
.n_params = 0,
.params = ParamList.init(allocator),
.n_audio_in = 0,
.n_audio_out = 0,
.ports = try allocator.alloc(*Port, 0),
};
defer self.deinit();
var args_it = std.process.args();
_ = args_it.skip();
var plugin_uri_opt: ?[]u8 = null;
while (args_it.next(allocator)) |arg_err| {
var arg = try arg_err;
if (std.mem.eql(u8, arg, "--version")) {
try print_version();
return;
} else if (std.mem.eql(u8, arg, "--help")) {
try print_usage();
return;
} else if (std.mem.eql(u8, arg, "-i")) {
in_path = try args_it.next(allocator).?;
} else if (std.mem.eql(u8, arg, "-o")) {
out_path = try args_it.next(allocator).?;
} else if (std.mem.eql(u8, arg, "-c")) {
var param_name = try args_it.next(allocator).?;
var value_arg = try args_it.next(allocator).?;
var param_value = try std.fmt.parseFloat(f32, value_arg);
try self.params.append(Param{ .sym = param_name, .value = param_value });
} else if (arg[0] == '-') {
try print_usage();
return;
} else {
plugin_uri_opt = arg;
}
}
if (in_path == null or out_path == null or plugin_uri_opt == null) {
try print_usage();
return;
}
self.in_path = try self.makeCStr(in_path.?);
self.out_path = try self.makeCStr(out_path.?);
var plugin_uri = try self.makeCStr(plugin_uri_opt.?);
self.world = c.lilv_world_new().?;
var uri: *c.LilvNode = c.lilv_new_uri(self.world, plugin_uri.ptr) orelse blk: {
std.debug.warn("Invalid plugin URI <{}>\n", plugin_uri);
return;
};
c.lilv_world_load_all(self.world);
const plugins: *const c.LilvPlugins = c.lilv_world_get_all_plugins(self.world);
const plugin_opt = c.lilv_plugins_get_by_uri(plugins, uri);
c.lilv_node_free(uri);
if (plugin_opt) |plugin| {
self.plugin = plugin;
} else {
std.debug.warn("Plugin <{}> not found\n", plugin_uri);
return;
}
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 in_file_opt = sopen(self.in_path, c.SFM_READ, &in_fmt);
if (in_file_opt) |in_file| {
self.in_file = in_file;
} else {
std.debug.warn("Failed to call sopen().\n");
return;
}
_ = try self.createPorts();
if (self.n_audio_in == 0 or
(in_fmt.channels != @intCast(c_int, self.n_audio_in) and in_fmt.channels != 1))
{
std.debug.warn("unable to map {} inputs to {} ports\n", in_fmt.channels, self.n_audio_in);
}
var it = self.params.iterator();
while (it.next()) |param| {
var param_sym = try self.makeCStr(param.sym);
var sym = c.lilv_new_string(self.world, param_sym.ptr);
const port = c.lilv_plugin_get_port_by_symbol(self.plugin, sym).?;
c.lilv_node_free(sym);
var idx = c.lilv_port_get_index(self.plugin, port);
self.ports[idx].value = param.value;
}
var out_fmt = c.SF_INFO{
.frames = c_int(0),
.samplerate = c_int(44100),
//.channels = @intCast(c_int, self.n_audio_out),
.channels = c_int(1),
.format = c.SF_FORMAT_ULAW | c.SF_FORMAT_RAW,
.sections = c_int(0),
.seekable = c_int(0),
};
var out_file_opt = sopen(self.out_path, c.SFM_WRITE, &out_fmt);
if (out_file_opt) |out_file| {
self.out_file = out_file;
} else {
std.debug.warn("Failed to call sopen() for outfile.\n");
return;
}
const n_ports: u32 = c.lilv_plugin_get_num_ports(self.plugin);
var in_buf = try self.allocator.alloc(f32, self.n_audio_in);
var out_buf = try self.allocator.alloc(f32, self.n_audio_out);
var instance_opt = c.lilv_plugin_instantiate(
self.plugin,
@intToFloat(f64, in_fmt.samplerate),
null,
);
if (instance_opt) |instance| {
self.instance = instance;
} else {
std.debug.warn("failed to instantiate\n");
return;
}
var i: u32 = 0;
var o: u32 = 0;
for (self.ports) |port, p_idx| {
var ptype = port.ptype;
var p = @intCast(u32, p_idx);
if (ptype == .Control) {
lilv_instance_connect_port(self.instance, p, &port.value);
} else if (ptype == .Audio) {
if (port.is_input) {
lilv_instance_connect_port(self.instance, p, &in_buf[i]);
i += 1;
} else {
lilv_instance_connect_port(self.instance, p, &out_buf[o]);
o += 1;
}
} else {
lilv_instance_connect_port(self.instance, p, null);
}
}
// Ports are now connected to buffers in interleaved format, so we can run
// a single frame at a time and avoid having to interleave buffers to
// read/write from/to sndfile.
lilv_instance_activate(self.instance);
const START = 10 * 44100;
// for the first START frames, copy from in to out
i = 0;
while (i < START) : (i += 1) {
_ = sread(self.in_file, in_fmt.channels, in_buf);
_ = c.sf_writef_float(self.out_file, in_buf.ptr, 1);
}
// seek to START then run plugin over the rest
var seeked = c.sf_seek(self.in_file, START, c.SEEK_SET);
std.debug.warn("{} seeked frames\n", seeked);
while (sread(self.in_file, in_fmt.channels, in_buf)) {
lilv_instance_run(self.instance, 1);
if (c.sf_writef_float(self.out_file, out_buf.ptr, 1) != 1) {
std.debug.warn("failed to write to output file\n");
return;
}
}
lilv_instance_deactivate(self.instance);
return;
}