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