1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
#ifndef RACK_AU_H
#define RACK_AU_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
// Opaque types
typedef struct RackAUScanner RackAUScanner;
typedef struct RackAUPlugin RackAUPlugin;
typedef struct RackAUGui RackAUGui;
// Plugin type enum
typedef enum {
RACK_AU_TYPE_EFFECT = 0,
RACK_AU_TYPE_INSTRUMENT = 1,
RACK_AU_TYPE_MIXER = 2,
RACK_AU_TYPE_FORMAT_CONVERTER = 3,
RACK_AU_TYPE_OTHER = 4,
} RackAUPluginType;
// Plugin info struct (passed to Rust)
typedef struct {
char name[256];
char manufacturer[256];
char path[1024];
char unique_id[64];
uint32_t version;
RackAUPluginType plugin_type;
} RackAUPluginInfo;
// Error codes (0 = success, negative = error)
#define RACK_AU_OK 0
#define RACK_AU_ERROR_GENERIC -1
#define RACK_AU_ERROR_NOT_FOUND -2
#define RACK_AU_ERROR_INVALID_PARAM -3
#define RACK_AU_ERROR_NOT_INITIALIZED -4
#define RACK_AU_ERROR_AUDIO_UNIT -1000 // Base for AudioUnit OSStatus errors
// ============================================================================
// Scanner API
// ============================================================================
// Create a new scanner
// Returns NULL if allocation fails
RackAUScanner* rack_au_scanner_new(void);
// Free scanner
void rack_au_scanner_free(RackAUScanner* scanner);
// Scan for plugins
// Returns number of plugins found (or would be found), or negative error code
//
// Two-pass usage pattern (recommended):
// 1. count = rack_au_scanner_scan(scanner, NULL, 0); // Get total count
// 2. rack_au_scanner_scan(scanner, array, count); // Fill array
//
// If plugins is NULL: Only counts plugins, does not extract details
// If plugins is not NULL: Fills array up to max_plugins
//
// IMPORTANT: Return value may exceed max_plugins if more plugins exist.
// Compare return value with max_plugins to detect truncation.
//
// plugins: output array (allocated by caller), or NULL to get count only
// max_plugins: size of output array (ignored if plugins is NULL)
int rack_au_scanner_scan(RackAUScanner* scanner, RackAUPluginInfo* plugins, size_t max_plugins);
// ============================================================================
// Plugin Instance API
// ============================================================================
// Create a new plugin instance from unique_id
RackAUPlugin* rack_au_plugin_new(const char* unique_id);
// Free plugin instance
void rack_au_plugin_free(RackAUPlugin* plugin);
// Initialize plugin
// Returns 0 on success, negative error code on failure
int rack_au_plugin_initialize(RackAUPlugin* plugin, double sample_rate, uint32_t max_block_size);
// Check if plugin is initialized
int rack_au_plugin_is_initialized(RackAUPlugin* plugin);
// Reset plugin state
// Clears all internal buffers, delay lines, and state without changing parameters.
// Useful for clearing reverb tails, delay lines, etc. between songs or after preset changes.
//
// Returns:
// 0 (RACK_AU_OK) on success
// RACK_AU_ERROR_NOT_INITIALIZED if plugin is not initialized
// RACK_AU_ERROR_AUDIO_UNIT + OSStatus if AudioUnitReset fails
//
// Thread-safety: Should be called from a non-realtime thread.
int rack_au_plugin_reset(RackAUPlugin* plugin);
// Get input channel count
// Returns number of input channels, or 0 if not initialized or query failed
// Thread-safety: Should be called after initialize()
int rack_au_plugin_get_input_channels(RackAUPlugin* plugin);
// Get output channel count
// Returns number of output channels, or 0 if not initialized or query failed
// Thread-safety: Should be called after initialize()
int rack_au_plugin_get_output_channels(RackAUPlugin* plugin);
// Process audio (planar format - one buffer per channel)
// Uses planar (non-interleaved) audio format matching AudioUnit internal format.
// This enables zero-copy processing in effect chains.
//
// inputs: array of input channel pointers (e.g., [left_ptr, right_ptr] for stereo)
// num_input_channels: number of input channels
// outputs: array of output channel pointers (e.g., [left_ptr, right_ptr] for stereo)
// num_output_channels: number of output channels
// frames: number of frames to process
//
// Channel Layout Examples:
// Mono: inputs = [mono_ptr], num_input_channels = 1
// Stereo: inputs = [left_ptr, right_ptr], num_input_channels = 2
// 5.1: inputs = [L, R, C, LFE, SL, SR], num_input_channels = 6
//
// Returns 0 on success, negative error code on failure
int rack_au_plugin_process(
RackAUPlugin* plugin,
const float* const* inputs,
uint32_t num_input_channels,
float* const* outputs,
uint32_t num_output_channels,
uint32_t frames
);
// Get parameter count
// Thread-safety: Read-only after initialization. Safe to call from any thread,
// but plugin instances should not be shared across threads (Send but not Sync).
int rack_au_plugin_parameter_count(RackAUPlugin* plugin);
// Get parameter value (normalized 0.0 to 1.0)
// Returns 0 on success, negative error code on failure
// Thread-safety: Can be called from any thread, but the same plugin instance
// must not be accessed concurrently. Parameter cache is read-only after init.
// Typical usage: one thread owns the plugin, calls from audio/UI threads are serialized.
int rack_au_plugin_get_parameter(RackAUPlugin* plugin, uint32_t index, float* value);
// Set parameter value (normalized 0.0 to 1.0)
// Returns 0 on success, negative error code on failure
// Thread-safety: Can be called from any thread, but the same plugin instance
// must not be accessed concurrently. Parameter cache is read-only after init.
// Note: Calling during audio processing may cause clicks/pops (AudioUnit internal behavior).
int rack_au_plugin_set_parameter(RackAUPlugin* plugin, uint32_t index, float value);
// Get parameter info
// name: output buffer for parameter name (allocated by caller)
// name_size: size of name buffer
// unit: output buffer for parameter unit string (allocated by caller, can be NULL)
// unit_size: size of unit buffer (ignored if unit is NULL)
// Returns 0 on success, negative error code on failure
int rack_au_plugin_parameter_info(
RackAUPlugin* plugin,
uint32_t index,
char* name,
size_t name_size,
float* min,
float* max,
float* default_value,
char* unit,
size_t unit_size
);
// ============================================================================
// Preset Management API
// ============================================================================
// Preset info struct
typedef struct {
char name[256];
int32_t preset_number;
} RackAUPresetInfo;
// Get factory preset count
// Returns number of factory presets, or 0 if plugin has no presets
// Thread-safety: Read-only after initialization. Safe to call from any thread.
int rack_au_plugin_get_preset_count(RackAUPlugin* plugin);
// Get preset info by index
// index: preset index (0 to preset_count - 1)
// name: output buffer for preset name (allocated by caller)
// name_size: size of name buffer
// preset_number: output parameter for preset number (used with load_preset)
// Returns 0 on success, negative error code on failure
int rack_au_plugin_get_preset_info(
RackAUPlugin* plugin,
uint32_t index,
char* name,
size_t name_size,
int32_t* preset_number
);
// Load a factory preset by preset number
// preset_number: the preset number from get_preset_info()
// Returns 0 on success, negative error code on failure
// Thread-safety: Should be called from the same thread that owns the plugin instance.
int rack_au_plugin_load_preset(RackAUPlugin* plugin, int32_t preset_number);
// Get plugin state size (for allocation)
// Returns size in bytes needed to store state, or 0 if state cannot be retrieved
// Thread-safety: Read-only after initialization. Safe to call from any thread.
int rack_au_plugin_get_state_size(RackAUPlugin* plugin);
// Get plugin state (full state including parameters, preset, etc.)
// data: output buffer for state data (allocated by caller)
// size: input/output - buffer size on input, actual size on output
// Returns 0 on success, negative error code on failure
// Thread-safety: Should be called from the same thread that owns the plugin instance.
// Typical usage: call get_state_size() first, allocate buffer, then call get_state()
int rack_au_plugin_get_state(RackAUPlugin* plugin, uint8_t* data, size_t* size);
// Set plugin state (restore full state including parameters, preset, etc.)
// data: state data (from previous get_state call)
// size: size of state data in bytes
// Returns 0 on success, negative error code on failure
// Thread-safety: Should be called from the same thread that owns the plugin instance.
int rack_au_plugin_set_state(RackAUPlugin* plugin, const uint8_t* data, size_t size);
// ============================================================================
// MIDI API
// ============================================================================
// MIDI event types
typedef enum {
RACK_AU_MIDI_NOTE_ON = 0x90,
RACK_AU_MIDI_NOTE_OFF = 0x80,
RACK_AU_MIDI_POLYPHONIC_AFTERTOUCH = 0xA0,
RACK_AU_MIDI_CONTROL_CHANGE = 0xB0,
RACK_AU_MIDI_PROGRAM_CHANGE = 0xC0,
RACK_AU_MIDI_CHANNEL_AFTERTOUCH = 0xD0,
RACK_AU_MIDI_PITCH_BEND = 0xE0,
// System messages (no channel)
RACK_AU_MIDI_SYSTEM_EXCLUSIVE = 0xF0,
RACK_AU_MIDI_TIME_CODE = 0xF1,
RACK_AU_MIDI_SONG_POSITION = 0xF2,
RACK_AU_MIDI_SONG_SELECT = 0xF3,
RACK_AU_MIDI_TUNE_REQUEST = 0xF6,
RACK_AU_MIDI_TIMING_CLOCK = 0xF8,
RACK_AU_MIDI_START = 0xFA,
RACK_AU_MIDI_CONTINUE = 0xFB,
RACK_AU_MIDI_STOP = 0xFC,
RACK_AU_MIDI_ACTIVE_SENSING = 0xFE,
RACK_AU_MIDI_SYSTEM_RESET = 0xFF,
} RackAUMidiEventType;
// MIDI event struct
typedef struct {
uint32_t sample_offset; // Sample offset within buffer
uint8_t status; // MIDI status byte
uint8_t data1; // First data byte (note/CC number)
uint8_t data2; // Second data byte (velocity/value)
uint8_t channel; // MIDI channel (0-15)
} RackAUMidiEvent;
// Send MIDI events to plugin
// events: array of MIDI events
// event_count: number of events in array
// Returns 0 on success, negative error code on failure
// Thread-safety: Should be called from the same thread that owns the plugin instance.
// Not safe to call concurrently with process() or other plugin operations.
int rack_au_plugin_send_midi(
RackAUPlugin* plugin,
const RackAUMidiEvent* events,
uint32_t event_count
);
// ============================================================================
// GUI API
// ============================================================================
// Callback type for async GUI creation
// user_data: user-provided data passed to rack_au_gui_create_async
// gui: created GUI handle, or NULL on error
// error_code: RACK_AU_OK on success, negative error code on failure
typedef void (*RackAUGuiCallback)(void* user_data, RackAUGui* gui, int error_code);
// Create GUI asynchronously
// Tries AUv3 (modern) → AUv2 (legacy) → generic parameter UI in order
// Callback is invoked on main thread when GUI is ready or creation fails
//
// Generic UI fallback:
// - Displays up to 20 parameters with sliders
// - Bidirectional: sliders update plugin parameters in real-time
// - Most plugins provide AUv3/AUv2 custom UIs with richer features
//
// IMPORTANT: This function must be called from the main thread
// The callback will also be invoked on the main thread
//
// plugin: plugin instance (must be initialized)
// callback: callback function to invoke when GUI is ready
// user_data: user data to pass to callback
//
// Thread-safety: Must be called from main thread. GUI operations are not thread-safe.
void rack_au_gui_create_async(
RackAUPlugin* plugin,
RackAUGuiCallback callback,
void* user_data
);
// Destroy GUI and clean up resources
// gui: GUI handle returned by rack_au_gui_create_async
// IMPORTANT: gui pointer becomes invalid immediately after this call
// Cleanup happens asynchronously on main thread
// Thread-safety: Should be called from main thread
void rack_au_gui_destroy(RackAUGui* gui);
// Get native NSView pointer for embedding in host UI
// Returns void* that can be cast to NSView* in Objective-C/Swift code
// gui: GUI handle
// Returns: NSView pointer as void*, or NULL if gui is invalid
// Thread-safety: Can be called from any thread (read-only operation)
void* rack_au_gui_get_view(RackAUGui* gui);
// Get view size
// gui: GUI handle
// width: output parameter for view width
// height: output parameter for view height
// Returns 0 on success, negative error code on failure
// Thread-safety: Can be called from any thread
int rack_au_gui_get_size(RackAUGui* gui, float* width, float* height);
// Create and show window with GUI
// Creates an NSWindow and displays the plugin GUI in it
// gui: GUI handle
// title: window title (or NULL for default "AudioUnit GUI")
// Returns 0 on success, negative error code on failure
// Thread-safety: Must be called from main thread
int rack_au_gui_show_window(RackAUGui* gui, const char* title);
// Hide window (without destroying GUI)
// gui: GUI handle
// Returns 0 on success, negative error code on failure
// Thread-safety: Must be called from main thread
int rack_au_gui_hide_window(RackAUGui* gui);
#ifdef __cplusplus
}
#endif
// ============================================================================
// Internal Helper (used by au_gui.mm)
// ============================================================================
#ifdef __OBJC__
// Get AudioComponentInstance from plugin (internal use only)
// Used by au_gui.mm to access the audio unit from opaque plugin handle
// Only available in Objective-C++ where AudioToolbox types are available
#include <AudioToolbox/AudioToolbox.h>
#ifdef __cplusplus
extern "C" {
#endif
AudioComponentInstance rack_au_plugin_get_audio_unit(RackAUPlugin* plugin);
#ifdef __cplusplus
}
#endif
#endif
#endif // RACK_AU_H