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