Skip to content

Commit 3ce0511

Browse files
committed
use field names and id for stable parameter state serialization
1 parent af34231 commit 3ce0511

File tree

4 files changed

+29
-18
lines changed

4 files changed

+29
-18
lines changed

src/clap/extensions/parameters.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub fn extension(comptime Plugin: type) *const c.clap_plugin_params_t {
8686

8787
switch (param) {
8888
inline else => |p| p.format(writer, @TypeOf(p).fromFloat(value)) catch {
89-
log.err("failed to format parameter '{s}': {}", .{ p.options.name, value });
89+
log.err("failed to format parameter '{s}': {}", .{ p.options.id.?, value });
9090
return false;
9191
},
9292
}
@@ -119,7 +119,7 @@ pub fn extension(comptime Plugin: type) *const c.clap_plugin_params_t {
119119

120120
out.?.* = switch (param) {
121121
inline else => |p| @TypeOf(p).toFloat(p.parse(text) catch {
122-
log.err("failed to parse parameter '{s}': {s}", .{ p.options.name, value_text });
122+
log.err("failed to parse parameter '{s}': '{s}'", .{ p.options.id.?, value_text });
123123
return false;
124124
}),
125125
};

src/clap/extensions/state.zig

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const Reader = struct {
7070
}
7171
};
7272

73-
pub fn extension(comptime Plugin: type) *const c.clap_plugin_state {
73+
pub fn extension(comptime _: type) *const c.clap_plugin_state {
7474
const state = struct {
7575
pub fn save(clap_plugin: [*c]const c.clap_plugin, stream: [*c]const c.clap_ostream) callconv(.c) bool {
7676
const data = clap.Data.fromClap(clap_plugin);
@@ -84,8 +84,12 @@ pub fn extension(comptime Plugin: type) *const c.clap_plugin_state {
8484
var map = msgpack.Payload.mapPayload(data.plugin.allocator);
8585
defer map.free(data.plugin.allocator);
8686

87-
inline for (data.plugin.parameters.?.slice, std.meta.fields(Plugin.Parameters)) |parameter, field| {
88-
map.mapPut(field.name, switch (parameter.*) {
87+
for (data.plugin.parameters.?.slice) |parameter| {
88+
const id = switch (parameter.*) {
89+
inline else => |*p| p.options.id.?,
90+
};
91+
92+
map.mapPut(id, switch (parameter.*) {
8993
.bool => |p| .boolToPayload(p.get()),
9094
.float => |p| .floatToPayload(p.get()),
9195
.int => |p| .intToPayload(p.get()),
@@ -100,7 +104,7 @@ pub fn extension(comptime Plugin: type) *const c.clap_plugin_state {
100104
};
101105

102106
switch (parameter.*) {
103-
inline else => |p| log.debug("saved parameter '{s}' = {any}", .{ field.name, p.get() }),
107+
inline else => |p| log.debug("saved parameter '{s}' = {any}", .{ id, p.get() }),
104108
}
105109
}
106110

@@ -126,9 +130,13 @@ pub fn extension(comptime Plugin: type) *const c.clap_plugin_state {
126130
};
127131
defer decoded.free(data.plugin.allocator);
128132

129-
inline for (data.plugin.parameters.?.slice, std.meta.fields(Plugin.Parameters)) |parameter, field| {
130-
if (decoded.mapGet(field.name) catch {
131-
log.err("failed to read parameter '{s}'", .{field.name});
133+
for (data.plugin.parameters.?.slice) |parameter| {
134+
const id = switch (parameter.*) {
135+
inline else => |*p| p.options.id.?,
136+
};
137+
138+
if (decoded.mapGet(id) catch {
139+
log.err("failed to read parameter '{s}'", .{id});
132140
return false;
133141
}) |value| {
134142
switch (parameter.*) {
@@ -140,7 +148,7 @@ pub fn extension(comptime Plugin: type) *const c.clap_plugin_state {
140148
}
141149

142150
switch (parameter.*) {
143-
inline else => |p| log.debug("read parameter '{s}' = {any}", .{ field.name, p.get() }),
151+
inline else => |p| log.debug("read parameter '{s}' = {any}", .{ id, p.get() }),
144152
}
145153
}
146154

src/zigplug/parameters.zig

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ pub fn Options(comptime T: type) type {
66
return struct {
77
/// Human-readable, "pretty" name to be displayed by the host or plugin GUI
88
name: [:0]const u8,
9+
/// Optional stable and unique identifier that will be used when saving parameter state. If null, the parameters
10+
/// struct field name will be used.
11+
id: ?[]const u8 = null,
912
/// Parameter will be initialized with this value
1013
default: T,
1114
// TODO: this should automatically be set for booleans and enums
@@ -150,7 +153,8 @@ pub const Parameter = union(ParameterType) {
150153
};
151154
}
152155

153-
/// Wrap an enum into an integer parameter with automatic formatting
156+
/// Wrap an enum into an integer parameter with automatic formatting. The order of enum values should remain stable
157+
/// between plugin versions.
154158
pub fn choice(comptime T: type, comptime options: ChoiceOptions(T)) Parameter {
155159
switch (@typeInfo(T)) {
156160
.@"enum" => |info| {

src/zigplug/zigplug.zig

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ pub const Plugin = struct {
132132
@compileError("`Parameters` struct field '" ++ field.name ++ "' has no default value");
133133
}
134134
},
135-
else => @compileError("`Parameters` type is not a struct"),
135+
else => @compileError("`Parameters` is not a struct"),
136136
}
137137

138138
const parameters_context = try allocator.create(Parameters);
@@ -142,12 +142,11 @@ pub const Plugin = struct {
142142
var parameters_slice = try allocator.alloc(*Parameter, fields.len);
143143

144144
inline for (fields, 0..) |field, i| {
145-
if (field.type != Parameter)
146-
@compileError("Parameter '" ++ field.name ++ "' is not of type 'zigplug.Parameter'");
147-
if (field.default_value_ptr == null)
148-
@compileError("Parameter '" ++ field.name ++ "' has no default value");
149-
150-
parameters_slice[i] = &@field(parameters_context, field.name);
145+
const param = &@field(parameters_context, field.name);
146+
switch (param.*) {
147+
inline else => |*p| p.options.id = p.options.id orelse field.name,
148+
}
149+
parameters_slice[i] = param;
151150
}
152151

153152
break :blk .{

0 commit comments

Comments
 (0)