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_WRITER_H
19 #define LV2_OSC_WRITER_H
20
21 #include <stdbool.h>
22 #include <string.h>
23
24 #include <osc.lv2/osc.h>
25 #include <osc.lv2/util.h>
26 #include <osc.lv2/endian.h>
27
28 #include <lv2/lv2plug.in/ns/ext/atom/util.h>
29
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33
34 #undef LV2_ATOM_TUPLE_FOREACH // there is a bug in LV2 1.10.0
35 #define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \
36 for (LV2_Atom* (iter) = lv2_atom_tuple_begin(tuple); \
37 !lv2_atom_tuple_is_end(LV2_ATOM_BODY(tuple), (tuple)->atom.size, (iter)); \
38 (iter) = lv2_atom_tuple_next(iter))
39
40 typedef struct _LV2_OSC_Writer LV2_OSC_Writer;
41 typedef struct _LV2_OSC_Writer_Frame LV2_OSC_Writer_Frame;
42
43 struct _LV2_OSC_Writer {
44 uint8_t *buf;
45 uint8_t *ptr;
46 const uint8_t *end;
47 };
48
49 struct _LV2_OSC_Writer_Frame {
50 uint8_t *ref;
51 };
52
53 static inline void
lv2_osc_writer_initialize(LV2_OSC_Writer * writer,uint8_t * buf,size_t size)54 lv2_osc_writer_initialize(LV2_OSC_Writer *writer, uint8_t *buf, size_t size)
55 {
56 writer->buf = buf;
57 writer->ptr = buf;
58 writer->end = buf + size;
59 }
60
61 static inline size_t
lv2_osc_writer_get_size(LV2_OSC_Writer * writer)62 lv2_osc_writer_get_size(LV2_OSC_Writer *writer)
63 {
64 if(writer->ptr > writer->buf)
65 return writer->ptr - writer->buf;
66
67 return 0;
68 }
69
70 static inline uint8_t *
lv2_osc_writer_finalize(LV2_OSC_Writer * writer,size_t * size)71 lv2_osc_writer_finalize(LV2_OSC_Writer *writer, size_t *size)
72 {
73 *size = lv2_osc_writer_get_size(writer);
74
75 if(*size)
76 return writer->buf;
77
78 return NULL;
79 }
80
81 static inline bool
lv2_osc_writer_overflow(LV2_OSC_Writer * writer,size_t size)82 lv2_osc_writer_overflow(LV2_OSC_Writer *writer, size_t size)
83 {
84 return writer->ptr + size >= writer->end;
85 }
86
87 static inline bool
lv2_osc_writer_htobe32(LV2_OSC_Writer * writer,union swap32_t * s32)88 lv2_osc_writer_htobe32(LV2_OSC_Writer *writer, union swap32_t *s32)
89 {
90 if(lv2_osc_writer_overflow(writer, 4))
91 return false;
92
93 s32->u = htobe32(s32->u);
94 *(uint32_t *)writer->ptr = s32->u;
95 writer->ptr += 4;
96
97 return true;
98 }
99
100 static inline bool
lv2_osc_writer_htobe64(LV2_OSC_Writer * writer,union swap64_t * s64)101 lv2_osc_writer_htobe64(LV2_OSC_Writer *writer, union swap64_t *s64)
102 {
103 if(lv2_osc_writer_overflow(writer, 8))
104 return false;
105
106 s64->u = htobe64(s64->u);
107 *(uint64_t *)writer->ptr = s64->u;
108 writer->ptr += 8;
109
110 return true;
111 }
112
113 static inline bool
lv2_osc_writer_add_int32(LV2_OSC_Writer * writer,int32_t i)114 lv2_osc_writer_add_int32(LV2_OSC_Writer *writer, int32_t i)
115 {
116 return lv2_osc_writer_htobe32(writer, &(union swap32_t){ .i = i });
117 }
118
119 static inline bool
lv2_osc_writer_add_float(LV2_OSC_Writer * writer,float f)120 lv2_osc_writer_add_float(LV2_OSC_Writer *writer, float f)
121 {
122 return lv2_osc_writer_htobe32(writer, &(union swap32_t){ .f = f });
123 }
124
125 static inline bool
lv2_osc_writer_add_string(LV2_OSC_Writer * writer,const char * s)126 lv2_osc_writer_add_string(LV2_OSC_Writer *writer, const char *s)
127 {
128 const size_t rawlen = strlen(s) + 1;
129 const size_t padded = LV2_OSC_PADDED_SIZE(rawlen);
130 if(lv2_osc_writer_overflow(writer, padded))
131 return false;
132
133 const uint32_t blank = 0;
134 memcpy(writer->ptr + padded - sizeof(uint32_t), &blank, sizeof(uint32_t));
135 memcpy(writer->ptr, s, rawlen);
136 writer->ptr += padded;
137
138 return true;
139 }
140
141 static inline bool
lv2_osc_writer_add_symbol(LV2_OSC_Writer * writer,const char * S)142 lv2_osc_writer_add_symbol(LV2_OSC_Writer *writer, const char *S)
143 {
144 return lv2_osc_writer_add_string(writer, S);
145 }
146
147 static inline bool
lv2_osc_writer_add_int64(LV2_OSC_Writer * writer,int64_t h)148 lv2_osc_writer_add_int64(LV2_OSC_Writer *writer, int64_t h)
149 {
150 return lv2_osc_writer_htobe64(writer, &(union swap64_t){ .h = h });
151 }
152
153 static inline bool
lv2_osc_writer_add_double(LV2_OSC_Writer * writer,double d)154 lv2_osc_writer_add_double(LV2_OSC_Writer *writer, double d)
155 {
156 return lv2_osc_writer_htobe64(writer, &(union swap64_t){ .d = d });
157 }
158
159 static inline bool
lv2_osc_writer_add_timetag(LV2_OSC_Writer * writer,uint64_t u)160 lv2_osc_writer_add_timetag(LV2_OSC_Writer *writer, uint64_t u)
161 {
162 return lv2_osc_writer_htobe64(writer, &(union swap64_t){ .u = u });
163 }
164
165 static inline bool
lv2_osc_writer_add_blob_inline(LV2_OSC_Writer * writer,int32_t len,uint8_t ** body)166 lv2_osc_writer_add_blob_inline(LV2_OSC_Writer *writer, int32_t len, uint8_t **body)
167 {
168 const size_t len_padded = LV2_OSC_PADDED_SIZE(len);
169 const size_t size = 4 + len_padded;
170 if(lv2_osc_writer_overflow(writer, size))
171 return false;
172
173 if(!lv2_osc_writer_add_int32(writer, len))
174 return false;
175
176 *body = writer->ptr;
177 //memset(&writer->ptr[len], 0x0, len_padded - len);
178 writer->ptr += len_padded;
179
180 return true;
181 }
182
183 static inline bool
lv2_osc_writer_add_blob(LV2_OSC_Writer * writer,int32_t len,const uint8_t * body)184 lv2_osc_writer_add_blob(LV2_OSC_Writer *writer, int32_t len, const uint8_t *body)
185 {
186 uint8_t *dst;
187 if(!lv2_osc_writer_add_blob_inline(writer, len, &dst))
188 return false;
189
190 memcpy(dst, body, len);
191
192 return true;
193 }
194
195 static inline bool
lv2_osc_writer_add_midi_inline(LV2_OSC_Writer * writer,int32_t len,uint8_t ** m)196 lv2_osc_writer_add_midi_inline(LV2_OSC_Writer *writer, int32_t len, uint8_t **m)
197 {
198 if( (len > 4) || lv2_osc_writer_overflow(writer, 4))
199 return false;
200
201 *m = writer->ptr;
202 //memset(&writer->ptr[len], 0x0, 4 - len);
203 writer->ptr += 4;
204
205 return true;
206 }
207
208 static inline bool
lv2_osc_writer_add_midi(LV2_OSC_Writer * writer,int32_t len,const uint8_t * m)209 lv2_osc_writer_add_midi(LV2_OSC_Writer *writer, int32_t len, const uint8_t *m)
210 {
211 uint8_t *dst;
212 if(!lv2_osc_writer_add_midi_inline(writer, len, &dst))
213 return false;
214
215 memcpy(dst, m, len);
216
217 return true;
218 }
219
220 static inline bool
lv2_osc_writer_add_rgba(LV2_OSC_Writer * writer,uint8_t r,uint8_t g,uint8_t b,uint8_t a)221 lv2_osc_writer_add_rgba(LV2_OSC_Writer *writer, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
222 {
223 if(lv2_osc_writer_overflow(writer, 4))
224 return false;
225
226 writer->ptr[0] = r;
227 writer->ptr[1] = g;
228 writer->ptr[2] = b;
229 writer->ptr[3] = a;
230 writer->ptr += 4;
231
232 return true;
233 }
234
235 static inline bool
lv2_osc_writer_add_char(LV2_OSC_Writer * writer,char c)236 lv2_osc_writer_add_char(LV2_OSC_Writer *writer, char c)
237 {
238 return lv2_osc_writer_add_int32(writer, (int32_t)c);
239 }
240
241 static inline bool
lv2_osc_writer_push_bundle(LV2_OSC_Writer * writer,LV2_OSC_Writer_Frame * frame,uint64_t t)242 lv2_osc_writer_push_bundle(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame, uint64_t t)
243 {
244 if(lv2_osc_writer_overflow(writer, 16))
245 return false;
246
247 frame->ref = writer->ptr;
248
249 strncpy((char *)writer->ptr, "#bundle", 8);
250 writer->ptr += 8;
251
252 return lv2_osc_writer_add_timetag(writer, t);
253 }
254
255 static inline bool
lv2_osc_writer_pop_bundle(LV2_OSC_Writer * writer,LV2_OSC_Writer_Frame * frame)256 lv2_osc_writer_pop_bundle(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame)
257 {
258 union swap32_t s32 = { .i = writer->ptr - frame->ref - 16};
259
260 if(s32.i <= 0)
261 {
262 writer->ptr = frame->ref;
263 return false;
264 }
265
266 return true;
267 }
268
269 static inline bool
lv2_osc_writer_push_item(LV2_OSC_Writer * writer,LV2_OSC_Writer_Frame * frame)270 lv2_osc_writer_push_item(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame)
271 {
272 if(lv2_osc_writer_overflow(writer, 4))
273 return false;
274
275 frame->ref = writer->ptr;
276 writer->ptr += 4;
277
278 return true;
279 }
280
281 static inline bool
lv2_osc_writer_pop_item(LV2_OSC_Writer * writer,LV2_OSC_Writer_Frame * frame)282 lv2_osc_writer_pop_item(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame)
283 {
284 union swap32_t s32 = { .i = writer->ptr - frame->ref - 4};
285
286 if(s32.i <= 0)
287 {
288 writer->ptr = frame->ref;
289 return false;
290 }
291
292 s32.u = htobe32(s32.u);
293 *(uint32_t *)frame->ref = s32.u;
294
295 return true;
296 }
297
298 static inline bool
lv2_osc_writer_add_path(LV2_OSC_Writer * writer,const char * path)299 lv2_osc_writer_add_path(LV2_OSC_Writer *writer, const char *path)
300 {
301 return lv2_osc_writer_add_string(writer, path);
302 }
303
304 static inline bool
lv2_osc_writer_add_format(LV2_OSC_Writer * writer,const char * fmt)305 lv2_osc_writer_add_format(LV2_OSC_Writer *writer, const char *fmt)
306 {
307 const size_t rawlen = strlen(fmt) + 1;
308 const size_t padded = LV2_OSC_PADDED_SIZE(rawlen + 1);
309 if(lv2_osc_writer_overflow(writer, padded))
310 return false;
311
312 const uint32_t blank = 0;
313 memcpy(writer->ptr + padded - sizeof(uint32_t), &blank, sizeof(uint32_t));
314 *writer->ptr++ = ',';
315 memcpy(writer->ptr, fmt, rawlen);
316 writer->ptr += padded - 1;
317
318 return true;
319 }
320
321 static inline bool
lv2_osc_writer_arg_varlist(LV2_OSC_Writer * writer,const char * fmt,va_list args)322 lv2_osc_writer_arg_varlist(LV2_OSC_Writer *writer, const char *fmt, va_list args)
323 {
324 for(const char *type = fmt; *type; type++)
325 {
326 switch( (LV2_OSC_Type)*type)
327 {
328 case LV2_OSC_INT32:
329 if(!lv2_osc_writer_add_int32(writer, va_arg(args, int32_t)))
330 return false;
331 break;
332 case LV2_OSC_FLOAT:
333 if(!lv2_osc_writer_add_float(writer, (float)va_arg(args, double)))
334 return false;
335 break;
336 case LV2_OSC_STRING:
337 if(!lv2_osc_writer_add_string(writer, va_arg(args, const char *)))
338 return false;
339 break;
340 case LV2_OSC_BLOB:
341 {
342 const int32_t len = va_arg(args, int32_t);
343 if(!lv2_osc_writer_add_blob(writer, len, va_arg(args, const uint8_t *)))
344 return false;
345 } break;
346
347 case LV2_OSC_TRUE:
348 case LV2_OSC_FALSE:
349 case LV2_OSC_NIL:
350 case LV2_OSC_IMPULSE:
351 break;
352
353 case LV2_OSC_INT64:
354 if(!lv2_osc_writer_add_int64(writer, va_arg(args, int64_t)))
355 return false;
356 break;
357 case LV2_OSC_DOUBLE:
358 if(!lv2_osc_writer_add_double(writer, va_arg(args, double)))
359 return false;
360 break;
361 case LV2_OSC_TIMETAG:
362 if(!lv2_osc_writer_add_timetag(writer, va_arg(args, uint64_t)))
363 return false;
364 break;
365
366 case LV2_OSC_MIDI:
367 {
368 const int32_t len = va_arg(args, int32_t);
369 if(!lv2_osc_writer_add_midi(writer, len, va_arg(args, const uint8_t *)))
370 return false;
371 } break;
372 case LV2_OSC_SYMBOL:
373 if(!lv2_osc_writer_add_symbol(writer, va_arg(args, const char *)))
374 return false;
375 break;
376 case LV2_OSC_CHAR:
377 if(!lv2_osc_writer_add_char(writer, va_arg(args, int)))
378 return false;
379 break;
380 case LV2_OSC_RGBA:
381 {
382 const uint8_t r = va_arg(args, unsigned);
383 const uint8_t g = va_arg(args, unsigned);
384 const uint8_t b = va_arg(args, unsigned);
385 const uint8_t a = va_arg(args, unsigned);
386 if(!lv2_osc_writer_add_rgba(writer, r, g, b, a))
387 return false;
388 } break;
389 }
390 }
391
392 return true;
393 }
394
395 static inline bool
lv2_osc_writer_arg_vararg(LV2_OSC_Writer * writer,const char * fmt,...)396 lv2_osc_writer_arg_vararg(LV2_OSC_Writer *writer, const char *fmt, ...)
397 {
398 va_list args;
399 va_start(args, fmt);
400
401 const bool res = lv2_osc_writer_arg_varlist(writer, fmt, args);
402
403 va_end(args);
404
405 return res;
406 }
407
408 static inline bool
lv2_osc_writer_message_varlist(LV2_OSC_Writer * writer,const char * path,const char * fmt,va_list args)409 lv2_osc_writer_message_varlist(LV2_OSC_Writer *writer, const char *path, const char *fmt, va_list args)
410 {
411 if(!lv2_osc_writer_add_path(writer, path))
412 return false;
413
414 if(!lv2_osc_writer_add_format(writer, fmt))
415 return false;
416
417 return lv2_osc_writer_arg_varlist(writer, fmt, args);
418 }
419
420 static inline bool
lv2_osc_writer_message_vararg(LV2_OSC_Writer * writer,const char * path,const char * fmt,...)421 lv2_osc_writer_message_vararg(LV2_OSC_Writer *writer, const char *path, const char *fmt, ...)
422 {
423 va_list args;
424 va_start(args, fmt);
425
426 const bool res = lv2_osc_writer_message_varlist(writer, path, fmt, args);
427
428 va_end(args);
429
430 return res;
431 }
432
433 static inline bool
lv2_osc_writer_packet(LV2_OSC_Writer * writer,LV2_OSC_URID * osc_urid,LV2_URID_Unmap * unmap,uint32_t size,const LV2_Atom_Object_Body * body)434 lv2_osc_writer_packet(LV2_OSC_Writer *writer, LV2_OSC_URID *osc_urid,
435 LV2_URID_Unmap *unmap, uint32_t size, const LV2_Atom_Object_Body *body)
436 {
437 if(body->otype == osc_urid->OSC_Bundle)
438 {
439 const LV2_Atom_Object *timetag = NULL;
440 const LV2_Atom_Tuple *items = NULL;
441
442 if(!lv2_osc_bundle_body_get(osc_urid, size, body, &timetag, &items))
443 return false;
444
445 LV2_OSC_Timetag tt;
446 LV2_OSC_Writer_Frame bndl = { .ref = 0 };
447
448 lv2_osc_timetag_get(osc_urid, &timetag->atom, &tt);
449 if(!lv2_osc_writer_push_bundle(writer, &bndl, lv2_osc_timetag_parse(&tt)))
450 return false;
451
452 LV2_ATOM_TUPLE_FOREACH(items, atom)
453 {
454 const LV2_Atom_Object *obj= (const LV2_Atom_Object *)atom;
455 LV2_OSC_Writer_Frame itm = { .ref = 0 };
456
457 if( !lv2_osc_writer_push_item(writer, &itm)
458 || !lv2_osc_writer_packet(writer, osc_urid, unmap, obj->atom.size, &obj->body)
459 || !lv2_osc_writer_pop_item(writer, &itm) )
460 {
461 return false;
462 }
463 }
464
465 return lv2_osc_writer_pop_bundle(writer, &bndl);
466 }
467 else if(body->otype == osc_urid->OSC_Message)
468 {
469 const LV2_Atom_String *path = NULL;
470 const LV2_Atom_Tuple *arguments = NULL;
471
472 if(lv2_osc_message_body_get(osc_urid, size, body, &path, &arguments))
473 {
474 if(!lv2_osc_writer_add_path(writer, LV2_ATOM_BODY_CONST(path)))
475 return false;
476
477 char fmt [128]; //TODO how big?
478 char *ptr = fmt;
479 LV2_ATOM_TUPLE_FOREACH(arguments, atom)
480 {
481 *ptr++ = lv2_osc_argument_type(osc_urid, atom);
482 }
483 *ptr = '\0';
484 if(!lv2_osc_writer_add_format(writer, fmt))
485 return false;
486
487 LV2_ATOM_TUPLE_FOREACH(arguments, atom)
488 {
489 const LV2_Atom_Object *obj= (const LV2_Atom_Object *)atom;
490
491 if(atom->type == osc_urid->ATOM_Int)
492 {
493 if(!lv2_osc_writer_add_int32(writer, ((const LV2_Atom_Int *)atom)->body))
494 return false;
495 }
496 else if(atom->type == osc_urid->ATOM_Float)
497 {
498 if(!lv2_osc_writer_add_float(writer, ((const LV2_Atom_Float *)atom)->body))
499 return false;
500 }
501 else if(atom->type == osc_urid->ATOM_String)
502 {
503 if(!lv2_osc_writer_add_string(writer, LV2_ATOM_BODY_CONST(atom)))
504 return false;
505 }
506 else if(atom->type == osc_urid->ATOM_Chunk)
507 {
508 if(!lv2_osc_writer_add_blob(writer, atom->size, LV2_ATOM_BODY_CONST(atom)))
509 return false;
510 }
511
512 else if(atom->type == osc_urid->ATOM_Long)
513 {
514 if(!lv2_osc_writer_add_int64(writer, ((const LV2_Atom_Long *)atom)->body))
515 return false;
516 }
517 else if(atom->type == osc_urid->ATOM_Double)
518 {
519 if(!lv2_osc_writer_add_double(writer, ((const LV2_Atom_Double *)atom)->body))
520 return false;
521 }
522 else if( (atom->type == osc_urid->ATOM_Object) && (obj->body.otype == osc_urid->OSC_Timetag) )
523 {
524 LV2_OSC_Timetag tt;
525 lv2_osc_timetag_get(osc_urid, &obj->atom, &tt);
526 if(!lv2_osc_writer_add_timetag(writer, lv2_osc_timetag_parse(&tt)))
527 return false;
528 }
529
530 // there is nothing to do for: true, false, nil, impulse
531
532 else if(atom->type == osc_urid->ATOM_URID)
533 {
534 const char *symbol = unmap->unmap(unmap->handle, ((const LV2_Atom_URID *)atom)->body);
535 if(!symbol || !lv2_osc_writer_add_symbol(writer, symbol))
536 return false;
537 }
538 else if(atom->type == osc_urid->MIDI_MidiEvent)
539 {
540 uint8_t *m = NULL;
541 if(!lv2_osc_writer_add_midi_inline(writer, atom->size + 1, &m))
542 return false;
543 m[0] = 0x0; // port
544 memcpy(&m[1], LV2_ATOM_BODY_CONST(atom), atom->size);
545 }
546 else if(atom->type == osc_urid->ATOM_Literal)
547 {
548 const LV2_Atom_Literal *lit = (LV2_Atom_Literal *)atom;
549
550 if(lit->body.datatype == osc_urid->OSC_Char)
551 {
552 const char c = *(const char *)LV2_ATOM_CONTENTS_CONST(LV2_Atom_Literal, lit);
553 if(!lv2_osc_writer_add_char(writer, c))
554 return false;
555 }
556 else if(lit->body.datatype == osc_urid->OSC_RGBA)
557 {
558 const char *rgba = LV2_ATOM_CONTENTS_CONST(LV2_Atom_Literal, atom);
559 uint8_t r, g, b, a;
560 if(sscanf(rgba, "%02"SCNx8"%02"SCNx8"%02"SCNx8"%02"SCNx8, &r, &g, &b, &a) != 4)
561 return false;
562 if(!lv2_osc_writer_add_rgba(writer, r, g, b, a))
563 return false;
564 }
565 }
566 }
567 }
568
569 return true;
570 }
571
572 return false;
573 }
574
575 #ifdef __cplusplus
576 } // extern "C"
577 #endif
578
579 #endif // LV2_OSC_WRITER_H
580