Subversion Repositories svn.mios32

Rev

Rev 1005 | Rev 1007 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1003 tk 1
/* -*- mode:C++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*- */
2
// $Id: OscHelper.cpp 1006 2010-04-23 23:49:05Z tk $
3
/*
4
 * OSC Help Routines
5
 * (taken from MIOS32_OSC)
6
 * Documentation: see there
7
 *
8
 * ==========================================================================
9
 *
10
 *  Copyright (C) 2010 Thorsten Klose (tk@midibox.org)
11
 *  Licensed for personal non-commercial use only.
12
 *  All other rights reserved.
13
 *
14
 * ==========================================================================
15
 */
16
 
17
#include "OscHelper.h"
18
#include <string.h>
19
 
1004 philetaylo 20
 
1006 tk 21
#define OSC_BUFFER_MAX 2048
22
 
23
 
1003 tk 24
//==============================================================================
25
OscHelper::OscHelper()
26
{
27
}
28
 
29
OscHelper::~OscHelper()
30
{
31
}
32
 
33
 
34
//==============================================================================
35
// strnlen() not available for all libc's, therefore we use a local solution here
36
static size_t my_strnlen(char *str, size_t max_len)
37
{
38
  size_t len = 0;
39
 
40
  while( *str++ && (len < max_len) )
41
    ++len;
42
 
43
  return len;
44
}
45
 
46
//==============================================================================
1004 philetaylo 47
// stpcpy is not available in windows, therefore we use a local solution here
48
#ifdef WIN32
49
static char *stpcpy(char *dest, const char *src){strcpy(dest,src);return dest +strlen(dest);}
50
#endif
51
 
52
//==============================================================================
1003 tk 53
/////////////////////////////////////////////////////////////////////////////
54
// Gets a word (4 bytes) from buffer
55
// \param[in] buffer pointer to OSC message buffer 
56
// \return 32bit unsigned integer
57
/////////////////////////////////////////////////////////////////////////////
58
unsigned OscHelper::getWord(unsigned char *buffer)
59
{
60
  // taking endianess into account
61
  return
62
    (((unsigned)*(buffer+0)) << 24) | (((unsigned)*(buffer+1)) << 16) |
63
    (((unsigned)*(buffer+2)) <<  8) | (((unsigned)*(buffer+3)) <<  0);
64
}
65
 
66
/////////////////////////////////////////////////////////////////////////////
67
// Puts a word (4 bytes) into buffer
68
// \param[in] buffer pointer to OSC message buffer 
69
// \param[in] word 32bit word
70
// \return buffer pointer behind the inserted entry
71
/////////////////////////////////////////////////////////////////////////////
72
unsigned char *OscHelper::putWord(unsigned char *buffer, unsigned word)
73
{
74
  *buffer++ = (word >> 24) & 0xff;
75
  *buffer++ = (word >> 16) & 0xff;
76
  *buffer++ = (word >>  8) & 0xff;
77
  *buffer++ = (word >>  0) & 0xff;
78
  return buffer;
79
}
80
 
81
/////////////////////////////////////////////////////////////////////////////
1006 tk 82
// Creates a word
83
/////////////////////////////////////////////////////////////////////////////
84
Array<uint8> OscHelper::createWord(const unsigned& word)
85
{
86
    unsigned char buffer[4];
87
    putWord(buffer, word);
88
    return Array<uint8>(buffer, 4);
89
}
90
 
91
 
92
/////////////////////////////////////////////////////////////////////////////
1003 tk 93
// Gets a timetag (8 bytes) from buffer
94
// \param[in] buffer pointer to OSC message buffer 
95
// \return timetag (seconds and fraction part)
96
/////////////////////////////////////////////////////////////////////////////
97
OscHelper::OscTimetagT OscHelper::getTimetag(unsigned char *buffer)
98
{
99
  OscTimetagT timetag;
100
  timetag.seconds = getWord(buffer);
101
  timetag.fraction = getWord(buffer+4);
102
  return timetag;
103
}
104
 
105
/////////////////////////////////////////////////////////////////////////////
106
// Puts a timetag (8 bytes) into buffer
107
// \param[in] buffer pointer to OSC message buffer 
108
// \param[in] timetag the timetag which should be inserted
109
// \return buffer pointer behind the inserted entry
110
/////////////////////////////////////////////////////////////////////////////
111
unsigned char *OscHelper::putTimetag(unsigned char *buffer, OscTimetagT timetag)
112
{
113
  buffer = putWord(buffer, timetag.seconds);
114
  buffer = putWord(buffer, timetag.fraction);
115
  return buffer;
116
}
117
 
118
/////////////////////////////////////////////////////////////////////////////
1006 tk 119
// Creates a timetag
120
/////////////////////////////////////////////////////////////////////////////
121
Array<uint8> OscHelper::createTimetag(const OscTimetagT& timetag)
122
{
123
    unsigned char buffer[8];
124
    putTimetag(buffer, timetag);
125
    return Array<uint8>(buffer, 8);
126
}
127
 
128
 
129
/////////////////////////////////////////////////////////////////////////////
1003 tk 130
// Gets a word (4 bytes) from buffer and converts it to a 32bit signed integer.
131
// \param[in] buffer pointer to OSC message buffer 
132
// \return 32bit signed integer
133
/////////////////////////////////////////////////////////////////////////////
134
int OscHelper::getInt(unsigned char *buffer)
135
{
136
  return (int)getWord(buffer);
137
}
138
 
