From 14b45598c100644712384b0bce3ad7b2f8943050 Mon Sep 17 00:00:00 2001 From: godofecht Date: Tue, 21 Jul 2026 11:15:31 +0100 Subject: [PATCH] Support Zig 0.15 alongside 0.14.1 Four separate 0.15 changes, all fixable with spellings that both versions accept, so this widens support rather than cutting over. Build system: - addStaticLibrary/addSharedLibrary fold into addLibrary with an explicit .linkage, and target/optimize move onto a module passed as .root_module. Calling convention (52 sites in src/vst3.zig and the gain example): - callconv(.C) -> callconv(.c). The CallingConvention union was reworked; 0.14.1 already accepted the lowercase form. std.Io rework: - net.Stream.readAll was removed. Replaced with a single read, which is the correct semantic here anyway: readAll blocks until the 8 KB buffer fills or the peer closes, so a keep-alive HTTP client would hang. ArrayList: - 0.15 made ArrayList unmanaged by default and dropped .init(allocator). ArrayListUnmanaged names the same type in both, so the allocator is passed per call instead. The webview dependency also had to move: its pinned commit used the removed build API. Bumped to a5eef0b, which uses the new one. That in turn renamed WindowSizeHint.Fixed to .fixed. Verified on both toolchains from a clean cache: 0.14.1 24/24 build steps, 35/35 tests 0.15.2 24/24 build steps, 35/35 tests, vst3 bundle still fat (x86_64 arm64) CI runs a matrix over both. Also corrects two stale README claims: the Zig version requirement, and a License section pointing at a project root that no longer holds the license. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 10 +++- README.md | 6 +- build.zig | 84 +++++++++++++++++---------- build.zig.zon | 4 +- examples/danzig-gain-ui/coreaudio.zig | 15 +++-- examples/danzig-gain-ui/root.zig | 2 +- examples/danzig-gain/root.zig | 28 ++++----- examples/danzig-webui/root.zig | 5 +- src/vst3.zig | 76 ++++++++++++------------ 9 files changed, 133 insertions(+), 97 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 296a8b3..6471005 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,12 +9,20 @@ on: jobs: build-and-test: runs-on: macos-15 + strategy: + fail-fast: false + matrix: + # The build files and sources use spellings valid in both: the + # root_module API, callconv(.c), ArrayListUnmanaged, and + # net.Stream.read. Testing both keeps that honest rather than assumed. + zig: ["0.14.1", "0.15.2"] + name: build-and-test (zig ${{ matrix.zig }}) steps: - uses: actions/checkout@v4 - uses: mlugg/setup-zig@v2 with: - version: 0.14.1 + version: ${{ matrix.zig }} # Pinned to macos-15 rather than macos-latest. macos-latest now ships # Xcode 26.5, whose SDK Zig 0.14.1 cannot link against: every libc diff --git a/README.md b/README.md index f60b40c..5a7dd6e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # 🎵 Danzig VST3 Framework [![CI](https://github.com/godofecht/danzig/actions/workflows/ci.yml/badge.svg)](https://github.com/godofecht/danzig/actions/workflows/ci.yml) -[![Zig](https://img.shields.io/badge/zig-0.14.1-f7a41d)](https://ziglang.org/) +[![Zig](https://img.shields.io/badge/zig-0.14.1%20%7C%200.15.2-f7a41d)](https://ziglang.org/) A modern, lightweight VST3 plugin development framework built in Zig with zero external dependencies. @@ -112,7 +112,7 @@ zig-out/ ## System Requirements -- Zig 0.12.0+ +- Zig 0.14.1 or 0.15.2 - macOS (currently Mach-O format) - VST3-compatible DAW @@ -154,7 +154,7 @@ See [PLUGINVAL_REPORT.md](PLUGINVAL_REPORT.md) for detailed validation results. ## License -See main project root for license information. +MIT. See [LICENSE](LICENSE). --- diff --git a/build.zig b/build.zig index 35b15e3..d0d4437 100644 --- a/build.zig +++ b/build.zig @@ -7,11 +7,14 @@ pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); // Danzig library (static) - const danzig_lib = b.addStaticLibrary(.{ + const danzig_lib = b.addLibrary(.{ .name = "danzig", - .root_source_file = b.path("src/root.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }), + .linkage = .static, }); // Danzig module for import by other targets @@ -20,11 +23,14 @@ pub fn build(b: *std.Build) void { }); // Danzig gain VST3 plugin (shared, embeds danzig) - const danzig_gain = b.addSharedLibrary(.{ + const danzig_gain = b.addLibrary(.{ .name = "DanzigGain", - .root_source_file = b.path("examples/danzig-gain/root.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("examples/danzig-gain/root.zig"), + .target = target, + .optimize = optimize, + }), + .linkage = .dynamic, }); danzig_gain.root_module.addImport("danzig", danzig_module); danzig_gain.linkLibrary(danzig_lib); @@ -35,9 +41,11 @@ pub fn build(b: *std.Build) void { // Test executable const danzig_test = b.addExecutable(.{ .name = "danzig_test", - .root_source_file = b.path("examples/danzig-test/root.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("examples/danzig-test/root.zig"), + .target = target, + .optimize = optimize, + }), }); danzig_test.linkLibrary(danzig_lib); b.installArtifact(danzig_test); @@ -45,9 +53,11 @@ pub fn build(b: *std.Build) void { // Standalone audio processor const danzig_gain_standalone = b.addExecutable(.{ .name = "danzig-gain-standalone", - .root_source_file = b.path("examples/danzig-gain-standalone/root.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("examples/danzig-gain-standalone/root.zig"), + .target = target, + .optimize = optimize, + }), }); danzig_gain_standalone.root_module.addImport("danzig", danzig_module); danzig_gain_standalone.linkLibrary(danzig_lib); @@ -60,9 +70,11 @@ pub fn build(b: *std.Build) void { // Web UI server const danzig_webui = b.addExecutable(.{ .name = "danzig-webui", - .root_source_file = b.path("examples/danzig-webui/root.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("examples/danzig-webui/root.zig"), + .target = target, + .optimize = optimize, + }), }); danzig_webui.root_module.addImport("danzig", danzig_module); danzig_webui.linkLibrary(danzig_lib); @@ -90,9 +102,11 @@ pub fn build(b: *std.Build) void { // Unit tests — pure Zig, no artifact or host required const unit_tests = b.addTest(.{ - .root_source_file = b.path("src/tests.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("src/tests.zig"), + .target = target, + .optimize = optimize, + }), }); const run_unit_tests = b.addRunArtifact(unit_tests); @@ -125,18 +139,24 @@ fn addVst3Bundle( inline for (arches, suffixes) |arch, suffix| { const arch_target = b.resolveTargetQuery(.{ .cpu_arch = arch, .os_tag = .macos }); - const lib = b.addStaticLibrary(.{ + const lib = b.addLibrary(.{ .name = "danzig_" ++ suffix, - .root_source_file = b.path("src/root.zig"), - .target = arch_target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("src/root.zig"), + .target = arch_target, + .optimize = optimize, + }), + .linkage = .static, }); - const plugin = b.addSharedLibrary(.{ + const plugin = b.addLibrary(.{ .name = "DanzigGain_" ++ suffix, - .root_source_file = b.path("examples/danzig-gain/root.zig"), - .target = arch_target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("examples/danzig-gain/root.zig"), + .target = arch_target, + .optimize = optimize, + }), + .linkage = .dynamic, }); plugin.root_module.addImport("danzig", danzig_module); plugin.linkLibrary(lib); @@ -189,9 +209,11 @@ fn addGuiExample( const gui = b.addExecutable(.{ .name = "danzig-gain-ui", - .root_source_file = b.path("examples/danzig-gain-ui/root.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("examples/danzig-gain-ui/root.zig"), + .target = target, + .optimize = optimize, + }), }); gui.root_module.addImport("danzig", danzig_module); gui.root_module.addImport("coreaudio", coreaudio_module); diff --git a/build.zig.zon b/build.zig.zon index 04f3d1a..01326b4 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -4,8 +4,8 @@ .fingerprint = 0x6383fbb1d610c5a6, .dependencies = .{ .webview = .{ - .url = "https://github.com/thechampagne/webview-zig/archive/5abc215.tar.gz", - .hash = "webview-0.1.0-ImQK_yb6IADsgQL8f72LGd4xkaSrxqQn9RyYy3U36s1J", + .url = "https://github.com/thechampagne/webview-zig/archive/a5eef0bbff65c914ea893dae0816b956bcdee6c6.tar.gz", + .hash = "webview-0.1.0-ImQK_8_aIABj-dmipgQ_Cw1trbEFExFQwMZetkgtT8fg", }, }, .paths = .{ diff --git a/examples/danzig-gain-ui/coreaudio.zig b/examples/danzig-gain-ui/coreaudio.zig index 41f997d..b872a29 100644 --- a/examples/danzig-gain-ui/coreaudio.zig +++ b/examples/danzig-gain-ui/coreaudio.zig @@ -104,7 +104,10 @@ pub fn enumerateDevices(allocator: std.mem.Allocator) ![]AudioDevice { const default_input = getDefaultDevice(c.kAudioDevicePropertyScopeInput); const default_output = getDefaultDevice(c.kAudioDevicePropertyScopeOutput); - var devices = std.ArrayList(AudioDevice).init(allocator); + // Unmanaged form: Zig 0.15 made ArrayList unmanaged by default and + // dropped .init(allocator). ArrayListUnmanaged spells the same type in + // both 0.14 and 0.15, so the allocator is passed per call instead. + var devices: std.ArrayListUnmanaged(AudioDevice) = .{}; for (device_ids) |did| { const input_channels = getChannelCount(did, c.kAudioDevicePropertyScopeInput); @@ -128,15 +131,15 @@ pub fn enumerateDevices(allocator: std.mem.Allocator) ![]AudioDevice { dev.name_len = fallback.len; } - try devices.append(dev); + try devices.append(allocator, dev); } - return devices.toOwnedSlice(); + return devices.toOwnedSlice(allocator); } pub fn devicesToJson(allocator: std.mem.Allocator, devices: []const AudioDevice) ![]u8 { - var json = std.ArrayList(u8).init(allocator); - const w = json.writer(); + var json: std.ArrayListUnmanaged(u8) = .{}; + var w = json.writer(allocator); try w.writeAll("{\"inputs\":["); var first_in = true; @@ -164,5 +167,5 @@ pub fn devicesToJson(allocator: std.mem.Allocator, devices: []const AudioDevice) } try w.writeAll("]}"); - return json.toOwnedSlice(); + return json.toOwnedSlice(allocator); } diff --git a/examples/danzig-gain-ui/root.zig b/examples/danzig-gain-ui/root.zig index 5c4f62a..d3f8223 100644 --- a/examples/danzig-gain-ui/root.zig +++ b/examples/danzig-gain-ui/root.zig @@ -17,7 +17,7 @@ pub fn main() !void { defer wv.destroy() catch {}; try wv.setTitle("DanzigGain"); - try wv.setSize(600, 700, .Fixed); + try wv.setSize(600, 700, .fixed); // Enumerate CoreAudio devices and inject into JS before loading UI const devices = coreaudio.enumerateDevices(allocator) catch &[_]coreaudio.AudioDevice{}; diff --git a/examples/danzig-gain/root.zig b/examples/danzig-gain/root.zig index 8293912..f533184 100644 --- a/examples/danzig-gain/root.zig +++ b/examples/danzig-gain/root.zig @@ -107,17 +107,17 @@ pub const ModuleInfo = extern struct { }; const IUnknownVTable = extern struct { - queryInterface: *const fn (*anyopaque, guid: [*]const u8, obj: *?*anyopaque) callconv(.C) i32, - addRef: *const fn (*anyopaque) callconv(.C) u32, - release: *const fn (*anyopaque) callconv(.C) u32, + queryInterface: *const fn (*anyopaque, guid: [*]const u8, obj: *?*anyopaque) callconv(.c) i32, + addRef: *const fn (*anyopaque) callconv(.c) u32, + release: *const fn (*anyopaque) callconv(.c) u32, }; const IPluginFactoryVTable = extern struct { base: IUnknownVTable, - getFactoryInfo: *const fn (*anyopaque, info: *anyopaque) callconv(.C) i32, - countClasses: *const fn (*anyopaque) callconv(.C) i32, - getClassInfo: *const fn (*anyopaque, index: i32, info: *anyopaque) callconv(.C) i32, - createInstance: *const fn (*anyopaque, cid: [*]const u8, iid: [*]const u8, obj: *?*anyopaque) callconv(.C) i32, + getFactoryInfo: *const fn (*anyopaque, info: *anyopaque) callconv(.c) i32, + countClasses: *const fn (*anyopaque) callconv(.c) i32, + getClassInfo: *const fn (*anyopaque, index: i32, info: *anyopaque) callconv(.c) i32, + createInstance: *const fn (*anyopaque, cid: [*]const u8, iid: [*]const u8, obj: *?*anyopaque) callconv(.c) i32, }; pub const PluginFactory = extern struct { @@ -127,40 +127,40 @@ pub const PluginFactory = extern struct { var gFactory: PluginFactory = undefined; -fn factory_queryInterface(self: *anyopaque, guid: [*]const u8, obj: *?*anyopaque) callconv(.C) i32 { +fn factory_queryInterface(self: *anyopaque, guid: [*]const u8, obj: *?*anyopaque) callconv(.c) i32 { _ = self; _ = guid; _ = obj; return -1; // kNoInterface } -fn factory_addRef(self: *anyopaque) callconv(.C) u32 { +fn factory_addRef(self: *anyopaque) callconv(.c) u32 { var factory = @as(*PluginFactory, @ptrCast(@alignCast(self))); factory.refCount += 1; return factory.refCount; } -fn factory_release(self: *anyopaque) callconv(.C) u32 { +fn factory_release(self: *anyopaque) callconv(.c) u32 { var factory = @as(*PluginFactory, @ptrCast(@alignCast(self))); if (factory.refCount > 0) factory.refCount -= 1; return factory.refCount; } -fn factory_getFactoryInfo(self: *anyopaque, _: *anyopaque) callconv(.C) i32 { +fn factory_getFactoryInfo(self: *anyopaque, _: *anyopaque) callconv(.c) i32 { _ = self; return 0; } -fn factory_countClasses(_: *anyopaque) callconv(.C) i32 { +fn factory_countClasses(_: *anyopaque) callconv(.c) i32 { return 1; } -fn factory_getClassInfo(_: *anyopaque, index: i32, _: *anyopaque) callconv(.C) i32 { +fn factory_getClassInfo(_: *anyopaque, index: i32, _: *anyopaque) callconv(.c) i32 { _ = index; return 0; } -fn factory_createInstance(_: *anyopaque, _: [*]const u8, _: [*]const u8, _: *?*anyopaque) callconv(.C) i32 { +fn factory_createInstance(_: *anyopaque, _: [*]const u8, _: [*]const u8, _: *?*anyopaque) callconv(.c) i32 { return 0; } diff --git a/examples/danzig-webui/root.zig b/examples/danzig-webui/root.zig index 476e21b..0fb7de1 100644 --- a/examples/danzig-webui/root.zig +++ b/examples/danzig-webui/root.zig @@ -41,7 +41,10 @@ pub fn main() !void { fn handleConnection(stream: std.net.Stream, html_content: []const u8) !void { var buffer: [8192]u8 = undefined; - const bytes_read = try stream.readAll(&buffer); + // A single read rather than readAll: readAll blocks until the buffer is + // full or the peer closes, which for a keep-alive HTTP client means + // hanging. It was also removed from net.Stream in Zig 0.15. + const bytes_read = try stream.read(&buffer); if (bytes_read == 0) return; diff --git a/src/vst3.zig b/src/vst3.zig index 684ecb9..0a6cee2 100644 --- a/src/vst3.zig +++ b/src/vst3.zig @@ -62,28 +62,28 @@ pub const ProcessContext = extern struct { }; pub const IUnknown = extern struct { - queryInterface: *const fn (?*IUnknown, *const IID, ?*[*]?*anyopaque) callconv(.C) TResult = undefined, - addRef: *const fn (?*IUnknown) callconv(.C) u32 = undefined, - release: *const fn (?*IUnknown) callconv(.C) u32 = undefined, + queryInterface: *const fn (?*IUnknown, *const IID, ?*[*]?*anyopaque) callconv(.c) TResult = undefined, + addRef: *const fn (?*IUnknown) callconv(.c) u32 = undefined, + release: *const fn (?*IUnknown) callconv(.c) u32 = undefined, }; pub const IPluginBase = extern struct { unknown: IUnknown, - initialize: *const fn (?*IPluginBase, ?*anyopaque) callconv(.C) TResult = undefined, - terminate: *const fn (?*IPluginBase) callconv(.C) TResult = undefined, + initialize: *const fn (?*IPluginBase, ?*anyopaque) callconv(.c) TResult = undefined, + terminate: *const fn (?*IPluginBase) callconv(.c) TResult = undefined, }; pub const IComponent = extern struct { pluginBase: IPluginBase, - getControllerClassId: *const fn (?*IComponent, ?*CUID) callconv(.C) TResult = undefined, - setIoMode: *const fn (?*IComponent, i32) callconv(.C) TResult = undefined, - getBusCount: *const fn (?*IComponent, BusDirection, u32) callconv(.C) u32 = undefined, - getBusInfo: *const fn (?*IComponent, BusDirection, u32, ?*BusInfo) callconv(.C) TResult = undefined, - getRoutingInfo: *const fn (?*IComponent, ?*RoutingInfo, ?*RoutingInfo) callconv(.C) TResult = undefined, - activateBus: *const fn (?*IComponent, BusDirection, u32, u8) callconv(.C) TResult = undefined, - setActive: *const fn (?*IComponent, u8) callconv(.C) TResult = undefined, - setState: *const fn (?*IComponent, ?*anyopaque) callconv(.C) TResult = undefined, - getState: *const fn (?*IComponent, ?*anyopaque) callconv(.C) TResult = undefined, + getControllerClassId: *const fn (?*IComponent, ?*CUID) callconv(.c) TResult = undefined, + setIoMode: *const fn (?*IComponent, i32) callconv(.c) TResult = undefined, + getBusCount: *const fn (?*IComponent, BusDirection, u32) callconv(.c) u32 = undefined, + getBusInfo: *const fn (?*IComponent, BusDirection, u32, ?*BusInfo) callconv(.c) TResult = undefined, + getRoutingInfo: *const fn (?*IComponent, ?*RoutingInfo, ?*RoutingInfo) callconv(.c) TResult = undefined, + activateBus: *const fn (?*IComponent, BusDirection, u32, u8) callconv(.c) TResult = undefined, + setActive: *const fn (?*IComponent, u8) callconv(.c) TResult = undefined, + setState: *const fn (?*IComponent, ?*anyopaque) callconv(.c) TResult = undefined, + getState: *const fn (?*IComponent, ?*anyopaque) callconv(.c) TResult = undefined, }; pub const BusInfo = extern struct { @@ -101,17 +101,17 @@ pub const RoutingInfo = extern struct { }; pub const IAudioProcessor = extern struct { - queryInterface: *const fn (?*IAudioProcessor, *const IID, ?*[*]?*anyopaque) callconv(.C) TResult = undefined, - addRef: *const fn (?*IAudioProcessor) callconv(.C) u32 = undefined, - release: *const fn (?*IAudioProcessor) callconv(.C) u32 = undefined, - setBusArrangements: *const fn (?*IAudioProcessor, [*]i64, u32, [*]i64, u32) callconv(.C) TResult = undefined, - getBusArrangement: *const fn (?*IAudioProcessor, i32, i32, [*]i64) callconv(.C) TResult = undefined, - canProcessSampleSize: *const fn (?*IAudioProcessor, i32) callconv(.C) TResult = undefined, - getLatencySamples: *const fn (?*IAudioProcessor) callconv(.C) u32 = undefined, - setupProcessing: *const fn (?*IAudioProcessor, ?*ProcessSetup) callconv(.C) TResult = undefined, - setProcessing: *const fn (?*IAudioProcessor, u8) callconv(.C) TResult = undefined, - process: *const fn (?*IAudioProcessor, ?*ProcessData) callconv(.C) TResult = undefined, - getTailSamples: *const fn (?*IAudioProcessor) callconv(.C) u32 = undefined, + queryInterface: *const fn (?*IAudioProcessor, *const IID, ?*[*]?*anyopaque) callconv(.c) TResult = undefined, + addRef: *const fn (?*IAudioProcessor) callconv(.c) u32 = undefined, + release: *const fn (?*IAudioProcessor) callconv(.c) u32 = undefined, + setBusArrangements: *const fn (?*IAudioProcessor, [*]i64, u32, [*]i64, u32) callconv(.c) TResult = undefined, + getBusArrangement: *const fn (?*IAudioProcessor, i32, i32, [*]i64) callconv(.c) TResult = undefined, + canProcessSampleSize: *const fn (?*IAudioProcessor, i32) callconv(.c) TResult = undefined, + getLatencySamples: *const fn (?*IAudioProcessor) callconv(.c) u32 = undefined, + setupProcessing: *const fn (?*IAudioProcessor, ?*ProcessSetup) callconv(.c) TResult = undefined, + setProcessing: *const fn (?*IAudioProcessor, u8) callconv(.c) TResult = undefined, + process: *const fn (?*IAudioProcessor, ?*ProcessData) callconv(.c) TResult = undefined, + getTailSamples: *const fn (?*IAudioProcessor) callconv(.c) u32 = undefined, }; pub const ProcessSetup = extern struct { @@ -123,19 +123,19 @@ pub const ProcessSetup = extern struct { pub const IEditController = extern struct { pluginBase: IPluginBase, - setComponentState: *const fn (?*IEditController, ?*anyopaque) callconv(.C) TResult = undefined, - setState: *const fn (?*IEditController, ?*anyopaque) callconv(.C) TResult = undefined, - getState: *const fn (?*IEditController, ?*anyopaque) callconv(.C) TResult = undefined, - getParameterCount: *const fn (?*IEditController) callconv(.C) i32 = undefined, - getParameterInfo: *const fn (?*IEditController, i32, ?*ParameterInfo) callconv(.C) TResult = undefined, - getParamStringByValue: *const fn (?*IEditController, i32, f64, [*]u16) callconv(.C) TResult = undefined, - getParamValueByString: *const fn (?*IEditController, i32, [*]const u16, [*]f64) callconv(.C) TResult = undefined, - normalizedParamToPlain: *const fn (?*IEditController, i32, f64) callconv(.C) f64 = undefined, - plainParamToNormalized: *const fn (?*IEditController, i32, f64) callconv(.C) f64 = undefined, - getParamNormalized: *const fn (?*IEditController, i32) callconv(.C) f64 = undefined, - setParamNormalized: *const fn (?*IEditController, i32, f64) callconv(.C) TResult = undefined, - setComponentHandler: *const fn (?*IEditController, ?*anyopaque) callconv(.C) TResult = undefined, - createView: *const fn (?*IEditController, [*:0]const u8) callconv(.C) ?*anyopaque = undefined, + setComponentState: *const fn (?*IEditController, ?*anyopaque) callconv(.c) TResult = undefined, + setState: *const fn (?*IEditController, ?*anyopaque) callconv(.c) TResult = undefined, + getState: *const fn (?*IEditController, ?*anyopaque) callconv(.c) TResult = undefined, + getParameterCount: *const fn (?*IEditController) callconv(.c) i32 = undefined, + getParameterInfo: *const fn (?*IEditController, i32, ?*ParameterInfo) callconv(.c) TResult = undefined, + getParamStringByValue: *const fn (?*IEditController, i32, f64, [*]u16) callconv(.c) TResult = undefined, + getParamValueByString: *const fn (?*IEditController, i32, [*]const u16, [*]f64) callconv(.c) TResult = undefined, + normalizedParamToPlain: *const fn (?*IEditController, i32, f64) callconv(.c) f64 = undefined, + plainParamToNormalized: *const fn (?*IEditController, i32, f64) callconv(.c) f64 = undefined, + getParamNormalized: *const fn (?*IEditController, i32) callconv(.c) f64 = undefined, + setParamNormalized: *const fn (?*IEditController, i32, f64) callconv(.c) TResult = undefined, + setComponentHandler: *const fn (?*IEditController, ?*anyopaque) callconv(.c) TResult = undefined, + createView: *const fn (?*IEditController, [*:0]const u8) callconv(.c) ?*anyopaque = undefined, }; pub const ParameterInfo = extern struct {