diff --git a/.gitignore b/.gitignore index d4224e5..7e9e158 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,22 @@ -/zig-cache -/zig-out +# This file is for zig-specific build artifacts. +# If you have OS-specific or editor-specific files to ignore, +# such as *.swp or .DS_Store, put those in your global +# ~/.gitignore and put this in your ~/.gitconfig: +# +# [core] +# excludesfile = ~/.gitignore +# +# Cheers! +# -andrewrk + +.zig-cache/ +zig-out/ +/release/ +/debug/ +/build/ +/build-*/ +/docgen_tmp/ + +# Although this was renamed to .zig-cache, let's leave it here for a few +# releases to make it less annoying to work with multiple branches. +zig-cache/ diff --git a/README.md b/README.md index 8f4db43..1b5dd6d 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,9 @@ A toolkit for LV2 plugin authors written in Zig. **WARNING: Here be dragons; tons of LV2 features aren't implemented and this isn't ready for production at all.** +## Zig version +Zig 0.15.1 + ## Installing ```bash git clone --recurse-submodules https://github.com/ziglibs/zig-lv2 @@ -11,3 +14,21 @@ git clone --recurse-submodules https://github.com/ziglibs/zig-lv2 ## Getting Started Check out the example in `examples` which contains both sample Zig code as well as its corresponding Turtle manifest. + +Build the example plugins with: + +```bash +zig build examples +``` + +Then add `zig-out/` to the LV2 plugins path in your host. + +There are also an example `build.zig` file you can use to build your plugin out of this tree: + +```bash +cp -r zig-lv2/examples/amp zig-amp +cd zig-amp/ +zig fetch --save ../zig-lv2/ +zig build +``` + diff --git a/build.zig b/build.zig index ac163e6..12736d3 100644 --- a/build.zig +++ b/build.zig @@ -1,21 +1,83 @@ const std = @import("std"); -const Builder = @import("std").build.Builder; - -const examples = &[_][]const u8{"amp", "fifths", "params"}; - -pub fn build(b: *Builder) !void { - const mode = b.standardReleaseOptions(); - inline for (examples) |example, i| { - const lib = b.addSharedLibrary(example, "examples/" ++ example ++ "/" ++ example ++ ".zig", .{ .unversioned = {} }); - - lib.addPackagePath("lv2", "src/lv2.zig"); - lib.setBuildMode(mode); - lib.setOutputDir("zig-out/" ++ example ++ ".lv2"); - lib.linkLibC(); - lib.addIncludeDir("lv2"); - - var step = b.step(example, "Build example \"" ++ example ++ "\""); - step.dependOn(&b.addInstallFileWithDir("examples/" ++ example ++ "/" ++ example ++ ".ttl", .Prefix, example ++ ".lv2/manifest.ttl").step); - step.dependOn(&lib.step); + +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + // Module + const mod = b.addModule("lv2", .{ + .root_source_file = b.path("src/lv2.zig"), + .target = target, + .optimize = optimize, + }); + + mod.addCSourceFile(.{ + .file = b.path("ext_lv2/atom_util.c") + }); + + // Library + const lib = b.addLibrary(.{ + .name = "lv2", + .linkage = .static, + .root_module = mod, + }); + lib.linkLibC(); + lib.addIncludePath(b.path("lv2")); + lib.addIncludePath(b.path("ext_lv2")); + b.installArtifact(lib); + + // // Unit tests for library + // const lib_unit_tests = b.addTest(.{ + // .root_module = mod, + // }); + // const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); + // // Test step + // const test_step = b.step("test", "Run unit tests"); + // test_step.dependOn(&run_lib_unit_tests.step); + + const examples_step = b.step("examples", "Build examples"); + + const options = .{ + .optimize = optimize, + .target = target, + .lv2 = mod, + }; + + const examples = &[_][]const u8{"amp", "fifths", "params"}; + + inline for (examples) |example| { + const stp = buildExample(b, options, example); + examples_step.dependOn(&stp.step); } } + +pub fn buildExample(b: *std.Build, options: anytype, comptime eg_name: []const u8) *std.Build.Step.InstallArtifact { + const cwd_path = "examples/" ++ eg_name ++ "/"; + + // build plugin + const dll = b.addLibrary(.{ + .name = eg_name, + .linkage = .dynamic, + .version = .{ .major = 0, .minor = 0, .patch = 2 }, + .root_module = b.createModule(.{ + .root_source_file = b.path(cwd_path ++ "src/" ++ eg_name ++ ".zig"), + .target = options.target, + .optimize = options.optimize, + }), + }); + + dll.root_module.addImport("lv2", options.lv2); + + // install plugin + const install_dll = b.addInstallArtifact(dll, .{ + .dest_dir = .{ .override = .{ .custom = eg_name ++ ".lv2", } } + }); + + // install manifest + const source = cwd_path ++ "src/" ++ eg_name ++ ".ttl"; + const dest = eg_name ++ ".lv2/manifest.ttl"; + const install_manifest = b.addInstallFileWithDir(b.path(source), .prefix, dest); + install_dll.step.dependOn(&install_manifest.step); + + return install_dll; +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..8f89777 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,77 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .lv2, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + + // Together with name, this represents a globally unique package + // identifier. This field is generated by the Zig toolchain when the + // package is first created, and then *never changes*. This allows + // unambiguous detection of one package being an updated version of + // another. + // + // When forking a Zig project, this id should be regenerated (delete the + // field and run `zig build`) if the upstream project is still maintained. + // Otherwise, the fork is *hostile*, attempting to take control over the + // original project's identity. Thus it is recommended to leave the comment + // on the following line intact, so that it shows up in code reviews that + // modify the field. + .fingerprint = 0xf3e64a61a3cf623, // Changing this has security and trust implications. + + // Tracks the earliest Zig version that the package considers to be a + // supported use case. + .minimum_zig_version = "0.15.1", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. If the contents of a URL change this will result in a hash mismatch + // // which will prevent zig from using it. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + // + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + "ext_lv2", + "lv2", + }, +} diff --git a/examples/amp/build.zig b/examples/amp/build.zig new file mode 100644 index 0000000..b5d4134 --- /dev/null +++ b/examples/amp/build.zig @@ -0,0 +1,50 @@ +// This builds the amp example plugin on its own +// +// To get the lv2 library dependency, first do : +// +// zig fetch --save ../../ + +const std = @import("std"); + +const eg_name = "amp"; +const cwd_path = ""; + +pub fn build(b: *std.Build) !void { + const options = .{ + .target = b.standardTargetOptions(.{}), + .optimize = b.standardOptimizeOption(.{}), + }; + + // lv2 library + const lv2 = b.dependency("lv2", .{ + .target = options.target, + .optimize = options.optimize, + }); + + // build plugin + const dll = b.addLibrary(.{ + .name = eg_name, + .linkage = .dynamic, + .version = .{ .major = 0, .minor = 0, .patch = 2 }, + .root_module = b.createModule(.{ + .root_source_file = b.path(cwd_path ++ "src/" ++ eg_name ++ ".zig"), + .target = options.target, + .optimize = options.optimize, + }), + }); + + dll.root_module.addImport("lv2", lv2.module("lv2")); + + // install plugin + const install_dll = b.addInstallArtifact(dll, .{ + .dest_dir = .{ .override = .{ .custom = eg_name ++ ".lv2", } } + }); + + // install manifest + const source = cwd_path ++ "src/" ++ eg_name ++ ".ttl"; + const dest = eg_name ++ ".lv2/manifest.ttl"; + const install_manifest = b.addInstallFileWithDir(b.path(source), .prefix, dest); + install_dll.step.dependOn(&install_manifest.step); + + b.getInstallStep().dependOn(&install_dll.step); +} diff --git a/examples/amp/amp.ttl b/examples/amp/src/amp.ttl similarity index 92% rename from examples/amp/amp.ttl rename to examples/amp/src/amp.ttl index 4113d85..8de93ac 100644 --- a/examples/amp/amp.ttl +++ b/examples/amp/src/amp.ttl @@ -6,7 +6,7 @@ a lv2:Plugin, lv2:AmplifierPlugin; - lv2:binary ; + lv2:binary ; doap:name "Amp"; doap:license ; @@ -51,4 +51,4 @@ lv2:index 2; lv2:symbol "output"; lv2:name "Output"; - ]. \ No newline at end of file + ]. diff --git a/examples/amp/amp.zig b/examples/amp/src/amp.zig similarity index 99% rename from examples/amp/amp.zig rename to examples/amp/src/amp.zig index ab01ca5..bf2f8af 100644 --- a/examples/amp/amp.zig +++ b/examples/amp/src/amp.zig @@ -22,7 +22,6 @@ fn decibelsToCoeff(g: f32) f32 { fn run(handle: *Amp.Handle, samples: u32) void { const coef = decibelsToCoeff(handle.gain.*); - var i: usize = 0; while (i < samples) : (i += 1) { handle.output[i] = handle.input[i] * coef; diff --git a/examples/fifths/fifths.ttl b/examples/fifths/src/fifths.ttl similarity index 92% rename from examples/fifths/fifths.ttl rename to examples/fifths/src/fifths.ttl index fd409fd..09c90f7 100644 --- a/examples/fifths/fifths.ttl +++ b/examples/fifths/src/fifths.ttl @@ -6,7 +6,7 @@ a lv2:Plugin ; - lv2:binary ; + lv2:binary ; doap:name "Fifths" ; doap:license ; @@ -29,4 +29,4 @@ lv2:index 1 ; lv2:symbol "out" ; lv2:name "Out" - ] . \ No newline at end of file + ] . diff --git a/examples/fifths/fifths.zig b/examples/fifths/src/fifths.zig similarity index 83% rename from examples/fifths/fifths.zig rename to examples/fifths/src/fifths.zig index 72229a3..ac645b5 100644 --- a/examples/fifths/fifths.zig +++ b/examples/fifths/src/fifths.zig @@ -12,7 +12,7 @@ pub const FifthsURIs = struct { patch_property: u32, patch_value: u32, - pub fn map(self: *@This(), map_: *lv2.URIDMap) void { + pub fn map(self: *@This(), map_: *lv2.urid.URIDMap) void { self.atom_path = map_.map(lv2.c.LV2_ATOM__Path); self.atom_resource = map_.map(lv2.c.LV2_ATOM__Resource); self.atom_sequence = map_.map(lv2.c.LV2_ATOM__Sequence); @@ -28,10 +28,10 @@ pub const FifthsURIs = struct { pub const Fifths = lv2.Plugin{ .uri = "http://augustera.me/fifths", .Handle = struct { - in: *lv2.AtomSequence, - out: *lv2.AtomSequence, + in: *lv2.atom.AtomSequence, + out: *lv2.atom.AtomSequence, - map: *lv2.URIDMap, + map: *lv2.urid.URIDMap, uris: FifthsURIs }, }; @@ -50,7 +50,10 @@ fn instantiate ( bundle_path: []const u8, features: lv2.Features ) anyerror!void { - handle.map = features.query(lv2.URIDMap).?; + _ = bundle_path; + _ = descriptor; + _ = rate; + handle.map = features.query(lv2.urid.URIDMap).?; handle.uris.map(handle.map); } @@ -62,11 +65,12 @@ const MidiNoteData = extern struct { }; const MidiNoteEvent = extern struct { - event: lv2.AtomEvent, + event: lv2.atom.AtomEvent, data: MidiNoteData }; fn run(handle: *Fifths.Handle, samples: u32) void { + _ = samples; const out_size = handle.out.atom.size; handle.out.clear(); handle.out.atom.kind = handle.in.atom.kind; @@ -76,7 +80,7 @@ fn run(handle: *Fifths.Handle, samples: u32) void { if (event.body.kind == handle.uris.midi_event) { _ = handle.out.appendEvent(out_size, event) catch @panic("Error appending!"); - var data = event.getDataAs(*MidiNoteData); + const data = event.getDataAs(*MidiNoteData); var fifth = std.mem.zeroes(MidiNoteEvent); fifth.event.time.frames = event.time.frames; diff --git a/examples/params/params.zig b/examples/params/params.zig deleted file mode 100644 index e433fe3..0000000 --- a/examples/params/params.zig +++ /dev/null @@ -1,147 +0,0 @@ -const std = @import("std"); -const lv2 = @import("lv2"); - -pub const URIs = struct { - atom_path: u32, - atom_resource: u32, - atom_sequence: u32, - atom_urid: u32, - atom_event_transfer: u32, - midi_event: u32, - patch_set: u32, - patch_subject: u32, - patch_property: u32, - patch_value: u32, - - pub fn map(self: *@This(), map_: *lv2.URIDMap) void { - self.atom_path = map_.map(lv2.c.LV2_ATOM__Path); - self.atom_resource = map_.map(lv2.c.LV2_ATOM__Resource); - self.atom_sequence = map_.map(lv2.c.LV2_ATOM__Sequence); - self.atom_urid = map_.map(lv2.c.LV2_ATOM__URID); - self.atom_event_transfer = map_.map(lv2.c.LV2_ATOM__eventTransfer); - self.midi_event = map_.map(lv2.c.LV2_MIDI__MidiEvent); - self.patch_set = map_.map(lv2.c.LV2_PATCH__Set); - self.patch_subject = map_.map(lv2.c.LV2_PATCH__subject); - self.patch_property = map_.map(lv2.c.LV2_PATCH__property); - self.patch_value = map_.map(lv2.c.LV2_PATCH__value); - } -}; - -pub const StateManager = lv2.StateManager(struct { - aint: lv2.AtomInt, - along: lv2.AtomLong, - afloat: lv2.AtomFloat, - adouble: lv2.AtomDouble, - abool: lv2.AtomBool, - astring: lv2.AtomString, - apath: lv2.AtomPath, - lfo: lv2.AtomFloat, - spring: lv2.AtomFloat -}); - -pub const Params = lv2.Plugin{ - .uri = "http://augustera.me/params", - .Handle = struct { - // Ports - in: *lv2.AtomSequence, - out: *lv2.AtomSequence, - - // Features - map: *lv2.URIDMap, - unmap: *lv2.URIDUnmap, - forge: lv2.AtomForge, - - // URIs - uris: URIs, - - // State - state_manager: StateManager - }, -}; - -comptime { - Params.exportPlugin(.{ - .instantiate = instantiate, - .run = run, - .activate = activate, - .deactivate = deactivate, - .extensionData = extensionData - }); -} - -fn instantiate ( - handle: *Params.Handle, - descriptor: *const lv2.Descriptor, - rate: f64, - bundle_path: []const u8, - features: lv2.Features -) anyerror!void { - handle.map = features.query(lv2.URIDMap).?; - handle.unmap = features.query(lv2.URIDUnmap).?; - handle.forge.init(handle.map); - - handle.uris.map(handle.map); - handle.state_manager.map(Params.uri, handle.map); -} - -fn activate(handle: *Params.Handle) void { -} - -fn deactivate(handle: *Params.Handle) void { - // debug_file.close(); -} - -fn log(comptime f: []const u8, a: anytype) void { - var debug_file = std.fs.cwd().createFile("C:/Programming/Zig/zig-lv2/log.b", .{}) catch {std.os.exit(1);}; - debug_file.writer().print(f, a) catch {}; -} - -fn run(handle: *Params.Handle, samples: u32) void { - handle.forge.setBuffer(handle.out.toBuffer(), handle.out.atom.size); - - var out_frame = std.mem.zeroes(lv2.AtomForgeFrame); - _ = handle.forge.writeSequenceHead(&out_frame, 0); - - var iter = handle.in.iterator(); - while (iter.next()) |event| { - @panic("B"); - // var obj = event.toAtomObject(); - // if (obj.body.kind == handle.uris.patch_set) { - // var subject: ?*lv2.AtomURID = null; - // var property: ?*lv2.AtomURID = null; - // var value: ?*lv2.Atom = null; - - // obj.query(&[_]lv2.AtomObjectQuery{ - // .{ .key = handle.uris.patch_subject, .value = @ptrCast(*?*lv2.Atom, &subject) }, - // .{ .key = handle.uris.patch_property, .value = @ptrCast(*?*lv2.Atom, &property) }, - // .{ .key = handle.uris.patch_value, .value = &value } - // }); - - // handle.state_manager.setParameter(property.?.body, value.?); - // } - } - - handle.forge.pop(&out_frame); - // @panic("Empty"); -} - -fn extensionData(uri: []const u8) ?*c_void { - if (StateManager.extensionData(uri)) |ext| return ext; - return null; -} - -pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { - var debug_file = std.fs.cwd().createFile("C:/Programming/Zig/zig-lv2/log.a", .{}) catch {std.os.exit(1);}; - debug_file.writer().writeAll(msg) catch {}; - - const debug_info = std.debug.getSelfDebugInfo() catch |err| { - debug_file.writer().print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch std.process.exit(1); - std.process.exit(1); - }; - std.debug.writeCurrentStackTrace(debug_file.writer(), std.debug.getSelfDebugInfo() catch std.os.exit(1), std.debug.detectTTYConfig(), @returnAddress()) catch |err| { - debug_file.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch std.process.exit(1); - std.process.exit(1); - }; - - std.process.exit(1); -} diff --git a/examples/params/params.ttl b/examples/params/src/params.ttl similarity index 85% rename from examples/params/params.ttl rename to examples/params/src/params.ttl index ba54712..b0bae9a 100644 --- a/examples/params/params.ttl +++ b/examples/params/src/params.ttl @@ -3,7 +3,7 @@ @prefix lv2: . @prefix param: . @prefix patch: . -@prefix plug: . +@prefix plug: . @prefix rdfs: . @prefix state: . @prefix urid: . @@ -69,7 +69,7 @@ plug:spring a lv2:Plugin , lv2:UtilityPlugin ; - lv2:binary ; + lv2:binary ; doap:name "Params" ; doap:license ; @@ -100,13 +100,13 @@ plug:spring ] ; # The plugin must list all parameters that can be written (e.g. changed by the # user) as patch:writable: - patch:writable plug:int , - plug:long , - plug:float , - plug:double , - plug:bool , - plug:string , - plug:path , + patch:writable plug:aint , + plug:along , + plug:afloat , + plug:adouble , + plug:abool , + plug:astring , + plug:apath , plug:spring ; # Similarly, parameters that may change internally must be listed as patch:readable, # meaning to host should watch for changes to the parameter's value: @@ -117,13 +117,13 @@ plug:spring # state:loadDefaultState feature (required above) requires that the host loads # the default state after instantiation but before running the plugin. state:state [ - plug:int 0 ; - plug:long "0"^^xsd:long ; - plug:float "0.1234"^^xsd:float ; - plug:double "0e0"^^xsd:double ; - plug:bool false ; - plug:string "Hello, world" ; - plug:path ; + plug:aint 0 ; + plug:along "0"^^xsd:long ; + plug:afloat "0.1234"^^xsd:float ; + plug:adouble "0e0"^^xsd:double ; + plug:abool false ; + plug:astring "Hello, world" ; + plug:apath ; plug:spring "0.0"^^xsd:float ; plug:lfo "0.0"^^xsd:float - ] . \ No newline at end of file + ] . diff --git a/examples/params/src/params.zig b/examples/params/src/params.zig new file mode 100644 index 0000000..1586518 --- /dev/null +++ b/examples/params/src/params.zig @@ -0,0 +1,180 @@ +const std = @import("std"); +const lv2 = @import("lv2"); + +pub const URIs = struct { + atom_path: u32, + atom_resource: u32, + atom_sequence: u32, + atom_urid: u32, + atom_event_transfer: u32, + state_changed: u32, + midi_event: u32, + patch_set: u32, + patch_subject: u32, + patch_property: u32, + patch_value: u32, + + pub fn map(self: *@This(), map_: *lv2.urid.URIDMap) void { + self.atom_path = map_.map(lv2.c.LV2_ATOM__Path); + self.atom_resource = map_.map(lv2.c.LV2_ATOM__Resource); + self.atom_sequence = map_.map(lv2.c.LV2_ATOM__Sequence); + self.atom_urid = map_.map(lv2.c.LV2_ATOM__URID); + self.atom_event_transfer = map_.map(lv2.c.LV2_ATOM__eventTransfer); + self.state_changed = map_.map(lv2.c.LV2_STATE__StateChanged); + self.midi_event = map_.map(lv2.c.LV2_MIDI__MidiEvent); + self.patch_set = map_.map(lv2.c.LV2_PATCH__Set); + self.patch_subject = map_.map(lv2.c.LV2_PATCH__subject); + self.patch_property = map_.map(lv2.c.LV2_PATCH__property); + self.patch_value = map_.map(lv2.c.LV2_PATCH__value); + } +}; + +pub const StateManager = lv2.state.StateManager( + struct { + aint: lv2.atom.AtomInt, + along: lv2.atom.AtomLong, + afloat: lv2.atom.AtomFloat, + adouble: lv2.atom.AtomDouble, + abool: lv2.atom.AtomBool, + astring: lv2.atom.AtomString(1024), + apath: lv2.atom.AtomPath(1024), + lfo: lv2.atom.AtomFloat, + spring: lv2.atom.AtomFloat + }, + Params.Handle +); + +pub const Params = lv2.Plugin{ + .uri = "http://augustera.me/params", + .Handle = struct { + // Ports + in: *lv2.atom.AtomSequence, + out: *lv2.atom.AtomSequence, + + // Features + map: *lv2.urid.URIDMap, + unmap: *lv2.urid.URIDUnmap, + forge: lv2.atom.forge.AtomForge, + + // URIs + uris: URIs, + + // State + state_manager: StateManager, + + //out_frame : lv2.AtomForgeFrame + }, +}; + +comptime { + Params.exportPlugin(.{ + .instantiate = instantiate, + .run = run, + .activate = activate, + .deactivate = deactivate, + .extensionData = extensionData + }); +} + +fn instantiate ( + handle: *Params.Handle, + descriptor: *const lv2.Descriptor, + rate: f64, + bundle_path: []const u8, + features: lv2.Features +) anyerror!void { + _ = descriptor; + _ = rate; + _ = bundle_path; + + handle.map = features.query(lv2.urid.URIDMap).?; + handle.unmap = features.query(lv2.urid.URIDUnmap).?; + handle.forge.init(handle.map); + + handle.uris.map(handle.map); + handle.state_manager.map(Params.uri, handle.map); +} + +fn activate(handle: *Params.Handle) void { + _ = handle; +} + +fn deactivate(handle: *Params.Handle) void { + // debug_file.close(); + _ = handle; +} + +fn log(comptime f: []const u8, a: anytype) void { + var debug_file = std.fs.cwd().createFile("C:/Programming/Zig/zig-lv2/log.b", .{}) catch {std.process.exit(1);}; + debug_file.writer().print(f, a) catch {}; +} + +fn run(handle: *Params.Handle, samples: u32) void { + _ = samples; + + // TODO: + // in eg-params, the input event loop handles patch_get events that emit the value of the object and puts them somewhere, as generic values? as setting other properties? + // the spring thing emmits patch_set events with its value all the way down + + // handle input events + var iter = handle.in.iterator(); + while (iter.next()) |event| { + var obj = event.toAtomObject(); + if (obj.body.kind == handle.uris.patch_set) { + var subject: ?*lv2.atom.AtomURID = null; + var property: ?*lv2.atom.AtomURID = null; + var value: ?*lv2.atom.Atom = null; + + obj.query(&[_]lv2.atom.AtomObjectQuery{ + .{ .key = handle.uris.patch_subject, .value = @ptrCast(&subject) }, + .{ .key = handle.uris.patch_property, .value = @ptrCast(&property) }, + .{ .key = handle.uris.patch_value, .value = &value } + }); + + handle.state_manager.setParameter(property.?.body, value.?); + } else { + std.debug.print("run() TODO: handle event kind {}\n", .{obj.body.kind}); + } + } + + const out_size = handle.out.atom.size; + handle.forge.setBuffer(handle.out.toBuffer(), out_size); + + var out_frame : lv2.atom.forge.AtomForgeFrame = std.mem.zeroes(lv2.atom.forge.AtomForgeFrame); + _ = handle.forge.writeSequenceHead(&out_frame, 0); + + if ( handle.state_manager.state.spring.body > 0.0 ) { + + if ( handle.state_manager.state.spring.body >= 0.001 ) { + handle.state_manager.state.spring.body -= 0.001; + } else { + handle.state_manager.state.spring.body = 0.0; + } + + // spring value patch set event + + _ = handle.forge.writeFrameTime(0); + var spring_frame : lv2.atom.forge.AtomForgeFrame = std.mem.zeroes(lv2.atom.forge.AtomForgeFrame); + _ = handle.forge.writeObject(&spring_frame, 0, handle.uris.patch_set); + _ = handle.forge.writeKey(handle.uris.patch_property); + _ = handle.forge.writeAtomURID(handle.state_manager.state_urid_map.spring); + _ = handle.forge.writeKey(handle.uris.patch_value); + _ = handle.forge.writeAtomFloat(handle.state_manager.state.spring.body); + handle.forge.pop(&spring_frame); + } + + handle.forge.pop(&out_frame); +} + +fn extensionData(uri: []const u8) ?*anyopaque { + if (StateManager.extensionData(uri)) |ext| return ext; + return null; +} + +pub const panic = std.debug.FullPanic(myPanic); + +fn myPanic(msg: []const u8, first_trace_addr: ?usize) noreturn { + _ = first_trace_addr; + std.debug.print("Panic! {s}\n", .{msg}); + std.process.exit(1); +} diff --git a/ext_lv2/atom_util.c b/ext_lv2/atom_util.c new file mode 100644 index 0000000..88c12cc --- /dev/null +++ b/ext_lv2/atom_util.c @@ -0,0 +1,54 @@ +#include "atom_util.h" + +extern LV2_Atom_Event* +ext_lv2_atom_sequence_begin(const LV2_Atom_Sequence_Body* body) +{ + return lv2_atom_sequence_begin(body); +}; + +extern LV2_Atom_Property_Body* +ext_lv2_atom_object_next(const LV2_Atom_Property_Body* i) +{ + return lv2_atom_object_next(i); +} + +extern bool +ext_lv2_atom_object_is_end(const LV2_Atom_Object_Body* body, + uint32_t size, + const LV2_Atom_Property_Body* i) +{ + return lv2_atom_object_is_end(body, size, i); +}; + +extern void +ext_lv2_atom_sequence_clear(LV2_Atom_Sequence* seq) { + lv2_atom_sequence_clear(seq); +} + +extern LV2_Atom_Event* +ext_lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, + uint32_t capacity, + const LV2_Atom_Event* event) +{ + return lv2_atom_sequence_append_event(seq, capacity, event); +} + +extern LV2_Atom_Event* +ext_lv2_atom_sequence_next(const LV2_Atom_Event* i) +{ + return lv2_atom_sequence_next(i); +} + +extern LV2_Atom_Property_Body* +ext_lv2_atom_object_begin(const LV2_Atom_Object_Body* body) +{ + return lv2_atom_object_begin(body); +} + +extern bool +ext_lv2_atom_sequence_is_end(const LV2_Atom_Sequence_Body* body, + uint32_t size, + const LV2_Atom_Event* i) +{ + return lv2_atom_sequence_is_end(body, size, i); +} diff --git a/ext_lv2/atom_util.h b/ext_lv2/atom_util.h new file mode 100644 index 0000000..e9211e4 --- /dev/null +++ b/ext_lv2/atom_util.h @@ -0,0 +1,38 @@ +#ifndef ATOM_UTIL_H +#define ATOM_UTIL_H + +#include "../lv2/include/lv2/atom/util.h" + +LV2_Atom_Event* +ext_lv2_atom_sequence_begin(const LV2_Atom_Sequence_Body* body); + +LV2_Atom_Property_Body* +ext_lv2_atom_object_next(const LV2_Atom_Property_Body* i); + +bool +ext_lv2_atom_object_is_end(const LV2_Atom_Object_Body* body, + uint32_t size, + const LV2_Atom_Property_Body* i); + +void +ext_lv2_atom_sequence_clear(LV2_Atom_Sequence* seq); + +LV2_Atom_Event* +ext_lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, + uint32_t capacity, + const LV2_Atom_Event* event); + +LV2_Atom_Event* +ext_lv2_atom_sequence_next(const LV2_Atom_Event* i); + +LV2_Atom_Property_Body* +ext_lv2_atom_object_begin(const LV2_Atom_Object_Body* body); + +bool +ext_lv2_atom_sequence_is_end(const LV2_Atom_Sequence_Body* body, + uint32_t size, + const LV2_Atom_Event* i); + +#endif + + diff --git a/lv2 b/lv2 index 611759d..93db9d7 160000 --- a/lv2 +++ b/lv2 @@ -1 +1 @@ -Subproject commit 611759daacc377a2dba97723097338fceffd6ef8 +Subproject commit 93db9d7b61737726747b81a586f807f9faa60a5c diff --git a/src/atom/atom.zig b/src/atom/atom.zig index 5fbb19e..1f0ce77 100644 --- a/src/atom/atom.zig +++ b/src/atom/atom.zig @@ -1,10 +1,11 @@ -const c = @import("../c.zig"); +const c = @import("../c.zig").headers; const std = @import("std"); const urid = @import("../urid.zig"); +pub const forge = @import("forge.zig"); pub const Atom = extern struct { const Self = @This(); - + size: urid.URID, kind: urid.URID }; @@ -22,7 +23,7 @@ pub const AtomPropertyBody = extern struct { value: Atom, pub fn init(event: *c.LV2_Atom_Property_Body) *Self { - return @ptrCast(*Self, event); + return @ptrCast(event); } }; @@ -38,10 +39,10 @@ pub const AtomObject = extern struct { body: AtomObjectBody, pub fn iterator(self: *Self) AtomObjectIterator { - return AtomObjectIterator.init(@ptrCast(*c.LV2_Atom_Object, self)); + return AtomObjectIterator.init(@ptrCast(self)); } - pub fn query(self: *Self, queries: []AtomObjectQuery) void { + pub fn query(self: *Self, queries: [] const AtomObjectQuery) void { var it = self.iterator(); while (it.next()) |prop| { for (queries) |q| { @@ -68,8 +69,8 @@ pub const AtomObjectIterator = struct { } pub fn next(self: *Self) ?*AtomPropertyBody { - self.last = if (self.last) |last| c.lv2_atom_object_next(last) else c.lv2_atom_object_begin(&self.object.body); - if (c.lv2_atom_object_is_end(&self.object.body, self.object.atom.size, self.last)) return null; + self.last = if (self.last) |last| c.ext_lv2_atom_object_next(last) else c.ext_lv2_atom_object_begin(&self.object.body); + if (c.ext_lv2_atom_object_is_end(&self.object.body, self.object.atom.size, self.last)) return null; return if (self.last) |l| AtomPropertyBody.init(l) else null; } }; @@ -86,15 +87,15 @@ pub const AtomEvent = extern struct { body: Atom, pub fn init(event: *c.LV2_Atom_Event) *Self { - return @ptrCast(*Self, event); + return @ptrCast(event); } pub fn getDataAs(self: *Self, comptime T: type) T { - return @intToPtr(T, @ptrToInt(self) + @sizeOf(Self)); + return @ptrFromInt(@intFromPtr(self) + @sizeOf(Self)); } pub fn toAtomObject(self: *Self) *AtomObject { - return @ptrCast(*AtomObject, &self.body); + return @ptrCast(&self.body); } }; @@ -112,20 +113,20 @@ pub const AtomSequence = extern struct { body: AtomSequenceBody, pub fn iterator(self: *Self) AtomSequenceIterator { - return AtomSequenceIterator.init(@ptrCast(*c.LV2_Atom_Sequence, self)); + return AtomSequenceIterator.init(@ptrCast(self)); } pub fn clear(self: *Self) void { - c.lv2_atom_sequence_clear(@ptrCast(*c.LV2_Atom_Sequence, self)); + c.ext_lv2_atom_sequence_clear(@ptrCast(self)); } pub fn appendEvent(self: *Self, out_size: u32, event: *AtomEvent) !*AtomEvent { - var maybe_appended_event = c.lv2_atom_sequence_append_event(@ptrCast(*c.LV2_Atom_Sequence, self), out_size, @ptrCast(*c.LV2_Atom_Event, event)); + const maybe_appended_event = c.ext_lv2_atom_sequence_append_event(@ptrCast(self), out_size, @ptrCast(event)); return if (maybe_appended_event) |appended_event| AtomEvent.init(appended_event) else error.AppendError; } pub fn toBuffer(self: *Self) [*c]u8 { - return @ptrCast([*c]u8, self); + return @ptrCast(self); } }; @@ -143,8 +144,8 @@ pub const AtomSequenceIterator = struct { } pub fn next(self: *Self) ?*AtomEvent { - self.last = if (self.last) |last| c.lv2_atom_sequence_next(last) else c.lv2_atom_sequence_begin(&self.seq.body); - if (c.lv2_atom_sequence_is_end(&self.seq.body, self.seq.atom.size, self.last)) return null; + self.last = if (self.last) |last| c.ext_lv2_atom_sequence_next(last) else c.ext_lv2_atom_sequence_begin(&self.seq.body); + if (c.ext_lv2_atom_sequence_is_end(&self.seq.body, self.seq.atom.size, self.last)) return null; return if (self.last) |l| AtomEvent.init(l) else null; } }; @@ -164,5 +165,14 @@ pub const AtomFloat = AtomOf(f32, c.LV2_ATOM__Float); pub const AtomDouble = AtomOf(f64, c.LV2_ATOM__Double); pub const AtomBool = AtomOf(bool, c.LV2_ATOM__Bool); pub const AtomURID = AtomOf(u32, c.LV2_ATOM__URID); -pub const AtomString = AtomOf([*:0]const u8, c.LV2_ATOM__String); -pub const AtomPath = AtomOf([*:0]const u8, c.LV2_ATOM__Path); + +//pub const AtomString = AtomOf([*:0]const u8, c.LV2_ATOM__String); +//pub const AtomPath = AtomOf([*:0]const u8, c.LV2_ATOM__Path); + +pub fn AtomString(comptime N: usize) type { + return AtomOf([N:0]u8, c.LV2_ATOM__String); +} + +pub fn AtomPath(comptime N: usize) type { + return AtomOf([N:0]u8, c.LV2_ATOM__Path); +} diff --git a/src/atom/forge.zig b/src/atom/forge.zig index 09cade1..eecabdd 100644 --- a/src/atom/forge.zig +++ b/src/atom/forge.zig @@ -1,15 +1,15 @@ -const c = @import("../c.zig"); +const c = @import("../c.zig").headers; const std = @import("std"); const atom = @import("atom.zig"); const urid = @import("../urid.zig"); -pub const AtomForgeSinkHandle = ?*c_void; +pub const AtomForgeSinkHandle = ?*anyopaque; pub const AtomForgeRef = isize; -pub const AtomForgeSink = ?fn (sink_handle: AtomForgeSinkHandle, buf: ?*const c_void, size: u32) callconv(.C) AtomForgeRef; -pub const AtomForgeDerefFunc = ?fn (sink_handle: AtomForgeSinkHandle, ref: AtomForgeRef) callconv(.C) [*c]atom.Atom; +pub const AtomForgeSink = ?*fn (sink_handle: AtomForgeSinkHandle, buf: ?*const anyopaque, size: u32) callconv(.c) AtomForgeRef; +pub const AtomForgeDerefFunc = ?*fn (sink_handle: AtomForgeSinkHandle, ref: AtomForgeRef) callconv(.c) [*c]atom.Atom; pub const AtomForgeFrame = extern struct { - parent: [*c]AtomForgeFrame, + parent: ?*AtomForgeFrame, ref: AtomForgeRef }; @@ -25,7 +25,7 @@ pub const AtomForge = extern struct { deref_func: AtomForgeDerefFunc, /// The handle to the output sink. sink_handle: AtomForgeSinkHandle, - stack: [*c]AtomForgeFrame, + stack: ?*AtomForgeFrame, /// Deprecated Blank: urid.URID, @@ -70,11 +70,11 @@ pub const AtomForge = extern struct { self.URID = map.map(c.LV2_ATOM__URID); self.Vector = map.map(c.LV2_ATOM__Vector); } - + /// Access the Atom pointed to by a reference. pub fn deref(self: *Self, ref: AtomForgeRef) *atom.Atom { if (ref < 0) @panic("huh"); - return if (self.buf != null) @intToPtr(*atom.Atom, std.math.absCast(ref)) else self.deref_func.?(self.sink_handle, ref); + return if (self.buf != null) @ptrFromInt(@abs(ref)) else self.deref_func.?(self.sink_handle, ref); } /// Push a stack frame. Automatically handled by container functions. @@ -99,10 +99,12 @@ pub const AtomForge = extern struct { /// Return true if the top of the stack is the given kind. pub fn topIs(self: *Self, kind: urid.URID) bool { - return - self.stack != null and - self.stack.ref != 0 and - self.deref(self.stack.ref).kind == kind; + if (self.stack) |st| { + if ( st.ref != 0 ) { + return self.deref(st.ref).kind == kind; + } + } + return false; } /// Return true if `kind` is an atom:Object @@ -118,7 +120,7 @@ pub const AtomForge = extern struct { /// Set forge buffer. pub fn setBuffer(self: *Self, buf: [*c]u8, size: usize) void { self.buf = buf; - self.size = @truncate(u32, size); + self.size = @truncate(size); self.offset = 0; self.deref_func = null; self.sink = null; @@ -127,50 +129,63 @@ pub const AtomForge = extern struct { } /// Set forge sink. - pub fn setSink(self: *Self, sink: AtomForgeSink, deref: AtomForgeDerefFunc, sink_handle: AtomForgeSinkHandle) void { + pub fn setSink(self: *Self, sink: AtomForgeSink, deref_arg: AtomForgeDerefFunc, sink_handle: AtomForgeSinkHandle) void { self.buf = null; self.size = 0; self.offset = 0; - self.deref_func = deref; + self.deref_func = deref_arg; self.sink = sink; self.sink_handle = sink_handle; } /// Writes raw output. - pub fn raw(self: *Self, data: ?*const c_void, size: u32) AtomForgeRef { + pub fn raw(self: *Self, data: ?*const anyopaque, size: u32) AtomForgeRef { var out: AtomForgeRef = 0; - + if (self.sink) |sink| { out = sink(self.sink_handle, data, size); } else { - out = @intCast(AtomForgeRef, @ptrToInt(self.buf)) + @bitCast(c_longlong, @as(c_ulonglong, self.offset)); - var mem: *u8 = self.buf + self.offset; + out = @intCast(@intFromPtr(self.buf)); + out += @bitCast(@as(c_ulonglong, self.offset)); + const mem: *u8 = self.buf + self.offset; if (self.offset + size > self.size) { return 0; } self.offset += size; - _ = c.memcpy(@ptrCast(?*c_void, mem), data, @bitCast(c_ulonglong, @as(c_ulonglong, size))); - // @memcpy(@ptrCast([*]u8, @ptrCast(?*c_void, mem)), @ptrCast([*]const u8, data.?), @bitCast(c_ulonglong, @as(c_ulonglong, size))); + _ = c.memcpy(@ptrCast(mem), data, @bitCast(@as(c_ulonglong, size))); + // @memcpy(@ptrCast([*]u8, @ptrCast(?*anyopaque, mem)), @ptrCast([*]const u8, data.?), @bitCast(c_ulonglong, @as(c_ulonglong, size))); } - if (self.stack != null) { - self.deref(self.stack.*.parent.*.ref).size += size; + //if (self.stack != null) { + //self.deref(self.stack.*.parent.*.ref).size += size; + //} + //for (LV2_Atom_Forge_Frame* f = forge->stack; f; f = f->parent) { + // lv2_atom_forge_deref(forge, f->ref)->size += size; + //} + + var f = self.stack; + while (f) |ff| { + self.deref(ff.ref).size += size; + f = ff.parent; } - + return out; } /// Pad so next write is 64-bit aligned. pub fn pad(self: *Self, written: u32) void { const pad_: u64 = 0; - var pad_size: u32 = c.lv2_atom_pad_size(written) - written; - _ = self.raw(@ptrCast(?*const c_void, &pad_), pad_size); + const pad_size: u32 = c.lv2_atom_pad_size(written) - written; + _ = self.raw(@ptrCast(&pad_), pad_size); } /// `raw` but with padding. - pub fn write(self: *Self, data: ?*const c_void, size: u32) AtomForgeRef { - var out = self.raw(data, size); + /// port of lv2_atom_forge_write() + /// static inline LV2_Atom_Forge_Ref + /// lv2_atom_forge_write(LV2_Atom_Forge* forge, const void* data, uint32_t size) + pub fn write(self: *Self, data: ?*const anyopaque, size: u32) AtomForgeRef { + const out = self.raw(data, size); if (out != 0) { self.pad(size); } @@ -179,9 +194,9 @@ pub const AtomForge = extern struct { /// Write a null-terminated string body. pub fn stringBody(self: *Self, str: []const u8, len: u32) AtomForgeRef { - var out = self.raw(@ptrCast(?*const c_void, str), len); + var out = self.raw(@ptrCast(str), len); if (out and o: { - out = self.raw(@ptrCast(?*const c_void, ""), 1); + out = self.raw(@ptrCast(""), 1); break :o out; }) { self.pad(len + 1); @@ -194,17 +209,19 @@ pub const AtomForge = extern struct { .size = size, .kind = kind }; - return self.raw(@ptrCast(?*const c_void, &at), @truncate(u32, @sizeOf(at))); + return self.raw(@ptrCast(&at), @truncate(@sizeOf(at))); } pub fn writeAtomPrimitive(self: *Self, at: *atom.Atom) AtomForgeRef { - return if (self.topIs(self.Vector)) self.raw(@ptrCast(?*const c_void, @ptrCast([*c]const u8, @alignCast(@import("std").meta.alignment(u8), at)) + @sizeOf(atom.Atom)), at.size) else self.write(@ptrCast(?*c_void, at), @truncate(u32, @sizeOf(at)) + at.size); + const aa : [*]u8 = @ptrCast(@alignCast(at)); + const ee : u32 = @truncate(@sizeOf(atom.Atom)); + return if (self.topIs(self.Vector)) self.raw( aa + @sizeOf(atom.Atom), at.size) else self.write(@ptrCast(at), ee + at.size); } - fn writeAtomOfType(self: *Self, comptime T: type, value: T, kind: urid.URID) AtomForgeRef { - var at = atom.AtomInt{ + fn writeAtomOfType(self: *Self, comptime T: type, comptime atomType: type, value: T, kind: urid.URID) AtomForgeRef { + var at = atomType{ .atom = .{ - .size = @sizeOf(value), + .size = @sizeOf(T), .kind = kind }, .body = value @@ -213,34 +230,35 @@ pub const AtomForge = extern struct { } pub fn writeAtomInt(self: *Self, value: i32) AtomForgeRef { - return self.writeAtomOfType(i32, value, self.Int); + return self.writeAtomOfType(i32, atom.AtomInt, value, self.Int); } pub fn writeAtomLong(self: *Self, value: i64) AtomForgeRef { - return self.writeAtomOfType(i64, value, self.Long); + return self.writeAtomOfType(i64, atom.AtomLong, value, self.Long); } pub fn writeAtomFloat(self: *Self, value: f32) AtomForgeRef { - return self.writeAtomOfType(f32, value, self.Float); + return self.writeAtomOfType(f32, atom.AtomFloat, value, self.Float); } pub fn writeAtomDouble(self: *Self, value: f64) AtomForgeRef { - return self.writeAtomOfType(f64, value, self.Double); + return self.writeAtomOfType(f64, atom.AtomDouble, value, self.Double); } pub fn writeAtomBool(self: *Self, value: bool) AtomForgeRef { - return self.writeAtomOfType(bool, value, self.Bool); + return self.writeAtomOfType(bool, atom.AtomBool, value, self.Bool); } pub fn writeAtomURID(self: *Self, value: urid.URID) AtomForgeRef { - return self.writeAtomOfType(urid.URID, value, self.URID); + return self.writeAtomOfType(urid.URID, atom.AtomURID, value, self.URID); } pub fn writeSequenceHead(self: *Self, frame: *AtomForgeFrame, unit: u32) AtomForgeRef { + const aa : u32 = @truncate(@sizeOf(atom.AtomSequenceBody)); var seq = atom.AtomSequence{ .atom = .{ // .size = @as(u32, @sizeOf(atom.AtomSequenceBody)), - .size = @bitCast(u32, @truncate(c_uint, @sizeOf(atom.AtomSequenceBody))), + .size = @bitCast(aa), .kind = self.Sequence }, .body = .{ @@ -248,8 +266,51 @@ pub const AtomForge = extern struct { .pad = 0 } }; - return self.push(frame, self.write(@ptrCast(?*const c_void, &seq), @bitCast(u32, @truncate(c_uint, @sizeOf(atom.AtomSequence))))); - // return self.push(frame, self.write(@ptrCast(?*const c_void, &seq), @sizeOf(atom.AtomSequence))); + const ee : u32 = @truncate(@sizeOf(atom.AtomSequence)); + return self.push(frame, self.write(@ptrCast(&seq), @bitCast(ee))); + // return self.push(frame, self.write(@ptrCast(?*const anyopaque, &seq), @sizeOf(atom.AtomSequence))); + } + + // port of: + // static inline LV2_Atom_Forge_Ref + // lv2_atom_forge_frame_time(LV2_Atom_Forge* forge, int64_t frames) + pub fn writeFrameTime(self: *Self, frames: i64) AtomForgeRef { + return self.write(&frames, @sizeOf(i64)); + } + + + // port of: + // static inline LV2_Atom_Forge_Ref + // lv2_atom_forge_object(LV2_Atom_Forge* forge, + // LV2_Atom_Forge_Frame* frame, + // LV2_URID id, + // LV2_URID otype) + pub fn writeObject(self: *Self, frame: *AtomForgeFrame, id: urid.URID, otype: urid.URID) AtomForgeRef { + const aa : u32 = @truncate(@sizeOf(atom.AtomObject)); + var oo = atom.AtomObject{ + .atom = .{ + .size = @bitCast(aa), + .kind = self.Object + }, + .body = .{ + .id = id, + .kind = otype + } + }; + const ee : u32 = @truncate(@sizeOf(atom.AtomObject)); + return self.push(frame, self.write(@ptrCast(&oo), @bitCast(ee))); + } + + // port of: + // static inline LV2_Atom_Forge_Ref + // lv2_atom_forge_key(LV2_Atom_Forge* forge, LV2_URID key) + pub fn writeKey(self: *Self, key : urid.URID) AtomForgeRef { + var oo = atom.AtomPropertyBody{ + .key = key, + .context = 0, + .value = atom.Atom{ .size = 0, .kind = 0,} + }; + return self.write(&oo, 2*@sizeOf(u32)); } }; diff --git a/src/c.zig b/src/c.zig index 649dd0b..8414e72 100644 --- a/src/c.zig +++ b/src/c.zig @@ -1,4 +1,4 @@ -pub usingnamespace @cImport({ +pub const headers = @cImport({ @cInclude("lv2/core/lv2.h"); @cInclude("lv2/core/lv2_util.h"); @cInclude("lv2/log/log.h"); @@ -6,7 +6,8 @@ pub usingnamespace @cImport({ @cInclude("lv2/urid/urid.h"); @cInclude("lv2/atom/atom.h"); - @cInclude("lv2/atom/util.h"); + + @cInclude("atom_util.h"); @cInclude("lv2/atom/forge.h"); @cInclude("lv2/midi/midi.h"); diff --git a/src/lv2.zig b/src/lv2.zig index 492bdfa..fd1ecb9 100644 --- a/src/lv2.zig +++ b/src/lv2.zig @@ -1,23 +1,21 @@ const std = @import("std"); -pub const c = @import("c.zig"); +pub const c = @import("c.zig").headers; -pub usingnamespace @import("urid.zig"); - -pub usingnamespace @import("atom/atom.zig"); -pub usingnamespace @import("atom/forge.zig"); - -pub usingnamespace @import("state.zig"); -pub usingnamespace @import("utils.zig"); +pub const urid = @import("urid.zig"); +pub const atom = @import("atom/atom.zig"); +pub const state = @import("state.zig"); +pub const utils = @import("utils.zig"); +pub const Features = utils.Features; pub const Descriptor = c.LV2_Descriptor; pub fn Handlers(comptime Handle_: type) type { return struct { run: ?fn (handle: *Handle_, samples: u32) void = null, - instantiate: ?fn (handle: *Handle_, descriptor: *const Descriptor, rate: f64, bundle_path: []const u8, features: Features) anyerror!void = null, + instantiate: ?fn (handle: *Handle_, descriptor: *const Descriptor, rate: f64, bundle_path: []const u8, features: utils.Features) anyerror!void = null, activate: ?fn (handle: *Handle_) void = null, deactivate: ?fn (handle: *Handle_) void = null, - extensionData: ?fn(uri: []const u8) ?*c_void = null + extensionData: ?fn(uri: []const u8) ?*anyopaque = null }; } @@ -32,70 +30,76 @@ pub const Plugin = struct { const Handle__ = self.Handle; fn toHandle(instance: c.LV2_Handle) *Handle__ { - return @ptrCast(*Handle__, @alignCast(@alignOf(*Handle__), instance)); + return @ptrCast(@alignCast(instance)); } - pub fn instantiate(descriptor: [*c]const Descriptor, rate: f64, bundle_path: [*c]const u8, features: [*c]const [*c]const c.LV2_Feature) callconv(.C) c.LV2_Handle { - var handle = std.heap.c_allocator.create(Handle__) catch { + pub fn instantiate(descriptor: [*c]const Descriptor, rate: f64, bundle_path: [*c]const u8, features: [*c]const [*c]const c.LV2_Feature) callconv(.c) c.LV2_Handle { + + const handle = std.heap.c_allocator.create(Handle__) catch { std.debug.print("Yeah you're kinda screwed!", .{}); - std.os.exit(1); + @trap(); }; - - if (handlers.instantiate) |act| act(handle, @ptrCast(*const Descriptor, descriptor), rate, std.mem.span(bundle_path), Features.init(@ptrCast(*const []c.LV2_Feature, features).*)) catch { + + if (handlers.instantiate) |act| { + const feats = utils.Features.init(features); + act(handle, @ptrCast(descriptor), rate, std.mem.span(bundle_path), feats) catch { std.heap.c_allocator.destroy(handle); - std.os.exit(1); - }; + @trap(); + }; + } - return @ptrCast(c.LV2_Handle, handle); + return @ptrCast(handle); } - pub fn cleanup(instance: c.LV2_Handle) callconv(.C) void { + pub fn cleanup(instance: c.LV2_Handle) callconv(.c) void { std.heap.c_allocator.destroy(toHandle(instance)); } - pub fn connect_port(instance: c.LV2_Handle, port: u32, data: ?*c_void) callconv(.C) void { + pub fn connect_port(instance: c.LV2_Handle, port: u32, data: ?*anyopaque) callconv(.c) void { var hnd = toHandle(instance); - inline for (std.meta.fields(@TypeOf(hnd.*))) |field, i| { + inline for (std.meta.fields(@TypeOf(hnd.*)), 0..) |field, i| { if (i == port) { - if (@typeInfo(field.field_type) != .Pointer) { - if (@hasDecl(field.field_type, "connectPort")) @field(@field(hnd, field.name), "connectPort")(data); - } else @field(hnd, field.name) = @ptrCast(field.field_type, @alignCast(@alignOf(field.field_type), data)); + if (@typeInfo(field.type) != .pointer) { + if (@hasDecl(field.type, "connectPort")) @field(@field(hnd, field.name), "connectPort")(data); + } else if (data != null) { + @field(hnd, field.name) = @ptrCast(@alignCast(data)); + } // NOTE: if the pointer is null nothing happens } } } - pub fn activate(instance: c.LV2_Handle) callconv(.C) void { + pub fn activate(instance: c.LV2_Handle) callconv(.c) void { if (handlers.activate) |act| act(toHandle(instance)); } - pub fn deactivate(instance: c.LV2_Handle) callconv(.C) void { + pub fn deactivate(instance: c.LV2_Handle) callconv(.c) void { if (handlers.deactivate) |deact| deact(toHandle(instance)); } - pub fn extension_data(uri: [*c]const u8) callconv(.C) ?*c_void { + pub fn extension_data(uri: [*c]const u8) callconv(.c) ?*anyopaque { if (handlers.extensionData) |rn| return rn(std.mem.span(uri)); return null; } - pub fn run(instance: c.LV2_Handle, n_samples: u32) callconv(.C) void { + pub fn run(instance: c.LV2_Handle, n_samples: u32) callconv(.c) void { if (handlers.run) |rn| rn(toHandle(instance), n_samples); } pub const __globalDescriptor = Descriptor{ .URI = URI_.ptr, .instantiate = instantiate, .connect_port = connect_port, .activate = activate, .run = run, .deactivate = deactivate, .cleanup = cleanup, .extension_data = extension_data }; - pub fn lv2_descriptor(index: u32) callconv(.C) [*c]const Descriptor { + pub fn lv2_descriptor(index: u32) callconv(.c) [*c]const Descriptor { return if (index == 0) &__globalDescriptor else null; } }; - @export(lv.instantiate, .{ .name = "instantiate", .linkage = .Strong }); - @export(lv.connect_port, .{ .name = "connect_port", .linkage = .Strong }); - @export(lv.activate, .{ .name = "activate", .linkage = .Strong }); - @export(lv.run, .{ .name = "run", .linkage = .Strong }); - @export(lv.deactivate, .{ .name = "deactivate", .linkage = .Strong }); - @export(lv.cleanup, .{ .name = "cleanup", .linkage = .Strong }); - @export(lv.extension_data, .{ .name = "extension_data", .linkage = .Strong }); - @export(lv.lv2_descriptor, .{ .name = "lv2_descriptor", .linkage = .Strong }); + @export(&lv.instantiate, .{ .name = "instantiate", .linkage = .strong }); + @export(&lv.connect_port, .{ .name = "connect_port", .linkage = .strong }); + @export(&lv.activate, .{ .name = "activate", .linkage = .strong }); + @export(&lv.run, .{ .name = "run", .linkage = .strong }); + @export(&lv.deactivate, .{ .name = "deactivate", .linkage = .strong }); + @export(&lv.cleanup, .{ .name = "cleanup", .linkage = .strong }); + @export(&lv.extension_data, .{ .name = "extension_data", .linkage = .strong }); + @export(&lv.lv2_descriptor, .{ .name = "lv2_descriptor", .linkage = .strong }); } }; diff --git a/src/state.zig b/src/state.zig index b9d678d..2d9789d 100644 --- a/src/state.zig +++ b/src/state.zig @@ -1,8 +1,7 @@ -const c = @import("c.zig"); const std = @import("std"); const lv2 = @import("lv2.zig"); -const StateStatus = extern enum(c_int) { +const StateStatus = enum(c_int) { state_success = 0, state_err_unknown = 1, state_err_bad_type = 2, @@ -14,63 +13,71 @@ const StateStatus = extern enum(c_int) { }; pub fn StateMap(comptime State: type) type { - var fields: [std.meta.fields(State).len]std.builtin.TypeInfo.StructField = undefined; - - for (std.meta.fields(State)) |field, i| { + var fields: [std.meta.fields(State).len]std.builtin.Type.StructField = undefined; + + for (std.meta.fields(State), 0..) |field, i| { fields[i] = .{ .name = field.name, - .field_type = u32, - .default_value = null, + .type = u32, + .default_value_ptr = null, .is_comptime = false, .alignment = @alignOf(u32), }; } return @Type(.{ - .Struct = .{ - .layout = .Auto, + .@"struct" = .{ + .layout = .auto, .fields = &fields, - .decls = &[_]std.builtin.TypeInfo.Declaration{}, + .decls = &[_]std.builtin.Type.Declaration{}, .is_tuple = false, } }); } -pub fn StateManager(comptime State: type) type { - var SM = StateMap(State); +pub fn StateManager(comptime State: type, comptime PluginHandle: type) type { + //var SM = StateMap(State); return struct { const Self = @This(); state: State, - state_urid_map: SM, - state_type_map: SM, + state_urid_map: StateMap(State), + state_type_map: StateMap(State), + + pub fn map(self: *Self, comptime uri: []const u8, map_: *lv2.urid.URIDMap) void { - pub fn map(self: *Self, comptime uri: []const u8, map_: *lv2.URIDMap) void { inline for (std.meta.fields(State)) |field| { @field(self.state_urid_map, field.name) = map_.map(uri ++ "#" ++ field.name); - @field(self.state_type_map, field.name) = map_.map(field.field_type.__atom_type); + @field(self.state_type_map, field.name) = map_.map(field.type.__atom_type); } } - pub fn extensionData(uri: []const u8) ?*c_void { + pub fn extensionData(uri: []const u8) ?*anyopaque { if (std.mem.eql(u8, uri, lv2.c.LV2_STATE__interface)) { - var state = lv2.c.LV2_State_Interface{ - .save = Self.save, - .restore = Self.restore + const S = struct { + var iface = lv2.c.LV2_State_Interface{ + .save = Self.save, + .restore = Self.restore + }; }; - return @ptrCast(*c_void, &state); + //Self.iface.save = Self.save; + //Self.iface.restore = Self.restore; + //var state = lv2.c.LV2_State_Interface{ + //.save = Self.save, + //.restore = Self.restore + //}; + return @ptrCast(&(S.iface)); } else return null; } - - pub fn setParameter(self: *Self, field_urid: lv2.URID, value: *lv2.Atom) void { - inline for (std.meta.fields(SM)) |s| { + + pub fn setParameter(self: *Self, field_urid: lv2.urid.URID, value: *lv2.atom.Atom) void { + inline for (std.meta.fields(StateMap(State))) |s| { if (@field(self.state_urid_map, s.name) == field_urid) { const to = *@TypeOf(@field(self.state, s.name)); - @field(self.state, s.name) = @ptrCast(to, @alignCast(@alignOf(to), value)).*; + @field(self.state, s.name) = @as(to, @ptrCast(@alignCast(value))).*; return; } } - @panic("Bad!!!"); } @@ -78,44 +85,63 @@ pub fn StateManager(comptime State: type) type { // This is used in the usual way when called by the host to save plugin state, // but also internally for writing messages in the audio thread by passing a // "store" function which actually writes the description to the forge. - pub fn save (handle: lv2.c.LV2_Handle, store: lv2.c.LV2_State_Store_Function, state_handle: lv2.c.LV2_State_Handle, flags: u32, features: [*c]const [*c]const lv2.c.LV2_Feature) callconv(.C) lv2.c.LV2_State_Status { - if (store == null) return @intToEnum(lv2.c.LV2_State_Status, 0); + pub fn save (handle: lv2.c.LV2_Handle, store: lv2.c.LV2_State_Store_Function, state_handle: lv2.c.LV2_State_Handle, flags: u32, features: [*c]const [*c]const lv2.c.LV2_Feature) callconv(.c) lv2.c.LV2_State_Status { + if (store == null) return 0; + + const plug_ptr : *PluginHandle = @ptrCast(@alignCast(handle)); + const state_parent_ptr : *Self = &(plug_ptr.*.state_manager); + //var map_path = lv2.getFeatureData(@as(*const []lv2.c.LV2_Feature, features).*, lv2.c.LV2_STATE__mapPath).?; - var state = @ptrCast(*State, @alignCast(@alignOf(*State), state_handle)); - var map_path = lv2.getFeatureData(@ptrCast(*const []lv2.c.LV2_Feature, features).*, lv2.c.LV2_STATE__mapPath).?; + _ = flags; + _ = features; - var status = @intToEnum(lv2.c.LV2_State_Status, 0); + var status = @as(lv2.c.LV2_State_Status, 0); inline for (std.meta.fields(State)) |field| { - var value = @field(state, field.name); + const key = @field(state_parent_ptr.state_urid_map, field.name); + const kind = @field(state_parent_ptr.state_type_map, field.name); + var value = @field(state_parent_ptr.state, field.name); status = store.?( - handle, - @field(@fieldParentPtr(Self, "state", state).state_urid_map, field.name), - @intToPtr(*lv2.Atom, @ptrToInt(&value) + @sizeOf(lv2.Atom)), + state_handle, + key, + @ptrFromInt(@intFromPtr(&value) + @sizeOf(lv2.atom.Atom)), value.atom.size, - @field(@fieldParentPtr(Self, "state", state).state_type_map, field.name), lv2.c.LV2_STATE_IS_POD | lv2.c.LV2_STATE_IS_PORTABLE + kind, + lv2.c.LV2_STATE_IS_POD | lv2.c.LV2_STATE_IS_PORTABLE ); } return status; } - pub fn restore (handle: lv2.c.LV2_Handle, retrieve: lv2.c.LV2_State_Retrieve_Function, state_handle: lv2.c.LV2_State_Handle, flags: u32, features: [*c]const [*c]const lv2.c.LV2_Feature) callconv(.C) lv2.c.LV2_State_Status { - var state = @ptrCast(*State, @alignCast(@alignOf(*State), state_handle)); - var map_path = lv2.getFeatureData(@ptrCast(*const []lv2.c.LV2_Feature, features).*, lv2.c.LV2_STATE__mapPath).?; - var status = @intToEnum(lv2.c.LV2_State_Status, 0); - + pub fn restore (handle: lv2.c.LV2_Handle, retrieve: lv2.c.LV2_State_Retrieve_Function, state_handle: lv2.c.LV2_State_Handle, flags: u32, features: [*c]const [*c]const lv2.c.LV2_Feature) callconv(.c) lv2.c.LV2_State_Status { + const plug_ptr : *PluginHandle = @ptrCast(@alignCast(handle)); + const state_parent_ptr : *Self = &(plug_ptr.*.state_manager); + + var status : lv2.c.LV2_State_Status = 0; + + _ = flags; + _ = features; + inline for (std.meta.fields(State)) |field| { - var key = @field(@fieldParentPtr(Self, "state", state).state_urid_map, field.name); + const key = @field(state_parent_ptr.state_urid_map, field.name); + + const field_name = field.name; + _ = field_name; var vsize: usize = 0; var vtype: u32 = 0; var vflags: u32 = 0; - if (retrieve.?(handle, key, &vsize, &vtype, &vflags)) |v| - {} - else status = @intToEnum(lv2.c.LV2_State_Status, 4); + if (retrieve.?(state_handle, key, &vsize, &vtype, &vflags)) |v| { + const to = @TypeOf(@field(state_parent_ptr.state, field.name).body); + const atm : *const to = @ptrCast(@alignCast(v)); + @field(state_parent_ptr.state, field.name).body = atm.*; + @field(state_parent_ptr.state, field.name).atom.size = std.math.cast(lv2.urid.URID, vsize) orelse 0; + @field(state_parent_ptr.state, field.name).atom.kind = vtype; + } + else status = 4; } return status; diff --git a/src/urid.zig b/src/urid.zig index 5c10c4e..94033b0 100644 --- a/src/urid.zig +++ b/src/urid.zig @@ -1,18 +1,18 @@ -const c = @import("c.zig"); +const c = @import("c.zig").headers; pub const URID = u32; -pub const MapHandle = ?*c_void; -pub const UnmapHandle = ?*c_void; +pub const MapHandle = ?*anyopaque; +pub const UnmapHandle = ?*anyopaque; pub const URIDMap = extern struct { const Self = @This(); handle: MapHandle, - map_: ?fn (MapHandle, [*c]const u8) callconv(.C) URID, + map_: ?*const fn (MapHandle, [*c]const u8) callconv(.c) URID, - pub fn fromData(data: *c_void) *Self { - return @ptrCast(*Self, @alignCast(@alignOf(*Self), data)); + pub fn fromData(data: *anyopaque) *Self { + return @ptrCast(@alignCast(data)); } pub fn toURI() []const u8 { @@ -20,7 +20,7 @@ pub const URIDMap = extern struct { } pub fn map(self: Self, uri: []const u8) u32 { - return self.map_.?(self.handle, @ptrCast([*c]const u8, uri)); + return self.map_.?(self.handle, @ptrCast(uri)); } }; @@ -28,10 +28,10 @@ pub const URIDUnmap = extern struct { const Self = @This(); handle: UnmapHandle, - unmap_: ?fn (UnmapHandle, URID) callconv(.C) [*c]const u8, + unmap_: ?*fn (UnmapHandle, URID) callconv(.c) [*c]const u8, - pub fn fromData(data: *c_void) *Self { - return @ptrCast(*Self, @alignCast(@alignOf(*Self), data)); + pub fn fromData(data: *anyopaque) *Self { + return @ptrCast(@alignCast(data)); } pub fn toURI() []const u8 { diff --git a/src/utils.zig b/src/utils.zig index ac4d612..599435e 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -1,22 +1,24 @@ -const c = @import("c.zig"); +const c = @import("c.zig").headers; const std = @import("std"); pub const Logger = struct { logger_internal: c.LV2_Log_Logger, - pub fn applyData(self: @This(), data: *c_void) void { - self.logger_internal.log = @ptrCast(*c.LV2_Log_Log, data); + pub fn applyData(self: @This(), data: *anyopaque) void { + self.logger_internal.log = @ptrCast(data); } }; pub const Features = struct { const Self = @This(); - features: []const c.LV2_Feature, + //features: []const c.LV2_Feature, + features: [*:null]const? *const c.LV2_Feature, - pub fn init(features: []const c.LV2_Feature) Self { + pub fn init(features: [*c]const [*c]const c.LV2_Feature) Self { return Self{ - .features = features + //.features = @as(*const[]c.LV2_Feature, @ptrCast(features)).* + .features = @ptrCast(features) }; } @@ -25,16 +27,27 @@ pub const Features = struct { } }; -pub fn getFeatureData(features: []const c.LV2_Feature, uri: []const u8) ?*c_void { - for (features) |filled_feat| { - if (filled_feat.URI != null and std.mem.eql(u8, uri, std.mem.span(filled_feat.URI))) { - if (filled_feat.data) |dd| return dd; +pub fn getFeatureData(features: [*:null]const? *const c.LV2_Feature, uri: []const u8) ?*anyopaque { + + //for (features) |filled_feat| { + //if (filled_feat.URI != null and std.mem.eql(u8, uri, std.mem.span(filled_feat.URI))) { + //if (filled_feat.data) |dd| return dd; + //} + //} + + var idx : usize = 0; + while( features[idx] != null ) { + if (features[idx]) |filled_feat| { + if (filled_feat.*.URI != null and std.mem.eql(u8, uri, std.mem.span(filled_feat.*.URI))) { + if (filled_feat.*.data) |dd| return dd; + } } + idx += 1; } return null; } -pub fn queryFeature(features: []const c.LV2_Feature, comptime T: type) ?*T { +pub fn queryFeature(features: [*:null]const? *const c.LV2_Feature, comptime T: type) ?*T { return @field(T, "fromData")(getFeatureData(features, @field(T, "toURI")()) orelse return null); }