-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathcplug.h
More file actions
323 lines (274 loc) · 13 KB
/
Copy pathcplug.h
File metadata and controls
323 lines (274 loc) · 13 KB
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
/* Released into the public domain by Tré Dudman - 2024
* For licensing and more info see https://github.com/Tremus/CPLUG */
#ifndef CPLUG_H
#define CPLUG_H
#ifdef __cplusplus
extern "C" {
#endif
/* ██████╗██████╗ ██╗ ██╗ ██╗ ██████╗
██╔════╝██╔══██╗██║ ██║ ██║██╔════╝
██║ ██████╔╝██║ ██║ ██║██║ ███╗
██║ ██╔═══╝ ██║ ██║ ██║██║ ██║
╚██████╗██║ ███████╗╚██████╔╝╚██████╔╝
╚═════╝╚═╝ ╚══════╝ ╚═════╝ ╚═════╝ */
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#ifdef CPLUG_SHARED
#ifdef _WIN32
#define CPLUG_API __declspec(dllexport)
#else
#define CPLUG_API __attribute__((visibility("default")))
#endif
#else
#define CPLUG_API
#endif // CPLUG_SHARED
#ifndef CPLUG_EVENT_QUEUE_SIZE
#define CPLUG_EVENT_QUEUE_SIZE 256
#endif
typedef union CplugEvent CplugEvent;
typedef struct CplugHostContext CplugHostContext;
typedef struct CplugProcessContext CplugProcessContext;
CPLUG_API void cplug_libraryLoad();
CPLUG_API void cplug_libraryUnload();
enum
{
CPLUG_PLUGIN_IS_STANDALONE,
CPLUG_PLUGIN_IS_CLAP,
CPLUG_PLUGIN_IS_VST3,
CPLUG_PLUGIN_IS_AUV2,
};
enum
{
CPLUG_FLAG_RESCAN_LATENCY = 1 << 0,
CPLUG_FLAG_RESCAN_TAIL_TIME = 1 << 1,
CPLUG_FLAG_RESCAN_BUS_COUNT = 1 << 2,
CPLUG_FLAG_RESCAN_BUS_NAMES = 1 << 3,
CPLUG_FLAG_RESCAN_PARAM_VALUES = 1 << 4,
CPLUG_FLAG_RESCAN_PARAM_NAMES = 1 << 5,
CPLUG_FLAG_RESCAN_PARAM_METADATA = 1 << 6,
};
struct CplugHostContext
{
uint32_t type; // CPLUG_PLUGIN_IS_XXX
// VST3 & AUv2 only, UI thread only.
void (*sendParamEvent)(CplugHostContext* ctx, const CplugEvent*);
void (*rescan)(CplugHostContext* ctx, uint32_t flags /* CPLUG_RESCAN_XXX */);
bool (*getHostName)(CplugHostContext* ctx, char* buf, size_t buflen);
// Ask permission from host to resize your area to specified w/h within their window
bool (*requestResize)(CplugHostContext* ctx, uint32_t width, uint32_t height);
};
CPLUG_API void* cplug_createPlugin(CplugHostContext* ctx);
CPLUG_API void cplug_destroyPlugin(void*);
CPLUG_API uint32_t cplug_getNumInputBusses(void*);
CPLUG_API uint32_t cplug_getNumOutputBusses(void*);
CPLUG_API uint32_t cplug_getInputBusChannelCount(void*, uint32_t bus_idx);
CPLUG_API uint32_t cplug_getOutputBusChannelCount(void*, uint32_t bus_idx);
// Copy UTF8 name to this buffer, including null terminating byte.
// NOTE: VST3 uses UTF16 strings with a max length of 128 characters. Cplug the handles UTF16<->UTF8 conversion
// CLAP has a max length of 256 bytes, AUv2 no limit (mandatory CFString), both are UTF8
CPLUG_API void cplug_getInputBusName(void*, uint32_t idx, char* buf, size_t buflen);
CPLUG_API void cplug_getOutputBusName(void*, uint32_t idx, char* buf, size_t buflen);
CPLUG_API uint32_t cplug_getLatencyInSamples(void*);
CPLUG_API uint32_t cplug_getTailInSamples(void*);
CPLUG_API void cplug_setSampleRateAndBlockSize(void*, double sampleRate, uint32_t maxBlockSize);
enum
{
CPLUG_EVENT_UNHANDLED_EVENT, // Empty event for unimplemented features. Do nothing
CPLUG_EVENT_PROCESS_AUDIO,
CPLUG_EVENT_PARAM_CHANGE_BEGIN,
CPLUG_EVENT_PARAM_CHANGE_UPDATE,
CPLUG_EVENT_PARAM_CHANGE_END,
CPLUG_EVENT_MIDI,
// CPLUG_EVENT_NOTE_EXPRESSION_VOLUME,
// CPLUG_EVENT_NOTE_EXPRESSION_PAN,
CPLUG_EVENT_NOTE_EXPRESSION_TUNING,
// CPLUG_EVENT_NOTE_EXPRESSION_VIBRATO,
// CPLUG_EVENT_NOTE_EXPRESSION_EXPRESSION, // similar to an expression pedal (MIDI CC 11), except polyphonic
// CPLUG_EVENT_NOTE_EXPRESSION_BRIGHTNESS,
};
union CplugEvent
{
uint32_t type; // CPLUG_EVENT_XXX
struct
{
uint32_t type;
uint32_t endFrame;
} processAudio;
struct
{
uint32_t type;
uint32_t id;
double value;
} parameter;
struct
{
uint32_t type;
uint32_t frame;
union
{
struct
{
uint8_t status;
uint8_t data1;
uint8_t data2;
};
uint8_t bytes[4];
uint32_t bytesAsInt;
};
} midi;
struct
{
uint32_t type;
int32_t key; // 0-127
double value;
} note_expression;
};
enum
{
CPLUG_FLAG_TRANSPORT_IS_PLAYING = 1 << 0,
CPLUG_FLAG_TRANSPORT_IS_LOOPING = 1 << 1,
CPLUG_FLAG_TRANSPORT_IS_RECORDING = 1 << 2,
CPLUG_FLAG_TRANSPORT_HAS_BPM = 1 << 3,
CPLUG_FLAG_TRANSPORT_HAS_TIME_SIGNATURE = 1 << 4,
CPLUG_FLAG_TRANSPORT_HAS_PLAYHEAD_BEATS = 1 << 5,
};
struct CplugProcessContext
{
uint32_t numFrames;
uint32_t numInputs;
uint32_t numOutputs;
uint32_t flags; // CPLUG_FLAG_TRANSPORT_XXX
double bpm;
double playheadBeats;
double loopStartBeats;
double loopEndBeats;
uint32_t timeSigNumerator;
uint32_t timeSigDenominator;
bool (*enqueueEvent)(CplugProcessContext* ctx, const CplugEvent*, uint32_t frameIdx);
bool (*dequeueEvent)(CplugProcessContext* ctx, CplugEvent*, uint32_t frameIdx);
float** (*getAudioInput)(const CplugProcessContext* ctx, uint32_t busIdx);
float** (*getAudioOutput)(const CplugProcessContext* ctx, uint32_t busIdx);
};
CPLUG_API void cplug_process(void* userPlugin, CplugProcessContext* ctx);
enum
{
// All formats
CPLUG_FLAG_PARAMETER_IS_READ_ONLY = 1 << 0,
CPLUG_FLAG_PARAMETER_IS_BOOL = 1 << 1,
// (VST3 | CLAP)
CPLUG_FLAG_PARAMETER_IS_INTEGER = 1 << 2,
CPLUG_FLAG_PARAMETER_IS_HIDDEN = 1 << 3, // Internal param, hidden from the DAWs GUI
CPLUG_FLAG_PARAMETER_IS_AUTOMATABLE = 1 << 4, // Parameter is automatable by the host
CPLUG_FLAG_PARAMETER_IS_BYPASS = 1 << 5
};
CPLUG_API uint32_t cplug_getNumParameters(void*);
CPLUG_API uint32_t cplug_getParameterID(void*, uint32_t paramIndex);
CPLUG_API uint32_t cplug_getParameterFlags(void*, uint32_t paramId); // CPLUG_FLAG_PARAMETER_XXX
CPLUG_API void cplug_getParameterRange(void*, uint32_t paramId, double* min, double* max);
// NOTE: AUv2 supports a max length of 52 bytes, VST3 128, CLAP 256
CPLUG_API void cplug_getParameterName(void*, uint32_t paramId, char* buf, size_t buflen);
CPLUG_API double cplug_getParameterValue(void*, uint32_t paramId);
CPLUG_API double cplug_getDefaultParameterValue(void*, uint32_t paramId);
// [hopefully audio thread] VST3 & AU only
CPLUG_API void cplug_setParameterValue(void*, uint32_t paramId, double value);
// VST3 only
CPLUG_API double cplug_denormaliseParameterValue(void*, uint32_t paramId, double value);
CPLUG_API double cplug_normaliseParameterValue(void*, uint32_t paramId, double value);
CPLUG_API double cplug_parameterStringToValue(void*, uint32_t paramId, const char*);
CPLUG_API void cplug_parameterValueToString(void*, uint32_t paramId, char* buf, size_t bufsize, double value);
// Returns -1 on error and 'numBytesToWrite' on success
typedef int64_t (*cplug_writeProc)(const void* stateCtx, void* writePos, size_t numBytesToWrite);
CPLUG_API void cplug_saveState(void* userPlugin, const void* stateCtx, cplug_writeProc writeProc);
// Returns 0 if all bytes are read, -1 on error, and 'maxBytesToRead' when there are remaining bytes to read
typedef int64_t (*cplug_readProc)(const void* stateCtx, void* readPos, size_t maxBytesToRead);
CPLUG_API void cplug_loadState(void* userPlugin, const void* stateCtx, cplug_readProc readProc);
// AUv2 hacks. Unfortunately Apple's API designs are offensive leaky abstractions.
enum
{
kAudioUnitProperty_UserPlugin = 'plug',
kAudioUnitProperty_CplugProcessContext = kAudioUnitProperty_UserPlugin + 1,
CPLUG_AUV2_OFFSET_PROCESS_CONTEXT = 64,
CPLUG_AUV2_OFFSET_NSVIEW = CPLUG_AUV2_OFFSET_PROCESS_CONTEXT + sizeof(CplugHostContext),
};
// NOTE: For AUv2, your pointer MUST be castable to NSView. AUv2 hosts expect an NSView & you simply override methods
// This is the only CPLUG method used in AUv2 builds.
CPLUG_API void* cplug_createGUI(void* userPlugin);
CPLUG_API void cplug_destroyGUI(void* userGUI);
// If not NULL, set your window/view as a child/subview. If NULL, remove from parent/superview.
// This is a good place to init/deinit your GFX and timer. Be prepared for this to be called multiple times with NULL
CPLUG_API void cplug_setParent(void* userGUI, void* hwnd_or_nsview);
// CLAP only. VST3 simply create/destroy your window.
CPLUG_API void cplug_setVisible(void* userGUI, bool visible);
CPLUG_API void cplug_setScaleFactor(void* userGUI, float scale);
CPLUG_API void cplug_getSize(void* userGUI, uint32_t* width, uint32_t* height);
// Host is trying to resize, but giving you the chance to overwrite their size
CPLUG_API void cplug_checkSize(void* userGUI, uint32_t* width, uint32_t* height);
// Host tells your plugin "this is your size"
CPLUG_API bool cplug_setSize(void* userGUI, uint32_t width, uint32_t height);
/* ██╗ ██╗████████╗██╗██╗ ███████╗
██║ ██║╚══██╔══╝██║██║ ██╔════╝
██║ ██║ ██║ ██║██║ ███████╗
██║ ██║ ██║ ██║██║ ╚════██║
╚██████╔╝ ██║ ██║███████╗███████║
╚═════╝ ╚═╝ ╚═╝╚══════╝╚══════╝*/
// clang-format off
typedef volatile int cplug_atomic_i32;
#if defined(_MSC_VER) && ! (__clang__)
extern long _InterlockedExchange(long volatile *Target, long Value);
extern long _InterlockedCompareExchange(long volatile *Destination, long ExChange, long Comperand);
extern long _InterlockedExchangeAdd(long volatile *Addend, long Value);
extern long _InterlockedAnd(long volatile *Addend, long Value);
static inline int cplug_atomic_exchange_i32 ( cplug_atomic_i32* ptr, int v) { return _InterlockedExchange ((volatile long*)ptr, v); }
static inline int cplug_atomic_load_i32(const cplug_atomic_i32* ptr) { return _InterlockedCompareExchange((volatile long*)ptr, 0, 0); }
static inline int cplug_atomic_fetch_add_i32( cplug_atomic_i32* ptr, int v) { return _InterlockedExchangeAdd ((volatile long*)ptr, v); }
static inline int cplug_atomic_fetch_and_i32( cplug_atomic_i32* ptr, int v) { return _InterlockedAnd ((volatile long*)ptr, v); }
#else
static inline int cplug_atomic_exchange_i32 ( cplug_atomic_i32* ptr, int v) { return __atomic_exchange_n(ptr, v, __ATOMIC_SEQ_CST); }
static inline int cplug_atomic_load_i32(const cplug_atomic_i32* ptr) { return __atomic_load_n (ptr, __ATOMIC_SEQ_CST); }
static inline int cplug_atomic_fetch_add_i32( cplug_atomic_i32* ptr, int v) { return __atomic_fetch_add (ptr, v, __ATOMIC_SEQ_CST); }
static inline int cplug_atomic_fetch_and_i32( cplug_atomic_i32* ptr, int v) { return __atomic_fetch_and (ptr, v, __ATOMIC_SEQ_CST); }
#endif
/* ██████╗ ███████╗██████╗ ██╗ ██╗ ██████╗
██╔══██╗██╔════╝██╔══██╗██║ ██║██╔════╝
██║ ██║█████╗ ██████╔╝██║ ██║██║ ███╗
██║ ██║██╔══╝ ██╔══██╗██║ ██║██║ ██║
██████╔╝███████╗██████╔╝╚██████╔╝╚██████╔╝
╚═════╝ ╚══════╝╚═════╝ ╚═════╝ ╚═════╝*/
#if !defined(__cplusplus) && !defined(_MSC_VER) && !defined(static_assert)
#define static_assert _Static_assert
#endif
#ifndef cplug_log
#ifdef NDEBUG
#define cplug_log(...)
#else
#include <stdarg.h>
#include <stdio.h>
static inline void cplug_printf(const char* const fmt, ...)
{
va_list args;
va_start(args, fmt);
// When debugging in a host, consider adding: freopen(".../Desktop/log.txt", "a", stderr);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
}
#define cplug_log cplug_printf
#endif // NDEBUG
#endif // cplug_log
#ifndef CPLUG_LOG_ASSERT
#ifdef NDEBUG
#define CPLUG_LOG_ASSERT(...)
#else
#define CPLUG_LOG_ASSERT(cond) if (!(cond)) { cplug_log("FAIL ASSERT: " #cond " - %s:%d", __FILE__, __LINE__); }
#endif // NDEBUG
#endif // CPLUG_LOG_ASSERT
#define CPLUG_LOG_ASSERT_RETURN(cond, ret) \
CPLUG_LOG_ASSERT(cond) \
if (!(cond)) return ret;
// clang-format on
#ifdef __cplusplus
}
#endif
#endif // !CPLUG_H