Skip to content

Commit fe1bfab

Browse files
committed
store parameters as floats and add modulation
1 parent 68ffd68 commit fe1bfab

File tree

2 files changed

+84
-60
lines changed

2 files changed

+84
-60
lines changed

src/clap/root.zig

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub const State = struct {
9090
}
9191
}
9292

93-
pub fn process(self: *State, comptime Plugin: type, clap_process: [*c]const c.clap_process, start: u32, end: u32) !void {
93+
pub fn processAudio(self: *State, comptime Plugin: type, clap_process: [*c]const c.clap_process, start: u32, end: u32) !void {
9494
const ports = Plugin.meta.audio_ports.?;
9595
const inputs = ports.in.len;
9696
const outputs = ports.out.len;
@@ -148,29 +148,46 @@ pub const State = struct {
148148
);
149149
}
150150

151-
// TODO: handle param_mod
152-
pub fn handleParamEvent(self: *State, event: *const c.clap_event_param_value) void {
153-
const param: *zigplug.Parameter = blk: {
154-
if (event.cookie) |ptr|
155-
break :blk @ptrCast(@alignCast(ptr))
156-
else {
157-
@branchHint(.unlikely);
158-
break :blk self.plugin.parameters.?.slice[event.param_id];
159-
}
160-
};
161-
162-
switch (param.*) {
163-
inline else => |*p| {
164-
const value = @TypeOf(p.*).fromFloat(event.value);
165-
p.set(value);
166-
zigplug.log.debug("parameter '{s}' = {any}", .{ p.options.name, value });
167-
},
168-
}
169-
}
170-
171151
pub fn handleEvent(self: *State, event: *const c.clap_event_header_t) void {
172152
switch (event.type) {
173-
c.CLAP_EVENT_PARAM_VALUE => self.handleParamEvent(@ptrCast(@alignCast(event))),
153+
c.CLAP_EVENT_PARAM_VALUE => {
154+
const param_event: *const c.clap_event_param_value = @ptrCast(@alignCast(event));
155+
const param: *zigplug.Parameter = blk: {
156+
if (param_event.cookie) |ptr|
157+
break :blk @ptrCast(@alignCast(ptr))
158+
else {
159+
@branchHint(.unlikely);
160+
break :blk self.plugin.parameters.?.slice[param_event.param_id];
161+
}
162+
};
163+
164+
switch (param.*) {
165+
inline else => |*p| {
166+
const value = @TypeOf(p.*).fromFloat(param_event.value);
167+
p.set(value);
168+
zigplug.log.debug("parameter '{s}' = {any}", .{ p.options.id.?, value });
169+
},
170+
}
171+
},
172+
c.CLAP_EVENT_PARAM_MOD => {
173+
const param_event: *const c.clap_event_param_mod = @ptrCast(@alignCast(event));
174+
const param: *zigplug.Parameter = blk: {
175+
if (param_event.cookie) |ptr|
176+
break :blk @ptrCast(@alignCast(ptr))
177+
else {
178+
@branchHint(.unlikely);
179+
break :blk self.plugin.parameters.?.slice[param_event.param_id];
180+
}
181+
};
182+
183+
switch (param.*) {
184+
inline else => |*p| {
185+
const amount = param_event.*.amount;
186+
const modulated = p.modulate(amount);
187+
zigplug.log.debug("parameter '{s}' mod + {any}", .{ p.options.id.?, modulated });
188+
},
189+
}
190+
},
174191
else => {},
175192
}
176193
}
@@ -235,13 +252,11 @@ fn ClapPlugin(comptime Plugin: type) type {
235252
for (0..event_count) |i| {
236253
const event = clap_process.*.in_events.*.get.?(clap_process.*.in_events, @intCast(i));
237254
switch (event.*.type) {
238-
c.CLAP_EVENT_PARAM_VALUE => {
239-
const value_event: *const c.clap_event_param_value = @ptrCast(@alignCast(event));
240-
255+
c.CLAP_EVENT_PARAM_VALUE, c.CLAP_EVENT_PARAM_MOD => {
241256
if (comptime Plugin.meta.sample_accurate_automation) {
242-
end = value_event.header.time;
257+
end = event.*.time;
243258

244-
state.process(Plugin, clap_process, start, end) catch |e| {
259+
state.processAudio(Plugin, clap_process, start, end) catch |e| {
245260
log.err("error while processing: {}", .{e});
246261
return c.CLAP_PROCESS_ERROR;
247262
};
@@ -250,13 +265,13 @@ fn ClapPlugin(comptime Plugin: type) type {
250265
end = samples;
251266
}
252267

253-
state.handleParamEvent(value_event);
268+
state.handleEvent(event);
254269
},
255270
else => state.handleEvent(event),
256271
}
257272
}
258273

259-
state.process(Plugin, clap_process, start, end) catch |e| {
274+
state.processAudio(Plugin, clap_process, start, end) catch |e| {
260275
log.err("error while processing: {}", .{e});
261276
return c.CLAP_PROCESS_ERROR;
262277
};

src/core/parameters.zig

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -100,61 +100,73 @@ pub fn Options(comptime T: type) type {
100100
const ParameterType = enum { float, int, uint, bool };
101101

102102
pub const Parameter = union(ParameterType) {
103-
fn Inner(comptime Type: type) type {
103+
fn Inner(comptime T: type) type {
104104
return struct {
105-
value: std.atomic.Value(Type),
106-
options: Options(Type),
105+
value: std.atomic.Value(f64),
106+
options: Options(T),
107+
108+
pub const param_type: ParameterType = switch (@typeInfo(T)) {
109+
.float => .float,
110+
.int => |info| switch (info.signedness) {
111+
.signed => .int,
112+
.unsigned => .uint,
113+
},
114+
.bool => .bool,
115+
else => @compileError("unsupported parameter type: " ++ @typeName(T)),
116+
};
107117

108-
pub fn init(comptime options: Options(Type)) @This() {
109-
if (options.special == .bypass and Type != bool)
110-
@compileError("Bypass parameter type must be bool, got " ++ @typeName(Type));
118+
pub fn init(comptime options: Options(T)) @This() {
119+
if (options.special == .bypass and T != bool)
120+
@compileError("Bypass parameter type must be bool, got " ++ @typeName(T));
111121

112122
if (options.unit != null and options.format != null)
113123
@compileError("Parameter '" ++ options.name ++ "' has both `format` and `unit`, which are mutually exclusive");
114124

115125
return .{
116-
.value = .init(options.default),
126+
.value = .init(toFloat(options.default)),
117127
.options = options,
118128
};
119129
}
120130

121131
pub fn set(self: *@This(), value: anytype) void {
122132
self.value.store(switch (@typeInfo(@TypeOf(value))) {
123-
.@"enum" => @intFromEnum(value),
124-
else => value,
133+
.@"enum" => @floatFromInt(@intFromEnum(value)),
134+
else => toFloat(value),
125135
}, .unordered);
126136
}
127137

128138
// TODO: this for enums
129-
pub fn get(self: *const @This()) Type {
130-
return self.value.load(.unordered);
139+
pub fn get(self: *const @This()) T {
140+
return fromFloat(self.value.load(.unordered));
131141
}
132142

133-
pub fn fromFloat(value: f64) Type {
134-
return switch (@typeInfo(Type)) {
143+
pub fn fromFloat(value: f64) T {
144+
return switch (param_type) {
135145
.float => @floatCast(value),
136-
.int => @intFromFloat(value),
146+
.int, .uint => @intFromFloat(value),
137147
.bool => value == 1,
138-
else => unreachable,
139148
};
140149
}
141150

142-
pub fn toFloat(value: Type) f64 {
143-
return switch (@typeInfo(@TypeOf(value))) {
151+
pub fn toFloat(value: T) f64 {
152+
return switch (param_type) {
144153
.float => @floatCast(value),
145-
.int => @floatFromInt(value),
146-
.bool => if (value) 1 else 0,
147-
else => unreachable,
154+
.int, .uint => @floatFromInt(value),
155+
.bool => @floatFromInt(@intFromBool(value)),
148156
};
149157
}
150158

151-
pub fn format(self: *const @This(), writer: *std.Io.Writer, value: Type) std.Io.Writer.Error!void {
159+
pub fn modulate(self: *@This(), amount: f64) void {
160+
_ = self.value.fetchAdd(amount, .monotonic);
161+
}
162+
163+
pub fn format(self: *const @This(), writer: *std.Io.Writer, value: T) std.Io.Writer.Error!void {
152164
if (self.options.format) |f|
153165
try f(value, writer)
154166
else
155167
try writer.print(
156-
switch (@typeInfo(@TypeOf(value))) {
157-
.float, .comptime_float => "{d}{s}",
168+
switch (param_type) {
169+
.float => "{d}{s}",
158170
else => "{}{s}",
159171
},
160172
.{ value, self.options.unit orelse "" },
@@ -163,7 +175,7 @@ pub const Parameter = union(ParameterType) {
163175
try writer.flush();
164176
}
165177

166-
pub fn parse(self: *const @This(), value: []const u8) !Type {
178+
pub fn parse(self: *const @This(), value: []const u8) !T {
167179
if (self.options.parse) |f|
168180
return f(value);
169181

@@ -172,14 +184,11 @@ pub const Parameter = union(ParameterType) {
172184
else
173185
value;
174186

175-
return switch (comptime @typeInfo(Type)) {
176-
.float => std.fmt.parseFloat(Type, str),
177-
.int => |t| try (switch (t.signedness) {
178-
.signed => std.fmt.parseInt,
179-
.unsigned => std.fmt.parseUnsigned,
180-
})(Type, str, 10),
187+
return switch (param_type) {
188+
.float => std.fmt.parseFloat(T, str),
189+
.int => std.fmt.parseInt(T, str, 10),
190+
.uint => std.fmt.parseUnsigned(T, str, 10),
181191
.bool => std.mem.eql(u8, str, "true"),
182-
else => unreachable,
183192
};
184193
}
185194
};

0 commit comments

Comments
 (0)