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 <math.h>
19 
20 #include <api_osc.h>
21 #include <api_atom.h>
22 #include <api_forge.h>
23 
24 #include <osc.lv2/util.h>
25 
26 typedef struct _osc_responder_data_t osc_responder_data_t;
27 
28 struct _osc_responder_data_t {
29 	moony_t *moony;
30 	bool matched;
31 };
32 
33 __realtime static inline bool
_osc_path_has_wildcards(const char * path)34 _osc_path_has_wildcards(const char *path)
35 {
36 	if(strpbrk(path, "?*[{}]"))
37 			return true;
38 	return false;
39 }
40 
41 //TODO can we rewrite this directly in C?
42 const char *loscresponder_match =
43 	"function __expand(v)\n"
44 	"	return coroutine.wrap(function()\n"
45 	"		local item = string.match(v, '%b{}')\n"
46 	"		if item then\n"
47 	"			for key in string.gmatch(item, '{?([^,}]*)') do\n"
48 	"				local vv = string.gsub(v, item, key)\n"
49 	"				for w in __expand(vv) do\n"
50 	"					coroutine.yield(w)\n"
51 	"				end\n"
52 	"			end\n"
53 	"		else\n"
54 	"			coroutine.yield(v)\n"
55 	"		end\n"
56 	"	end)\n"
57 	"end\n"
58 	"\n"
59 	"function __match(v, o, ...)\n"
60 	"	local matched = false\n"
61 	"	v = string.gsub(v, '%?', '.')\n"
62 	"	v = string.gsub(v, '%*', '.*')\n"
63 	"	v = string.gsub(v, '%[%!', '[^')\n"
64 	"	v = '^' .. v .. '$'\n"
65 	"	for w in __expand(v) do\n"
66 	"		for k, x in pairs(o) do\n"
67 	"			if string.match(k, w) then\n"
68 	"				x(o, ...)\n"
69 	"				matched = matched or true\n"
70 	"			end\n"
71 	"		end\n"
72 	"	end\n"
73 	"	return matched\n"
74 	"end";
75 
76 __realtime static inline void
_loscresponder_method(const char * path,const LV2_Atom_Tuple * arguments,void * data)77 _loscresponder_method(const char *path, const LV2_Atom_Tuple *arguments, void *data)
78 {
79 	osc_responder_data_t *ord = data;
80 	moony_t *moony = ord->moony;
81 	lua_State *L = moony_current(moony);
82 	//LV2_Atom_Forge *forge = &moony->forge;
83 	LV2_OSC_URID *osc_urid = &moony->osc_urid;
84 	//LV2_URID_Unmap *unmap = moony->unmap;
85 
86 	// 1: uservalue
87 	// 2: frames
88 	// 3: data
89 
90 	int has_wildcard = 0;
91 	if(_osc_path_has_wildcards(path))
92 	{
93 		lua_getglobal(L, "__match"); // push pattern has_wildcard function
94 		lua_pushstring(L, path); // path
95 		has_wildcard = 1;
96 	}
97 	else if(lua_getfield(L, 1, path) == LUA_TNIL) // raw string match
98 	{
99 		lua_pop(L, 1); // nil
100 		ord->matched = ord->matched || false;
101 		return;
102 	}
103 
104 	lua_pushvalue(L, 1); // self
105 	lua_pushvalue(L, 2); // frames
106 	lua_pushvalue(L, 3); // data
107 
108 	// push format string
109 	luaL_Buffer B;
110 	luaL_buffinit(L, &B);
111 	LV2_ATOM_TUPLE_FOREACH(arguments, atom)
112 	{
113 		const LV2_OSC_Type type = lv2_osc_argument_type(osc_urid, atom);
114 		if(type)
115 			luaL_addchar(&B, type);
116 	}
117 	luaL_pushresult(&B);
118 
119 	int oldtop = lua_gettop(L);
120 
121 	LV2_ATOM_TUPLE_FOREACH(arguments, atom)
122 	{
123 		//const LV2_Atom_Object *obj= (const LV2_Atom_Object *)atom;
124 
125 		switch(lv2_osc_argument_type(osc_urid, atom))
126 		{
127 			case LV2_OSC_INT32:
128 			{
129 				int32_t i;
130 				if(lv2_osc_int32_get(osc_urid, atom, &i))
131 					lua_pushinteger(L, i);
132 				break;
133 			}
134 			case LV2_OSC_FLOAT:
135 			{
136 				float f;
137 				if(lv2_osc_float_get(osc_urid, atom, &f))
138 					lua_pushnumber(L, f);
139 				break;
140 			}
141 			case LV2_OSC_STRING:
142 			{
143 				const char *s;
144 				if(lv2_osc_string_get(osc_urid, atom, &s))
145 					lua_pushstring(L, s);
146 				break;
147 			}
148 			case LV2_OSC_BLOB:
149 			{
150 				const uint8_t *b;
151 				uint32_t len;
152 				if(lv2_osc_blob_get(osc_urid, atom, &len, &b))
153 					lua_pushlstring(L, (const char *)b, len);
154 				break;
155 			}
156 
157 			case LV2_OSC_INT64:
158 			{
159 				int64_t h;
160 				if(lv2_osc_int64_get(osc_urid, atom, &h))
161 					lua_pushinteger(L, h);
162 				break;
163 			}
164 			case LV2_OSC_DOUBLE:
165 			{
166 				double d;
167 				if(lv2_osc_double_get(osc_urid, atom, &d))
168 					lua_pushnumber(L, d);
169 				break;
170 			}
171 			case LV2_OSC_TIMETAG:
172 			{
173 				LV2_OSC_Timetag tt;
174 				if(lv2_osc_timetag_get(osc_urid, atom, &tt))
175 					lua_pushinteger(L, lv2_osc_timetag_parse(&tt));
176 				break;
177 			}
178 
179 			case LV2_OSC_TRUE:
180 			{
181 				lua_pushboolean(L, 1);
182 				break;
183 			}
184 			case LV2_OSC_FALSE:
185 			{
186 				lua_pushboolean(L, 0);
187 				break;
188 			}
189 			case LV2_OSC_NIL:
190 			{
191 				lua_pushnil(L);
192 				break;
193 			}
194 			case LV2_OSC_IMPULSE:
195 			{
196 				lua_pushnumber(L, HUGE_VAL);
197 				break;
198 			}
199 
200 			case LV2_OSC_SYMBOL:
201 			{
202 				LV2_URID S;
203 				if(lv2_osc_symbol_get(&moony->osc_urid, atom, &S))
204 					lua_pushinteger(L, S);
205 				break;
206 			}
207 			case LV2_OSC_MIDI:
208 			{
209 				const uint8_t *m;
210 				uint32_t len;
211 				if(lv2_osc_midi_get(&moony->osc_urid, atom, &len, &m))
212 					lua_pushlstring(L, (const char *)m, len);
213 				break;
214 			}
215 			case LV2_OSC_CHAR:
216 			{
217 				char c;
218 				if(lv2_osc_char_get(&moony->osc_urid, atom, &c))
219 					lua_pushinteger(L, c);
220 				break;
221 			}
222 			case LV2_OSC_RGBA:
223 			{
224 				uint8_t r, g, b, a;
225 				if(lv2_osc_rgba_get(&moony->osc_urid, atom, &r, &g, &b, &a))
226 					lua_pushinteger(L, ((int64_t)r << 24) | (g << 16) | (b << 8) | a);
227 				break;
228 			}
229 		}
230 	}
231 
232 	lua_call(L, 4 + has_wildcard + lua_gettop(L) - oldtop, has_wildcard);
233 
234 	if(has_wildcard)
235 	{
236 		ord->matched = ord->matched || lua_toboolean(L, -1);
237 		lua_pop(L, 1);
238 	}
239 	else // raw string
240 	{
241 		ord->matched = ord->matched || true;
242 	}
243 }
244 
245 __realtime static int
_loscresponder__call(lua_State * L)246 _loscresponder__call(lua_State *L)
247 {
248 	moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
249 	const bool *through = lua_touserdata(L, 1);
250 
251 	lua_settop(L, 4); // discard superfluous arguments
252 	// 1: self
253 	// 2: frames
254 	// 3: data
255 	// 4: atom
256 
257 	latom_t *latom = NULL;
258 	if(luaL_testudata(L, 4, "latom"))
259 		latom = lua_touserdata(L, 4);
260 	lua_pop(L, 1); // atom
261 
262 	// check for valid atom and event type
263 	const LV2_Atom_Object *obj = latom ? (const LV2_Atom_Object *)latom->atom : NULL;
264 
265 	if(  !latom
266 		|| !lv2_atom_forge_is_object_type(&moony->forge, obj->atom.type)
267 		|| !(lv2_osc_is_message_or_bundle_type(&moony->osc_urid, obj->body.otype)))
268 	{
269 		lua_pushboolean(L, 0); // not handled
270 		return 1;
271 	}
272 
273 	// replace self with its uservalue
274 	lua_getuservalue(L, 1);
275 	lua_replace(L, 1);
276 
277 	osc_responder_data_t ord = {
278 		.moony = moony,
279 		.matched = false
280 	};
281 
282 	lv2_osc_body_unroll(&moony->osc_urid, latom->atom->size, latom->body.obj,
283 		_loscresponder_method, &ord);
284 
285 	if(!ord.matched && *through) // not handled and through mode
286 	{
287 		const int64_t frames = luaL_checkinteger(L, 2);
288 		lforge_t *lforge = luaL_checkudata(L, 3, "lforge");
289 
290 		if(frames < lforge->last.frames)
291 			luaL_error(L, "invalid frame time, must not decrease");
292 		lforge->last.frames = frames;
293 
294 		if(  !lv2_atom_forge_frame_time(lforge->forge, frames)
295 			|| !lv2_atom_forge_atom(lforge->forge, latom->atom->size, latom->atom->type)
296 			|| !lv2_atom_forge_write(lforge->forge, latom->body.raw, latom->atom->size) )
297 			luaL_error(L, forge_buffer_overflow);
298 	}
299 
300 	lua_pushboolean(L, 1); // handled
301 	lua_pushboolean(L, ord.matched); // matched a registered path
302 	return 2;
303 }
304 
305 __realtime static int
_loscresponder__index(lua_State * L)306 _loscresponder__index(lua_State *L)
307 {
308 	const bool *through = lua_touserdata(L, 1);
309 	const char *key = luaL_checkstring(L, 2);
310 	// 1: self
311 	// 2: key
312 
313 	if(!strcmp(key, "through"))
314 	{
315 		lua_pushboolean(L, *through);
316 	}
317 	else
318 	{
319 		lua_pushnil(L);
320 	}
321 
322 	return 1;
323 }
324 
325 __realtime static int
_loscresponder__newindex(lua_State * L)326 _loscresponder__newindex(lua_State *L)
327 {
328 	// 1: self
329 	// 2: key
330 	// 3: val
331 	bool *through = lua_touserdata(L, 1);
332 	const char *key = luaL_checkstring(L, 2);
333 
334 	if(!strcmp(key, "through"))
335 	{
336 		*through = lua_toboolean(L, 3);
337 	}
338 
339 	return 0;
340 }
341 
342 __realtime int
_loscresponder(lua_State * L)343 _loscresponder(lua_State *L)
344 {
345 	//moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
346 
347 	lua_settop(L, 2); // discard superfluous arguments
348 
349 	const bool _through = lua_toboolean(L, 2);
350 	lua_pop(L, 1); // bool
351 
352 	// o = new
353 	bool *through = lua_newuserdata(L, sizeof(bool));
354 	*through= _through;
355 
356 	// o.uservalue = uservalue
357 	lua_insert(L, 1);
358 	lua_setuservalue(L, -2);
359 
360 	// setmetatable(o, self)
361 	luaL_getmetatable(L, "loscresponder");
362 	lua_setmetatable(L, -2);
363 
364 	// return o
365 	return 1;
366 }
367 
368 const luaL_Reg loscresponder_mt [] = {
369 	{"__call", _loscresponder__call},
370 	{"__index", _loscresponder__index},
371 	{"__newindex", _loscresponder__newindex},
372 	{NULL, NULL}
373 };
374