139
/////////////////////////////////////////////////////////////////////////////
140
// Puts a 32bit signed integer into buffer
141
// \param[in] buffer pointer to OSC message buffer 
142
// \param[in] value the integer value which should be inserted
143
// \return buffer pointer behind the inserted entry
144
/////////////////////////////////////////////////////////////////////////////
145
unsigned char *OscHelper::putInt(unsigned char *buffer, int value)
146
{
147
  return putWord(buffer, (unsigned)value);
148
}
149
 
150
/////////////////////////////////////////////////////////////////////////////
1006 tk 151
// Creates a 32bit signed integer
152
/////////////////////////////////////////////////////////////////////////////
153
Array<uint8> OscHelper::createInt(const int& value)
154
{
155
    unsigned char buffer[4];
156
    putInt(buffer, value);
157
    return Array<uint8>(buffer, 4);
158
}
159
 
160
 
161
/////////////////////////////////////////////////////////////////////////////
1003 tk 162
// Gets a word (4 bytes) from buffer and converts it to a float with 
163
// normal precession
164
// \param[in] buffer pointer to OSC message buffer 
165
// \return float with normal procession
166
/////////////////////////////////////////////////////////////////////////////
167
float OscHelper::getFloat(unsigned char *buffer)
168
{
169
#if 0
170
  unsigned word = getWord(buffer);
171
  return *(float *)(&word);
172
#else
173
  // TK: doesn't work with my gcc installation (i686-apple-darwin9-gcc-4.0.1):
174
  // float not converted correctly - it works when optimisation is disabled!
175
  // according to http://gcc.gnu.org/ml/gcc-bugs/2003-02/msg01128.html this isn't a bug...
176
  // workaround:
177
  union { unsigned word; float f; } converted;
178
  converted.word = getWord(buffer);
179
  return converted.f;
180
#endif
181
}
182
 
183
 
184
/////////////////////////////////////////////////////////////////////////////
185
// Puts a float with normal precission into buffer
186
// \param[in] buffer pointer to OSC message buffer 
187
// \param[in] value the float value which should be inserted
188
// \return buffer pointer behind the inserted entry
189
/////////////////////////////////////////////////////////////////////////////
190
unsigned char *OscHelper::putFloat(unsigned char *buffer, float value)
191
{
192
  union { unsigned word; float f; } converted;
193
  converted.f = value;
194
  return putWord(buffer, converted.word);
195
}
196
 
197
/////////////////////////////////////////////////////////////////////////////
1006 tk 198
// Creates a float with normal precission
199
/////////////////////////////////////////////////////////////////////////////
200
Array<uint8> OscHelper::createFloat(const float& value)
201
{
202
    unsigned char buffer[4];
203
    putFloat(buffer, value);
204
    return Array<uint8>(buffer, 4);
205
}
206
 
207
 
208
/////////////////////////////////////////////////////////////////////////////
1003 tk 209
// Returns pointer to a string in message buffer
210
// \param[in] buffer pointer to OSC message buffer 
211
// \return zero-terminated string
212
/////////////////////////////////////////////////////////////////////////////
213
char *OscHelper::getString(unsigned char *buffer)
214
{
215
  return (char *)buffer; // OSC protocol ensures zero termination (checked in MIOS32_OSC_SearchElement)
216
}
217
 
218
/////////////////////////////////////////////////////////////////////////////
219
// Puts a string into buffer and pads with 0 until word boundary has been reached
220
// \param[in] buffer pointer to OSC message buffer 
221
// \param[in] value the string which should be inserted
222
// \return buffer pointer behind the inserted entry
223
/////////////////////////////////////////////////////////////////////////////
1006 tk 224
unsigned char *OscHelper::putString(unsigned char *buffer, const char *str)
1003 tk 225
{
226
  unsigned char *buffer_start = buffer;
227
 
228
  buffer = (unsigned char *)stpcpy((char *)buffer, str);
229
  *buffer++ = 0;
230
 
231
  // pad with zeroes until word boundary is reached
232
  while( (unsigned)(buffer-buffer_start) % 4 )
233
    *buffer++ = 0;
234
 
235
  return buffer;
236
}
237
 
238
/////////////////////////////////////////////////////////////////////////////
1006 tk 239
// Creates a string and pads with 0 until word boundary has been reached
240
/////////////////////////////////////////////////////////////////////////////
241
Array<uint8> OscHelper::createString(const String &str)
242
{
243
    unsigned char *buffer = new unsigned char(str.length() + 4);
244
    unsigned char *endPtr = buffer;
245
    endPtr = putString(buffer, (const char *)str);
246
    Array<uint8> tmp = Array<uint8>(buffer, endPtr-buffer);
247
    delete buffer;
248
    return tmp;
249
}
250
 
251
 
252
/////////////////////////////////////////////////////////////////////////////
1003 tk 253
// Returns the length of a Blob
254
// \param[in] buffer pointer to OSC message buffer 
255
// \return blob length
256
/////////////////////////////////////////////////////////////////////////////
257
unsigned OscHelper::getBlobLength(unsigned char *buffer)
258
{
1006 tk 259
  return getWord(buffer);
1003 tk 260
}
261
 
262
/////////////////////////////////////////////////////////////////////////////
263
// Returns the data part of a Blob
264
// \param[in] buffer pointer to OSC message buffer 
265
// \return pointer to data part of a Blob
266
/////////////////////////////////////////////////////////////////////////////
267
unsigned char *OscHelper::getBlobData(unsigned char *buffer)
268
{
269
  return buffer+4;
270
}
271
 
