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