1 /*
2 * Copyright (c) 2015-2021 Hanspeter Portner (dev@open-music-kontrollers.ch)
3 *
4 * This is free software: you can redistribute it and/or modify
5 * it under the terms of the Artistic License 2.0 as published by
6 * The Perl Foundation.
7 *
8 * This source is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * Artistic License 2.0 for more details.
12 *
13 * You should have received a copy of the Artistic License 2.0
14 * along the source as a COPYING file. If not, obtain it from
15 * http://www.perlfoundation.org/artistic_license_2_0.
16 */
17
18 #include <moony.h>
19 #include <api_atom.h>
20 #include <api_forge.h>
21 #include <lock_stash.h>
22
23 #include <lauxlib.h>
24
25 typedef struct _Handle Handle;
26
27 struct _Handle {
28 moony_t moony;
29 bool once;
30
31 unsigned max_val;
32
33 uint32_t sample_count;
34 const LV2_Atom_Sequence *event_in [4];
35 LV2_Atom_Sequence *event_out [4];
36
37 const LV2_Atom_Sequence *control;
38 LV2_Atom_Sequence *notify;
39
40 LV2_Atom_Forge forge [4];
41
42 lock_stash_t stash [5];
43 bool stashed;
44 uint32_t stash_nsamples;
45 };
46
47 __non_realtime static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)48 instantiate(const LV2_Descriptor* descriptor, double rate, const char *bundle_path, const LV2_Feature *const *features)
49 {
50 Handle *handle = (Handle *)calloc(1, sizeof(Handle));
51 if(!handle)
52 return NULL;
53 mlock(handle, sizeof(Handle));
54
55 if(moony_init(&handle->moony, descriptor->URI, rate, features, 0x80000, false)) // 512 KB initial memory
56 {
57 free(handle);
58 return NULL;
59 }
60 moony_vm_nrt_enter(handle->moony.vm);
61 moony_open(&handle->moony, handle->moony.vm, moony_current(&handle->moony));
62 moony_vm_nrt_leave(handle->moony.vm);
63
64 if(!strcmp(descriptor->URI, MOONY_A1XA1_URI))
65 handle->max_val = 1;
66 else if(!strcmp(descriptor->URI, MOONY_A2XA2_URI))
67 handle->max_val = 2;
68 else if(!strcmp(descriptor->URI, MOONY_A4XA4_URI))
69 handle->max_val = 4;
70 else
71 handle->max_val = 1;
72
73 for(unsigned i=0; i<handle->max_val; i++)
74 lv2_atom_forge_init(&handle->forge[i], handle->moony.map);
75
76 for(unsigned i=0; i<handle->max_val + 1; i++)
77 {
78 _stash_init(&handle->stash[i], handle->moony.map);
79 _stash_reset(&handle->stash[i]);
80 }
81
82 return handle;
83 }
84
85 __realtime static void
connect_port(LV2_Handle instance,uint32_t port,void * data)86 connect_port(LV2_Handle instance, uint32_t port, void *data)
87 {
88 Handle *handle = (Handle *)instance;
89
90 if(port < handle->max_val)
91 handle->event_in[port] = (const LV2_Atom_Sequence *)data;
92 else if(port < handle->max_val*2)
93 handle->event_out[port - handle->max_val] = (LV2_Atom_Sequence *)data;
94 else if(port == handle->max_val*2)
95 handle->control = (const LV2_Atom_Sequence *)data;
96 else if(port == handle->max_val*2 + 1)
97 handle->notify = (LV2_Atom_Sequence *)data;
98 }
99
100 __realtime static inline void
_run_period(lua_State * L,const char * cmd,Handle * handle,uint32_t nsamples,const LV2_Atom_Sequence ** event_in,const LV2_Atom_Sequence * control)101 _run_period(lua_State *L, const char *cmd, Handle *handle, uint32_t nsamples,
102 const LV2_Atom_Sequence **event_in, const LV2_Atom_Sequence *control)
103 {
104 if(lua_getglobal(L, cmd) != LUA_TNIL)
105 {
106 lua_pushinteger(L, nsamples);
107
108 // push control / notify pair
109 {
110 latom_t *latom = moony_newuserdata(L, &handle->moony, MOONY_UDATA_ATOM, true);
111 latom->atom = (const LV2_Atom *)control;
112 latom->body.raw = LV2_ATOM_BODY_CONST(latom->atom);
113
114 lforge_t *lforge = moony_newuserdata(L, &handle->moony, MOONY_UDATA_FORGE, true);
115 lforge->depth = 0;
116 lforge->last.frames = 0;
117 lforge->forge = &handle->moony.notify_forge;
118 }
119
120 // push sequence / forge pair(s)
121 for(unsigned i=0; i<handle->max_val; i++)
122 {
123 latom_t *latom = moony_newuserdata(L, &handle->moony, MOONY_UDATA_ATOM, true);
124 latom->atom = (const LV2_Atom *)event_in[i];
125 latom->body.raw = LV2_ATOM_BODY_CONST(latom->atom);
126
127 lforge_t *lforge = moony_newuserdata(L, &handle->moony, MOONY_UDATA_FORGE, true);
128 lforge->depth = 0;
129 lforge->last.frames = 0;
130 lforge->forge = &handle->forge[i];
131 }
132
133 lua_call(L, 1 + 2*handle->max_val + 2, 0);
134 }
135 }
136
137 __realtime static int
_run(lua_State * L)138 _run(lua_State *L)
139 {
140 Handle *handle = lua_touserdata(L, lua_upvalueindex(1));
141
142 // apply stash, if any
143 if(handle->stashed)
144 {
145 const LV2_Atom_Sequence *event_in [4] = {
146 [0] = &handle->stash[0].seq,
147 [1] = &handle->stash[1].seq,
148 [2] = &handle->stash[2].seq,
149 [3] = &handle->stash[3].seq
150 };
151 const LV2_Atom_Sequence *control = &handle->stash[handle->max_val].seq;
152
153 _run_period(L, "run", handle, handle->stash_nsamples, event_in, control);
154
155 for(unsigned i=0; i<handle->max_val; i++)
156 {
157 LV2_ATOM_SEQUENCE_FOREACH(handle->event_out[i], ev)
158 ev->time.frames = 0; // overwrite time stamps
159 }
160 LV2_ATOM_SEQUENCE_FOREACH(handle->notify, ev)
161 ev->time.frames = 0; // overwrite time stamps
162 }
163
164 if(handle->once)
165 {
166 _run_period(L, "once", handle, handle->sample_count, handle->event_in, handle->control);
167 handle->once = false;
168 }
169
170 _run_period(L, "run", handle, handle->sample_count, handle->event_in, handle->control);
171
172 return 0;
173 }
174
175 __realtime static void
run(LV2_Handle instance,uint32_t nsamples)176 run(LV2_Handle instance, uint32_t nsamples)
177 {
178 Handle *handle = (Handle *)instance;
179
180 handle->sample_count = nsamples;
181
182 // prepare event_out sequence
183 LV2_Atom_Forge_Frame frame [4];
184
185 for(unsigned i=0; i<handle->max_val; i++)
186 {
187 uint32_t capacity = handle->event_out[i]->atom.size;
188 lv2_atom_forge_set_buffer(&handle->forge[i], (uint8_t *)handle->event_out[i], capacity);
189 lv2_atom_forge_sequence_head(&handle->forge[i], &frame[i], 0);
190 }
191
192 moony_pre(&handle->moony, handle->notify);
193
194 if(_try_lock(&handle->moony.state_lock))
195 {
196 // apply stash, if any
197 if(handle->stashed)
198 {
199 lock_stash_t *stash = &handle->stash[handle->max_val];
200 handle->once = moony_in(&handle->moony, &stash->seq, handle->notify);
201
202 LV2_ATOM_SEQUENCE_FOREACH(handle->notify, ev)
203 ev->time.frames = 0; // overwrite time stamps
204 }
205
206 // handle UI comm
207 handle->once = moony_in(&handle->moony, handle->control, handle->notify)
208 || handle->once;
209
210 // run
211 if(!moony_bypass(&handle->moony))
212 {
213 lua_State *L = moony_current(&handle->moony);
214
215 if(lua_gettop(L) != 1)
216 {
217 // cache for reuse
218 lua_settop(L, 0);
219 lua_pushlightuserdata(L, handle);
220 lua_pushcclosure(L, _run, 1);
221 }
222 lua_pushvalue(L, 1); // _run with upvalue
223 if(lua_pcall(L, 0, 0, 0))
224 moony_error(&handle->moony);
225
226 moony_freeuserdata(&handle->moony);
227 #ifdef USE_MANUAL_GC
228 lua_gc(L, LUA_GCSTEP, 0);
229 #endif
230 }
231
232 if(handle->stashed)
233 {
234 for(unsigned i=0; i<handle->max_val + 1; i++)
235 _stash_reset(&handle->stash[i]);
236
237 handle->stash_nsamples = 0;
238 handle->stashed = false;
239 }
240
241 _unlock(&handle->moony.state_lock);
242 }
243 else
244 {
245 lock_stash_t *stash;
246 // stash incoming events to later apply
247
248 for(unsigned i=1; i<handle->max_val; i++)
249 {
250 stash = &handle->stash[i];
251
252 LV2_ATOM_SEQUENCE_FOREACH(handle->event_in[i], ev)
253 {
254 if(stash->ref)
255 stash->ref = lv2_atom_forge_frame_time(&stash->forge, handle->stash_nsamples + ev->time.frames);
256 if(stash->ref)
257 stash->ref = lv2_atom_forge_write(&stash->forge, &ev->body, lv2_atom_total_size(&ev->body));
258 }
259 }
260
261 stash = &handle->stash[handle->max_val];
262 LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev)
263 {
264 if(stash->ref)
265 stash->ref = lv2_atom_forge_frame_time(&stash->forge, handle->stash_nsamples + ev->time.frames);
266 if(stash->ref)
267 stash->ref = lv2_atom_forge_write(&stash->forge, &ev->body, lv2_atom_total_size(&ev->body));
268 }
269
270 handle->stash_nsamples += nsamples;
271 handle->stashed = true;
272 }
273
274 for(unsigned i=0; i<handle->max_val; i++)
275 if(&frame[i] != handle->forge[i].stack) // intercept assert
276 moony_err(&handle->moony, "forge frame mismatch");
277
278 // clear output sequence buffers upon error
279 if(moony_bypass(&handle->moony))
280 {
281 for(unsigned i=0; i<handle->max_val; i++)
282 lv2_atom_sequence_clear(handle->event_out[i]);
283 }
284 else
285 {
286 for(unsigned i=0; i<handle->max_val; i++)
287 lv2_atom_forge_pop(&handle->forge[i], &frame[i]);
288 }
289
290 // handle UI comm
291 moony_out(&handle->moony, handle->notify, nsamples - 1);
292 }
293
294 __non_realtime static void
cleanup(LV2_Handle instance)295 cleanup(LV2_Handle instance)
296 {
297 Handle *handle = (Handle *)instance;
298
299 moony_deinit(&handle->moony);
300 munlock(handle, sizeof(Handle));
301 free(handle);
302 }
303
304 const LV2_Descriptor a1xa1 = {
305 .URI = MOONY_A1XA1_URI,
306 .instantiate = instantiate,
307 .connect_port = connect_port,
308 .activate = NULL,
309 .run = run,
310 .deactivate = NULL,
311 .cleanup = cleanup,
312 .extension_data = extension_data
313 };
314
315 const LV2_Descriptor a2xa2 = {
316 .URI = MOONY_A2XA2_URI,
317 .instantiate = instantiate,
318 .connect_port = connect_port,
319 .activate = NULL,
320 .run = run,
321 .deactivate = NULL,
322 .cleanup = cleanup,
323 .extension_data = extension_data
324 };
325
326 const LV2_Descriptor a4xa4 = {
327 .URI = MOONY_A4XA4_URI,
328 .instantiate = instantiate,
329 .connect_port = connect_port,
330 .activate = NULL,
331 .run = run,
332 .deactivate = NULL,
333 .cleanup = cleanup,
334 .extension_data = extension_data
335 };
336