272
/////////////////////////////////////////////////////////////////////////////
273
// Puts an OSC-Blob into buffer and pads with 0 until word boundary has been reached
274
// \param[in] buffer pointer to OSC message buffer 
275
// \param[in] data blob data
276
// \param[in] len blob size
277
// \return buffer pointer behind the inserted entry
278
/////////////////////////////////////////////////////////////////////////////
279
unsigned char *OscHelper::putBlob(unsigned char *buffer, unsigned char *data, unsigned len)
280
{
281
  // ensure that length considers word alignment
282
  unsigned aligned_len = (len+3) & 0xfffffffc;
283
 
284
  // add length
285
  buffer = putWord(buffer, aligned_len);
286
 
287
  // add bytes
288
  int i;
289
  for(i=0; i<len; ++i)
290
    *buffer++ = *data++;
291
 
292
  // pad with zeroes
1006 tk 293
  while( i % 4 ) {
1003 tk 294
    *buffer++ = 0;
1006 tk 295
    ++i;
296
  }
1003 tk 297
 
298
  return buffer;
299
}
300
 
1006 tk 301
/////////////////////////////////////////////////////////////////////////////
302
// Creates an OSC-Blob and pads with 0 until word boundary has been reached
303
/////////////////////////////////////////////////////////////////////////////
304
Array<uint8> OscHelper::createBlob(unsigned char *data, const unsigned& len)
305
{
306
    unsigned char *buffer = new unsigned char(len+4);
307
    unsigned char *endPtr = buffer;
308
    endPtr = putBlob(buffer, data, len);
309
    Array<uint8> tmp = Array<uint8>(buffer, endPtr-buffer);
310
    delete buffer;
311
    return tmp;
312
}
1003 tk 313
 
1006 tk 314
 
315
 
1003 tk 316
/////////////////////////////////////////////////////////////////////////////
317
// Gets two words (8 bytes) from buffer and converts them to a 64bit signed integer.
318
// \param[in] buffer pointer to OSC message buffer 
319
// \return 64bit signed integer
320
/////////////////////////////////////////////////////////////////////////////
321
long long OscHelper::getLongLong(unsigned char *buffer)
322
{
323
  return ((long long)getWord(buffer) << 32) | getWord(buffer+4);
324
}
325
 
326
/////////////////////////////////////////////////////////////////////////////
327
// Puts a 64bit signed integer into buffer
328
// \param[in] buffer pointer to OSC message buffer 
329
// \param[in] value the "long long" value which should be inserted
330
// \return buffer pointer behind the inserted entry
331
/////////////////////////////////////////////////////////////////////////////
332
unsigned char *OscHelper::putLongLong(unsigned char *buffer, long long value)
333
{
334
  buffer = putWord(buffer, (unsigned)(value >> 32));
335
  buffer = putWord(buffer, (unsigned)value);
336
  return buffer;
337
}
338
 
1006 tk 339
/////////////////////////////////////////////////////////////////////////////
340
// Creates a 64bit signed integer
341
/////////////////////////////////////////////////////////////////////////////
342
Array<uint8> OscHelper::createLongLong(const long long &value)
343
{
344
    unsigned char buffer[8];
345
    putLongLong(buffer, value);
346
    return Array<uint8>(buffer, 8);
347
}
1003 tk 348
 
1006 tk 349
 
1003 tk 350
/////////////////////////////////////////////////////////////////////////////
351
// Gets two words (8 bytes) from buffer and converts them to a float with 
352
// double precession
353
// \param[in] buffer pointer to OSC message buffer 
354
// \return float with double procession
355
/////////////////////////////////////////////////////////////////////////////
356
double OscHelper::getDouble(unsigned char *buffer)
357
{
358
#if 0
359
  long long word = ((long long)getWord(buffer) << 32) | getWord(buffer+4);
360
  return *(double *)(&word);
361
#else
362
  // TK: doesn't work with my gcc installation (i686-apple-darwin9-gcc-4.0.1):
363
  // float not converted correctly - it works when optimisation is disabled!
364
  // according to http://gcc.gnu.org/ml/gcc-bugs/2003-02/msg01128.html this isn't a bug...
365
  // workaround:
366
  union { long long word; double d; } converted;
367
  converted.word = getLongLong(buffer);
368
  return converted.d;
369
#endif
370
}
371
 
372
/////////////////////////////////////////////////////////////////////////////
373
// Puts a float with double precission into buffer
374
// \param[in] buffer pointer to OSC message buffer 
375
// \param[in] value the double value which should be inserted
376
// \return buffer pointer behind the inserted entry
377
/////////////////////////////////////////////////////////////////////////////
378
unsigned char *OscHelper::putDouble(unsigned char *buffer, double value)
379
{
380
  union { long long word; double d; } converted;
381
  converted.d = value;
382
  return putLongLong(buffer, converted.word);
383
}
384
 
1006 tk 385
/////////////////////////////////////////////////////////////////////////////
386
// Creates a float with double precission
387
/////////////////////////////////////////////////////////////////////////////
388
Array<uint8> OscHelper::createDouble(const double& value)
389
{
390
    unsigned char buffer[8];
391
    putDouble(buffer, value);
392
    return Array<uint8>(buffer, 8);
393
}
1003 tk 394
 
1006 tk 395
 
1003 tk 396
/////////////////////////////////////////////////////////////////////////////
397
// Returns a character
398
// \param[in] buffer pointer to OSC message buffer 
399
// \return a single character
400
/////////////////////////////////////////////////////////////////////////////
401
char OscHelper::getChar(unsigned char *buffer)
402
{
403
  return *buffer; // just for completeness..
404
}
405
 
