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
#ifndef RACK_VST3_H
#define RACK_VST3_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
// Opaque types
typedef struct RackVST3Scanner RackVST3Scanner;
typedef struct RackVST3Plugin RackVST3Plugin;
typedef struct RackVST3Gui RackVST3Gui;
// Plugin type enum
typedef enum {
RACK_VST3_TYPE_EFFECT = 0,
RACK_VST3_TYPE_INSTRUMENT = 1,
RACK_VST3_TYPE_ANALYZER = 2,
RACK_VST3_TYPE_SPATIAL = 3,
RACK_VST3_TYPE_OTHER = 4,
} RackVST3PluginType;
// Plugin info struct (passed to Rust)
typedef struct {
char name[256];
char manufacturer[256];
char path[1024];
char unique_id[64]; // VST3 uses UID (16 bytes as hex string)
uint32_t version;
RackVST3PluginType plugin_type;
char category[128]; // VST3 subcategories (e.g., "Fx|Reverb")
} RackVST3PluginInfo;
// Error codes (0 = success, negative = error)
#define RACK_VST3_OK 0
#define RACK_VST3_ERROR_GENERIC -1
#define RACK_VST3_ERROR_NOT_FOUND -2
#define RACK_VST3_ERROR_INVALID_PARAM -3
#define RACK_VST3_ERROR_NOT_INITIALIZED -4
#define RACK_VST3_ERROR_LOAD_FAILED -5
#define RACK_VST3_ERROR_NOT_SUPPORTED -6 // Feature not supported by this plugin
// ============================================================================
// Scanner API
// ============================================================================
// Create a new scanner
// Returns NULL if allocation fails
RackVST3Scanner* rack_vst3_scanner_new(void);
// Free scanner
void rack_vst3_scanner_free(RackVST3Scanner* scanner);
// Add a search path for VST3 plugins
// Returns 0 on success, negative error code on failure
int rack_vst3_scanner_add_path(RackVST3Scanner* scanner, const char* path);
// Add system default VST3 search paths
// Returns 0 on success, negative error code on failure
int rack_vst3_scanner_add_default_paths(RackVST3Scanner* 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_vst3_scanner_scan(scanner, NULL, 0); // Get total count
// 2. rack_vst3_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_vst3_scanner_scan(RackVST3Scanner* scanner, RackVST3PluginInfo* plugins, size_t max_plugins);
// ============================================================================
// Plugin Instance API
// ============================================================================
// Create a new plugin instance from path and UID
// path: path to .vst3 bundle/folder
// uid: plugin UID (from scan result)
// Returns plugin instance or NULL on error
RackVST3Plugin* rack_vst3_plugin_new(const char* path, const char* uid);
// Free plugin instance
void rack_vst3_plugin_free(RackVST3Plugin* plugin);
// Initialize plugin
// Returns 0 on success, negative error code on failure
int rack_vst3_plugin_initialize(RackVST3Plugin* plugin, double sample_rate, uint32_t max_block_size);
// Check if plugin is initialized
int rack_vst3_plugin_is_initialized(RackVST3Plugin* 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_VST3_OK) on success
// RACK_VST3_ERROR_NOT_INITIALIZED if plugin is not initialized
// negative error code on failure
//
// Thread-safety: Should be called from a non-realtime thread.
int rack_vst3_plugin_reset(RackVST3Plugin* 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_vst3_plugin_get_input_channels(RackVST3Plugin* 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_vst3_plugin_get_output_channels(RackVST3Plugin* plugin);
// Process audio (planar format - one buffer per channel)
// Uses planar (non-interleaved) audio format matching VST3 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_vst3_plugin_process(
RackVST3Plugin* 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.
int rack_vst3_plugin_parameter_count(RackVST3Plugin* 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.
int rack_vst3_plugin_get_parameter(RackVST3Plugin* 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.
// Note: Calling during audio processing may cause clicks/pops.
int rack_vst3_plugin_set_parameter(RackVST3Plugin* 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_vst3_plugin_parameter_info(
RackVST3Plugin* 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;
} RackVST3PresetInfo;
// 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_vst3_plugin_get_preset_count(RackVST3Plugin* 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_vst3_plugin_get_preset_info(
RackVST3Plugin* 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_vst3_plugin_load_preset(RackVST3Plugin* plugin, int32_t preset_number);
// Get plugin state size (for allocation)
// Returns actual size in bytes needed to store state, or 0 if state cannot be retrieved
//
// NOTE: This function determines the actual state size by serializing the state.
// It is not a constant or estimate - it queries the plugin's current state.
// For large plugins (samplers, complex synths), this may take some time.
// The returned size is accurate for immediate use with get_state().
//
// Thread-safety: Should be called from the same thread that owns the plugin instance.
int rack_vst3_plugin_get_state_size(RackVST3Plugin* 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.
int rack_vst3_plugin_get_state(RackVST3Plugin* 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_vst3_plugin_set_state(RackVST3Plugin* plugin, const uint8_t* data, size_t size);
// ============================================================================
// MIDI API
// ============================================================================
// MIDI event types (matches VST3 event types)
typedef enum {
RACK_VST3_MIDI_NOTE_ON = 0x90,
RACK_VST3_MIDI_NOTE_OFF = 0x80,
RACK_VST3_MIDI_POLYPHONIC_AFTERTOUCH = 0xA0,
RACK_VST3_MIDI_CONTROL_CHANGE = 0xB0,
RACK_VST3_MIDI_PROGRAM_CHANGE = 0xC0,
RACK_VST3_MIDI_CHANNEL_AFTERTOUCH = 0xD0,
RACK_VST3_MIDI_PITCH_BEND = 0xE0,
} RackVST3MidiEventType;
// 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)
} RackVST3MidiEvent;
// Send MIDI events to plugin
// events: array of MIDI events
// event_count: number of events in array
//
// NOTE: VST3 has native support for Note On/Off, Polyphonic Aftertouch, and Control Change.
// Program Change, Channel Aftertouch, and Pitch Bend use custom encoding via
// LegacyMIDICCOutEvent with controlNumber >= 0x80. Not all VST3 plugins support
// these non-native event types. If a plugin doesn't respond to Program Change,
// Channel Aftertouch, or Pitch Bend, it's a limitation of the plugin itself.
//
// 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_vst3_plugin_send_midi(
RackVST3Plugin* plugin,
const RackVST3MidiEvent* events,
uint32_t event_count
);
#ifdef __cplusplus
}
#endif
#endif // RACK_VST3_H