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 float *val_in [4];
35 float *val_out [4];
36
37 const LV2_Atom_Sequence *control;
38 LV2_Atom_Sequence *notify;
39
40 lock_stash_t stash;
41 bool stashed;
42 uint32_t stash_nsamples;
43 };
44
45 __non_realtime static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)46 instantiate(const LV2_Descriptor* descriptor, double rate, const char *bundle_path, const LV2_Feature *const *features)
47 {
48 Handle *handle = (Handle *)calloc(1, sizeof(Handle));
49 if(!handle)
50 return NULL;
51 mlock(handle, sizeof(Handle));
52
53 if(moony_init(&handle->moony, descriptor->URI, rate, features, 0x80000, false)) // 512 KB initial memory
54 {
55 free(handle);
56 return NULL;
57 }
58 moony_vm_nrt_enter(handle->moony.vm);
59 moony_open(&handle->moony, handle->moony.vm, moony_current(&handle->moony));
60 moony_vm_nrt_leave(handle->moony.vm);
61
62 if(!strcmp(descriptor->URI, MOONY_C1XC1_URI))
63 handle->max_val = 1;
64 else if(!strcmp(descriptor->URI, MOONY_C2XC2_URI))
65 handle->max_val = 2;
66 else if(!strcmp(descriptor->URI, MOONY_C4XC4_URI))
67 handle->max_val = 4;
68 else
69 handle->max_val = 1;
70
71 _stash_init(&handle->stash, handle->moony.map);
72 _stash_reset(&handle->stash);
73
74 return handle;
75 }
76
77 __realtime static void
connect_port(LV2_Handle instance,uint32_t port,void * data)78 connect_port(LV2_Handle instance, uint32_t port, void *data)
79 {
80 Handle *handle = (Handle *)instance;
81
82 if(port < handle->max_val)
83 handle->val_in[port] = (const float *)data;
84 else if(port < handle->max_val*2)
85 handle->val_out[port - handle->max_val] = (float *)data;
86 else if(port == handle->max_val*2)
87 handle->control = (const LV2_Atom_Sequence *)data;
88 else if(port == handle->max_val*2 + 1)
89 handle->notify = (LV2_Atom_Sequence *)data;
90 }
91
92 __realtime static inline void
_run_period(lua_State * L,const char * cmd,Handle * handle,uint32_t nsamples,const LV2_Atom_Sequence * control)93 _run_period(lua_State *L, const char *cmd, Handle *handle, uint32_t nsamples,
94 const LV2_Atom_Sequence *control)
95 {
96 int top = lua_gettop(L);
97 if(lua_getglobal(L, cmd) != LUA_TNIL)
98 {
99 lua_pushinteger(L, nsamples);
100
101 // push control / notify pair
102 {
103 latom_t *latom = moony_newuserdata(L, &handle->moony, MOONY_UDATA_ATOM, true);
104 latom->atom = (const LV2_Atom *)control;
105 latom->body.raw = LV2_ATOM_BODY_CONST(latom->atom);
106
107 lforge_t *lforge = moony_newuserdata(L, &handle->moony, MOONY_UDATA_FORGE, true);
108 lforge->depth = 0;
109 lforge->last.frames = 0;
110 lforge->forge = &handle->moony.notify_forge;
111 }
112
113 // push values
114 for(unsigned i=0; i<handle->max_val; i++)
115 lua_pushnumber(L, *handle->val_in[i]);
116
117 lua_call(L, 1 + handle->max_val + 2, LUA_MULTRET);
118
119 unsigned ret = lua_gettop(L) - top;
120 unsigned max = ret > handle->max_val ? handle->max_val : ret; // discard superfluous returns
121 for(unsigned i=0; i<max; i++)
122 *handle->val_out[i] = luaL_optnumber(L, i+1, 0.f);
123 for(unsigned i=ret; i<handle->max_val; i++) // fill in missing returns with 0.f
124 *handle->val_out[i] = 0.f;
125 }
126 }
127
128 __realtime static int
_run(lua_State * L)129 _run(lua_State *L)
130 {
131 Handle *handle = lua_touserdata(L, lua_upvalueindex(1));
132
133 // apply stash, if any
134 if(handle->stashed)
135 {
136 const LV2_Atom_Sequence *control = &handle->stash.seq;
137
138 _run_period(L, "run", handle, handle->stash_nsamples, control);
139
140 LV2_ATOM_SEQUENCE_FOREACH(handle->notify, ev)
141 ev->time.frames = 0; // overwrite time stamps
142 }
143
144 if(handle->once)
145 {
146 _run_period(L, "once", handle, handle->sample_count, handle->control);
147 handle->once = false;
148 }
149
150 _run_period(L, "run", handle, handle->sample_count, handle->control);
151
152 return 0;
153 }
154
155 __realtime static void
run(LV2_Handle instance,uint32_t nsamples)156 run(LV2_Handle instance, uint32_t nsamples)
157 {
158 Handle *handle = (Handle *)instance;
159
160 handle->sample_count = nsamples;
161
162 moony_pre(&handle->moony, handle->notify);
163
164 if(_try_lock(&handle->moony.state_lock))
165 {
166 // apply stash, if any
167 if(handle->stashed)
168 {
169 lock_stash_t *stash = &handle->stash;
170 handle->once = moony_in(&handle->moony, &stash->seq, handle->notify);
171
172 LV2_ATOM_SEQUENCE_FOREACH(handle->notify, ev)
173 ev->time.frames = 0; // overwrite time stamps
174 }
175
176 // handle UI comm
177 handle->once = moony_in(&handle->moony, handle->control, handle->notify)
178 || handle->once;
179
180 // run
181 if(!moony_bypass(&handle->moony))
182 {
183 lua_State *L = moony_current(&handle->moony);
184
185 if(lua_gettop(L) != 1)
186 {
187 // cache for reuse
188 lua_settop(L, 0);
189 lua_pushlightuserdata(L, handle);
190 lua_pushcclosure(L, _run, 1);
191 }
192 lua_pushvalue(L, 1); // _run with upvalue
193 if(lua_pcall(L, 0, 0, 0))
194 moony_error(&handle->moony);
195
196 moony_freeuserdata(&handle->moony);
197 #ifdef USE_MANUAL_GC
198 lua_gc(L, LUA_GCSTEP, 0);
199 #endif
200 }
201
202 if(handle->stashed)
203 {
204 _stash_reset(&handle->stash);
205
206 handle->stash_nsamples = 0;
207 handle->stashed = false;
208 }
209
210 _unlock(&handle->moony.state_lock);
211 }
212 else
213 {
214 // stash incoming events to later apply
215
216 lock_stash_t *stash = &handle->stash;
217 LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev)
218 {
219 if(stash->ref)
220 stash->ref = lv2_atom_forge_frame_time(&stash->forge, handle->stash_nsamples + ev->time.frames);
221 if(stash->ref)
222 stash->ref = lv2_atom_forge_write(&stash->forge, &ev->body, lv2_atom_total_size(&ev->body));
223 }
224
225 handle->stash_nsamples += nsamples;
226 handle->stashed = true;
227 }
228
229 // clear output ports upon error
230 if(moony_bypass(&handle->moony))
231 for(unsigned i=0; i<handle->max_val; i++)
232 *handle->val_out[i] = 0.f;
233
234 // handle UI comm
235 moony_out(&handle->moony, handle->notify, nsamples - 1);
236 }
237
238 __non_realtime static void
cleanup(LV2_Handle instance)239 cleanup(LV2_Handle instance)
240 {
241 Handle *handle = (Handle *)instance;
242
243 moony_deinit(&handle->moony);
244 munlock(handle, sizeof(Handle));
245 free(handle);
246 }
247
248 const LV2_Descriptor c1xc1 = {
249 .URI = MOONY_C1XC1_URI,
250 .instantiate = instantiate,
251 .connect_port = connect_port,
252 .activate = NULL,
253 .run = run,
254 .deactivate = NULL,
255 .cleanup = cleanup,
256 .extension_data = extension_data
257 };
258
259 const LV2_Descriptor c2xc2 = {
260 .URI = MOONY_C2XC2_URI,
261 .instantiate = instantiate,
262 .connect_port = connect_port,
263 .activate = NULL,
264 .run = run,
265 .deactivate = NULL,
266 .cleanup = cleanup,
267 .extension_data = extension_data
268 };
269
270 const LV2_Descriptor c4xc4 = {
271 .URI = MOONY_C4XC4_URI,
272 .instantiate = instantiate,
273 .connect_port = connect_port,
274 .activate = NULL,
275 .run = run,
276 .deactivate = NULL,
277 .cleanup = cleanup,
278 .extension_data = extension_data
279 };
280