406
/////////////////////////////////////////////////////////////////////////////
407
// Puts a character into buffer and pads with 3 zeros (word aligned)
408
// \param[in] buffer pointer to OSC message buffer 
409
// \param[in] c the character which should be inserted
410
// \return buffer pointer behind the inserted entry
411
/////////////////////////////////////////////////////////////////////////////
412
unsigned char *OscHelper::putChar(unsigned char *buffer, char c)
413
{
414
  return putWord(buffer, (unsigned)c);
415
}
416
 
1006 tk 417
/////////////////////////////////////////////////////////////////////////////
418
// Creates a character and pads with 3 zeros
419
/////////////////////////////////////////////////////////////////////////////
420
Array<uint8> OscHelper::createChar(const char& value)
421
{
422
    unsigned char buffer[4];
423
    putChar(buffer, value);
424
    return Array<uint8>(buffer, 4);
425
}
1003 tk 426
 
427
 
428
/////////////////////////////////////////////////////////////////////////////
429
// Returns a MIDI package
430
// \param[in] buffer pointer to OSC message buffer 
431
// \return a MIOS32 compliant MIDI package
432
/////////////////////////////////////////////////////////////////////////////
433
unsigned OscHelper::getMIDI(unsigned char *buffer)
434
{
435
    // note: no extra conversion to MIOS32 MIDI package format
436
    return getWord(buffer);
437
}
438
 
439
/////////////////////////////////////////////////////////////////////////////
440
// Puts a MIDI package into buffer
441
// \param[in] buffer pointer to OSC message buffer 
442
// \param[in] p the MIDI package which should be inserted
443
// \return buffer pointer behind the inserted entry
444
/////////////////////////////////////////////////////////////////////////////
445
unsigned char *OscHelper::putMIDI(unsigned char *buffer, unsigned p)
446
{
447
    // note: no extra conversion to MIOS32 MIDI package format
448
    return putWord(buffer, p);
449
}
1005 tk 450
 
1006 tk 451
/////////////////////////////////////////////////////////////////////////////
452
// Creates a MIDI package
453
/////////////////////////////////////////////////////////////////////////////
454
Array<uint8> OscHelper::createMIDI(const unsigned& p)
455
{
456
    unsigned char buffer[4];
457
    putMIDI(buffer, p);
458
    return Array<uint8>(buffer, 4);
459
}
1005 tk 460
 
1006 tk 461
 
1005 tk 462
/////////////////////////////////////////////////////////////////////////////
463
// Parses an incoming OSC packet and calls OSC methods defined in searchTree
464
// on matching addresses
465
// \param[in] packet pointer to OSC packet
466
// \param[in] len length of packet
467
// \param[in] searchTree a tree which defines address parts and methods to be called
468
// \return 0 if packet has been parsed w/o errors
469
// \return -1 if packet format invalid
470
// \return -2 if the packet contains an OSC element with invalid format
471
// \return -3 if the packet contains an OSC element with an unsupported format
472
// returns -4 if MIOS32_OSC_MAX_PATH_PARTS has been exceeded
473
/////////////////////////////////////////////////////////////////////////////
474
int OscHelper::parsePacket(unsigned char *packet, const unsigned& len, OscHelper::OscSearchTreeT *searchTree)
475
{
476
    // store osc arguments (and more...) into oscArgs variable
477
    OscArgsT oscArgs;
478
 
479
    // check if we got a bundle
480
    if( strncmp((char *)packet, "#bundle", len) == 0 ) {
481
        unsigned pos = 8;
482
 
483
        // we expect at least 8 bytes for the timetag
484
        if( (pos+8) > len )
485
            return -1; // invalid format
486
 
487
 
488
        // get timetag
489
        oscArgs.timetag = getTimetag((unsigned char *)packet+pos);
490
        pos += 8;
491
 
492
        // parse elements
493
        while( (pos+4) <= len ) {
494
            // get element size
495
            unsigned elem_size = getWord((unsigned char *)(packet+pos));
496
            pos += 4;
497
 
498
            // invalid packet if elem_size exceeds packet length
499
            if( (pos+elem_size) > len )
500
                return -1; // invalid packet
501
 
502
            // parse element if size > 0
503
            if( elem_size ) {
504
                int status = searchElement((unsigned char *)(packet+pos), elem_size, &oscArgs, searchTree);
505
                if( status < 0 )
506
                    return status;
507
            }
508
 
509
            // switch to next element
510
            pos += elem_size;
511
        }
512
 
513
    } else {
514
        // no timetag
515
        oscArgs.timetag.seconds = 0;
1006 tk 516
        oscArgs.timetag.fraction = 1;
1005 tk 517
 
518
        // get element size
519
        unsigned elemSize = getWord(packet);
520
 
521
        // invalid packet if elemSize exceeds packet length
522
        if( elemSize > len )
523
            return -1; // invalid packet
524
 
525
        // parse element if size > 0
526
        if( elemSize ) {
527
            int status = searchElement((unsigned char *)(packet+4), elemSize, &oscArgs, searchTree);
528
            if( status < 0 )
529
                return status;
530
        }
531
    }
532
 
533
    return 0; // no error
534
}
535
 
536
 
