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