-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlv2-ecl.c
343 lines (275 loc) · 8.57 KB
/
lv2-ecl.c
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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ecl/ecl.h>
#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
#include "lv2-ecl-internal.h"
// move to lisp side.
#define AMP_URI "http://lv2plug.in/plugins/eg-amp"
static DescAssoc g_plassoc[NUM_DESCRIPTORS];
static HandleDescAssoc g_hdassoc[NUM_INSTANCES];
static int g_plassoc_initialized = FALSE;
static int g_hdassoc_initialized = FALSE;
static int g_cl_booted = FALSE;
// Take a string, and return a malloced string which is what was passed in,
// but with the literal " " quotes around it. You must free the memory
// when done with it.
static char* stringify(const char *string)
{
char *str = NULL;
str = calloc(strlen(string) + 2, sizeof(char));
strcpy(str, "\"");
strcat(str, string);
strcat(str, "\"");
return str;
}
static void initialize_plugin_internals(void)
{
int i;
if (g_plassoc_initialized == FALSE) {
for (i = 0; i < NUM_DESCRIPTORS; i++) {
g_plassoc[i].initialized = FALSE;
// Each desc always points to the same C functions in this api.
// We do this because we inverse delegate to a lisp object which
// should handle this object.
g_plassoc[i].lv2_desc.instantiate = instantiate;
g_plassoc[i].lv2_desc.connect_port = connect_port;
g_plassoc[i].lv2_desc.activate = activate;
g_plassoc[i].lv2_desc.run = run;
g_plassoc[i].lv2_desc.deactivate = deactivate;
g_plassoc[i].lv2_desc.cleanup = cleanup;
g_plassoc[i].lv2_desc.extension_data = extension_data;
}
g_plassoc_initialized = TRUE;
}
if (g_hdassoc_initialized == FALSE) {
for (i = 0; i < NUM_INSTANCES; i++) {
g_hdassoc[i].initialized = FALSE;
g_hdassoc[i].lv2_desc_index = NONE;
// initialize the handle to a unique value, it doesn't matter what
// the bit pattern is here, only that they are unique from all the
// rest. Yes, I know (void*)0 is NULL, but it doesn't matter.
g_hdassoc[i].handle = (void*)i;
}
g_hdassoc_initialized = TRUE;
}
}
static void initialize_ecl(void)
{
int fake_argc = 1;
char *fake_argv[] = {"InternalPlugin", NULL};
if (g_cl_booted == FALSE) {
// Initialize ECL
cl_boot(fake_argc, fake_argv);
// Use the UFFI package before we do anything.
cl_use_package(1, (cl_find_package(ecl_make_keyword("UFFI"))));
// Initalize the Common Lisp plugin library.
// Magic name is previously known
extern void I_libfoo(cl_object);
read_VV(OBJNULL, I_libfoo);
g_cl_booted = TRUE;
}
}
// ///////////////////////////////////////////////////////////////////////
// Stuff to deal with the g_plassoc array
// ///////////////////////////////////////////////////////////////////////
// search the g_plassoc table and return me an index into the
// AssocDesc association array, or
//
static int da_allocate(void)
{
int i;
for (i = 0; i < NUM_DESCRIPTORS; i++) {
if (g_plassoc[i].initialized == FALSE) {
g_plassoc[i].initialized = TRUE;
return i;
}
}
return NONE;
}
static void da_associate(int index, cl_object lisp_obj)
{
/* XXX bitwise copy, can I do that to a cl_object correctly? */
g_plassoc[index].lisp_lv2_desc = lisp_obj;
}
// given an index, return the address of the lv2 descriptor from there.
static LV2_Descriptor* da_get_address(int index)
{
return &g_plassoc[index].lv2_desc;
}
// given a lv2_desc pointer, return the index associated with it.
static int da_get_index(const LV2_Descriptor *lv2_desc)
{
int i;
for (i = 0; i < NUM_DESCRIPTORS; i++) {
if (&g_plassoc[i].lv2_desc == lv2_desc) {
return i;
}
}
return NONE;
}
static DescAssoc* da_get_da(int index)
{
return &g_plassoc[index];
}
// ///////////////////////////////////////////////////////////////////////
// Stuff to deal with g_hdassoc array
// ///////////////////////////////////////////////////////////////////////
// return an index to a usable HandleDescAssoc structure.
static int hda_allocate(void)
{
int i;
for (i = 0; i < NUM_INSTANCES; i++) {
if (g_hdassoc[i].initialized == FALSE) {
g_hdassoc[i].initialized = TRUE;
return i;
}
}
return NONE;
}
// This is so when the host give us just a handle, we can figure out which
// lv2_descriptor's instantiate method created it so we can trampoline it
// to the correct lisp function.
static void hda_associate(int lv2_handle_index,
int lv2_desc_index, cl_object lisp_handle)
{
HandleDescAssoc *hda = NULL;
hda = &g_hdassoc[lv2_handle_index];
hda->lv2_desc_index = lv2_desc_index;
hda->lisp_handle = lisp_handle;
}
// This is the thing we end up giving back to the host which we use to
// identify the instance and its association later.
static LV2_Handle hda_get_address(int index)
{
return g_hdassoc[index].handle;
}
static HandleDescAssoc* hda_get_hda(int index)
{
return &g_hdassoc[index];
}
// ///////////////////////////////////////////////////////////////////////
// C LV2 entry point first called by the host.
// ///////////////////////////////////////////////////////////////////////
const LV2_Descriptor*
lv2_descriptor(uint32_t index)
{
int lv2_desc_index;
initialize_plugin_internals();
initialize_ecl();
// Trampoline this request to the ECL side, then unpack the resultant
// return value into something suitable for the caller.
cl_object obj =
cl_funcall(2, c_string_to_object("lv2-descriptor"),
MAKE_FIXNUM(index));
printf("C: lv2_descriptor got from lisp:");
cl_pprint(1, obj);
cl_princ(1, c_string_to_object("#\\Newline"));
if (obj == Cnil) {
// Don't make an association in this case.
return NULL;
}
// Associate the lisp description with a C address of a real LV2_Descriptor
lv2_desc_index = da_allocate();
da_associate(lv2_desc_index, obj);
// The application will use this to talk about this specific plugin.
return da_get_address(lv2_desc_index);
}
// Create a new plugin instance.
static LV2_Handle
instantiate(const LV2_Descriptor* descriptor,
double rate,
const char* bundle_path,
const LV2_Feature* const* features)
{
char *real_string = NULL;
int lv2_desc_index;
int lv2_handle_index;
DescAssoc *da = NULL;
LV2_Handle ret;
// lookup the DescAssoc with this descripter pointer
lv2_desc_index = da_get_index(descriptor);
da = da_get_da(lv2_desc_index);
printf("C: instantiate() for LV2_Desc called:\n"
"\tlv2_desc_index = %d\n"
"\tdescriptor = %p\n"
"\trate = %f\n"
"\tbundle path = %s\n"
"\tfeatures array = %p\n",
lv2_desc_index, descriptor, rate, bundle_path, features);
// Call the mirrored lisp instantiate function from the associated
// lisp version of the lv2-descriptor. This is going to return a
// cl_object that represents an LV2_Handle.
// first, get the function we need to call from the right lisp lv2
// descriptor.
cl_object lisp_instantiate_function =
cl_funcall(2,
c_string_to_object("lv2-instantiate"),
da->lisp_lv2_desc);
// Then call it, passing the usual stuff. lisp will return to us the
// handle it wants back.
real_string = stringify(bundle_path);
cl_object lisp_handle =
cl_funcall(5,
lisp_instantiate_function,
da->lisp_lv2_desc,
ecl_make_double_float(rate),
c_string_to_object(real_string),
Cnil);
free(real_string);
// Associate the lisp_handle with a duck handle we're going to give to the
// host.
lv2_handle_index = hda_allocate();
hda_associate(lv2_handle_index,
lv2_desc_index, lisp_handle);
// Then, return the fake LV2_Handle to the host.
ret = hda_get_address(lv2_handle_index);
return ret;
}
// Connect a port to a buffer (audio thread, must be RT safe).
static void
connect_port(LV2_Handle instance,
uint32_t port,
void* data)
{
int lv2_handle_index;
HandleDescAssoc *handle_assoc = NULL;
ecl_import_current_thread(ECL_NIL, ECL_NIL);
// reverse map the handle back to a pointer to the HandleDesc
// handle_assoc = get_handle_desc_address(instance);
// stuff
ecl_release_current_thread();
}
// Initialise and prepare the plugin instance for running.
static void
activate(LV2_Handle instance)
{
// Nothing to do here in this trivial mostly stateless plugin.
}
// Process a block of audio (audio thread, must be RT safe).
static void
run(LV2_Handle instance, uint32_t n_samples)
{
ecl_import_current_thread(ECL_NIL, ECL_NIL);
// Do something
ecl_release_current_thread();
}
// Finish running (counterpart to activate()).
static void
deactivate(LV2_Handle instance)
{
// Nothing to do here in this trivial mostly stateless plugin.
}
// Destroy a plugin instance (counterpart to instantiate()).
static void
cleanup(LV2_Handle instance)
{
// do something.
}
// Return extension data provided by the plugin.
static const void*
extension_data(const char* uri)
{
// This plugin has no extension data.
return NULL;
}