537
/////////////////////////////////////////////////////////////////////////////
538
// Internal function:
539
// parses a single OSC element
540
// returns -2 if element has invalid format
541
// returns -3 if element contains an unsupported format
542
// returns -4 if MIOS32_OSC_MAX_PATH_PARTS has been exceeded
543
/////////////////////////////////////////////////////////////////////////////
544
int OscHelper::searchElement(unsigned char *buffer, const unsigned& len, OscHelper::OscArgsT *oscArgs, OscHelper::OscSearchTreeT *searchTree)
545
{
546
    // exit immediately if element is empty
547
    if( !len )
548
        return 0;
549
 
550
    // according to OSC spec, the path could be ommitted, and the element could start with the argument list
551
    // I don't know how to handle this correctly, but we should at least exit in this case
552
    unsigned char *path = buffer;
553
    if( *path == ',' )
554
        return -3; // unsupported element format
555
 
556
    // path: determine string length
557
    size_t pathLen = my_strnlen((char *)path, len);
558
 
559
    // check for valid string
560
    if( pathLen < 2 || path[pathLen] != 0 ) // expect at least two characters, e.g. "/*"
561
        return -2; // invalid element format
562
 
563
    // path must start with '/'
564
    if( *path != '/' )
565
        return -2; // invalid element format
566
 
567
    // tags are starting at word aligned offset
568
    // add +1 to pathLen, since \0 terminator is counted as well
569
    size_t tagsPos = (pathLen+1 + 3) & 0xfffffc;
570
 
571
    // check that tags are at valid position
572
    if( tagsPos >= len )
573
        return -2; // invalid element format
574
 
575
    // tags: determine string length
576
    unsigned char *tags = (unsigned char *)(buffer + tagsPos);
577
    size_t tagsLen = my_strnlen((char *)tags, len-tagsPos);
578
 
579
    // check for valid string
580
    if( tagsLen == 0 || tags[tagsLen] != 0 )
581
        return -2; // invalid element format
582
 
583
    // check that tags are starting with comma
584
    if( *tags != ',' )
585
        return -2; // invalid element format
586
 
587
    // number of arguments:
588
    unsigned numArgs = tagsLen - 1;
589
 
590
    // limit by max number of args which can be stored in oscArgs structure
591
    if( numArgs > MIOS32_OSC_MAX_ARGS )
592
        numArgs = MIOS32_OSC_MAX_ARGS;
593
 
594
    // arguments are starting at word aligned offset
595
    // add +1 to tagsLen, since \0 terminator is counted as well
596
    size_t argPos = (tagsPos + tagsLen+1 + 3) & 0xfffffc;
597
 
598
    // parse arguments
599
    oscArgs->numArgs = 0;
600
    unsigned arg;
601
    for(arg=0; arg<numArgs; ++arg) {
602
        // check that argument is at valid position
603
        if( argPos > len ) // TK: use > instead of >= to cover non-value parameters like T/F/I/...
604
            return -2; // invalid element format
605
 
606
        // store type and pointer to argument
607
        oscArgs->argType[oscArgs->numArgs] = tags[arg+1];
608
        oscArgs->argPtr[oscArgs->numArgs] = (unsigned char *)(buffer + argPos);
609
 
610
        // branch depending on argument tag
611
        unsigned char knownArg = 0;
612
        switch( tags[arg+1] ) {
613
        case 'i': // int32
614
        case 'f': // float32
615
        case 'c': // ASCII character
616
        case 'r': // 32bit RGBA color
617
        case 'm': // 4 byte MIDI message
618
            knownArg = 1;
619
            argPos += 4;
620
            break;
621
 
622
        case 's':   // OSC-string
623
        case 'S': { // OSC alternate string
624
            knownArg = 1;
625
            char *str = (char *)oscArgs->argPtr[oscArgs->numArgs];
626
            size_t strLen = my_strnlen(str, len-argPos);
627
            // check for valid string
628
            if( strLen == 0 || str[strLen] != 0 )
629
                return -2; // invalid element format
630
            // next argument at word aligned offset
631
            // add +1 to strLen, since \0 terminator is counted as well
632
            argPos = (argPos + strLen+1 + 3) & 0xfffffc;
633
        } break;
634
 
635
        case 'b': { // OSC-blob
636
            knownArg = 1;
637
            unsigned blobLen = getBlobLength((unsigned char *)(buffer + argPos));
638
            // next argument at word aligned offset
639
            argPos = (argPos + 4 + blobLen + 3) & 0xfffffc;
640
        } break;
641
 
642
        case 'h': // long64
643
        case 't': // OSC timetag
644
        case 'd': // float64 (double)
645
            knownArg = 1;
646
            argPos += 8;
647
            break;
648
 
649
        case 'T': // TRUE
650
        case 'F': // FALSE
651
        case 'N': // NIL
652
        case 'I': // Infinitum
653
        case '[': // Begin of Array
654
        case ']': // End of Array
655
            knownArg = 1;
656
            break;
657
        }
658
 
659
        // according to OSC V1.0 spec, nonstandard arguments should be discarded (don't report error)
660
        // since we don't know the position to the next argument, we have to stop parsing here!
661
        if( knownArg )
662
            ++oscArgs->numArgs;
663
        else
664
            break;
665
    }
666
 
667
    // finally parse for elements which are matching the OSC address
668
    oscArgs->numPathParts = 0;
669
    return searchPath((char *)&path[1], oscArgs, 0x00000000, searchTree);
670
}
671
 
672
 
