Subversion Repositories svn.mios32

Rev

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