1 /*
2 * Copyright (c) 2015-2016 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 #ifndef LV2_OSC_UTIL_H
19 #define LV2_OSC_UTIL_H
20
21 #include <assert.h>
22 #include <ctype.h>
23 #include <inttypes.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #include <osc.lv2/osc.h>
28
29 #include <lv2/lv2plug.in/ns/ext/atom/util.h>
30
31 #ifdef __cplusplus
32 extern "C" {
33 #endif
34
35 #ifndef __unused
36 # define __unused __attribute__((unused))
37 #endif
38
39 #undef LV2_ATOM_TUPLE_FOREACH // there is a bug in LV2 1.10.0
40 #define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \
41 for (LV2_Atom* (iter) = lv2_atom_tuple_begin(tuple); \
42 !lv2_atom_tuple_is_end(LV2_ATOM_BODY(tuple), (tuple)->atom.size, (iter)); \
43 (iter) = lv2_atom_tuple_next(iter))
44
45 typedef void (*LV2_OSC_Method)(const char *path,
46 const LV2_Atom_Tuple *arguments, void *data);
47
48 // characters not allowed in OSC path string
49 static const char invalid_path_chars [] = {
50 ' ', '#',
51 '\0'
52 };
53
54 // allowed characters in OSC format string
55 static const char valid_format_chars [] = {
56 LV2_OSC_INT32, LV2_OSC_FLOAT, LV2_OSC_STRING, LV2_OSC_BLOB,
57 LV2_OSC_TRUE, LV2_OSC_FALSE, LV2_OSC_NIL, LV2_OSC_IMPULSE,
58 LV2_OSC_INT64, LV2_OSC_DOUBLE, LV2_OSC_TIMETAG,
59 LV2_OSC_SYMBOL, LV2_OSC_MIDI,
60 '\0'
61 };
62
63 /**
64 TODO
65 */
66 static inline bool
lv2_osc_check_path(const char * path)67 lv2_osc_check_path(const char *path)
68 {
69 assert(path);
70
71 if(path[0] != '/')
72 return false;
73
74 for(const char *ptr=path+1; *ptr!='\0'; ptr++)
75 if( (isprint(*ptr) == 0) || (strchr(invalid_path_chars, *ptr) != NULL) )
76 return false;
77
78 return true;
79 }
80
81 /**
82 TODO
83 */
84 static inline bool
lv2_osc_check_fmt(const char * format,int offset)85 lv2_osc_check_fmt(const char *format, int offset)
86 {
87 assert(format);
88
89 if(offset && (format[0] != ',') )
90 return false;
91
92 for(const char *ptr=format+offset; *ptr!='\0'; ptr++)
93 if(strchr(valid_format_chars, *ptr) == NULL)
94 return false;
95
96 return true;
97 }
98
99 /**
100 TODO
101 */
102 static inline uint64_t
lv2_osc_timetag_parse(const LV2_OSC_Timetag * timetag)103 lv2_osc_timetag_parse(const LV2_OSC_Timetag *timetag)
104 {
105 return ((uint64_t)timetag->integral << 32) | timetag->fraction;
106 }
107
108 /**
109 TODO
110 */
111 static inline LV2_OSC_Timetag *
lv2_osc_timetag_create(LV2_OSC_Timetag * timetag,uint64_t tt)112 lv2_osc_timetag_create(LV2_OSC_Timetag *timetag, uint64_t tt)
113 {
114 timetag->integral = tt >> 32;
115 timetag->fraction = tt & 0xffffffff;
116
117 return timetag;
118 }
119
120 #define LV2_OSC_TIMETAG_CREATE(tt) \
121 lv2_osc_timetag_create(&(LV2_OSC_Timetag){.integral = 0, .fraction = 0}, (tt))
122
123 /**
124 TODO
125 */
126 static inline bool
lv2_osc_is_packet_type(LV2_OSC_URID * osc_urid,LV2_URID type)127 lv2_osc_is_packet_type(LV2_OSC_URID *osc_urid, LV2_URID type)
128 {
129 return type == osc_urid->OSC_Packet;
130 }
131
132 /**
133 TODO
134 */
135 static inline bool
lv2_osc_is_bundle_type(LV2_OSC_URID * osc_urid,LV2_URID type)136 lv2_osc_is_bundle_type(LV2_OSC_URID *osc_urid, LV2_URID type)
137 {
138 return type == osc_urid->OSC_Bundle;
139 }
140
141 /**
142 TODO
143 */
144 static inline bool
lv2_osc_is_message_type(LV2_OSC_URID * osc_urid,LV2_URID type)145 lv2_osc_is_message_type(LV2_OSC_URID *osc_urid, LV2_URID type)
146 {
147 return type == osc_urid->OSC_Message;
148 }
149
150 /**
151 TODO
152 */
153 static inline bool
lv2_osc_is_message_or_bundle_type(LV2_OSC_URID * osc_urid,LV2_URID type)154 lv2_osc_is_message_or_bundle_type(LV2_OSC_URID *osc_urid, LV2_URID type)
155 {
156 return lv2_osc_is_message_type(osc_urid, type)
157 || lv2_osc_is_bundle_type(osc_urid, type);
158 }
159
160 static inline LV2_OSC_Type
lv2_osc_argument_type(LV2_OSC_URID * osc_urid,const LV2_Atom * atom)161 lv2_osc_argument_type(LV2_OSC_URID *osc_urid, const LV2_Atom *atom)
162 {
163 const LV2_Atom_Object *obj = (const LV2_Atom_Object *)atom;
164
165 if(atom->type == osc_urid->ATOM_Int)
166 return LV2_OSC_INT32;
167 else if(atom->type == osc_urid->ATOM_Float)
168 return LV2_OSC_FLOAT;
169 else if(atom->type == osc_urid->ATOM_String)
170 return LV2_OSC_STRING;
171 else if(atom->type == osc_urid->ATOM_Chunk)
172 return LV2_OSC_BLOB;
173
174 else if(atom->type == osc_urid->ATOM_Long)
175 return LV2_OSC_INT64;
176 else if(atom->type == osc_urid->ATOM_Double)
177 return LV2_OSC_DOUBLE;
178 else if( (atom->type == osc_urid->ATOM_Object) && (obj->body.otype == osc_urid->OSC_Timetag) )
179 return LV2_OSC_TIMETAG;
180
181 else if(atom->type == osc_urid->ATOM_Bool)
182 {
183 if(((const LV2_Atom_Bool *)atom)->body)
184 return LV2_OSC_TRUE;
185 else
186 return LV2_OSC_FALSE;
187 }
188 else if(atom->type == osc_urid->ATOM_Literal)
189 {
190 const LV2_Atom_Literal *lit = (const LV2_Atom_Literal *)atom;
191 if(lit->body.datatype == osc_urid->OSC_Nil)
192 return LV2_OSC_NIL;
193 else if(lit->body.datatype == osc_urid->OSC_Impulse)
194 return LV2_OSC_IMPULSE;
195 else if(lit->body.datatype == osc_urid->OSC_Char)
196 return LV2_OSC_CHAR;
197 else if(lit->body.datatype == osc_urid->OSC_RGBA)
198 return LV2_OSC_RGBA;
199 }
200
201 else if(atom->type == osc_urid->ATOM_URID)
202 return LV2_OSC_SYMBOL;
203 else if(atom->type == osc_urid->MIDI_MidiEvent)
204 return LV2_OSC_MIDI;
205
206 return '\0';
207 }
208
209 static inline const LV2_Atom *
lv2_osc_int32_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom,int32_t * i)210 lv2_osc_int32_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom,
211 int32_t *i)
212 {
213 assert(i);
214 *i = ((const LV2_Atom_Int *)atom)->body;
215
216 return lv2_atom_tuple_next(atom);
217 }
218
219 static inline const LV2_Atom *
lv2_osc_float_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom,float * f)220 lv2_osc_float_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom,
221 float *f)
222 {
223 assert(f);
224 *f = ((const LV2_Atom_Float *)atom)->body;
225
226 return lv2_atom_tuple_next(atom);
227 }
228
229 static inline const LV2_Atom *
lv2_osc_string_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom,const char ** s)230 lv2_osc_string_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom,
231 const char **s)
232 {
233 assert(s);
234 *s = LV2_ATOM_BODY_CONST(atom);
235
236 return lv2_atom_tuple_next(atom);
237 }
238
239 static inline const LV2_Atom *
lv2_osc_blob_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom,uint32_t * size,const uint8_t ** b)240 lv2_osc_blob_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom,
241 uint32_t *size, const uint8_t **b)
242 {
243 assert(size && b);
244 *size = atom->size;
245 *b = LV2_ATOM_BODY_CONST(atom);
246
247 return lv2_atom_tuple_next(atom);
248 }
249
250 static inline const LV2_Atom *
lv2_osc_int64_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom,int64_t * h)251 lv2_osc_int64_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom,
252 int64_t *h)
253 {
254 assert(h);
255 *h = ((const LV2_Atom_Long *)atom)->body;
256
257 return lv2_atom_tuple_next(atom);
258 }
259
260 static inline const LV2_Atom *
lv2_osc_double_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom,double * d)261 lv2_osc_double_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom,
262 double *d)
263 {
264 assert(d);
265 *d = ((const LV2_Atom_Double *)atom)->body;
266
267 return lv2_atom_tuple_next(atom);
268 }
269
270 static inline const LV2_Atom *
lv2_osc_timetag_get(LV2_OSC_URID * osc_urid,const LV2_Atom * atom,LV2_OSC_Timetag * timetag)271 lv2_osc_timetag_get(LV2_OSC_URID *osc_urid, const LV2_Atom *atom,
272 LV2_OSC_Timetag *timetag)
273 {
274 assert(timetag);
275
276 const LV2_Atom_Long *integral = NULL;
277 const LV2_Atom_Long *fraction = NULL;
278
279 lv2_atom_object_get((const LV2_Atom_Object *)atom,
280 osc_urid->OSC_timetagIntegral, &integral,
281 osc_urid->OSC_timetagFraction, &fraction,
282 0);
283
284 if( integral && (integral->atom.type == osc_urid->ATOM_Long)
285 && fraction && (fraction->atom.type == osc_urid->ATOM_Long) )
286 {
287 timetag->integral = integral->body;
288 timetag->fraction = fraction->body;
289 }
290 else
291 {
292 // set to immediate
293 timetag->integral = 0;
294 timetag->fraction = 1;
295 }
296
297 return lv2_atom_tuple_next(atom);
298 }
299
300 static inline const LV2_Atom *
lv2_osc_true_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom)301 lv2_osc_true_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom)
302 {
303 return lv2_atom_tuple_next(atom);
304 }
305
306 static inline const LV2_Atom *
lv2_osc_false_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom)307 lv2_osc_false_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom)
308 {
309 return lv2_atom_tuple_next(atom);
310 }
311
312 static inline const LV2_Atom *
lv2_osc_nil_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom)313 lv2_osc_nil_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom)
314 {
315 return lv2_atom_tuple_next(atom);
316 }
317
318 static inline const LV2_Atom *
lv2_osc_impulse_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom)319 lv2_osc_impulse_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom)
320 {
321 return lv2_atom_tuple_next(atom);
322 }
323
324 static inline const LV2_Atom *
lv2_osc_symbol_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom,LV2_URID * S)325 lv2_osc_symbol_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom,
326 LV2_URID *S)
327 {
328 assert(S);
329 *S = ((const LV2_Atom_URID *)atom)->body;
330
331 return lv2_atom_tuple_next(atom);
332 }
333
334 static inline const LV2_Atom *
lv2_osc_midi_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom,uint32_t * size,const uint8_t ** m)335 lv2_osc_midi_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom,
336 uint32_t *size, const uint8_t **m)
337 {
338 assert(size && m);
339 *size = atom->size;
340 *m = LV2_ATOM_BODY_CONST(atom);
341
342 return lv2_atom_tuple_next(atom);
343 }
344
345 static inline const LV2_Atom *
lv2_osc_char_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom,char * c)346 lv2_osc_char_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom, char *c)
347 {
348 assert(c);
349 const char *str = LV2_ATOM_CONTENTS_CONST(LV2_Atom_Literal, atom);
350 *c = str[0];
351
352 return lv2_atom_tuple_next(atom);
353 }
354
355 static inline const LV2_Atom *
lv2_osc_rgba_get(LV2_OSC_URID * osc_urid __unused,const LV2_Atom * atom,uint8_t * r,uint8_t * g,uint8_t * b,uint8_t * a)356 lv2_osc_rgba_get(LV2_OSC_URID *osc_urid __unused, const LV2_Atom *atom,
357 uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *a)
358 {
359 assert(r && g && b && a);
360 const char *str = LV2_ATOM_CONTENTS_CONST(LV2_Atom_Literal, atom);
361
362 uint8_t *key [4] = {
363 r, g, b, a
364 };
365
366 const char *pos = str;
367 char *endptr;
368
369 for(unsigned count = 0; count < 4; count++, pos += 2)
370 {
371 char buf [5] = {'0', 'x', pos[0], pos[1], '\0'};
372
373 *key[count] = strtol(buf, &endptr, 16);
374 }
375
376 return lv2_atom_tuple_next(atom);
377 }
378
379 /**
380 TODO
381 */
382 static inline bool
lv2_osc_bundle_body_get(LV2_OSC_URID * osc_urid,uint32_t size,const LV2_Atom_Object_Body * body,const LV2_Atom_Object ** timetag,const LV2_Atom_Tuple ** items)383 lv2_osc_bundle_body_get(LV2_OSC_URID *osc_urid, uint32_t size, const LV2_Atom_Object_Body *body,
384 const LV2_Atom_Object **timetag, const LV2_Atom_Tuple **items)
385 {
386 assert(timetag && items);
387
388 *timetag = NULL;
389 *items = NULL;
390
391 lv2_atom_object_body_get(size, body,
392 osc_urid->OSC_bundleTimetag, timetag,
393 osc_urid->OSC_bundleItems, items,
394 0);
395
396 if(!*timetag || ((*timetag)->atom.type != osc_urid->ATOM_Object) || ((*timetag)->body.otype != osc_urid->OSC_Timetag))
397 return false;
398 if(!*items || ((*items)->atom.type != osc_urid->ATOM_Tuple))
399 return false;
400
401 return true;
402 }
403
404 /**
405 TODO
406 */
407 static inline bool
lv2_osc_bundle_get(LV2_OSC_URID * osc_urid,const LV2_Atom_Object * obj,const LV2_Atom_Object ** timetag,const LV2_Atom_Tuple ** items)408 lv2_osc_bundle_get(LV2_OSC_URID *osc_urid, const LV2_Atom_Object *obj,
409 const LV2_Atom_Object **timetag, const LV2_Atom_Tuple **items)
410 {
411 return lv2_osc_bundle_body_get(osc_urid, obj->atom.size, &obj->body,
412 timetag, items);
413 }
414
415 /**
416 TODO
417 */
418 static inline bool
lv2_osc_message_body_get(LV2_OSC_URID * osc_urid,uint32_t size,const LV2_Atom_Object_Body * body,const LV2_Atom_String ** path,const LV2_Atom_Tuple ** arguments)419 lv2_osc_message_body_get(LV2_OSC_URID *osc_urid, uint32_t size, const LV2_Atom_Object_Body *body,
420 const LV2_Atom_String **path, const LV2_Atom_Tuple **arguments)
421 {
422 assert(path && arguments);
423
424 *path = NULL;
425 *arguments = NULL;
426
427 lv2_atom_object_body_get(size, body,
428 osc_urid->OSC_messagePath, path,
429 osc_urid->OSC_messageArguments, arguments,
430 0);
431
432 if(!*path || ((*path)->atom.type != osc_urid->ATOM_String))
433 return false;
434 // message without arguments is valid
435 if( *arguments && ((*arguments)->atom.type != osc_urid->ATOM_Tuple))
436 return false;
437
438 return true;
439 }
440
441 /**
442 TODO
443 */
444 static inline bool
lv2_osc_message_get(LV2_OSC_URID * osc_urid,const LV2_Atom_Object * obj,const LV2_Atom_String ** path,const LV2_Atom_Tuple ** arguments)445 lv2_osc_message_get(LV2_OSC_URID *osc_urid, const LV2_Atom_Object *obj,
446 const LV2_Atom_String **path, const LV2_Atom_Tuple **arguments)
447 {
448 return lv2_osc_message_body_get(osc_urid, obj->atom.size, &obj->body,
449 path, arguments);
450 }
451
452 static inline bool
lv2_osc_body_unroll(LV2_OSC_URID * osc_urid,uint32_t size,const LV2_Atom_Object_Body * body,LV2_OSC_Method method,void * data)453 lv2_osc_body_unroll(LV2_OSC_URID *osc_urid, uint32_t size, const LV2_Atom_Object_Body *body,
454 LV2_OSC_Method method, void *data)
455 {
456 if(body->otype == osc_urid->OSC_Bundle)
457 {
458 const LV2_Atom_Object *timetag = NULL;
459 const LV2_Atom_Tuple *items = NULL;
460
461 if(!lv2_osc_bundle_body_get(osc_urid, size, body, &timetag, &items))
462 return false;
463
464 LV2_OSC_Timetag tt;
465 lv2_osc_timetag_get(osc_urid, &timetag->atom, &tt);
466
467 LV2_ATOM_TUPLE_FOREACH(items, atom)
468 {
469 const LV2_Atom_Object *obj= (const LV2_Atom_Object *)atom;
470
471 if(!lv2_osc_body_unroll(osc_urid, obj->atom.size, &obj->body, method, data))
472 return false;
473 }
474
475 return true;
476 }
477 else if(body->otype == osc_urid->OSC_Message)
478 {
479 const LV2_Atom_String *path = NULL;
480 const LV2_Atom_Tuple *arguments = NULL;
481
482 if(!lv2_osc_message_body_get(osc_urid, size, body, &path, &arguments))
483 return false;
484
485 if(method)
486 method(LV2_ATOM_BODY_CONST(path), arguments, data);
487
488 return true;
489 }
490
491 return false;
492 }
493
494 static inline bool
lv2_osc_unroll(LV2_OSC_URID * osc_urid,const LV2_Atom_Object * obj,LV2_OSC_Method method,void * data)495 lv2_osc_unroll(LV2_OSC_URID *osc_urid, const LV2_Atom_Object *obj,
496 LV2_OSC_Method method, void *data)
497 {
498 return lv2_osc_body_unroll(osc_urid, obj->atom.size, &obj->body, method, data);
499 }
500
501 #ifdef __cplusplus
502 } // extern "C"
503 #endif
504
505 #endif // LV2_OSC_UTIL_H
506