673
/////////////////////////////////////////////////////////////////////////////
674
// Internal function:
675
// searches in searchTree for matching OSC addresses
676
// returns -4 if MIOS32_OSC_MAX_PATH_PARTS has been exceeded
677
/////////////////////////////////////////////////////////////////////////////
678
int OscHelper::searchPath(char *path, OscHelper::OscArgsT *oscArgs, const unsigned& methodArg, OscHelper::OscSearchTreeT *searchTree)
679
{
680
    if( oscArgs->numPathParts >= MIOS32_OSC_MAX_PATH_PARTS )
681
        return -4; // maximum number of path parts exceeded
682
 
683
    while( searchTree->address != NULL ) {
684
        // compare OSC address with name of tree item
685
        unsigned char match = 1;
686
        unsigned char wildcard = 0;
687
 
688
        char *str1 = path;
689
        char *str2 = (char *)searchTree->address;
690
        size_t sepPos = 0;
691
 
692
        while( *str1 != 0 && *str1 != '/' ) {
693
            if( *str1 == '*' ) {
694
                // '*' wildcard: continue to end of address part
695
                while( *str1 != 0 && *str1 != '/' ) {
696
                    ++sepPos;
697
                    ++str1;
698
                }
699
                wildcard = 1;
700
                break;
701
            } else {
702
                // no wildcard: check for matching characters
703
                ++sepPos;
704
                if( *str2 == 0 || (*str2 != *str1 && *str1 != '?') ) {
705
                    match = 0;
706
                    break;
707
                }
708
                ++str1;
709
                ++str2;
710
            }
711
        }
712
 
713
        if( !wildcard && *str2 != 0 ) // we haven't parsed the complete string
714
            match = 0;
715
 
716
        if( match ) {
717
            // store number of path parts in local variable, since content of oscArgs is changed recursively
718
            // we don't want to copy the whole structure to save (a lot of...) memory
719
            unsigned char numPathParts = oscArgs->numPathParts;
720
            // add pointer to path part
721
            oscArgs->pathPart[numPathParts] = (char *)searchTree->address;
722
            oscArgs->numPathParts = numPathParts + 1;
723
 
724
            // OR method args of current node to the args to propagate optional parameters
725
            unsigned combinedMethodArg = methodArg | searchTree->methodArg;
726
 
727
            if( searchTree->oscListener ) {
728
                searchTree->oscListener->parsedOscPacket(*oscArgs, combinedMethodArg);
729
            } else if( searchTree->next ) {
730
 
731
                // continue search in next hierarchy level
732
                int status = searchPath((char *)&path[sepPos+1], oscArgs, combinedMethodArg, searchTree->next);
733
                if( status < 0 )
734
                    return status;
735
            }
736
 
737
            // restore number of path parts (which has been changed recursively)
738
            oscArgs->numPathParts = numPathParts;
739
        }
740
 
741
        ++searchTree;
742
    }
743
 
744
    // Table Terminator has been reached - check if method is defined that will be executed on any received OSC element
745
    // TODO: bring this into MIOS32 as well?
746
    if( searchTree->oscListener ) {
747
        oscArgs->numPathParts = 1;
748
        oscArgs->pathPart[0] = (char *)path;
749
        searchTree->oscListener->parsedOscPacket(*oscArgs, 0);
750
    }
751
 
752
    return 0; // no error
753
}
754
 
755
 
756
/////////////////////////////////////////////////////////////////////////////
757
// Converts an OSC element in readable format.
758
//
759
// Usage Example:
760
// \code
761
// void parsedOscPacket(const OscHelper::OscArgsT& oscArgs, const const unsigned& methodArg)
762
// {
763
//     if( oscString == String::empty )
764
//         oscString = T("@") + String::formatted(T("%d.%d "), oscArgs.timetag.seconds, oscArgs.timetag.fraction);
765
//     else
766
//         oscString += " ";
767
// 
768
//     oscString += OscHelper::element2String(oscArgs);
769
// }
770
// \endcode
771
//
772
// \param[in] oscArgs pointer to OSC argument list as forwarded by parsePacket()
773
// \return < 0 on errors
774
/////////////////////////////////////////////////////////////////////////////
775
String OscHelper::element2String(const OscArgsT& oscArgs)
776
{
777
    String str;
778
 
779
    for(int i=0; i<oscArgs.numPathParts; ++i)
780
        str += T("/") + String(oscArgs.pathPart[i]);
781
 
782
    for(int i=0; i < oscArgs.numArgs; ++i) {
783
        str += T(" ") + String::formatted(T("%c"), oscArgs.argType[i]);
784
 
785
        switch( oscArgs.argType[i] ) {
786
        case 'i': // int32
787
            str += String(getInt(oscArgs.argPtr[i]));
788
            break;
789
 
790
        case 'f': // float32
1006 tk 791
            str += String::formatted(T("%g"), getFloat(oscArgs.argPtr[i]));
1005 tk 792
            break;
793
 
794
        case 's': // string
795
        case 'S': // alternate string
796
            str += String(getString(oscArgs.argPtr[i]));
797
            break;
798
 
1006 tk 799
        case 'b': { // blob
800
            unsigned len = getBlobLength(oscArgs.argPtr[i]);
801
            unsigned char *data = getBlobData(oscArgs.argPtr[i]);
802
            str += String(len) + T(":0x") + String::toHexString(data, len);
803
        } break;
1005 tk 804
 
805
        case 'h': // int64
806
            str += String(getLongLong(oscArgs.argPtr[i]));
807
            break;
808
 
809
        case 't': { // timetag
810
            OscTimetagT timetag = getTimetag(oscArgs.argPtr[i]);
811
            str += String::formatted(T("%d.%d"), timetag.seconds, timetag.fraction);
812
        } break;
813
 
814
        case 'd': // float64 (double)
1006 tk 815
            str += String::formatted(T("%g"), getDouble(oscArgs.argPtr[i]));
1005 tk 816
            break;
817
 
818
        case 'c': // ASCII character
819
            str += String::formatted(T("%c"), getChar(oscArgs.argPtr[i]));
820
            break;
821
 
822
        case 'r': // 32 bit RGBA color
823
            str += String::formatted(T("0x%08x"), getWord(oscArgs.argPtr[i]));
824
            break;
825
 
826
        case 'm': // MIDI message
827
            str += String::formatted(T("0x%08x"), getMIDI(oscArgs.argPtr[i]));
828
            break;
829
 
830
        case 'T': // TRUE
831
        case 'F': // FALSE
832
        case 'N': // NIL
833
        case 'I': // Infinitum
834
        case '[': // beginning of array
835
        case ']': // end of array
836
            break;
837
 
838
        default:
839
            str += T("(unknown)");
840
        }
841
    }
842
 
843
    return str;
844
}
1006 tk 845
 
846
 
847
/////////////////////////////////////////////////////////////////////////////
848
// Local help function to create an element
849
/////////////////////////////////////////////////////////////////////////////
850
Array<uint8> OscHelper::createElement(const Array<uint8>& oscPath, const String& oscArgsString, const Array<uint8>& oscArgs)
851
{
852
    // what looks better - the original MIOS32 based method, or this C++ like approach?
853
 
854
    Array<uint8> tmp;
855
    tmp.addArray(oscPath);
856
    tmp.addArray(createString(oscArgsString));
857
    tmp.addArray(oscArgs);
858
 
859
    Array<uint8> tmp2;
860
    tmp2.addArray(createWord(tmp.size()));
861
    tmp2.addArray(tmp);
862
 
863
    return tmp2;
864
}
865
 
866
 
867
/////////////////////////////////////////////////////////////////////////////
868
// Converts a String into a complete OSC packet
869
// \return an empty Array<uint8> on errors
870
/////////////////////////////////////////////////////////////////////////////
871
Array<uint8> OscHelper::string2Packet(const String& oscString, String& statusMessage)
872
{
873
    StringArray words;
874
 
875
    statusMessage = T("ERROR: internal error!");
876
 
877
    // split string into words
878
    int wordBegin = 0;
879
    while( wordBegin < oscString.length() ) {
880
        int wordEnd=oscString.indexOfChar(wordBegin, ' ');
881
 
882
        if( wordEnd < 0 ) {
883
            String lastWord = oscString.substring(wordBegin);
884
            if( lastWord != String::empty )
885
                words.add(lastWord);
886
            break;
887
        } else if( wordEnd == wordBegin )
888
            ++wordBegin;
889
        else {
890
            words.add(oscString.substring(wordBegin, wordEnd));
891
            wordBegin = wordEnd+1;
892
        }
893
    }
894
 
895
    // empty packet?
896
    if( !words.size() ) {
897
        statusMessage = String::empty;
898
        return Array<uint8>();
899
    }
900
 
901
 
902
    // prepare timetag
903
    OscTimetagT timetag;
904
    timetag.seconds = 0;
905
    timetag.fraction = 1;
906
    bool gotTimetag = 0;
907
 
908
    // parse elements
909
    int numElements = 0;
910
    Array<uint8> oscElements;
911
    Array<uint8> oscPath;
912
    String oscArgsString(",");
913
    Array<uint8> oscArgs;
914
 
915
    for(int i=0; i<words.size(); ++i) {
916
        String word = words[i];
917
        String arg = String::formatted(T("%c"), word[0]);
918
 
919
        switch( word[0] ) {
920
        case '@': {
921
            if( gotTimetag ) {
922
                statusMessage = T("ERROR: more than one timetag!");
923
                return Array<uint8>();
924
            } else if( i != 0 ) {
925
                statusMessage = T("ERROR: timetag expected as first argument!");
926
                return Array<uint8>();
927
            } else {
928
                unsigned seconds;
929
                unsigned fraction;
930
                if( sscanf((const char*)word, "@%u.%u", &seconds, &fraction) != 2 ) {
931
                    statusMessage = T("syntax: <seconds>.<fraction>");
932
                    return Array<uint8>();
933
                } else {
934
                    timetag.seconds = seconds;
935
                    timetag.fraction = fraction;
936
                    gotTimetag = 1;
937
                }
938
            }
939
        } break;
940
 
941
        case '/': {
942
            if( oscPath.size() != 0 ) {
943
                ++numElements;
944
                oscElements.addArray(createElement(oscPath, oscArgsString, oscArgs));
945
            }
946
 
947
            oscPath = createString(word);
948
            oscArgsString = ",";
949
            oscArgs.clear();
950
        } break;
951
 
952
        case 'i': // int32
953
            if( word.length() == 1 ) {
954
                statusMessage = T("please add integer value");
955
                return Array<uint8>();
956
            } else {
957
                oscArgsString += arg;
958
                oscArgs.addArray(createInt(word.substring(1).getIntValue()));
959
            }
960
            break;
961
 
962
        case 'f': // float32
963
            if( word.length() == 1 ) {
964
                statusMessage = T("please add float value");
965
                return Array<uint8>();
966
            } else {
967
                oscArgsString += arg;
968
                oscArgs.addArray(createFloat(word.substring(1).getFloatValue()));
969
            }
970
            break;
971
 
972
        case 's': // string
973
        case 'S': // alternate string
974
            if( word.length() == 1 ) {
975
                statusMessage = T("please add string");
976
                return Array<uint8>();
977
            } else {
978
                oscArgsString += arg;
979
                oscArgs.addArray(createString(word.substring(1)));
980
            }
981
            break;
982
 
983
        case 'b': { // blob
984
            int len;
985
            unsigned value;
986
            if( sscanf((const char*)word.substring(1), "%d:%x", &len, &value) != 2 ) {
987
                statusMessage = T("please enter blob length and hex value (syntax: <len>:<data>)");
988
                return Array<uint8>();
989
            } else if( len != 4 ) {
990
                statusMessage = T(":-/ only 4 byte blobs supported yet! :-/");
991
                return Array<uint8>();
992
            } else {
993
                oscArgsString += arg;
994
                unsigned char buffer[4];
995
                putWord(buffer, value);
996
                oscArgs.addArray(createBlob(buffer, len));
997
            }
998
        } break;
999
 
1000
        case 'h': // int64
1001
            if( word.length() == 1 ) {
1002
                statusMessage = T("please enter large integer value");
1003
                return Array<uint8>();
1004
            } else {
1005
                oscArgsString += arg;
1006
                oscArgs.addArray(createLongLong(word.substring(1).getLargeIntValue()));
1007
            }
1008
            break;
1009
 
1010
        case 't': // timetag
1011
            if( word.length() == 1 ) {
1012
                statusMessage = T("please enter timetag value");
1013
                return Array<uint8>();
1014
            } else {
1015
                unsigned seconds;
1016
                unsigned fraction;
1017
                if( sscanf((const char*)word.substring(1), "%u.%u", &seconds, &fraction) != 2 ) {
1018
                    statusMessage = T("syntax: <seconds>.<fraction>");
1019
                    return Array<uint8>();
1020
                } else {
1021
                    oscArgsString += arg;
1022
                    timetag.seconds = seconds;
1023
                    timetag.fraction = fraction;
1024
                    oscArgs.addArray(createTimetag(timetag));
1025
                }
1026
            }
1027
            break;
1028
 
1029
        case 'd': // float64 (double)
1030
            if( word.length() == 1 ) {
1031
                statusMessage = T("please enter double precission float value");
1032
                return Array<uint8>();
1033
            } else {
1034
                oscArgsString += arg;
1035
                oscArgs.addArray(createDouble(word.substring(1).getDoubleValue()));
1036
            }
1037
            break;
1038
 
1039
        case 'c': // ASCII character
1040
            if( word.length() == 1 ) {
1041
                statusMessage = T("please enter character");
1042
                return Array<uint8>();
1043
            } else if( word.length() > 2 ) {
1044
                statusMessage = String(T("ERROR: expecting only a single character for '") + arg + T("' argument!"));
1045
                return Array<uint8>();
1046
            } else {
1047
                oscArgsString += arg;
1048
                oscArgs.addArray(createChar(word[1]));
1049
            }
1050
            break;
1051
 
1052
        case 'r': // 32 bit RGBA color
1053
        case 'm': { // MIDI message
1054
            if( word.length() == 1 ) {
1055
                statusMessage = T("please enter hex value");
1056
                return Array<uint8>();
1057
            } else {
1058
                unsigned value;
1059
                if( sscanf((const char*)word.substring(1), "%x", &value) != 1 ) {
1060
                    statusMessage = String(T("ERROR: expecting hex value for '") + arg + T("' argument!"));
1061
                    return Array<uint8>();
1062
                } else {
1063
                    oscArgsString += arg;
1064
                    oscArgs.addArray(createWord(value));
1065
                }
1066
            }
1067
        } break;
1068
 
1069
        case 'T': // TRUE
1070
        case 'F': // FALSE
1071
        case 'N': // NIL
1072
        case 'I': // Infinitum
1073
        case '[': // beginning of array
1074
        case ']': // end of array
1075
 
1076
            if( word.length() > 1 ) {
1077
                statusMessage = String(T("ERROR: unexpected value after '") + arg + T("' argument!"));
1078
                return Array<uint8>();
1079
            } else {
1080
                oscArgsString += arg;
1081
            }
1082
            break;
1083
 
1084
        default:
1085
            statusMessage = String(T("ERROR: unknown argument type '") + arg + T("'!"));
1086
            return Array<uint8>();
1087
        }
1088
    }
1089
 
1090
    // no path detected: empty packet!
1091
    if( oscPath.size() == 0 ) {
1092
        statusMessage = String::empty;
1093
        return Array<uint8>();
1094
    }
1095
 
1096
    // packet too large?
1097
    if( oscElements.size() > (OSC_BUFFER_MAX-20) ) {
1098
        statusMessage = T("ERROR: OSC packet too large!");
1099
        return Array<uint8>();
1100
    }
1101
 
1102
    // add last element
1103
    ++numElements;
1104
    oscElements.addArray(createElement(oscPath, oscArgsString, oscArgs));
1105
 
1106
    // finally create packet
1107
    Array<uint8>tmp;
1108
    tmp.addArray(createString("#bundle"));
1109
    tmp.addArray(createTimetag(timetag));
1110
    tmp.addArray(oscElements);
1111
 
1112
    statusMessage = String(T("valid OSC packet with ") + String(numElements) + T(" element"));
1113
    if( numElements > 1 ) statusMessage += T("s");
1114
    return tmp;
1115
}