├── Examples └── ArduinoSerial │ └── ArduinoSerial.ino ├── LICENSE ├── Osc99 ├── Osc99.h ├── OscAddress.c ├── OscAddress.h ├── OscBundle.c ├── OscBundle.h ├── OscCommon.c ├── OscCommon.h ├── OscError.c ├── OscError.h ├── OscMessage.c ├── OscMessage.h ├── OscPacket.c ├── OscPacket.h ├── OscSlip.c └── OscSlip.h └── README.md /Examples/ArduinoSerial/ArduinoSerial.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ArduinoSerial.ino 3 | * @author Seb Madgwick 4 | * @brief Simple OSC99 example for the Arduino Uno. 5 | * 6 | * THE FOLLOWING TWO ACTIONS MUST BE TAKEN TO USE THIS EXAMPLE: 7 | * 8 | * 1) The OSC99 source files (i.e. the "Osc99" directory) must be added to the 9 | * Arduino libraries folder. See: https://www.arduino.cc/en/guide/libraries 10 | * 11 | * 2) The Arduino Uno only has a small amount of RAM (2 kB). The value of 12 | * MAX_TRANSPORT_SIZE must be reduced to 150 in OscCommon.h. 13 | */ 14 | 15 | #include "Osc99.h" 16 | 17 | OscSlipDecoder oscSlipDecoder; 18 | 19 | void setup() { 20 | Serial.begin(115200); 21 | OscSlipDecoderInitialise(&oscSlipDecoder); 22 | oscSlipDecoder.processPacket = ProcessPacket; // assign callback function 23 | } 24 | 25 | void loop() { 26 | 27 | // Send analogue inputs every 100 ms (10 Hz) 28 | static unsigned long previousMillis; // static means that the value will be remembered each loop 29 | unsigned long currentMillis = millis(); 30 | if((currentMillis - previousMillis) > 100) { 31 | sendAnalogueInputsMessage(); 32 | previousMillis = currentMillis; 33 | } 34 | 35 | // Process each received byte through the SLIP decoder 36 | while(Serial.available() > 0) { 37 | OscSlipDecoderProcessByte(&oscSlipDecoder, Serial.read()); 38 | } 39 | } 40 | 41 | // This function is called for each OSC packet received by the SLIP decoder 42 | void ProcessPacket(OscPacket* const oscPacket) { 43 | oscPacket->processMessage = &ProcessMessage; // assign callback function 44 | OscPacketProcessMessages(oscPacket); 45 | } 46 | 47 | // This function is called for each OSC message found within the received OSC packet 48 | void ProcessMessage(const OscTimeTag* const oscTimeTag, OscMessage* const oscMessage) { 49 | 50 | // If message address is "/hello" then send our hello message 51 | if (OscAddressMatch(oscMessage->oscAddressPattern, "/hello") == true) { 52 | sendHelloMessage(); 53 | return; 54 | } 55 | 56 | // If message address is "/digital/write" then get use the message arguments to set a digital pin 57 | if (OscAddressMatch(oscMessage->oscAddressPattern, "/digital/write")) { 58 | int32_t pinNumber, pinState; 59 | if(OscMessageGetArgumentAsInt32(oscMessage, &pinNumber) != OscErrorNone) { 60 | return; // error: unable to get first int32 argument 61 | } 62 | if(OscMessageGetArgumentAsInt32(oscMessage, &pinState) != OscErrorNone) { 63 | return; // error: unable to get second int32 argument 64 | } 65 | if(pinNumber < 2) { 66 | return; // error: cannot modify pins being used for serial 67 | } 68 | pinMode(pinNumber, OUTPUT); 69 | digitalWrite(pinNumber, pinState); 70 | return; 71 | } 72 | 73 | // Message address not recognised so send our error message to let the user know 74 | sendErrorMessage(); 75 | } 76 | 77 | void sendHelloMessage() { 78 | OscMessage oscMessage; 79 | OscMessageInitialise(&oscMessage, "/hello"); 80 | OscMessageAddString(&oscMessage, "Hi!"); 81 | sendOscContents(&oscMessage); 82 | } 83 | 84 | void sendErrorMessage() { 85 | OscMessage oscMessage; 86 | OscMessageInitialise(&oscMessage, "/error"); 87 | OscMessageAddString(&oscMessage, "OSC address not recognised."); 88 | sendOscContents(&oscMessage); 89 | } 90 | 91 | void sendAnalogueInputsMessage() { 92 | OscMessage oscMessage; 93 | OscMessageInitialise(&oscMessage, "/analog/read"); 94 | OscMessageAddInt32(&oscMessage, analogRead(A0)); // read analogue input pin 95 | OscMessageAddInt32(&oscMessage, analogRead(A1)); 96 | OscMessageAddInt32(&oscMessage, analogRead(A2)); 97 | OscMessageAddInt32(&oscMessage, analogRead(A3)); 98 | sendOscContents(&oscMessage); 99 | } 100 | 101 | void sendOscContents(const void* const oscContents) { 102 | 103 | // Create OSC packet from OSC message or OSC bundle 104 | OscPacket oscPacket; 105 | if(OscPacketInitialiseFromContents(&oscPacket, oscContents) != OscErrorNone) { 106 | return; // error: unable to create an OSC packet from the OSC contents 107 | } 108 | 109 | // Encode SLIP packet 110 | char slipPacket[MAX_OSC_PACKET_SIZE]; 111 | size_t slipPacketSize; 112 | if(OscSlipEncodePacket(&oscPacket, &slipPacketSize, slipPacket, sizeof (slipPacket)) != OscErrorNone) { 113 | return; // error: the encoded SLIP packet is too long for the size of slipPacket 114 | } 115 | 116 | // Send SLIP packet 117 | Serial.write((uint8_t*)slipPacket, slipPacketSize); // typecast from char* to uint8_t* 118 | } 119 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 x-io Technologies 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Osc99/Osc99.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Osc99.h 3 | * @author Seb Madgwick 4 | * @brief Main header file for the library. This is the only file that needs to 5 | * be included when using the library. 6 | * 7 | * OSC99 is a portable ANSI C99 compliant OSC library developed for use with 8 | * embedded systems. OSC99 implements the OSC 1.0 specification including all 9 | * optional argument types. The library also includes a SLIP module for 10 | * encoding and decoding OSC packets via unframed protocols such as UART/serial 11 | * as required by the OSC the 1.1 specification. 12 | * 13 | * The following definitions may be modified in OscCommon.h as required by the 14 | * user application: LITTLE_ENDIAN_PLATFORM, MAX_TRANSPORT_SIZE, 15 | * OSC_ERROR_MESSAGES_ENABLED. 16 | * 17 | * See http://opensoundcontrol.org/spec-1_0 18 | */ 19 | 20 | #ifndef OSC99_H 21 | #define OSC99_H 22 | 23 | //------------------------------------------------------------------------------ 24 | // Includes 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | #include "OscAddress.h" 31 | #include "OscError.h" 32 | #include "OscPacket.h" 33 | #include "OscSlip.h" 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | 39 | #endif 40 | //------------------------------------------------------------------------------ 41 | // End of file 42 | -------------------------------------------------------------------------------- /Osc99/OscAddress.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscAddress.c 3 | * @author Seb Madgwick 4 | * @brief Functions for matching and manipulating OSC address patterns and OSC 5 | * addresses. 6 | * See http://opensoundcontrol.org/spec-1_0 7 | */ 8 | 9 | //------------------------------------------------------------------------------ 10 | // Includes 11 | 12 | #include "OscAddress.h" 13 | #include 14 | 15 | //------------------------------------------------------------------------------ 16 | // Function prototypes 17 | 18 | static bool MatchLiteral(const char * oscAddressPattern, const char * oscAddress, const bool isPartial); 19 | static bool MatchExpression(const char * * const oscAddressPattern, const char * * const oscAddress, const bool isPartial); 20 | static bool MatchStar(const char * * const oscAddressPattern, const char * * const oscAddress, const bool isPartial); 21 | static bool MatchCharacter(const char * * const oscAddressPattern, const char * * const oscAddress, const bool isPartial); 22 | static bool MatchBrackets(const char * * const oscAddressPattern, const char * * const oscAddress); 23 | static bool MatchCurlyBraces(const char * * const oscAddressPattern, const char * * const oscAddress, const bool isPartial); 24 | 25 | //------------------------------------------------------------------------------ 26 | // Functions 27 | 28 | /** 29 | * @brief Matches an OSC address pattern with a target OSC address. 30 | * 31 | * Returns true if the OSC address pattern matches the target OSC address. The 32 | * target OSC address cannot contain any special characters: '?', ' *', '[]', or 33 | * '{}'. 34 | * 35 | * Example use: 36 | * @code 37 | * OscMessage oscMessage; 38 | * OscMessageInitialise(&oscMessage, "/example/oscAddress/pattern"); 39 | * if(OscAddressMatch(oscMessage.oscAddressPattern, "/example/oscAddress/pattern") == true) { 40 | * printf("Match!"); 41 | * } 42 | * @endcode 43 | * 44 | * @param oscAddressPattern OSC address pattern. 45 | * @param oscAddress Target OSC address. 46 | * @return True if the OSC address pattern and target oscAddress match. 47 | */ 48 | bool OscAddressMatch(const char * oscAddressPattern, const char * const oscAddress) { 49 | return MatchLiteral(oscAddressPattern, oscAddress, false); 50 | } 51 | 52 | /** 53 | * @brief Matches an OSC address pattern with a partial target OSC address that 54 | * has been truncated. 55 | * 56 | * Returns true if the OSC address pattern matches the partial target OSC 57 | * address. The target OSC address cannot contain any special characters: 58 | * '?', ' *', '[]', or '{}'. 59 | * 60 | * Matching to a partial OSC address can simplify the process of filtering 61 | * through multiple similar OSC address. For example, matching to the following 62 | * list would be quicker if an incoming message was first matched to either 63 | * "/inputs" and "/outputs". 64 | * - "/inputs/digital" 65 | * - "/inputs/analogue" 66 | * - "/inputs/serial" 67 | * - "/outputs/digital" 68 | * - "/outputs/pwm" 69 | * - "/outputs/serial" 70 | * 71 | * Example use: 72 | * @code 73 | * OscMessage oscMessage; 74 | * OscMessageInitialise(&oscMessage, "/example/oscAddress/pattern"); 75 | * if(OscAddressMatch(oscMessage.oscAddressPattern, "/example") == true) { 76 | * printf("Match!"); 77 | * } 78 | * @endcode 79 | * 80 | * @param oscAddressPattern OSC address pattern. 81 | * @param oscAddress Partial target OSC address. 82 | * @return True if the OSC address pattern matches the partial target OSC 83 | * oscAddress. 84 | */ 85 | bool OscAddressMatchPartial(const char * oscAddressPattern, const char * const oscAddress) { 86 | return MatchLiteral(oscAddressPattern, oscAddress, true); 87 | } 88 | 89 | /** 90 | * @brief Matches literal OSC address pattern with target OSC address. 91 | * 92 | * The OSC address pattern is initially assumed to be literal and not to contain 93 | * any special characters: '?', ' *', '[]', or '{}'. If a special character is 94 | * found then the result of MatchExpression is returned. Matching literal OSC 95 | * address patterns is faster than matching OSC address patterns that contain 96 | * special characters. 97 | * 98 | * This is an internal function and cannot be called by the user application. 99 | * 100 | * @param oscAddressPattern First character of OSC address pattern. 101 | * @param oscAddress First character of target OSC address. 102 | * @param isPartial Flag indicating if a partial match is acceptable. 103 | * @return True if OSC address pattern and target OSC address match. 104 | */ 105 | static bool MatchLiteral(const char * oscAddressPattern, const char * oscAddress, const bool isPartial) { 106 | while (*oscAddressPattern != '\0') { 107 | if (*oscAddress == '\0') { 108 | if (isPartial == true) { 109 | return true; 110 | } else { 111 | return MatchExpression(&oscAddressPattern, &oscAddress, isPartial); // handle trailing 'zero character' expressions 112 | } 113 | return false; // fail: OSC address pattern too short 114 | } 115 | switch (*oscAddressPattern) { 116 | case '?': 117 | case '*': 118 | case '[': 119 | case '{': 120 | return MatchExpression(&oscAddressPattern, &oscAddress, isPartial); 121 | default: 122 | if (*oscAddressPattern != *oscAddress) { 123 | return false; // fail: character mismatch 124 | } 125 | break; 126 | } 127 | oscAddressPattern++; 128 | oscAddress++; 129 | } 130 | if (*oscAddress != '\0') { 131 | return false; // fail: OSC address pattern too long 132 | } 133 | return true; 134 | } 135 | 136 | /** 137 | * @brief Matches an OSC address pattern expression with a target OSC address. 138 | * 139 | * The OSC address pattern expression may contain any combination of special 140 | * characters: '?', ' *', '[]', or '{}'. 141 | * 142 | * This is an internal function and cannot be called by the user application. 143 | * 144 | * @param oscAddressPattern First character of OSC address pattern. 145 | * @param oscAddress First character of target OSC address. 146 | * @param isPartial Flag indicating if a partial match is acceptable. 147 | * @return True if OSC address pattern and target OSC address match. 148 | */ 149 | static bool MatchExpression(const char * * const oscAddressPattern, const char * * const oscAddress, const bool isPartial) { 150 | while (**oscAddressPattern != '\0') { 151 | if (**oscAddress == '\0') { 152 | if (isPartial == true) { 153 | return true; 154 | } 155 | } 156 | if (**oscAddressPattern == '*') { 157 | if (MatchStar(oscAddressPattern, oscAddress, isPartial) == false) { 158 | return false; // fail: unable to match star sequence 159 | } 160 | } else { 161 | if (MatchCharacter(oscAddressPattern, oscAddress, isPartial) == false) { 162 | return false; // fail: unable to match single character, bracketed list or curly braced list 163 | } 164 | } 165 | } 166 | if (**oscAddress != '\0') { 167 | return false; // fail: OSC address pattern too long 168 | } 169 | return true; 170 | } 171 | 172 | /** 173 | * @brief Matches an OSC address pattern expression starting with a star with 174 | * the next character(s) in the target OSC address. 175 | * 176 | * The OSC address pattern must start with a ' *' character. A ' *' character 177 | * will be matched to any sequence of zero or more characters in the OSC address 178 | * up to the next '/' character or to the end of the OSC address. For example, 179 | * the OSC address pattern "/colour/b *" would match the OSC addresses 180 | * "/colour/blue", "/colour/black" and "/colour/brown". 181 | * 182 | * The oscAddressPattern and oscAddress pointers are advanced to the character 183 | * proceeding the matched sequence. This function calls MatchCharacter and so 184 | * becomes recursive if the expression contains multiple stars. 185 | * 186 | * This is an internal function and cannot be called by the user application. 187 | * 188 | * @param oscAddressPattern First character of OSC address pattern. 189 | * @param oscAddress First character of target OSC address. 190 | * @param isPartial Flag indicating if a partial match is acceptable. 191 | * @return True if OSC address pattern and target OSC address match. 192 | */ 193 | static bool MatchStar(const char * * const oscAddressPattern, const char * * const oscAddress, const bool isPartial) { 194 | 195 | // Advance OSC address pattern pointer to character proceeding star(s) 196 | while (**oscAddressPattern == '*') { 197 | (*oscAddressPattern)++; 198 | } 199 | 200 | // Advance OSC address pattern pointer to end of part if star is last character 201 | if ((**oscAddressPattern == '/') || (**oscAddressPattern == '\0')) { 202 | while ((**oscAddress != '/') && (**oscAddress != '\0')) { 203 | (*oscAddress)++; 204 | } 205 | return true; 206 | } 207 | 208 | // Attempt to match remainder of expression for each possible star match 209 | do { 210 | const char * oscAddressPatternCache = *oscAddressPattern; // cache character oscAddress proceeding star 211 | 212 | // Advance OSC address pattern to next match of character proceeding star 213 | while (MatchCharacter(oscAddressPattern, oscAddress, isPartial) == false) { 214 | (*oscAddress)++; 215 | if ((**oscAddress == '/') || (**oscAddress == '\0')) { 216 | if ((isPartial == true) && (**oscAddress == '\0')) { 217 | return true; 218 | } 219 | return false; // fail: OSC address pattern part ended before match 220 | } 221 | } 222 | const char * oscAddressCache = (*oscAddress); // cache character oscAddress proceeding current star match 223 | 224 | // Attempt to match remainder of expression 225 | if (MatchExpression(oscAddressPattern, oscAddress, isPartial) == true) { // potentially recursive 226 | return true; 227 | } else { 228 | *oscAddressPattern = oscAddressPatternCache; 229 | *oscAddress = oscAddressCache; 230 | } 231 | } while (true); 232 | } 233 | 234 | /** 235 | * @brief Matches an OSC address pattern starting with literal character, 236 | * bracketed list or curly braced list with the next character(s) in the target 237 | * OSC address. 238 | * 239 | * The oscAddressPattern and oscAddress pointers are advanced to the character 240 | * proceeding the matched sequence only if a match is found. The pointers are 241 | * not modified if a match cannot be found. 242 | * 243 | * This is an internal function and cannot be called by the user application. 244 | * 245 | * @param oscAddressPattern First character of OSC address pattern. 246 | * @param oscAddress First character of target OSC address. 247 | * @param isPartial Flag indicating if a partial match is acceptable. 248 | * @return True if OSC address pattern and target OSC address match. 249 | */ 250 | static bool MatchCharacter(const char * * const oscAddressPattern, const char * * const oscAddress, const bool isPartial) { 251 | const char * oscAddressPatternCache = *oscAddressPattern; 252 | const char * oscAddressCache = *oscAddress; 253 | switch (**oscAddressPattern) { 254 | case '[': 255 | if (MatchBrackets(oscAddressPattern, oscAddress) == true) { 256 | return true; 257 | } 258 | break; 259 | case ']': 260 | break; // fail: unbalanced brackets 261 | case '{': 262 | if (MatchCurlyBraces(oscAddressPattern, oscAddress, isPartial) == true) { 263 | return true; 264 | } 265 | break; 266 | case '}': 267 | break; // fail: unbalanced curly braces 268 | default: 269 | if ((**oscAddressPattern == **oscAddress) || (**oscAddressPattern == '?')) { 270 | (*oscAddressPattern)++; 271 | (*oscAddress)++; 272 | return true; 273 | } 274 | break; 275 | } 276 | *oscAddressPattern = oscAddressPatternCache; 277 | *oscAddress = oscAddressCache; 278 | return false; 279 | } 280 | 281 | /** 282 | * @brief Matches an OSC address pattern starting with a bracketed list of 283 | * characters with the next character in the target OSC address. 284 | * 285 | * The OSC address pattern must start with a '[' character. A match will occur 286 | * if any of the characters in the bracketed list match the next character in 287 | * the OSC address. If the first character in the list is '!' then the list 288 | * will be negated (i.e. a match will occur if the next character in the OSC 289 | * address does not appear in the bracketed list). Any two characters separated 290 | * by a '-' character will represent the inclusive range of characters between 291 | * the two characters. The two characters may be in acceding or descending 292 | * order. A bracketed list may contain both '!' and '-' characters and may also 293 | * include multiple character ranges. For example, the OSC address pattern 294 | * "/abc[!d-hijkp-l]qrst" will match the OSC address "/abcXqrst" provided that 295 | * character X is not within the range 'd' to 'p'. 296 | * 297 | * The oscAddressPattern and oscAddress pointers are advanced to the character 298 | * proceeding the matched sequence if a match is found. This function should 299 | * only be called internally by MatchCharacter. 300 | * 301 | * This is an internal function and cannot be called by the user application. 302 | * 303 | * @param oscAddressPattern First character of OSC address pattern. 304 | * @param oscAddress First character of target OSC address. 305 | * @return True if OSC address pattern and target OSC address match. 306 | */ 307 | static bool MatchBrackets(const char * * const oscAddressPattern, const char * * const oscAddress) { 308 | (*oscAddressPattern)++; // increment past opening bracket 309 | 310 | // Check if list is negated 311 | bool negatedList = false; 312 | if (**oscAddressPattern == '!') { 313 | negatedList = true; 314 | (*oscAddressPattern)++; // increment past '!' 315 | } 316 | 317 | // Match each character in list 318 | bool match = negatedList; 319 | while (**oscAddressPattern != ']') { 320 | if ((**oscAddressPattern == '/') || (**oscAddressPattern == '\0')) { 321 | return false; // fail: unbalanced brackets 322 | } 323 | 324 | // If character is part of hyphenated range 325 | if ((*(*oscAddressPattern + 1) == '-') && (*(*oscAddressPattern + 2) != ']')) { 326 | if ((*(*oscAddressPattern + 2) == '/') || (*(*oscAddressPattern + 2) == '\0')) { 327 | return false; // fail: unbalanced brackets 328 | } 329 | 330 | // Handle acceding/descending range 331 | char lowerChar = **oscAddressPattern; 332 | char upperChar = *(*oscAddressPattern + 2); 333 | if (lowerChar > upperChar) { 334 | lowerChar = *(*oscAddressPattern + 2); 335 | upperChar = **oscAddressPattern; 336 | } 337 | 338 | // Check if target character in range 339 | if ((**oscAddress >= lowerChar) && (**oscAddress <= upperChar)) { 340 | if (negatedList == true) { 341 | match = false; // fail: character matched in negated list 342 | } else { 343 | match = true; 344 | } 345 | } 346 | (*oscAddressPattern) += 3; // increment past hyphenated characters 347 | } else { 348 | 349 | // Else match single character 350 | if (**oscAddressPattern == **oscAddress) { 351 | if (negatedList == true) { 352 | match = false; // fail: character matched in negated list 353 | } else { 354 | match = true; 355 | } 356 | } 357 | (*oscAddressPattern)++; 358 | } 359 | } 360 | (*oscAddressPattern)++; // increment past closing bracket 361 | (*oscAddress)++; // increment past matched character 362 | return match; 363 | } 364 | 365 | /** 366 | * @brief Matches an OSC address pattern starting with a curly braced list of 367 | * substrings with the next character(s) in the target OSC address. 368 | * 369 | * The OSC address pattern must start with a '{' character. A match will occur 370 | * if a substring with the curly braced list matches the next substring in the 371 | * target OSC address. If multiple substrings are matched then the longest 372 | * matching substring will be used. A substring may contain zero characters. 373 | * For example, the OSC address pattern "/{in,out,,}puts/enable" would match OSC 374 | * addresses "/inputs/enable" and "/outputs/enable" and "/puts/enable". 375 | * 376 | * The oscAddressPattern and oscAddress pointers are advanced to the character 377 | * proceeding the matched sequence if a match is found. This function should 378 | * only be called internally by MatchCharacter. 379 | * 380 | * This is an internal function and cannot be called by the user application. 381 | * 382 | * @param oscAddressPattern First character of OSC address pattern. 383 | * @param oscAddress First character of target OSC address. 384 | * @param isPartial Flag indicating if a partial match is acceptable. 385 | * @return True if OSC address pattern and target OSC address match. 386 | */ 387 | static bool MatchCurlyBraces(const char * * const oscAddressPattern, const char * * const oscAddress, const bool isPartial) { 388 | const char * endOfSubstring = *oscAddressPattern; 389 | size_t matchedSubStringLength = 0; 390 | bool match = false; 391 | while (**oscAddressPattern != '}') { 392 | if ((**oscAddressPattern == '/') || (**oscAddressPattern == '\0')) { 393 | return false; // fail: unbalanced curly braces 394 | } 395 | 396 | // Advance to end of substring 397 | while ((*endOfSubstring != ',') && (*endOfSubstring != '}')) { 398 | if ((*endOfSubstring == '/') || (*endOfSubstring == '\0')) { 399 | return false; // fail: unbalanced curly braces 400 | } 401 | endOfSubstring++; 402 | } 403 | 404 | // Determine substring length 405 | (*oscAddressPattern)++; // increment past '{' or ',' 406 | size_t subStringLength = endOfSubstring - *oscAddressPattern; 407 | if (isPartial == true) { 408 | size_t oscAddressLength = strlen(*oscAddress); 409 | if (subStringLength > oscAddressLength) { 410 | subStringLength = oscAddressLength; // limit length to not exceed partial target 411 | } 412 | } 413 | 414 | // Match substring 415 | if (strncmp(*oscAddressPattern, *oscAddress, subStringLength) == 0) { 416 | match = true; 417 | if (subStringLength > matchedSubStringLength) { 418 | matchedSubStringLength = subStringLength; 419 | } 420 | } 421 | *oscAddressPattern = endOfSubstring; // advance to next ',' or '}' 422 | endOfSubstring++; // increment past ',' or '}' 423 | } 424 | (*oscAddressPattern)++; // increment past '{' or ',' 425 | *oscAddress += matchedSubStringLength; // increment past matched substring 426 | return match; 427 | } 428 | 429 | /** 430 | * @brief Returns true if the OSC address pattern is literal. 431 | * 432 | * A literal OSC address pattern cannot contain any special characters: '?', 433 | * ' *', '[]', or '{}'. In some applications it is desirable to reject OSC 434 | * address patterns that contain special characters because the use of special 435 | * characters risks invoking critical methods unintentionally. For example, 436 | * critical methods such as "/shutdown" or "/selfdestruct" risk being invoked 437 | * unintentionally the OSC address pattern "/s *". 438 | * 439 | * Example use: 440 | * @code 441 | * OscMessage oscMessage; 442 | * OscMessageInitialise(&oscMessage, "/example/oscAddress/pattern"); 443 | * if(OscAddressIsLiteral(oscMessage.oscAddressPattern)) { 444 | * printf("Is literal"); 445 | * } 446 | * @endcode 447 | * 448 | * @param oscAddressPattern First character of OSC address pattern. 449 | * @return True if the OSC address pattern is literal. 450 | */ 451 | bool OscAddressIsLiteral(const char * oscAddressPattern) { 452 | while (*oscAddressPattern != '\0') { 453 | switch (*oscAddressPattern) { 454 | case '?': 455 | case '*': 456 | case '[': 457 | case '{': 458 | return false; 459 | default: 460 | break; 461 | } 462 | oscAddressPattern++; 463 | } 464 | return true; 465 | } 466 | 467 | /** 468 | * @brief Returns the number of parts that make up an OSC address or an OSC 469 | * address pattern. 470 | * 471 | * The parts of an OSC address or an OSC address pattern are the substrings 472 | * between adjacent pairs of forward slash characters and the substring after 473 | * the last forward slash character. This function may be called in conjunction 474 | * with OscAddressGetPartAtIndex to copy a given part. 475 | * 476 | * Example use: 477 | * @code 478 | * OscMessage oscMessage; 479 | * OscMessageInitialise(&oscMessage, "/example/oscAddress/pattern"); 480 | * const unsigned int numberOfParts = OscAddressGetNumberOfParts(oscMessage.oscAddressPattern); 481 | * printf("Number of parts = %d", numberOfParts); // should be 3 482 | * @endcode 483 | * 484 | * @param oscAddressPattern OSC address or an OSC address pattern. 485 | * @return Number of parts that make up the message OSC address or an OSC 486 | * oscAddress pattern. 487 | */ 488 | unsigned int OscAddressGetNumberOfParts(const char * oscAddressPattern) { 489 | unsigned int numberOfParts = 0; 490 | while (*oscAddressPattern != '\0') { 491 | if (*oscAddressPattern == '/') { 492 | numberOfParts++; 493 | } 494 | oscAddressPattern++; 495 | } 496 | return numberOfParts; 497 | } 498 | 499 | /** 500 | * @brief Copies the OSC address or OSC address pattern part at the specified 501 | * index. 502 | * 503 | * The parts of an OSC Address or an OSC address pattern are the substrings 504 | * between adjacent pairs of forward slash characters and the substring after 505 | * the last forward slash character. This functions copies the part at the 506 | * specified index (starting from zero) to a destination address. The part is 507 | * provided as a null-terminated string. OscAddressGetNumberOfParts may be 508 | * called first to determine the number of parts available. 509 | * 510 | * Example use: 511 | * @code 512 | * OscMessage oscMessage; 513 | * OscMessageInitialise(&oscMessage, "/example/oscAddress/pattern"); 514 | * const unsigned int numberOfParts = OscAddressGetNumberOfParts(oscMessage.oscAddressPattern); 515 | * unsigned int index; 516 | * for (index = 0; index < numberOfParts; index++) { 517 | * char string[16]; 518 | * OscAddressGetPartAtIndex(oscMessage.oscAddressPattern, index, string, sizeof (string)); 519 | * printf("%s ", string); 520 | * } 521 | * @endcode 522 | * 523 | * @param oscAddressPattern OSC address or an OSC address pattern. 524 | * @param index Index (starting from zero) of part to be copied. 525 | * @param destination Destination oscAddress for part string. 526 | * @param destinationSize Destination size that cannot be exceeded. 527 | * @return Error code (0 if successful). 528 | */ 529 | OscError OscAddressGetPartAtIndex(const char * oscAddressPattern, const unsigned int index, char * const destination, const size_t destinationSize) { 530 | 531 | // Advance oscAddressPattern oscAddress to start of part 532 | unsigned int partCount = 0; 533 | while (partCount < (index + 1)) { 534 | while (*oscAddressPattern != '\0') { 535 | if (*oscAddressPattern == '/') { 536 | partCount++; 537 | break; 538 | } 539 | oscAddressPattern++; 540 | } 541 | if (*oscAddressPattern == '\0') { 542 | return OscErrorNotEnoughPartsInAddressPattern; // error: not enough parts in message oscAddress 543 | } 544 | } 545 | 546 | // Copy part to destination as string 547 | unsigned int destinationIndex = 0; 548 | while (*oscAddressPattern != '\0') { 549 | destination[destinationIndex] = *oscAddressPattern; 550 | if (++destinationIndex >= destinationSize) { 551 | return OscErrorDestinationTooSmall; // error: destination too small 552 | } 553 | oscAddressPattern++; 554 | if (*oscAddressPattern == '/') { 555 | break; 556 | } 557 | } 558 | destination[destinationIndex] = '\0'; // terminate as string 559 | return OscErrorNone; 560 | } 561 | 562 | //------------------------------------------------------------------------------ 563 | // End of file 564 | -------------------------------------------------------------------------------- /Osc99/OscAddress.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscAddress.h 3 | * @author Seb Madgwick 4 | * @brief Functions for matching and manipulating OSC address patterns and OSC 5 | * addresses. 6 | * See http://opensoundcontrol.org/spec-1_0 7 | */ 8 | 9 | #ifndef OSC_ADDRESS_H 10 | #define OSC_ADDRESS_H 11 | 12 | //------------------------------------------------------------------------------ 13 | // Includes 14 | 15 | #include "OscCommon.h" 16 | #include "OscError.h" 17 | #include 18 | #include 19 | 20 | //------------------------------------------------------------------------------ 21 | // Function prototypes 22 | 23 | bool OscAddressMatch(const char * oscAddressPattern, const char * const oscAddress); 24 | bool OscAddressMatchPartial(const char * oscAddressPattern, const char * const oscAddress); 25 | bool OscAddressIsLiteral(const char * oscAddressPattern); 26 | unsigned int OscAddressGetNumberOfParts(const char * oscAddressPattern); 27 | OscError OscAddressGetPartAtIndex(const char * oscAddressPattern, const unsigned int index, char * const destination, const size_t destinationSize); 28 | 29 | #endif 30 | 31 | //------------------------------------------------------------------------------ 32 | // End of file 33 | -------------------------------------------------------------------------------- /Osc99/OscBundle.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscBundle.c 3 | * @author Seb Madgwick 4 | * @brief Functions and structures for constructing and deconstructing OSC 5 | * bundles. 6 | * See http://opensoundcontrol.org/spec-1_0 7 | */ 8 | 9 | //------------------------------------------------------------------------------ 10 | // Includes 11 | 12 | #include "OscBundle.h" 13 | 14 | //------------------------------------------------------------------------------ 15 | // Functions 16 | 17 | /** 18 | * @brief Initialises an OSC bundle with a specified OSC time tag. 19 | * 20 | * An OSC bundle must be initialised before use. The oscTimeTag argument may be 21 | * specified as oscTimeTagZero for an OSC time tag value of zero. This may be 22 | * of use if the OSC time tag value is irrelevant to the user application, if 23 | * the contained OSC messages should be invoke immediately, or if the OSC time 24 | * tag value is intended to be overwritten after initialisation of the OSC 25 | * bundle. 26 | * 27 | * Example use: 28 | * @code 29 | * OscBundle oscBundle; 30 | * OscBundleInitialise(&oscBundle, oscTimeTagZero); 31 | * oscBundle.oscTimeTag.value = 0x100000000; // overwrite OSC time tag with value of 1 second 32 | * @endcode 33 | * 34 | * @param oscBundle OSC bundle to be initialised. 35 | * @param oscTimeTag OSC time tag. 36 | */ 37 | void OscBundleInitialise(OscBundle * const oscBundle, const OscTimeTag oscTimeTag) { 38 | oscBundle->header[0] = OSC_BUNDLE_HEADER[0]; 39 | oscBundle->header[1] = OSC_BUNDLE_HEADER[1]; 40 | oscBundle->header[2] = OSC_BUNDLE_HEADER[2]; 41 | oscBundle->header[3] = OSC_BUNDLE_HEADER[3]; 42 | oscBundle->header[4] = OSC_BUNDLE_HEADER[4]; 43 | oscBundle->header[5] = OSC_BUNDLE_HEADER[5]; 44 | oscBundle->header[6] = OSC_BUNDLE_HEADER[6]; 45 | oscBundle->header[7] = OSC_BUNDLE_HEADER[7]; 46 | oscBundle->oscTimeTag = oscTimeTag; 47 | oscBundle->oscBundleElementsSize = 0; 48 | } 49 | 50 | /** 51 | * @brief Adds an OSC message or OSC bundle to an OSC bundle. 52 | * 53 | * The oscContents argument must point to an initialised OSC message or OSC 54 | * bundle. This function may be called multiple times to add multiple OSC 55 | * messages or OSC bundles to a containing OSC bundle. If the remaining 56 | * capacity of the containing OSC bundle is insufficient to hold the additional 57 | * contents then the additional contents will be discarded and the function will 58 | * return an error. 59 | * 60 | * Example use: 61 | * @code 62 | * OscMessage oscMessageToAdd; 63 | * OscMessageInitialise(&oscMessageToAdd, "/example/address/pattern"); 64 | * 65 | * OscBundle oscBundleToAdd; 66 | * OscBundleInitialise(&oscBundleToAdd, oscTimeTagZero); 67 | * 68 | * OscBundle oscBundle; 69 | * OscBundleInitialise(&oscBundle, oscTimeTagZero); 70 | * OscBundleAddContents(&oscBundle, &oscMessageToAdd); 71 | * OscBundleAddContents(&oscBundle, &oscBundleToAdd); 72 | * @endcode 73 | * 74 | * @param oscBundle OSC bundle that will contain the OSC message or OSC bundle 75 | * to be added. 76 | * @param oscContents OSC message or OSC bundle to be added to the OSC bundle. 77 | * @return Error code (0 if successful). 78 | */ 79 | OscError OscBundleAddContents(OscBundle * const oscBundle, const void * const oscContents) { 80 | if ((oscBundle->oscBundleElementsSize + sizeof (OscArgument32)) > MAX_OSC_BUNDLE_ELEMENTS_SIZE) { 81 | return OscErrorBundleFull; // error: bundle full 82 | } 83 | OscBundleElement oscBundleElement; 84 | oscBundleElement.contents = &oscBundle->oscBundleElements[oscBundle->oscBundleElementsSize + sizeof (OscArgument32)]; 85 | OscError oscError = OscErrorInvalidContents; // error: invalid or uninitialised OSC contents 86 | if (OscContentsIsMessage(oscContents) == true) { 87 | size_t oscBundleElementSize; 88 | oscError = OscMessageToCharArray((OscMessage *) oscContents, &oscBundleElementSize, oscBundleElement.contents, OscBundleGetRemainingCapacity(oscBundle)); 89 | oscBundleElement.size.int32 = (int32_t) oscBundleElementSize; 90 | } 91 | if (OscContentsIsBundle(oscContents) == true) { 92 | size_t oscBundleElementSize; 93 | oscError = OscBundleToCharArray((OscBundle *) oscContents, &oscBundleElementSize, oscBundleElement.contents, OscBundleGetRemainingCapacity(oscBundle)); 94 | oscBundleElement.size.int32 = (int32_t) oscBundleElementSize; 95 | } 96 | if (oscError != 0) { 97 | return oscError; // error: ??? 98 | } 99 | oscBundle->oscBundleElements[oscBundle->oscBundleElementsSize++] = oscBundleElement.size.byteStruct.byte3; 100 | oscBundle->oscBundleElements[oscBundle->oscBundleElementsSize++] = oscBundleElement.size.byteStruct.byte2; 101 | oscBundle->oscBundleElements[oscBundle->oscBundleElementsSize++] = oscBundleElement.size.byteStruct.byte1; 102 | oscBundle->oscBundleElements[oscBundle->oscBundleElementsSize++] = oscBundleElement.size.byteStruct.byte0; 103 | oscBundle->oscBundleElementsSize += oscBundleElement.size.int32; 104 | return OscErrorNone; 105 | } 106 | 107 | /** 108 | * @brief Empties an OSC bundle. 109 | * 110 | * All OSC bundle elements contained within the OSC bundle are discarded. The 111 | * OSC bundle's OSC time tag is not modified. 112 | * 113 | * Example use: 114 | * @code 115 | * OscBundleEmpty(&oscBundle); 116 | * @endcode 117 | * 118 | * @param oscBundle OSC bundle to be emptied. 119 | */ 120 | void OscBundleEmpty(OscBundle * const oscBundle) { 121 | oscBundle->oscBundleElementsSize = 0; 122 | } 123 | 124 | /** 125 | * @brief Returns true if the OSC bundle is empty. 126 | * 127 | * An empty OSC bundle contains no OSC bundle elements (OSC messages or OSC 128 | * bundles) but does retain an OSC time tag. 129 | * 130 | * Example use: 131 | * @code 132 | * if(OscBundleIsEmpty(&oscBundle)) { 133 | * printf("oscBundle is empty."); 134 | * } 135 | * @endcode 136 | * 137 | * @param oscBundle OSC bundle. 138 | * @return True if the OSC bundle is empty. 139 | */ 140 | bool OscBundleIsEmpty(OscBundle * const oscBundle) { 141 | return oscBundle->oscBundleElementsSize == 0; 142 | } 143 | 144 | /** 145 | * @brief Returns the remaining capacity (number of bytes) of an OSC bundle. 146 | * 147 | * The remaining capacity of an OSC bundle is the number of bytes available to 148 | * contain an OSC message or OSC bundle. 149 | * 150 | * Example use: 151 | * @code 152 | * const size_t remainingCapacity = OscBundleGetRemainingCapacity(&oscBundle); 153 | * @endcode 154 | * 155 | * @param oscBundle OSC bundle. 156 | * @return Remaining capacity (number of bytes) of an OSC bundle. 157 | */ 158 | size_t OscBundleGetRemainingCapacity(const OscBundle * const oscBundle) { 159 | const size_t remainingCapacity = MAX_OSC_BUNDLE_ELEMENTS_SIZE - oscBundle->oscBundleElementsSize - sizeof (OscArgument32); // account for int32 size required by OSC bundle element 160 | if ((int) remainingCapacity < 0) { 161 | return 0; // avoid negative result of capacity calculation 162 | } 163 | return remainingCapacity; 164 | } 165 | 166 | /** 167 | * @brief Returns the size (number of bytes) of an OSC bundle. 168 | * 169 | * An example use of this function would be to check whether the OSC bundle size 170 | * exceeds the remaining capacity of a containing OSC bundle. 171 | * 172 | * Example use: 173 | * @code 174 | * if(OscBundleGetSize(&oscBundleChild) > OscBundleGetRemainingCapacity(&oscBundleParent)) { 175 | * printf("oscBundleChild is too large to be contained within oscBundleParent"); 176 | * } 177 | * @endcode 178 | * 179 | * @param oscBundle OSC bundle. 180 | * @return Size (number of bytes) of the OSC bundle. 181 | */ 182 | size_t OscBundleGetSize(const OscBundle * const oscBundle) { 183 | return sizeof (OSC_BUNDLE_HEADER) + sizeof (OscTimeTag) + oscBundle->oscBundleElementsSize; 184 | } 185 | 186 | /** 187 | * @brief Converts an OSC bundle into a byte array to be contained within an OSC 188 | * packet or containing OSC bundle. 189 | * 190 | * This function is used internally and should not be used by the user 191 | * application. 192 | * 193 | * @param oscBundle OSC bundle. 194 | * @param oscBundleSize OSC bundle size. 195 | * @param destination Destination byte array. 196 | * @param destinationSize Destination size that cannot exceed. 197 | * @return Error code (0 if successful). 198 | */ 199 | OscError OscBundleToCharArray(const OscBundle * const oscBundle, size_t * const oscBundleSize, char * const destination, const size_t destinationSize) { 200 | *oscBundleSize = 0; // size will be 0 if function unsuccessful 201 | if ((sizeof (OSC_BUNDLE_HEADER) + sizeof (OscTimeTag) + oscBundle->oscBundleElementsSize) > destinationSize) { 202 | return OscErrorDestinationTooSmall; // error: destination too small 203 | } 204 | size_t destinationIndex = 0; 205 | unsigned int index; 206 | for (index = 0; index < sizeof (OSC_BUNDLE_HEADER); index++) { 207 | destination[destinationIndex++] = oscBundle->header[index]; 208 | } 209 | destination[destinationIndex++] = oscBundle->oscTimeTag.byteStruct.byte7; 210 | destination[destinationIndex++] = oscBundle->oscTimeTag.byteStruct.byte6; 211 | destination[destinationIndex++] = oscBundle->oscTimeTag.byteStruct.byte5; 212 | destination[destinationIndex++] = oscBundle->oscTimeTag.byteStruct.byte4; 213 | destination[destinationIndex++] = oscBundle->oscTimeTag.byteStruct.byte3; 214 | destination[destinationIndex++] = oscBundle->oscTimeTag.byteStruct.byte2; 215 | destination[destinationIndex++] = oscBundle->oscTimeTag.byteStruct.byte1; 216 | destination[destinationIndex++] = oscBundle->oscTimeTag.byteStruct.byte0; 217 | for (index = 0; index < oscBundle->oscBundleElementsSize; index++) { 218 | destination[destinationIndex++] = oscBundle->oscBundleElements[index]; 219 | } 220 | *oscBundleSize = destinationIndex; 221 | return OscErrorNone; 222 | } 223 | 224 | /** 225 | * @brief Initialises an OSC bundle from a byte array contained within an OSC 226 | * packet or containing OSC bundle. 227 | * 228 | * This function is used internally and should not be used by the user 229 | * application. 230 | * 231 | * @param oscBundle OSC bundle. 232 | * @param source Byte array. 233 | * @param numberOfBytes Number of bytes in byte array. 234 | * @return Error code (0 if successful). 235 | */ 236 | OscError OscBundleInitialiseFromCharArray(OscBundle * const oscBundle, const char * const source, const size_t numberOfBytes) { 237 | unsigned int sourceIndex = 0; 238 | 239 | // Return error if not valid bundle 240 | if (numberOfBytes % 4 != 0) { 241 | return OscErrorSizeIsNotMultipleOfFour; // error: size not multiple of 4 242 | } 243 | if (numberOfBytes < MIN_OSC_BUNDLE_SIZE) { 244 | return OscErrorBundleSizeTooSmall; // error: too few bytes to contain bundle 245 | } 246 | if (numberOfBytes > MAX_OSC_BUNDLE_SIZE) { 247 | return OscErrorBundleSizeTooLarge; // error: size exceeds maximum bundle size 248 | } 249 | if (source[sourceIndex] != '#') { 250 | return OscErrorNoHashAtStartOfBundle; // error: first byte is not '#' 251 | } 252 | 253 | // Header 254 | oscBundle->header[0] = source[sourceIndex++]; 255 | oscBundle->header[1] = source[sourceIndex++]; 256 | oscBundle->header[2] = source[sourceIndex++]; 257 | oscBundle->header[3] = source[sourceIndex++]; 258 | oscBundle->header[4] = source[sourceIndex++]; 259 | oscBundle->header[5] = source[sourceIndex++]; 260 | oscBundle->header[6] = source[sourceIndex++]; 261 | oscBundle->header[7] = source[sourceIndex++]; 262 | 263 | // OSC time tag 264 | oscBundle->oscTimeTag.byteStruct.byte7 = source[sourceIndex++]; 265 | oscBundle->oscTimeTag.byteStruct.byte6 = source[sourceIndex++]; 266 | oscBundle->oscTimeTag.byteStruct.byte5 = source[sourceIndex++]; 267 | oscBundle->oscTimeTag.byteStruct.byte4 = source[sourceIndex++]; 268 | oscBundle->oscTimeTag.byteStruct.byte3 = source[sourceIndex++]; 269 | oscBundle->oscTimeTag.byteStruct.byte2 = source[sourceIndex++]; 270 | oscBundle->oscTimeTag.byteStruct.byte1 = source[sourceIndex++]; 271 | oscBundle->oscTimeTag.byteStruct.byte0 = source[sourceIndex++]; 272 | 273 | // Osc bundle elements 274 | oscBundle->oscBundleElementsSize = 0; 275 | oscBundle->oscBundleElementsIndex = 0; 276 | do { 277 | oscBundle->oscBundleElements[oscBundle->oscBundleElementsSize++] = source[sourceIndex++]; 278 | } while (sourceIndex < numberOfBytes); 279 | 280 | return OscErrorNone; 281 | } 282 | 283 | /** 284 | * @brief Returns true if an OSC bundle element is available based on the 285 | * current oscBundleElementsIndex value. 286 | * 287 | * This function is used internally and should not be used by the user 288 | * application. 289 | * 290 | * @param oscBundle OSC bundle. 291 | * @return True if a bundle element is available. 292 | */ 293 | bool OscBundleIsBundleElementAvailable(const OscBundle * const oscBundle) { 294 | return (oscBundle->oscBundleElementsIndex + sizeof (OscArgument32)) < oscBundle->oscBundleElementsSize; 295 | } 296 | 297 | /** 298 | * @brief Gets the next OSC bundle element available within the OSC bundle based 299 | * on the current oscBundleElementsIndex. 300 | * 301 | * oscBundleElementsIndex will be incremented to the next OSC bundle element if 302 | * this function is successful. Otherwise, the oscBundleElementsIndex will 303 | * remain unmodified. 304 | * 305 | * This function is used internally and should not be used by the user 306 | * application. 307 | * 308 | * @param oscBundle OSC bundle. 309 | * @param oscBundleElement OSC bundle element. 310 | * @return Error code (0 if successful). 311 | */ 312 | OscError OscBundleGetBundleElement(OscBundle * const oscBundle, OscBundleElement * const oscBundleElement) { 313 | if ((oscBundle->oscBundleElementsIndex + sizeof (OscArgument32)) >= oscBundle->oscBundleElementsSize) { 314 | return OscErrorBundleElementNotAvailable; // error: too few bytes to contain bundle element 315 | } 316 | oscBundleElement->size.byteStruct.byte3 = oscBundle->oscBundleElements[oscBundle->oscBundleElementsIndex++]; 317 | oscBundleElement->size.byteStruct.byte2 = oscBundle->oscBundleElements[oscBundle->oscBundleElementsIndex++]; 318 | oscBundleElement->size.byteStruct.byte1 = oscBundle->oscBundleElements[oscBundle->oscBundleElementsIndex++]; 319 | oscBundleElement->size.byteStruct.byte0 = oscBundle->oscBundleElements[oscBundle->oscBundleElementsIndex++]; 320 | if (oscBundleElement->size.int32 < 0) { 321 | return OscErrorNegativeBundleElementSize; // error: size cannot be negative 322 | } 323 | if ((oscBundleElement->size.int32 % 4) != 0) { 324 | return OscErrorSizeIsNotMultipleOfFour; // error: size not multiple of 4 325 | } 326 | if ((oscBundle->oscBundleElementsIndex + oscBundleElement->size.int32) > oscBundle->oscBundleElementsSize) { 327 | return OscErrorInvalidElementSize; // error: too few bytes for indicated size 328 | } 329 | oscBundleElement->contents = &oscBundle->oscBundleElements[oscBundle->oscBundleElementsIndex]; 330 | oscBundle->oscBundleElementsIndex += oscBundleElement->size.int32; 331 | return OscErrorNone; 332 | } 333 | 334 | //------------------------------------------------------------------------------ 335 | // End of file 336 | -------------------------------------------------------------------------------- /Osc99/OscBundle.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscBundle.h 3 | * @author Seb Madgwick 4 | * @brief Functions and structures for constructing and deconstructing OSC 5 | * bundles. 6 | * See http://opensoundcontrol.org/spec-1_0 7 | */ 8 | 9 | #ifndef OSC_BUNDLE_H 10 | #define OSC_BUNDLE_H 11 | 12 | //------------------------------------------------------------------------------ 13 | // Includes 14 | 15 | #include "OscCommon.h" 16 | #include "OscError.h" 17 | #include "OscMessage.h" 18 | #include 19 | 20 | //------------------------------------------------------------------------------ 21 | // Definitions 22 | 23 | /** 24 | * @brief OSC bundle header. These are the first 8 bytes (including the 25 | * terminating null character) that appear at the start of every bundle. 26 | */ 27 | #define OSC_BUNDLE_HEADER "#bundle" 28 | 29 | /** 30 | * @brief Minimum size (number of bytes) of an OSC bundle as per the OSC 31 | * specification. 32 | */ 33 | #define MIN_OSC_BUNDLE_SIZE ((sizeof(OSC_BUNDLE_HEADER) + sizeof(OscTimeTag))) 34 | 35 | /** 36 | * @brief Maximum size (number of bytes) of an OSC bundle equal to the maximum 37 | * packet size permitted by the transport layer. 38 | */ 39 | #define MAX_OSC_BUNDLE_SIZE (MAX_TRANSPORT_SIZE) 40 | 41 | /** 42 | * @brief Maximum combined size (number of bytes) of all OSC bundle elements 43 | * that may be contained within an OSC bundle. 44 | */ 45 | #define MAX_OSC_BUNDLE_ELEMENTS_SIZE (MAX_OSC_BUNDLE_SIZE - sizeof(OSC_BUNDLE_HEADER) - sizeof(OscTimeTag)) 46 | 47 | /** 48 | * @brief OSC bundle structure. Structure members are used internally and 49 | * should not be used by the user application. 50 | */ 51 | typedef struct { 52 | char header[sizeof (OSC_BUNDLE_HEADER)]; // must be the first member so that the first byte of structure is equal to '#'. 53 | OscTimeTag oscTimeTag; 54 | char oscBundleElements[MAX_OSC_BUNDLE_ELEMENTS_SIZE]; 55 | size_t oscBundleElementsSize; 56 | unsigned int oscBundleElementsIndex; 57 | } OscBundle; 58 | 59 | /** 60 | * @brief OSC bundle element structure. This structure is used internally and 61 | * should not be used by the user application. 62 | */ 63 | typedef struct { 64 | OscArgument32 size; // int32 65 | void * contents; // pointer to bundle element contents 66 | } OscBundleElement; 67 | 68 | //------------------------------------------------------------------------------ 69 | // Function prototypes 70 | 71 | void OscBundleInitialise(OscBundle * const oscBundle, const OscTimeTag oscTimeTag); 72 | OscError OscBundleAddContents(OscBundle * const oscBundle, const void * const oscContents); 73 | void OscBundleEmpty(OscBundle * const oscBundle); 74 | bool OscBundleIsEmpty(OscBundle * const oscBundle); 75 | size_t OscBundleGetRemainingCapacity(const OscBundle * const oscBundle); 76 | size_t OscBundleGetSize(const OscBundle * const oscBundle); 77 | OscError OscBundleToCharArray(const OscBundle * const oscBundle, size_t * const oscBundleSize, char * const destination, const size_t destinationSize); 78 | OscError OscBundleInitialiseFromCharArray(OscBundle * const oscBundle, const char * const source, const size_t numberOfBytes); 79 | bool OscBundleIsBundleElementAvailable(const OscBundle * const oscBundle); 80 | OscError OscBundleGetBundleElement(OscBundle * const oscBundle, OscBundleElement * const oscBundleElement); 81 | 82 | #endif 83 | 84 | //------------------------------------------------------------------------------ 85 | // End of file 86 | -------------------------------------------------------------------------------- /Osc99/OscCommon.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscCommon.c 3 | * @author Seb Madgwick 4 | * @brief Definitions, types, and functions used throughout library. 5 | * See http://opensoundcontrol.org/spec-1_0 6 | */ 7 | 8 | //------------------------------------------------------------------------------ 9 | // Includes 10 | 11 | #include "OscCommon.h" 12 | 13 | //------------------------------------------------------------------------------ 14 | // Variables 15 | 16 | const OscTimeTag oscTimeTagZero = { 17 | .value = 0, 18 | }; 19 | 20 | //------------------------------------------------------------------------------ 21 | // Functions 22 | 23 | /** 24 | * @brief Returns true if the OSC contents is an OSC message. 25 | * @param oscContents OSC packet, OSC bundle, or OSC message. 26 | * @return True if the OSC contents is an OSC message. 27 | */ 28 | bool OscContentsIsMessage(const void * const oscContents) { 29 | return (*(char *) (oscContents) == '/'); 30 | } 31 | 32 | /** 33 | * @brief Returns true if the OSC contents is an OSC bundle. 34 | * @param oscContents OSC packet, OSC bundle, or OSC message. 35 | * @return True if the OSC contents is an OSC bundle. 36 | */ 37 | bool OscContentsIsBundle(const void * const oscContents) { 38 | return (*(char *) (oscContents) == '#'); 39 | } 40 | 41 | //------------------------------------------------------------------------------ 42 | // End of file 43 | -------------------------------------------------------------------------------- /Osc99/OscCommon.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscCommon.h 3 | * @author Seb Madgwick 4 | * @brief Definitions, types, and functions used throughout library. 5 | * See http://opensoundcontrol.org/spec-1_0 6 | */ 7 | 8 | #ifndef OSC_COMMON_H 9 | #define OSC_COMMON_H 10 | 11 | //------------------------------------------------------------------------------ 12 | // Includes 13 | 14 | #include // DBL_MANT_DIG 15 | #include 16 | #include 17 | 18 | //------------------------------------------------------------------------------ 19 | // Definitions - Application/platform specific 20 | 21 | /** 22 | * @brief Comment out this definition if the platform is big-endian. For 23 | * example: Arduino, Atmel AVR, Microchip PIC, Intel x86-64 are little-endian. 24 | * See http://en.wikipedia.org/wiki/Endianness 25 | */ 26 | #define LITTLE_ENDIAN_PLATFORM 27 | 28 | /** 29 | * @brief Maximum packet size permitted by the transport layer. Reducing this 30 | * value will reduce the amount of memory required. 31 | */ 32 | #define MAX_TRANSPORT_SIZE (1472) 33 | 34 | /** 35 | * @brief Comment out this definition to prevent the OscErrorGetMessage function 36 | * from providing detailed error messages. This will reduce the amount of 37 | * memory required. 38 | */ 39 | #define OSC_ERROR_MESSAGES_ENABLED 40 | 41 | //------------------------------------------------------------------------------ 42 | // Definitions - 32-bit argument types 43 | 44 | /** 45 | * @brief 32-bit RGBA colour. 46 | * See http://en.wikipedia.org/wiki/RGBA_color_space 47 | */ 48 | typedef struct __attribute__((__packed__)) { 49 | #ifdef LITTLE_ENDIAN_PLATFORM 50 | char alpha; // LSB 51 | char blue; 52 | char green; 53 | char red; // MSB 54 | #else 55 | char red; // MSB 56 | char green; 57 | char blue; 58 | char alpha; // LSB 59 | #endif 60 | } 61 | RgbaColour; 62 | 63 | /** 64 | * @brief 4 byte MIDI message as described in OSC 1.0 specification. 65 | */ 66 | typedef struct __attribute__((__packed__)) { 67 | #ifdef LITTLE_ENDIAN_PLATFORM 68 | char data2; // LSB 69 | char data1; 70 | char status; 71 | char portID; // MSB 72 | #else 73 | char portID; // MSB 74 | char status; 75 | char data1; 76 | char data2; // LSB 77 | #endif 78 | } 79 | MidiMessage; 80 | 81 | /** 82 | * @brief Union of all 32-bit OSC argument types defined in OSC 1.0 83 | * specification. 84 | */ 85 | typedef union { 86 | int32_t int32; 87 | float float32; 88 | RgbaColour rgbaColour; 89 | MidiMessage midiMessage; 90 | 91 | struct __attribute__((__packed__)) { 92 | #ifdef LITTLE_ENDIAN_PLATFORM 93 | char byte0; // LSB 94 | char byte1; 95 | char byte2; 96 | char byte3; // MSB 97 | #else 98 | char byte3; // MSB 99 | char byte2; 100 | char byte1; 101 | char byte0; // LSB 102 | #endif 103 | } 104 | byteStruct; 105 | } OscArgument32; 106 | 107 | //------------------------------------------------------------------------------ 108 | // Definitions - 64-bit argument types 109 | 110 | /** 111 | * @brief OSC time tag. Same representation used by NTP timestamps. 112 | */ 113 | typedef union { 114 | uint64_t value; 115 | 116 | struct __attribute__((__packed__)) { 117 | uint32_t fraction; 118 | uint32_t seconds; 119 | } 120 | dwordStruct; 121 | 122 | struct __attribute__((__packed__)) { 123 | #ifdef LITTLE_ENDIAN_PLATFORM 124 | char byte0; // LSB 125 | char byte1; 126 | char byte2; 127 | char byte3; 128 | char byte4; 129 | char byte5; 130 | char byte6; 131 | char byte7; // MSB 132 | #else 133 | char byte7; // MSB 134 | char byte6; 135 | char byte5; 136 | char byte4; 137 | char byte3; 138 | char byte2; 139 | char byte1; 140 | char byte0; // LSB 141 | #endif 142 | } 143 | byteStruct; 144 | } OscTimeTag; 145 | 146 | /** 147 | * @brief 64-bit double. Defined as double or long double depending on 148 | * platform. 149 | */ 150 | #if (DBL_MANT_DIG == 53) 151 | typedef double Double64; 152 | #else 153 | typedef long double Double64; // use long double if double is not 64-bit 154 | #endif 155 | 156 | /** 157 | * @brief Union of all 64-bit OSC argument types defined in OSC 1.0 158 | * specification. 159 | */ 160 | typedef union { 161 | uint64_t int64; 162 | OscTimeTag oscTimeTag; 163 | Double64 double64; 164 | 165 | struct __attribute__((__packed__)) { 166 | #ifdef LITTLE_ENDIAN_PLATFORM 167 | char byte0; // LSB 168 | char byte1; 169 | char byte2; 170 | char byte3; 171 | char byte4; 172 | char byte5; 173 | char byte6; 174 | char byte7; // MSB 175 | #else 176 | char byte7; // MSB 177 | char byte6; 178 | char byte5; 179 | char byte4; 180 | char byte3; 181 | char byte2; 182 | char byte1; 183 | char byte0; // LSB 184 | #endif 185 | } 186 | byteStruct; 187 | } OscArgument64; 188 | 189 | //------------------------------------------------------------------------------ 190 | // Variable declarations 191 | 192 | extern const OscTimeTag oscTimeTagZero; 193 | 194 | //------------------------------------------------------------------------------ 195 | // Function prototypes 196 | 197 | bool OscContentsIsMessage(const void * const oscContents); 198 | bool OscContentsIsBundle(const void * const oscContents); 199 | 200 | #endif 201 | 202 | //------------------------------------------------------------------------------ 203 | // End of file 204 | -------------------------------------------------------------------------------- /Osc99/OscError.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscError.c 3 | * @author Seb Madgwick 4 | * @brief Errors returned by library functions. 5 | * See http://opensoundcontrol.org/spec-1_0 6 | */ 7 | 8 | //------------------------------------------------------------------------------ 9 | // Includes 10 | 11 | #include "OscCommon.h" 12 | #include "OscError.h" 13 | 14 | //------------------------------------------------------------------------------ 15 | // Functions 16 | 17 | /** 18 | * @brief Returns the error message associated with the error code. 19 | * 20 | * Example use: 21 | * @code 22 | * OscMessage oscMessage; 23 | * const OscError oscError = OscMessageInitialise(&oscMessage, "/example/address/pattern"); 24 | * printf(OscErrorGetMessage(oscError)); 25 | * @endcode 26 | * 27 | * @param oscError Error code returned by library function. 28 | * @return Error message string. 29 | */ 30 | char * OscErrorGetMessage(const OscError oscError) { 31 | #ifdef OSC_ERROR_MESSAGES_ENABLED 32 | switch (oscError) { 33 | case OscErrorNone: 34 | return (char *) &"No error."; 35 | 36 | /* Common errors */ 37 | case OscErrorDestinationTooSmall: 38 | return (char *) &"Destination size too small to contain the number of bytes available."; 39 | case OscErrorSizeIsNotMultipleOfFour: 40 | return (char *) &"OSC packet or OSC contents size must be a multiple of four."; 41 | case OscErrorCallbackFunctionUndefined: 42 | return (char *) &"Callback function undefined."; 43 | 44 | /* OscAddress errors */ 45 | case OscErrorNotEnoughPartsInAddressPattern: 46 | return (char *) &"Not enough parts in OSC address pattern to get part at specified index."; 47 | 48 | /* OscMessage errors */ 49 | case OscErrorNoSlashAtStartOfMessage: 50 | return (char *) &"OSC address pattern does not start with a slash character."; 51 | case OscErrorAddressPatternTooLong: 52 | return (char *) &"OSC address pattern length cannot exceed MAX_OSC_ADDRESS_PATTERN_LENGTH."; 53 | case OscErrorTooManyArguments: 54 | return (char *) &"Number of arguments cannot exceed MAX_NUMBER_OF_ARGUMENTS."; 55 | case OscErrorArgumentsSizeTooLarge: 56 | return (char *) &"Total arguments size cannot exceed MAX_ARGUMENTS_SIZE."; 57 | case OscErrorUndefinedAddressPattern: 58 | return (char *) &"Undefined OSC address pattern."; 59 | case OscErrorMessageSizeTooSmall: 60 | return (char *) &"OSC message size too small to be a valid OSC message."; 61 | case OscErrorMessageSizeTooLarge: 62 | return (char *) &"OSC message size cannot exceed MAX_OSC_MESSAGE_SIZE."; 63 | case OscErrorSourceEndsBeforeEndOfAddressPattern: 64 | return (char *) &"Source data ends before the end of address pattern."; 65 | case OscErrorSourceEndsBeforeStartOfTypeTagString: 66 | return (char *) &"Source data ends before the start of type tag string."; 67 | case OscErrorTypeTagStringToLong: 68 | return (char *) &"Type tag string length cannot exceed MAX_OSC_TYPE_TAG_STRING_LENGTH."; 69 | case OscErrorSourceEndsBeforeEndOfTypeTagString: 70 | return (char *) &"Source data ends before the end of type tag string."; 71 | case OscErrorUnexpectedEndOfSource: 72 | return (char *) &"Unexpected end of source data."; 73 | case OscErrorNoArgumentsAvailable: 74 | return (char *) &"No arguments available."; 75 | case OscErrorUnexpectedArgumentType: 76 | return (char *) &"Unexpected argument type."; 77 | case OscErrorMessageTooShortForArgumentType: 78 | return (char *) &"OSC message is too short to contain argument type."; 79 | 80 | /* OscBundle errors */ 81 | case OscErrorBundleFull: 82 | return (char *) &"Not enough space available in OSC bundle to contain contents."; 83 | case OscErrorBundleSizeTooSmall: 84 | return (char *) &"OSC bundle size too small to be a valid OSC bundle."; 85 | case OscErrorBundleSizeTooLarge: 86 | return (char *) &"OSC bundle size cannot exceed MAX_OSC_BUNDLE_SIZE."; 87 | case OscErrorNoHashAtStartOfBundle: 88 | return (char *) &"OSC bundle does not start with a hash character."; 89 | case OscErrorBundleElementNotAvailable: 90 | return (char *) &"OSC bundle element not available."; 91 | case OscErrorNegativeBundleElementSize: 92 | return (char *) &"OSC bundle element size cannot be negative."; 93 | case OscErrorInvalidElementSize: 94 | return (char *) &"OSC bundle too short to contain the OSC bundle element size."; 95 | 96 | /* OscPacket errors */ 97 | case OscErrorInvalidContents: 98 | return (char *) &"OSC contents is not an OSC bundle or OSC message."; 99 | case OscErrorPacketSizeTooLarge: 100 | return (char *) &"OSC packet size cannot exceed MAX_OSC_PACKET_SIZE."; 101 | case OscErrorContentsEmpty: 102 | return (char *) &"OSC contents size cannot be zero."; 103 | 104 | /* OscSlip errors */ 105 | case OscErrorEncodedSlipPacketTooLong: 106 | return (char *) &"Encoded SLIP packet size cannot exceed OSC_SLIP_DECODER_BUFFER_SIZE."; 107 | case OscErrorUnexpectedByteAfterSlipEsc: 108 | return (char *) &"Unexpected byte after SLIP ESC byte."; 109 | case OscErrorDecodedSlipPacketTooLong: 110 | return (char *) &"Decoded SLIP packet size cannot exceed MAX_OSC_PACKET_SIZE."; 111 | } 112 | return (char *) &"Unknown error."; 113 | #else 114 | return (char *) &"OSC error."; 115 | #endif 116 | } 117 | 118 | //------------------------------------------------------------------------------ 119 | // End of file 120 | -------------------------------------------------------------------------------- /Osc99/OscError.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscError.h 3 | * @author Seb Madgwick 4 | * @brief Errors returned by library functions. 5 | * See http://opensoundcontrol.org/spec-1_0 6 | */ 7 | 8 | #ifndef OSC_ERROR_H 9 | #define OSC_ERROR_H 10 | 11 | //------------------------------------------------------------------------------ 12 | // Definitions 13 | 14 | /** 15 | * @brief Enumerated error codes for debugging and user feedback. 16 | */ 17 | typedef enum { 18 | OscErrorNone = 0, 19 | 20 | /* Common errors */ 21 | OscErrorDestinationTooSmall, 22 | OscErrorSizeIsNotMultipleOfFour, 23 | OscErrorCallbackFunctionUndefined, 24 | 25 | /* OscAddress errors */ 26 | OscErrorNotEnoughPartsInAddressPattern, 27 | 28 | /* OscMessage errors */ 29 | OscErrorNoSlashAtStartOfMessage, 30 | OscErrorAddressPatternTooLong, 31 | OscErrorTooManyArguments, 32 | OscErrorArgumentsSizeTooLarge, 33 | OscErrorUndefinedAddressPattern, 34 | OscErrorMessageSizeTooSmall, 35 | OscErrorMessageSizeTooLarge, 36 | OscErrorSourceEndsBeforeEndOfAddressPattern, 37 | OscErrorSourceEndsBeforeStartOfTypeTagString, 38 | OscErrorTypeTagStringToLong, 39 | OscErrorSourceEndsBeforeEndOfTypeTagString, 40 | OscErrorUnexpectedEndOfSource, 41 | OscErrorNoArgumentsAvailable, 42 | OscErrorUnexpectedArgumentType, 43 | OscErrorMessageTooShortForArgumentType, 44 | 45 | /* OscBundle errors */ 46 | OscErrorBundleFull, 47 | OscErrorBundleSizeTooSmall, 48 | OscErrorBundleSizeTooLarge, 49 | OscErrorNoHashAtStartOfBundle, 50 | OscErrorBundleElementNotAvailable, 51 | OscErrorNegativeBundleElementSize, 52 | OscErrorInvalidElementSize, 53 | 54 | /* OscPacket errors */ 55 | OscErrorInvalidContents, 56 | OscErrorPacketSizeTooLarge, 57 | OscErrorContentsEmpty, 58 | 59 | /* OscSlip errors */ 60 | OscErrorEncodedSlipPacketTooLong, 61 | OscErrorUnexpectedByteAfterSlipEsc, 62 | OscErrorDecodedSlipPacketTooLong, 63 | 64 | } OscError; 65 | 66 | //------------------------------------------------------------------------------ 67 | // Function prototypes 68 | 69 | char * OscErrorGetMessage(const OscError oscError); 70 | 71 | #endif 72 | 73 | //------------------------------------------------------------------------------ 74 | // End of file 75 | -------------------------------------------------------------------------------- /Osc99/OscMessage.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscMessage.c 3 | * @author Seb Madgwick 4 | * @brief Functions and structures for constructing and deconstructing OSC 5 | * messages. 6 | * See http://opensoundcontrol.org/spec-1_0 7 | */ 8 | 9 | //------------------------------------------------------------------------------ 10 | // Includes 11 | 12 | #include // SCHAR_MAX 13 | #include "OscMessage.h" 14 | #include // strlen 15 | 16 | //------------------------------------------------------------------------------ 17 | // Function prototypes 18 | 19 | static int TerminateOscString(char * const oscString, size_t * const oscStringSize, const size_t maxOscStringSize); 20 | 21 | //------------------------------------------------------------------------------ 22 | // Functions - Message construction 23 | 24 | /** 25 | * @brief Initialises an OSC message. 26 | * 27 | * An OSC message must be initialised before use. The oscAddressPattern 28 | * argument must be a null terminated string of zero of more characters. A 29 | * message may be initialised without an address pattern by parsing an 30 | * oscAddressPattern value of "". This may be of use if the address pattern is 31 | * undetermined at the time of initialisation. In which case, the address 32 | * pattern may be set later using OscMessageSetAddressPattern. 33 | * 34 | * Example use: 35 | * @code 36 | * OscMessage oscMessage; 37 | * OscMessageInitialise(&oscMessage, "/example/address/pattern"); 38 | * @endcode 39 | * 40 | * @param oscMessage OSC message to be initialised. 41 | * @param oscAddressPattern OSC address pattern as null terminated string. 42 | * @return Error code (0 if successful). 43 | */ 44 | OscError OscMessageInitialise(OscMessage * const oscMessage, const char * oscAddressPattern) { 45 | oscMessage->oscAddressPattern[0] = '\0'; // null terminate string 46 | oscMessage->oscTypeTagString[0] = ','; 47 | oscMessage->oscTypeTagString[1] = '\0'; // null terminate string 48 | oscMessage->oscAddressPatternLength = 0; 49 | oscMessage->oscTypeTagStringLength = 1; // includes comma 50 | oscMessage->argumentsSize = 0; 51 | oscMessage->oscTypeTagStringIndex = 1; // skip comma 52 | oscMessage->argumentsIndex = 0; 53 | if (*oscAddressPattern != '\0') { 54 | return OscMessageSetAddressPattern(oscMessage, oscAddressPattern); 55 | } 56 | return OscErrorNone; 57 | } 58 | 59 | /** 60 | * @brief Sets the OSC address pattern of an OSC message. 61 | * 62 | * The oscAddressPattern argument must be a null terminated string of zero of 63 | * more characters. The existing OSC address pattern will be overwritten. 64 | * 65 | * Example use: 66 | * @code 67 | * OscMessage oscMessage; 68 | * OscMessageInitialise(&oscMessage, ""); 69 | * OscMessageSetAddressPattern(&oscMessage, "/example/address/pattern"); 70 | * @endcode 71 | * 72 | * @param oscMessage OSC message. 73 | * @param oscAddressPattern OSC address pattern as null terminated string. 74 | * @return Error code (0 if successful). 75 | */ 76 | OscError OscMessageSetAddressPattern(OscMessage * const oscMessage, const char * oscAddressPattern) { 77 | oscMessage->oscAddressPatternLength = 0; 78 | return OscMessageAppendAddressPattern(oscMessage, oscAddressPattern); 79 | } 80 | 81 | /** 82 | * @brief Appends OSC address pattern parts to the OSC address pattern of an 83 | * OSC message. 84 | * 85 | * The appendedParts argument must be a null terminated string. 86 | * 87 | * Example use: 88 | * @code 89 | * OscMessage oscMessage; 90 | * OscMessageInitialise(&oscMessage, ""); 91 | * OscMessageAppendAddressPattern(&oscMessage, "/example"); 92 | * OscMessageAppendAddressPattern(&oscMessage, "/address"); 93 | * OscMessageAppendAddressPattern(&oscMessage, "/pattern"); 94 | * @endcode 95 | * 96 | * @param oscMessage OSC message. 97 | * @param appendedParts OSC pattern parts to be appended. 98 | * @return Error code (0 if successful). 99 | */ 100 | OscError OscMessageAppendAddressPattern(OscMessage * const oscMessage, const char * appendedParts) { 101 | if (*appendedParts != '/') { 102 | return OscErrorNoSlashAtStartOfMessage; // error: address must start with '/' 103 | } 104 | while (*appendedParts != '\0') { 105 | if (oscMessage->oscAddressPatternLength >= MAX_OSC_ADDRESS_PATTERN_LENGTH) { 106 | return OscErrorAddressPatternTooLong; // error: address pattern too long 107 | } 108 | oscMessage->oscAddressPattern[oscMessage->oscAddressPatternLength++] = *appendedParts++; 109 | } 110 | oscMessage->oscAddressPattern[oscMessage->oscAddressPatternLength] = '\0'; // null terminate string 111 | return OscErrorNone; 112 | } 113 | 114 | /** 115 | * @brief Adds a 32-bit integer argument to an OSC message. 116 | * 117 | * Example use: 118 | * @code 119 | * OscMessageAddInt32(&oscMessage, 123); 120 | * @endcode 121 | * 122 | * @param oscMessage OSC message. 123 | * @param int32 32-bit integer to be added as argument to the OSC message. 124 | * @return Error code (0 if successful). 125 | */ 126 | OscError OscMessageAddInt32(OscMessage * const oscMessage, const int32_t int32) { 127 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 128 | return OscErrorTooManyArguments; // error: too many arguments 129 | } 130 | if ((oscMessage->argumentsSize + sizeof (OscArgument32)) > MAX_ARGUMENTS_SIZE) { 131 | return OscErrorArgumentsSizeTooLarge; // error: message full 132 | } 133 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = OscTypeTagInt32; 134 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 135 | OscArgument32 oscArgument32; 136 | oscArgument32.int32 = int32; 137 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte3; 138 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte2; 139 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte1; 140 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte0; 141 | return OscErrorNone; 142 | } 143 | 144 | /** 145 | * @brief Adds a 32-bit float argument to an OSC message. 146 | * 147 | * Example use: 148 | * @code 149 | * OscMessageAddFloat32(&oscMessage, 3.14f); 150 | * @endcode 151 | * 152 | * @param oscMessage OSC message. 153 | * @param float32 32-bit float to be added as argument to the OSC message. 154 | * @return Error code (0 if successful). 155 | */ 156 | OscError OscMessageAddFloat32(OscMessage * const oscMessage, const float float32) { 157 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 158 | return OscErrorTooManyArguments; // error: too many arguments 159 | } 160 | if ((oscMessage->argumentsSize + sizeof (OscArgument32)) > MAX_ARGUMENTS_SIZE) { 161 | return OscErrorArgumentsSizeTooLarge; // error: message full 162 | } 163 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = OscTypeTagFloat32; 164 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 165 | OscArgument32 oscArgument32; 166 | oscArgument32.float32 = float32; 167 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte3; 168 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte2; 169 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte1; 170 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte0; 171 | return OscErrorNone; 172 | } 173 | 174 | /** 175 | * @brief Adds a string argument to an OSC message. 176 | * 177 | * Example use: 178 | * @code 179 | * OscMessageAddString(&oscMessage, "Hello World!"); 180 | * @endcode 181 | * 182 | * @param oscMessage OSC message. 183 | * @param string String to be added as argument to the OSC message. 184 | * @return Error code (0 if successful). 185 | */ 186 | OscError OscMessageAddString(OscMessage * const oscMessage, const char * string) { 187 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 188 | return OscErrorTooManyArguments; // error: too many arguments 189 | } 190 | size_t argumentsSize = oscMessage->argumentsSize; // local copy in case function returns error 191 | while (*string != '\0') { 192 | if (argumentsSize >= MAX_ARGUMENTS_SIZE) { 193 | return OscErrorArgumentsSizeTooLarge; // error: message full 194 | } 195 | oscMessage->arguments[argumentsSize++] = *string++; 196 | } 197 | if (TerminateOscString(oscMessage->arguments, &argumentsSize, MAX_ARGUMENTS_SIZE) != 0) { 198 | return OscErrorArgumentsSizeTooLarge; // error: message full 199 | } 200 | oscMessage->argumentsSize = argumentsSize; 201 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = OscTypeTagString; 202 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 203 | return OscErrorNone; 204 | } 205 | 206 | /** 207 | * @brief Adds a blob (byte array) argument to an OSC message. 208 | * 209 | * Example use: 210 | * @code 211 | * const char source[] = { 0x00, 0x01, 0x02, 0x03, 0x04 }; 212 | * OscMessageAddBlob(&oscMessage, source, sizeof(source)); 213 | * @endcode 214 | * 215 | * @param oscMessage OSC message. 216 | * @param source Byte array to be added as argument. 217 | * @param numberOfBytes Number of bytes in byte array to be added as argument. 218 | * @return Error code (0 if successful). 219 | */ 220 | OscError OscMessageAddBlob(OscMessage * const oscMessage, const char * const source, const size_t numberOfBytes) { 221 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 222 | return OscErrorTooManyArguments; // error: too many arguments 223 | } 224 | if ((oscMessage->argumentsSize + sizeof (OscArgument32) + numberOfBytes) > MAX_ARGUMENTS_SIZE) { 225 | return OscErrorArgumentsSizeTooLarge; // error: message full 226 | } 227 | size_t argumentsSize = oscMessage->argumentsSize; // local copy in case function returns error 228 | OscArgument32 blobSize; 229 | blobSize.int32 = (int32_t) numberOfBytes; 230 | oscMessage->arguments[argumentsSize++] = blobSize.byteStruct.byte3; 231 | oscMessage->arguments[argumentsSize++] = blobSize.byteStruct.byte2; 232 | oscMessage->arguments[argumentsSize++] = blobSize.byteStruct.byte1; 233 | oscMessage->arguments[argumentsSize++] = blobSize.byteStruct.byte0; 234 | unsigned int sourceIndex; 235 | for (sourceIndex = 0; sourceIndex < numberOfBytes; sourceIndex++) { 236 | oscMessage->arguments[argumentsSize++] = source[sourceIndex]; 237 | } 238 | while ((argumentsSize % 4) != 0) { 239 | if (argumentsSize >= MAX_ARGUMENTS_SIZE) { 240 | return OscErrorArgumentsSizeTooLarge; // error: message full 241 | } 242 | oscMessage->arguments[argumentsSize++] = 0; 243 | } 244 | oscMessage->argumentsSize = argumentsSize; 245 | oscMessage->oscTypeTagString[(oscMessage->oscTypeTagStringLength)++] = OscTypeTagBlob; 246 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 247 | return OscErrorNone; 248 | } 249 | 250 | /** 251 | * @brief Adds a 64-bit integer argument to an OSC message. 252 | * 253 | * Example use: 254 | * @code 255 | * OscMessageAddInt64(&oscMessage, 123); 256 | * @endcode 257 | * 258 | * @param oscMessage OSC message. 259 | * @param int64 64-bit integer to be added as argument to the OSC message. 260 | * @return Error code (0 if successful). 261 | */ 262 | OscError OscMessageAddInt64(OscMessage * const oscMessage, const uint64_t int64) { 263 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 264 | return OscErrorTooManyArguments; // error: too many arguments 265 | } 266 | if ((oscMessage->argumentsSize + sizeof (OscArgument64)) > MAX_ARGUMENTS_SIZE) { 267 | return OscErrorArgumentsSizeTooLarge; // error: message full 268 | } 269 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = OscTypeTagInt64; 270 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 271 | OscArgument64 oscArgument64; 272 | oscArgument64.int64 = int64; 273 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte7; 274 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte6; 275 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte5; 276 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte4; 277 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte3; 278 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte2; 279 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte1; 280 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte0; 281 | return OscErrorNone; 282 | } 283 | 284 | /** 285 | * @brief Adds an OSC time tag argument to an OSC message. 286 | * 287 | * Example use: 288 | * @code 289 | * OscMessageAddTimeTag(&oscMessage, oscTimeTagZero); 290 | * @endcode 291 | * 292 | * @param oscMessage OSC message. 293 | * @param oscTimeTag OSC time tag to be added as argument to the OSC message. 294 | * @return Error code (0 if successful). 295 | */ 296 | OscError OscMessageAddTimeTag(OscMessage * const oscMessage, const OscTimeTag oscTimeTag) { 297 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 298 | return OscErrorTooManyArguments; // error: too many arguments 299 | } 300 | if ((oscMessage->argumentsSize + sizeof (OscTimeTag)) > MAX_ARGUMENTS_SIZE) { 301 | return OscErrorArgumentsSizeTooLarge; // error: message full 302 | } 303 | oscMessage->oscTypeTagString[(oscMessage->oscTypeTagStringLength)++] = OscTypeTagTimeTag; 304 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 305 | oscMessage->arguments[oscMessage->argumentsSize++] = oscTimeTag.byteStruct.byte7; 306 | oscMessage->arguments[oscMessage->argumentsSize++] = oscTimeTag.byteStruct.byte6; 307 | oscMessage->arguments[oscMessage->argumentsSize++] = oscTimeTag.byteStruct.byte5; 308 | oscMessage->arguments[oscMessage->argumentsSize++] = oscTimeTag.byteStruct.byte4; 309 | oscMessage->arguments[oscMessage->argumentsSize++] = oscTimeTag.byteStruct.byte3; 310 | oscMessage->arguments[oscMessage->argumentsSize++] = oscTimeTag.byteStruct.byte2; 311 | oscMessage->arguments[oscMessage->argumentsSize++] = oscTimeTag.byteStruct.byte1; 312 | oscMessage->arguments[oscMessage->argumentsSize++] = oscTimeTag.byteStruct.byte0; 313 | return OscErrorNone; 314 | } 315 | 316 | /** 317 | * @brief Adds a 64-bit double argument to an OSC message. 318 | * 319 | * Example use: 320 | * @code 321 | * OscMessageAddDouble(&oscMessage, 3.14); 322 | * @endcode 323 | * 324 | * @param oscMessage OSC message. 325 | * @param double64 64-bit double to be added as argument to the OSC message. 326 | * @return Error code (0 if successful). 327 | */ 328 | OscError OscMessageAddDouble(OscMessage * const oscMessage, const Double64 double64) { 329 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 330 | return OscErrorTooManyArguments; // error: too many arguments 331 | } 332 | if ((oscMessage->argumentsSize + sizeof (OscArgument64)) > MAX_ARGUMENTS_SIZE) { 333 | return OscErrorArgumentsSizeTooLarge; // error: message full 334 | } 335 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = OscTypeTagDouble; 336 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 337 | OscArgument64 oscArgument64; 338 | oscArgument64.double64 = double64; 339 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte7; 340 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte6; 341 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte5; 342 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte4; 343 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte3; 344 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte2; 345 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte1; 346 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument64.byteStruct.byte0; 347 | return OscErrorNone; 348 | } 349 | 350 | /** 351 | * @brief Adds an alternate string argument to an OSC message. 352 | * 353 | * Example use: 354 | * @code 355 | * OscMessageAddAlternateString(&oscMessage, "Hello World!"); 356 | * @endcode 357 | * 358 | * @param oscMessage OSC message. 359 | * @param string String to be added as argument to the OSC message. 360 | * @return Error code (0 if successful). 361 | */ 362 | OscError OscMessageAddAlternateString(OscMessage * const oscMessage, const char * string) { 363 | const OscError oscError = OscMessageAddString(oscMessage, string); 364 | if (oscError != OscErrorNone) { 365 | return oscError; 366 | } 367 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength - 1] = OscTypeTagAlternateString; 368 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 369 | return OscErrorNone; 370 | } 371 | 372 | /** 373 | * @brief Adds a char argument to an OSC message. 374 | * 375 | * Example use: 376 | * @code 377 | * OscMessageAddCharacter(&oscMessage, 'a'); 378 | * @endcode 379 | * 380 | * @param oscMessage OSC message. 381 | * @param asciiChar Character to be added as argument to the OSC message. 382 | * @return Error code (0 if successful). 383 | */ 384 | OscError OscMessageAddCharacter(OscMessage * const oscMessage, const char asciiChar) { 385 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 386 | return OscErrorTooManyArguments; // error: too many arguments 387 | } 388 | if ((oscMessage->argumentsSize + sizeof (OscArgument32)) > MAX_ARGUMENTS_SIZE) { 389 | return OscErrorArgumentsSizeTooLarge; // error: message full 390 | } 391 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = OscTypeTagCharacter; 392 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 393 | oscMessage->arguments[oscMessage->argumentsSize++] = 0; 394 | oscMessage->arguments[oscMessage->argumentsSize++] = 0; 395 | oscMessage->arguments[oscMessage->argumentsSize++] = 0; 396 | oscMessage->arguments[oscMessage->argumentsSize++] = asciiChar; 397 | return OscErrorNone; 398 | } 399 | 400 | /** 401 | * @brief Adds a 32-bit RGBA colour argument to an OSC message. 402 | * 403 | * Example use: 404 | * @code 405 | * const RgbaColour rgbaColour = { 0x00, 0x00, 0x00, 0x00 }; 406 | * OscMessageAddRgbaColour(&oscMessage, rgbaColour); 407 | * @endcode 408 | * 409 | * @param oscMessage OSC message. 410 | * @param rgbaColour 32-bit RGBA colour to be added as argument to the OSC 411 | * message. 412 | * @return Error code (0 if successful). 413 | */ 414 | OscError OscMessageAddRgbaColour(OscMessage * const oscMessage, const RgbaColour rgbaColour) { 415 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 416 | return OscErrorTooManyArguments; // error: too many arguments 417 | } 418 | if ((oscMessage->argumentsSize + sizeof (OscArgument32)) > MAX_ARGUMENTS_SIZE) { 419 | return OscErrorArgumentsSizeTooLarge; // error: message full 420 | } 421 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = OscTypeTagRgbaColour; 422 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 423 | OscArgument32 oscArgument32; 424 | oscArgument32.rgbaColour = rgbaColour; 425 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte3; 426 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte2; 427 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte1; 428 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte0; 429 | return OscErrorNone; 430 | } 431 | 432 | /** 433 | * @brief Adds a 4 byte MIDI message argument to an OSC message. 434 | * 435 | * Example use: 436 | * @code 437 | * const MidiMessage midiMessage = { 0x00, 0x00, 0x00, 0x00 }; 438 | * OscMessageAddMidiMessage(&oscMessage, midiMessage); 439 | * @endcode 440 | * 441 | * @param oscMessage OSC message. 442 | * @param midiMessage 4 byte MIDI message to be added as argument to the OSC 443 | * message. 444 | * @return Error code (0 if successful). 445 | */ 446 | OscError OscMessageAddMidiMessage(OscMessage * const oscMessage, const MidiMessage midiMessage) { 447 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 448 | return OscErrorTooManyArguments; // error: too many arguments 449 | } 450 | if ((oscMessage->argumentsSize + sizeof (OscArgument32)) > MAX_ARGUMENTS_SIZE) { 451 | return OscErrorArgumentsSizeTooLarge; // error: message full 452 | } 453 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = OscTypeTagMidiMessage; 454 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 455 | OscArgument32 oscArgument32; 456 | oscArgument32.midiMessage = midiMessage; 457 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte3; 458 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte2; 459 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte1; 460 | oscMessage->arguments[oscMessage->argumentsSize++] = oscArgument32.byteStruct.byte0; 461 | return OscErrorNone; 462 | } 463 | 464 | /** 465 | * @brief Adds a boolean argument to an OSC message. 466 | * 467 | * Example use: 468 | * @code 469 | * OscMessageAddBool(&oscMessage, true); 470 | * @endcode 471 | * 472 | * @param oscMessage OSC message. 473 | * @param boolean Boolean to be added as argument to the OSC message. 474 | * @return Error code (0 if successful). 475 | */ 476 | OscError OscMessageAddBool(OscMessage * const oscMessage, const bool boolean) { 477 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 478 | return OscErrorTooManyArguments; // error: too many arguments 479 | } 480 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = boolean ? OscTypeTagTrue : OscTypeTagFalse; 481 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 482 | return OscErrorNone; 483 | } 484 | 485 | /** 486 | * @brief Adds a nil argument to an OSC message. 487 | * 488 | * Example use: 489 | * @code 490 | * OscMessageAddNil(&oscMessage); 491 | * @endcode 492 | * 493 | * @param oscMessage OSC message. 494 | * @return Error code (0 if successful). 495 | */ 496 | OscError OscMessageAddNil(OscMessage * const oscMessage) { 497 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 498 | return OscErrorTooManyArguments; // error: too many arguments 499 | } 500 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = OscTypeTagNil; 501 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 502 | return OscErrorNone; 503 | } 504 | 505 | /** 506 | * @brief Adds an infinitum argument to an OSC message. 507 | * 508 | * Example use: 509 | * @code 510 | * OscMessageAddInfinitum(&oscMessage); 511 | * @endcode 512 | * 513 | * @param oscMessage OSC message. 514 | * @return Error code (0 if successful). 515 | */ 516 | OscError OscMessageAddInfinitum(OscMessage * const oscMessage) { 517 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 518 | return OscErrorTooManyArguments; // error: too many arguments 519 | } 520 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = OscTypeTagInfinitum; 521 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 522 | return OscErrorNone; 523 | } 524 | 525 | /** 526 | * @brief Adds a 'begin array' argument to an OSC message. 527 | * 528 | * Example use: 529 | * @code 530 | * OscMessageAddBeginArray(&oscMessage); 531 | * @endcode 532 | * 533 | * @param oscMessage OSC message. 534 | * @return Error code (0 if successful). 535 | */ 536 | OscError OscMessageAddBeginArray(OscMessage * const oscMessage) { 537 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 538 | return OscErrorTooManyArguments; // error: too many arguments 539 | } 540 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = OscTypeTagBeginArray; 541 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 542 | return OscErrorNone; 543 | } 544 | 545 | /** 546 | * @brief Adds a 'end array' argument to an OSC message. 547 | * 548 | * Example use: 549 | * @code 550 | * OscMessageAddEndArray(&oscMessage); 551 | * @endcode 552 | * 553 | * @param oscMessage OSC message. 554 | * @return Error code (0 if successful). 555 | */ 556 | OscError OscMessageAddEndArray(OscMessage * const oscMessage) { 557 | if (oscMessage->oscTypeTagStringLength > MAX_NUMBER_OF_ARGUMENTS) { 558 | return OscErrorTooManyArguments; // error: too many arguments 559 | } 560 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength++] = OscTypeTagEndArray; 561 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 562 | return OscErrorNone; 563 | } 564 | 565 | /** 566 | * @brief Returns the size (number of bytes) of an OSC message. 567 | * 568 | * An example use of this function would be to check whether the OSC message size 569 | * exceeds the remaining capacity of a containing OSC bundle. 570 | * 571 | * Example use: 572 | * @code 573 | * if(OscMessageGetSize(&oscMessage) > OscBundleGetRemainingCapacity(&oscBundle)) { 574 | * printf("oscMessage is too large to be contained within oscBundle"); 575 | * } 576 | * @endcode 577 | * 578 | * @param oscMessage OSC message. 579 | * @return Size (number of bytes) of the OSC bundle. 580 | */ 581 | size_t OscMessageGetSize(const OscMessage * const oscMessage) { 582 | size_t messageSize = 0; 583 | messageSize += oscMessage->oscAddressPatternLength + 1; // include null character 584 | if ((messageSize % 4) != 0) { 585 | messageSize += 4 - messageSize % 4; // increase to multiple of 4 586 | } 587 | messageSize += oscMessage->oscTypeTagStringLength + 1; // include null character 588 | if ((messageSize % 4) != 0) { 589 | messageSize += 4 - messageSize % 4; // increase to multiple of 4 590 | } 591 | messageSize += oscMessage->argumentsSize; 592 | return messageSize; 593 | } 594 | 595 | /** 596 | * @brief Converts an OSC message into a byte array to be contained within an 597 | * OSC packet or OSC bundle. 598 | * 599 | * This function is used internally and should not be used by the user 600 | * application. 601 | * 602 | * @param oscMessage OSC message. 603 | * @param oscMessageSize OSC message size. 604 | * @param destination Destination byte array. 605 | * @param destinationSize Destination size that cannot exceed. 606 | * @return Error code (0 if successful). 607 | */ 608 | OscError OscMessageToCharArray(const OscMessage * const oscMessage, size_t * const oscMessageSize, char * const destination, const size_t destinationSize) { 609 | *oscMessageSize = 0; // size will be 0 if function unsuccessful 610 | size_t destinationIndex = 0; 611 | unsigned int index; 612 | 613 | // Address pattern 614 | if (oscMessage->oscAddressPatternLength == 0) { 615 | return OscErrorUndefinedAddressPattern; // error: address pattern not set 616 | } 617 | if (oscMessage->oscAddressPattern[0] != '/') { 618 | return OscErrorNoSlashAtStartOfMessage; // error: address pattern does not start with '/' 619 | } 620 | if (oscMessage->oscAddressPatternLength > destinationSize) { 621 | return OscErrorDestinationTooSmall; // error: destination too small 622 | } 623 | for (index = 0; index < oscMessage->oscAddressPatternLength; index++) { 624 | destination[destinationIndex++] = oscMessage->oscAddressPattern[index]; 625 | } 626 | if (TerminateOscString(destination, &destinationIndex, destinationSize) != 0) { 627 | return OscErrorDestinationTooSmall; // error: destination too small 628 | } 629 | 630 | // Type tag string 631 | if ((destinationIndex + oscMessage->oscTypeTagStringLength) > destinationSize) { 632 | return OscErrorDestinationTooSmall; // error: destination too small 633 | } 634 | for (index = 0; index < oscMessage->oscTypeTagStringLength; index++) { 635 | destination[destinationIndex++] = oscMessage->oscTypeTagString[index]; 636 | } 637 | if (TerminateOscString(destination, &destinationIndex, destinationSize) != 0) { 638 | return OscErrorDestinationTooSmall; // error: destination too small 639 | } 640 | 641 | // Arguments 642 | if ((destinationIndex + oscMessage->argumentsSize) > destinationSize) { 643 | return OscErrorDestinationTooSmall; // error: destination too small 644 | } 645 | for (index = 0; index < oscMessage->argumentsSize; index++) { 646 | destination[destinationIndex++] = oscMessage->arguments[index]; 647 | } 648 | 649 | *oscMessageSize = destinationIndex; 650 | return OscErrorNone; 651 | } 652 | 653 | /** 654 | * @brief Terminates an OSC string with one or more null characters so that the 655 | * OSC string size is a multiple of 4. 656 | * 657 | * This is an internal function and cannot be called by the user application. 658 | * 659 | * @param oscString OSC string to the terminated. 660 | * @param oscStringSize Size of the OSC string. 661 | * @param maxOscStringSize Maximum size of the OSC string that cannot be 662 | * exceeded. 663 | * @return 0 if successful. 664 | */ 665 | static int TerminateOscString(char * const oscString, size_t * const oscStringSize, const size_t maxOscStringSize) { 666 | do { 667 | if (*oscStringSize >= maxOscStringSize) { 668 | return 1; // error: string exceeds maximum size 669 | } 670 | oscString[(*oscStringSize)++] = '\0'; 671 | } while (*oscStringSize % 4 != 0); 672 | return 0; 673 | } 674 | 675 | //------------------------------------------------------------------------------ 676 | // Functions - Message deconstruction 677 | 678 | /** 679 | * @brief Initialises an OSC message from a byte array contained within an OSC 680 | * packet or OSC bundle. 681 | * 682 | * This function is used internally and should not be used by the user 683 | * application. 684 | * 685 | * @param oscMessage OSC message. 686 | * @param source Byte array. 687 | * @param numberOfBytes Number of bytes within the byte array. 688 | * @return Error code (0 if successful). 689 | */ 690 | OscError OscMessageInitialiseFromCharArray(OscMessage * const oscMessage, const char * const source, const size_t numberOfBytes) { 691 | OscMessageInitialise(oscMessage, ""); 692 | 693 | // Return error if not valid OSC message 694 | if ((numberOfBytes % 4) != 0) { 695 | return OscErrorSizeIsNotMultipleOfFour; // error: size not multiple of 4 696 | } 697 | if (numberOfBytes < MIN_OSC_MESSAGE_SIZE) { 698 | return OscErrorMessageSizeTooSmall; // error: too few bytes to contain an OSC message 699 | } 700 | if (numberOfBytes > MAX_OSC_MESSAGE_SIZE) { 701 | return OscErrorMessageSizeTooLarge; // error: size exceeds maximum OSC message size 702 | } 703 | if (source[0] != '/') { 704 | return OscErrorNoSlashAtStartOfMessage; // error: first byte is not '/' 705 | } 706 | 707 | // OSC address pattern 708 | unsigned int sourceIndex = 0; 709 | while (source[sourceIndex] != '\0') { 710 | oscMessage->oscAddressPattern[oscMessage->oscAddressPatternLength] = source[sourceIndex]; 711 | if (++oscMessage->oscAddressPatternLength > MAX_OSC_ADDRESS_PATTERN_LENGTH) { 712 | return OscErrorAddressPatternTooLong; // error: OSC address pattern too long 713 | } 714 | if (++sourceIndex >= numberOfBytes) { 715 | return OscErrorSourceEndsBeforeEndOfAddressPattern; // error: unexpected end of source 716 | } 717 | } 718 | oscMessage->oscAddressPattern[oscMessage->oscAddressPatternLength] = '\0'; // null terminate string 719 | 720 | // Advance index to OSC type tag string 721 | while (source[sourceIndex - 1] != ',') { // skip index past comma 722 | if (++sourceIndex >= numberOfBytes) { 723 | return OscErrorSourceEndsBeforeStartOfTypeTagString; // error: unexpected end of source 724 | } 725 | } 726 | 727 | // OSC type tag string 728 | while (source[sourceIndex] != '\0') { 729 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = source[sourceIndex]; 730 | if (++oscMessage->oscTypeTagStringLength > MAX_OSC_TYPE_TAG_STRING_LENGTH) { 731 | return OscErrorTypeTagStringToLong; // error: type tag string too long 732 | } 733 | if (++sourceIndex >= numberOfBytes) { 734 | return OscErrorSourceEndsBeforeEndOfTypeTagString; // error: unexpected end of source 735 | } 736 | } 737 | oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringLength] = '\0'; // null terminate string 738 | 739 | // Advance index to arguments 740 | do { 741 | if (++sourceIndex > numberOfBytes) { 742 | return OscErrorUnexpectedEndOfSource; // error: unexpected end of source 743 | } 744 | } while (sourceIndex % 4 != 0); 745 | 746 | // Arguments 747 | while (sourceIndex < numberOfBytes) { 748 | oscMessage->arguments[oscMessage->argumentsSize++] = source[sourceIndex++]; 749 | } 750 | 751 | return OscErrorNone; 752 | } 753 | 754 | /** 755 | * @brief Returns true if an argument is available indicated by the current 756 | * oscTypeTagStringIndex value. 757 | * 758 | * Example use: 759 | * @code 760 | * if(OscMessageIsArgumentAvailable(&oscMessage)) { 761 | * printf("Argument is available"); 762 | * } 763 | * @endcode 764 | * 765 | * @param oscMessage OSC message. 766 | * @return True if an argument is available. 767 | */ 768 | bool OscMessageIsArgumentAvailable(OscMessage * const oscMessage) { 769 | return oscMessage->oscTypeTagStringIndex <= oscMessage->oscTypeTagStringLength - 1; 770 | } 771 | 772 | /** 773 | * @brief Returns OSC type tag of the next argument available within an OSC 774 | * message indicated by the current oscTypeTagStringIndex value. 775 | * 776 | * A null character (value zero) will be returned if no arguments are available. 777 | * 778 | * Example use: 779 | * @code 780 | * const OscTypeTag oscTypeTag = OscMessageGetArgumentType(&oscMessage); 781 | * printf("The next argument is: %c", (char)oscTypeTag); 782 | * @endcode 783 | * 784 | * @param oscMessage OSC message. 785 | * @return Next type tag in type tag string. 786 | */ 787 | OscTypeTag OscMessageGetArgumentType(OscMessage * const oscMessage) { 788 | if (oscMessage->oscTypeTagStringIndex > oscMessage->oscTypeTagStringLength) { 789 | return '\0'; // error: end of type tag string 790 | } 791 | return (OscTypeTag) oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex]; 792 | } 793 | 794 | /** 795 | * @brief Skips the next argument available within an OSC message indicated by 796 | * the current oscTypeTagStringIndex value. 797 | * 798 | * Example use: 799 | * @code 800 | * while(true) { // loop to skip to first int32 argument 801 | * if(OscMessageGetArgumentType(&oscMessage) == OscTypeTagInt32) { 802 | * break; // found int32 argument 803 | * } 804 | * if(OscMessageSkipArgument(&oscMessage)) { 805 | * break; // error: no more arguments available 806 | * } 807 | * } 808 | * @endcode 809 | * 810 | * @param oscMessage OSC message. 811 | * @return Error code (0 if successful). 812 | */ 813 | OscError OscMessageSkipArgument(OscMessage * const oscMessage) { 814 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] == '\0') { 815 | return OscErrorNoArgumentsAvailable; // error: end of type tag string 816 | } 817 | oscMessage->oscTypeTagStringIndex++; 818 | return OscErrorNone; 819 | } 820 | 821 | /** 822 | * @brief Gets a 32-bit integer argument from an OSC message. 823 | * 824 | * The next argument available within the OSC message (indicated by the internal 825 | * index oscTypeTagStringIndex) must be a 32-bit integer else this function 826 | * will return an error. The internal index oscTypeTagStringIndex, will only 827 | * be incremented to the next argument if this function is successful. The user 828 | * application may determine the next argument type by first calling 829 | * OscMessageGetArgumentType. 830 | * 831 | * Example use: 832 | * @code 833 | * switch (OscMessageGetArgumentType(&oscMessage)) { 834 | * case OscTypeTagInt32: 835 | * { 836 | * int32_t int32; 837 | * OscMessageGetInt32(&oscMessage, &int32); 838 | * printf("Value = %d", int32); 839 | * break; 840 | * } 841 | * default: 842 | * printf("Expected argument not available"); 843 | * break; 844 | * } 845 | * @endcode 846 | * 847 | * @param oscMessage OSC message. 848 | * @param int32 32-bit integer argument. 849 | * @return Error code (0 if successful). 850 | */ 851 | OscError OscMessageGetInt32(OscMessage * const oscMessage, int32_t * const int32) { 852 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] == '\0') { 853 | return OscErrorNoArgumentsAvailable; // error: end of type tag string 854 | } 855 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] != OscTypeTagInt32) { 856 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 857 | } 858 | if ((oscMessage->argumentsIndex + sizeof (OscArgument32)) > oscMessage->argumentsSize) { 859 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 860 | } 861 | OscArgument32 oscArgument32; 862 | oscArgument32.byteStruct.byte3 = oscMessage->arguments[oscMessage->argumentsIndex++]; 863 | oscArgument32.byteStruct.byte2 = oscMessage->arguments[oscMessage->argumentsIndex++]; 864 | oscArgument32.byteStruct.byte1 = oscMessage->arguments[oscMessage->argumentsIndex++]; 865 | oscArgument32.byteStruct.byte0 = oscMessage->arguments[oscMessage->argumentsIndex++]; 866 | *int32 = oscArgument32.int32; 867 | oscMessage->oscTypeTagStringIndex++; 868 | return OscErrorNone; 869 | } 870 | 871 | /** 872 | * @brief Gets a 32-bit float argument from an OSC message. 873 | * 874 | * The next argument available within the OSC message (indicated by the internal 875 | * index oscTypeTagStringIndex) must be a 32-bit float else this function 876 | * will return an error. The internal index oscTypeTagStringIndex, will only 877 | * be incremented to the next argument if this function is successful. The user 878 | * application may determine the next argument type by first calling 879 | * OscMessageGetArgumentType. 880 | * 881 | * Example use: 882 | * @code 883 | * switch (OscMessageGetArgumentType(&oscMessage)) { 884 | * case OscTypeTagFloat32: 885 | * { 886 | * float float32; 887 | * OscMessageGetFloat32(&oscMessage, &float32); 888 | * printf("Value = %f", float32); 889 | * break; 890 | * } 891 | * default: 892 | * printf("Expected argument not available"); 893 | * break; 894 | * } 895 | * @endcode 896 | * 897 | * @param oscMessage OSC message. 898 | * @param float32 32-bit float argument. 899 | * @return Error code (0 if successful). 900 | */ 901 | OscError OscMessageGetFloat32(OscMessage * const oscMessage, float * const float32) { 902 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] == '\0') { 903 | return OscErrorNoArgumentsAvailable; // error: end of type tag string 904 | } 905 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] != OscTypeTagFloat32) { 906 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 907 | } 908 | if ((oscMessage->argumentsIndex + sizeof (OscArgument32)) > oscMessage->argumentsSize) { 909 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 910 | } 911 | OscArgument32 oscArgument32; 912 | oscArgument32.byteStruct.byte3 = oscMessage->arguments[oscMessage->argumentsIndex++]; 913 | oscArgument32.byteStruct.byte2 = oscMessage->arguments[oscMessage->argumentsIndex++]; 914 | oscArgument32.byteStruct.byte1 = oscMessage->arguments[oscMessage->argumentsIndex++]; 915 | oscArgument32.byteStruct.byte0 = oscMessage->arguments[oscMessage->argumentsIndex++]; 916 | *float32 = oscArgument32.float32; 917 | oscMessage->oscTypeTagStringIndex++; 918 | return OscErrorNone; 919 | } 920 | 921 | /** 922 | * @brief Gets a string or alternate string argument from an OSC message. 923 | * 924 | * The next argument available within the OSC message (indicated by the internal 925 | * index oscTypeTagStringIndex) must be a string else this function will return 926 | * an error. The internal index oscTypeTagStringIndex, will only be 927 | * incremented to the next argument if this function is successful. The user 928 | * application may determine the next argument type by first calling 929 | * OscMessageGetArgumentType. 930 | * 931 | * Example use: 932 | * @code 933 | * switch (OscMessageGetArgumentType(&oscMessage)) { 934 | * case OscTypeTagString: 935 | * { 936 | * char string[128]; 937 | * OscMessageGetString(&oscMessage, string, sizeof(string)); 938 | * printf("Value = %s", string); 939 | * break; 940 | * } 941 | * default: 942 | * printf("Expected argument not available"); 943 | * break; 944 | * } 945 | * @endcode 946 | * 947 | * @param oscMessage OSC message. 948 | * @param destination String or alternate string argument 949 | * @param destinationSize Size of the destination that cannot be exceeded. 950 | * @return Error code (0 if successful). 951 | */ 952 | OscError OscMessageGetString(OscMessage * const oscMessage, char * const destination, const size_t destinationSize) { 953 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] == '\0') { 954 | return OscErrorNoArgumentsAvailable; // error: end of type tag string 955 | } 956 | if ((oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] != OscTypeTagString) 957 | && (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] != OscTypeTagAlternateString)) { 958 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 959 | } 960 | if ((oscMessage->argumentsIndex + sizeof ("\0\0\0")) > oscMessage->argumentsSize) { 961 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 962 | } 963 | if (sizeof ("\0\0\0") > destinationSize) { 964 | return OscErrorDestinationTooSmall; // error: destination too small 965 | } 966 | unsigned int argumentsIndex = oscMessage->argumentsIndex; // local copy in case function returns error 967 | unsigned int destinationIndex = 0; 968 | do { 969 | destination[destinationIndex] = oscMessage->arguments[argumentsIndex]; 970 | if (++destinationIndex > destinationSize) { 971 | return OscErrorDestinationTooSmall; // error: destination too small 972 | } 973 | } while (oscMessage->arguments[argumentsIndex++] != '\0'); 974 | while ((argumentsIndex % 4) != 0) { 975 | if (++argumentsIndex > oscMessage->argumentsSize) { 976 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 977 | } 978 | } 979 | oscMessage->argumentsIndex = argumentsIndex; 980 | oscMessage->oscTypeTagStringIndex++; 981 | return OscErrorNone; 982 | } 983 | 984 | /** 985 | * @brief Gets a blob (byte array) argument from an OSC message. 986 | * 987 | * The next argument available within the OSC message (indicated by the internal 988 | * index oscTypeTagStringIndex) must be a blob else this function will return 989 | * an error. The internal index oscTypeTagStringIndex, will only be 990 | * incremented to the next argument if this function is successful. The user 991 | * application may determine the next argument type by first calling 992 | * OscMessageGetArgumentType. 993 | * 994 | * Example use: 995 | * @code 996 | * switch (OscMessageGetArgumentType(&oscMessage)) { 997 | * case OscTypeTagBlob: 998 | * { 999 | * char byteArray[128]; 1000 | * size_t numberOfBytes; 1001 | * OscMessageGetBlob(&oscMessage, &numberOfBytes, byteArray, sizeof(byteArray)); 1002 | * unsigned int index = 0; 1003 | * while(index <= numberOfBytes) { 1004 | * printf("%u,", (unsigned int)byteArray[index]); 1005 | * } 1006 | * break; 1007 | * } 1008 | * default: 1009 | * printf("Expected argument not available"); 1010 | * break; 1011 | * } 1012 | * @endcode 1013 | * 1014 | * @param oscMessage OSC message. 1015 | * @param blobSize Blob argument size (number of bytes). 1016 | * @param destination Blob argument (byte array). 1017 | * @param destinationSize Size of the destination that cannot be exceeded. 1018 | * @return Error code (0 if successful). 1019 | */ 1020 | OscError OscMessageGetBlob(OscMessage * const oscMessage, size_t * const blobSize, char * const destination, const size_t destinationSize) { 1021 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] == '\0') { 1022 | return OscErrorNoArgumentsAvailable; // error: end of type tag string 1023 | } 1024 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] != OscTypeTagBlob) { 1025 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1026 | } 1027 | if ((oscMessage->argumentsIndex + sizeof (OscArgument32)) > oscMessage->argumentsSize) { 1028 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 1029 | } 1030 | unsigned int argumentsIndex = oscMessage->argumentsIndex; // local copy in case function returns error 1031 | OscArgument32 blobSizeArgument; 1032 | blobSizeArgument.byteStruct.byte3 = oscMessage->arguments[argumentsIndex++]; 1033 | blobSizeArgument.byteStruct.byte2 = oscMessage->arguments[argumentsIndex++]; 1034 | blobSizeArgument.byteStruct.byte1 = oscMessage->arguments[argumentsIndex++]; 1035 | blobSizeArgument.byteStruct.byte0 = oscMessage->arguments[argumentsIndex++]; 1036 | if ((argumentsIndex + blobSizeArgument.int32) > oscMessage->argumentsSize) { 1037 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 1038 | } 1039 | if (blobSizeArgument.int32 > destinationSize) { 1040 | return OscErrorDestinationTooSmall; // error: destination too small 1041 | } 1042 | unsigned int destinationIndex; 1043 | for (destinationIndex = 0; destinationIndex < blobSizeArgument.int32; destinationIndex++) { 1044 | destination[destinationIndex] = oscMessage->arguments[argumentsIndex++]; 1045 | } 1046 | while ((argumentsIndex % 4) != 0) { 1047 | if (++argumentsIndex > oscMessage->argumentsSize) { 1048 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 1049 | } 1050 | } 1051 | oscMessage->argumentsIndex = argumentsIndex; 1052 | *blobSize = blobSizeArgument.int32; 1053 | oscMessage->oscTypeTagStringIndex++; 1054 | return OscErrorNone; 1055 | } 1056 | 1057 | /** 1058 | * @brief Gets a 64-bit integer argument from an OSC message. 1059 | * 1060 | * The next argument available within the OSC message (indicated by the internal 1061 | * index oscTypeTagStringIndex) must be a 64-bit integer else this function 1062 | * will return an error. The internal index oscTypeTagStringIndex, will only 1063 | * be incremented to the next argument if this function is successful. The user 1064 | * application may determine the next argument type by first calling 1065 | * OscMessageGetArgumentType. 1066 | * 1067 | * Example use: 1068 | * @code 1069 | * switch (OscMessageGetArgumentType(&oscMessage)) { 1070 | * case OscTypeTagInt64: 1071 | * { 1072 | * int64_t int64; 1073 | * OscMessageGetInt64(&oscMessage, &int64); 1074 | * printf("Value = %d", int64); 1075 | * break; 1076 | * } 1077 | * default: 1078 | * printf("Expected argument not available"); 1079 | * break; 1080 | * } 1081 | * @endcode 1082 | * 1083 | * @param oscMessage OSC message. 1084 | * @param int64 64-bit integer argument. 1085 | * @return Error code (0 if successful). 1086 | */ 1087 | OscError OscMessageGetInt64(OscMessage * const oscMessage, int64_t * const int64) { 1088 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] == '\0') { 1089 | return OscErrorNoArgumentsAvailable; // error: end of type tag string 1090 | } 1091 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] != OscTypeTagInt64) { 1092 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1093 | } 1094 | if ((oscMessage->argumentsIndex + sizeof (OscArgument64)) > oscMessage->argumentsSize) { 1095 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 1096 | } 1097 | OscArgument64 oscArgument64; 1098 | oscArgument64.byteStruct.byte7 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1099 | oscArgument64.byteStruct.byte6 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1100 | oscArgument64.byteStruct.byte5 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1101 | oscArgument64.byteStruct.byte4 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1102 | oscArgument64.byteStruct.byte3 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1103 | oscArgument64.byteStruct.byte2 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1104 | oscArgument64.byteStruct.byte1 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1105 | oscArgument64.byteStruct.byte0 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1106 | *int64 = oscArgument64.int64; 1107 | oscMessage->oscTypeTagStringIndex++; 1108 | return OscErrorNone; 1109 | } 1110 | 1111 | /** 1112 | * @brief Gets an OSC time tag argument from an OSC message. 1113 | * 1114 | * The next argument available within the OSC message (indicated by the internal 1115 | * index oscTypeTagStringIndex) must be an OSC time tag else this function 1116 | * will return an error. The internal index oscTypeTagStringIndex, will only 1117 | * be incremented to the next argument if this function is successful. The user 1118 | * application may determine the next argument type by first calling 1119 | * OscMessageGetArgumentType. 1120 | * 1121 | * Example use: 1122 | * @code 1123 | * switch (OscMessageGetArgumentType(&oscMessage)) { 1124 | * case OscTypeTagTimeTag: 1125 | * { 1126 | * OscTimeTag oscTimeTag; 1127 | * OscMessageGetTimeTag(&oscMessage, &oscTimeTag); 1128 | * printf("Value = %u", (unsigned int)oscTimeTag.dwordStruct.seconds); 1129 | * break; 1130 | * } 1131 | * default: 1132 | * printf("Expected argument not available"); 1133 | * break; 1134 | * } 1135 | * @endcode 1136 | * 1137 | * @param oscMessage OSC message. 1138 | * @param oscTimeTag OSC time tag argument. 1139 | * @return Error code (0 if successful). 1140 | */ 1141 | OscError OscMessageGetTimeTag(OscMessage * const oscMessage, OscTimeTag * const oscTimeTag) { 1142 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] == '\0') { 1143 | return OscErrorNoArgumentsAvailable; // error: end of type tag string 1144 | } 1145 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] != OscTypeTagTimeTag) { 1146 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1147 | } 1148 | if ((oscMessage->argumentsIndex + sizeof (OscTimeTag)) > oscMessage->argumentsSize) { 1149 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 1150 | } 1151 | oscTimeTag->byteStruct.byte7 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1152 | oscTimeTag->byteStruct.byte6 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1153 | oscTimeTag->byteStruct.byte5 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1154 | oscTimeTag->byteStruct.byte4 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1155 | oscTimeTag->byteStruct.byte3 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1156 | oscTimeTag->byteStruct.byte2 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1157 | oscTimeTag->byteStruct.byte1 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1158 | oscTimeTag->byteStruct.byte0 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1159 | oscMessage->oscTypeTagStringIndex++; 1160 | return OscErrorNone; 1161 | } 1162 | 1163 | /** 1164 | * @brief Gets a 64-bit double argument from an OSC message. 1165 | * 1166 | * The next argument available within the OSC message (indicated by the internal 1167 | * index oscTypeTagStringIndex) must be a 64-bit double else this function 1168 | * will return an error. The internal index oscTypeTagStringIndex, will only 1169 | * be incremented to the next argument if this function is successful. The user 1170 | * application may determine the next argument type by first calling 1171 | * OscMessageGetArgumentType. 1172 | * 1173 | * Example use: 1174 | * @code 1175 | * switch (OscMessageGetArgumentType(&oscMessage)) { 1176 | * case OscTypeTagDouble: 1177 | * { 1178 | * double double64; 1179 | * OscMessageGetDouble(&oscMessage, &double64); 1180 | * printf("Value = %f", double64); 1181 | * break; 1182 | * } 1183 | * default: 1184 | * printf("Expected argument not available"); 1185 | * break; 1186 | * } 1187 | * @endcode 1188 | * 1189 | * @param oscMessage OSC message. 1190 | * @param double64 64-bit double argument. 1191 | * @return Error code (0 if successful). 1192 | */ 1193 | OscError OscMessageGetDouble(OscMessage * const oscMessage, Double64 * const double64) { 1194 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] == '\0') { 1195 | return OscErrorNoArgumentsAvailable; // error: end of type tag string 1196 | } 1197 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] != OscTypeTagDouble) { 1198 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1199 | } 1200 | if ((oscMessage->argumentsIndex + sizeof (OscArgument64)) > oscMessage->argumentsSize) { 1201 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 1202 | } 1203 | OscArgument64 oscArgument64; 1204 | oscArgument64.byteStruct.byte7 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1205 | oscArgument64.byteStruct.byte6 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1206 | oscArgument64.byteStruct.byte5 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1207 | oscArgument64.byteStruct.byte4 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1208 | oscArgument64.byteStruct.byte3 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1209 | oscArgument64.byteStruct.byte2 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1210 | oscArgument64.byteStruct.byte1 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1211 | oscArgument64.byteStruct.byte0 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1212 | *double64 = oscArgument64.double64; 1213 | oscMessage->oscTypeTagStringIndex++; 1214 | return OscErrorNone; 1215 | } 1216 | 1217 | /** 1218 | * @brief Gets a character argument from an OSC message. 1219 | * 1220 | * The next argument available within the OSC message (indicated by the internal 1221 | * index oscTypeTagStringIndex) must be a character else this function will 1222 | * return an error. The internal index oscTypeTagStringIndex, will only be 1223 | * incremented to the next argument if this function is successful. The user 1224 | * application may determine the next argument type by first calling 1225 | * OscMessageGetArgumentType. 1226 | * 1227 | * Example use: 1228 | * @code 1229 | * switch (OscMessageGetArgumentType(&oscMessage)) { 1230 | * case OscTypeTagCharacter: 1231 | * { 1232 | * char character; 1233 | * OscMessageGetCharacter(&oscMessage, &character); 1234 | * printf("Value = %c", character); 1235 | * break; 1236 | * } 1237 | * default: 1238 | * printf("Expected argument not available"); 1239 | * break; 1240 | * } 1241 | * @endcode 1242 | * 1243 | * @param oscMessage OSC message. 1244 | * @param character Character argument. 1245 | * @return Error code (0 if successful). 1246 | */ 1247 | OscError OscMessageGetCharacter(OscMessage * const oscMessage, char * const character) { 1248 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] == '\0') { 1249 | return OscErrorNoArgumentsAvailable; // error: end of type tag string 1250 | } 1251 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] != OscTypeTagCharacter) { 1252 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1253 | } 1254 | if ((oscMessage->argumentsIndex + sizeof (OscArgument32)) > oscMessage->argumentsSize) { 1255 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 1256 | } 1257 | oscMessage->argumentsIndex += 3; 1258 | *character = oscMessage->arguments[oscMessage->argumentsIndex++]; 1259 | oscMessage->oscTypeTagStringIndex++; 1260 | return OscErrorNone; 1261 | } 1262 | 1263 | /** 1264 | * @brief Gets a 32-bit RGBA colour argument from an OSC message. 1265 | * 1266 | * The next argument available within the OSC message (indicated by the internal 1267 | * index oscTypeTagStringIndex) must be a 32-bit RGBA colour else this function 1268 | * will return an error. The internal index oscTypeTagStringIndex, will only 1269 | * be incremented to the next argument if this function is successful. The user 1270 | * application may determine the next argument type by first calling 1271 | * OscMessageGetArgumentType. 1272 | * 1273 | * Example use: 1274 | * @code 1275 | * switch (OscMessageGetArgumentType(&oscMessage)) { 1276 | * case OscTypeTagRgbaColour: 1277 | * { 1278 | * RgbaColour rgbaColour; 1279 | * OscMessageGetRgbaColour(&oscMessage, &rgbaColour); 1280 | * printf("Value = %u,%u,%u,%u", rgbaColour.red, rgbaColour.green, rgbaColour.blue, rgbaColour.alpha); 1281 | * break; 1282 | * } 1283 | * default: 1284 | * printf("Expected argument not available"); 1285 | * break; 1286 | * } 1287 | * @endcode 1288 | * 1289 | * @param oscMessage OSC message. 1290 | * @param rgbaColour 32-bit RGBA colour argument. 1291 | * @return Error code (0 if successful). 1292 | */ 1293 | OscError OscMessageGetRgbaColour(OscMessage * const oscMessage, RgbaColour * const rgbaColour) { 1294 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] == '\0') { 1295 | return OscErrorNoArgumentsAvailable; // error: end of type tag string 1296 | } 1297 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] != OscTypeTagRgbaColour) { 1298 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1299 | } 1300 | if ((oscMessage->argumentsIndex + sizeof (OscArgument32)) > oscMessage->argumentsSize) { 1301 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 1302 | } 1303 | OscArgument32 oscArgument32; 1304 | oscArgument32.byteStruct.byte3 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1305 | oscArgument32.byteStruct.byte2 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1306 | oscArgument32.byteStruct.byte1 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1307 | oscArgument32.byteStruct.byte0 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1308 | *rgbaColour = oscArgument32.rgbaColour; 1309 | oscMessage->oscTypeTagStringIndex++; 1310 | return OscErrorNone; 1311 | } 1312 | 1313 | /** 1314 | * @brief Gets a 4 byte MIDI message argument from an OSC message. 1315 | * 1316 | * The next argument available within the OSC message (indicated by the internal 1317 | * index oscTypeTagStringIndex) must be a 4 byte MIDI message else this 1318 | * function will return an error. The internal index oscTypeTagStringIndex, 1319 | * will only be incremented to the next argument if this function is successful. 1320 | * The user application may determine the next argument type by first calling 1321 | * OscMessageGetArgumentType. 1322 | * 1323 | * Example use: 1324 | * @code 1325 | * switch (OscMessageGetArgumentType(&oscMessage)) { 1326 | * case OscTypeTagMidiMessage: 1327 | * { 1328 | * MidiMessage midiMessage; 1329 | * OscMessageGetMidiMessage(&oscMessage, &midiMessage); 1330 | * printf("Value = %u,%u,%u,%u", midiMessage.portID, midiMessage.status, midiMessage.data1, midiMessage.data2); 1331 | * break; 1332 | * } 1333 | * default: 1334 | * printf("Expected argument not available"); 1335 | * break; 1336 | * } 1337 | * @endcode 1338 | * 1339 | * @param oscMessage OSC message. 1340 | * @param midiMessage 4 byte MIDI message argument. 1341 | * @return Error code (0 if successful). 1342 | */ 1343 | OscError OscMessageGetMidiMessage(OscMessage * const oscMessage, MidiMessage * const midiMessage) { 1344 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] == '\0') { 1345 | return OscErrorNoArgumentsAvailable; // error: end of type tag string 1346 | } 1347 | if (oscMessage->oscTypeTagString[oscMessage->oscTypeTagStringIndex] != OscTypeTagMidiMessage) { 1348 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1349 | } 1350 | if ((oscMessage->argumentsIndex + sizeof (OscArgument32)) > oscMessage->argumentsSize) { 1351 | return OscErrorMessageTooShortForArgumentType; // error: message too short to contain argument 1352 | } 1353 | OscArgument32 oscArgument32; 1354 | oscArgument32.byteStruct.byte3 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1355 | oscArgument32.byteStruct.byte2 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1356 | oscArgument32.byteStruct.byte1 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1357 | oscArgument32.byteStruct.byte0 = oscMessage->arguments[oscMessage->argumentsIndex++]; 1358 | *midiMessage = oscArgument32.midiMessage; 1359 | oscMessage->oscTypeTagStringIndex++; 1360 | return OscErrorNone; 1361 | } 1362 | 1363 | /** 1364 | * @brief Interprets the next argument in the OSC message as an int32 even if 1365 | * the argument is of another type. 1366 | * 1367 | * The argument provided must be of a numerical type: int32, float32, int64, 1368 | * OSC time tag, 64-bit double, character, boolean, nil, or infinitum. The 1369 | * internal index oscTypeTagStringIndex will only be incremented to the next 1370 | * argument if this function is successful. 1371 | * 1372 | * Example use: 1373 | * @code 1374 | * int32_t int32; 1375 | * OscMessageGetArgumentAsInt32(&oscMessage, &int32); 1376 | * printf("Value = %d", int32); 1377 | * @endcode 1378 | * 1379 | * @param oscMessage OSC message. 1380 | * @param int32 32-bit integer argument. 1381 | * @return Error code (0 if successful). 1382 | */ 1383 | OscError OscMessageGetArgumentAsInt32(OscMessage * const oscMessage, int32_t * const int32) { 1384 | if (OscMessageIsArgumentAvailable(oscMessage) == false) { 1385 | return OscErrorNoArgumentsAvailable; 1386 | } 1387 | switch (OscMessageGetArgumentType(oscMessage)) { 1388 | case OscTypeTagInt32: 1389 | { 1390 | return OscMessageGetInt32(oscMessage, int32); 1391 | } 1392 | case OscTypeTagFloat32: 1393 | { 1394 | float float32; 1395 | const OscError oscError = OscMessageGetFloat32(oscMessage, &float32); 1396 | *int32 = (int32_t) float32; 1397 | return oscError; 1398 | } 1399 | case OscTypeTagInt64: 1400 | { 1401 | int64_t int64; 1402 | const OscError oscError = OscMessageGetInt64(oscMessage, &int64); 1403 | *int32 = (int32_t) int64; 1404 | return oscError; 1405 | } 1406 | case OscTypeTagTimeTag: 1407 | { 1408 | OscTimeTag oscTimeTag; 1409 | const OscError oscError = OscMessageGetTimeTag(oscMessage, &oscTimeTag); 1410 | *int32 = (int32_t) oscTimeTag.value; 1411 | return oscError; 1412 | } 1413 | case OscTypeTagDouble: 1414 | { 1415 | Double64 double64; 1416 | const OscError oscError = OscMessageGetDouble(oscMessage, &double64); 1417 | *int32 = (int32_t) double64; 1418 | return oscError; 1419 | } 1420 | case OscTypeTagCharacter: 1421 | { 1422 | char character; 1423 | const OscError oscError = OscMessageGetCharacter(oscMessage, &character); 1424 | *int32 = (int32_t) character; 1425 | return oscError; 1426 | } 1427 | case OscTypeTagTrue: 1428 | { 1429 | *int32 = (int32_t) true; 1430 | return OscErrorNone; 1431 | } 1432 | case OscTypeTagFalse: 1433 | { 1434 | *int32 = (int32_t) false; 1435 | return OscErrorNone; 1436 | } 1437 | case OscTypeTagNil: 1438 | { 1439 | *int32 = 0; 1440 | return OscErrorNone; 1441 | } 1442 | case OscTypeTagInfinitum: 1443 | { 1444 | *int32 = UINT32_MAX; 1445 | return OscErrorNone; 1446 | } 1447 | default: 1448 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1449 | } 1450 | return OscErrorNone; 1451 | } 1452 | 1453 | /** 1454 | * @brief Interprets the next argument in the OSC message as an float32 even if 1455 | * the argument is of another type. 1456 | * 1457 | * The argument provided must be of a numerical type: int32, float32, int64, 1458 | * OSC time tag, 64-bit double, character, boolean, nil, or infinitum. The 1459 | * internal index oscTypeTagStringIndex will only be incremented to the next 1460 | * argument if this function is successful. 1461 | * 1462 | * Example use: 1463 | * @code 1464 | * float float32; 1465 | * OscMessageGetArgumentAsFloat32(&oscMessage, &float32); 1466 | * printf("Value = %f", float32); 1467 | * @endcode 1468 | * 1469 | * @param oscMessage OSC message. 1470 | * @param float32 32-bit float argument. 1471 | * @return Error code (0 if successful). 1472 | */ 1473 | OscError OscMessageGetArgumentAsFloat32(OscMessage * const oscMessage, float * const float32) { 1474 | if (OscMessageIsArgumentAvailable(oscMessage) == false) { 1475 | return OscErrorNoArgumentsAvailable; 1476 | } 1477 | switch (OscMessageGetArgumentType(oscMessage)) { 1478 | case OscTypeTagInt32: 1479 | { 1480 | int32_t int32; 1481 | const OscError oscError = OscMessageGetInt32(oscMessage, &int32); 1482 | *float32 = (float) int32; 1483 | return oscError; 1484 | } 1485 | case OscTypeTagFloat32: 1486 | { 1487 | return OscMessageGetFloat32(oscMessage, float32); 1488 | } 1489 | case OscTypeTagInt64: 1490 | { 1491 | int64_t int64; 1492 | const OscError oscError = OscMessageGetInt64(oscMessage, &int64); 1493 | *float32 = (float) int64; 1494 | return oscError; 1495 | } 1496 | case OscTypeTagTimeTag: 1497 | { 1498 | OscTimeTag oscTimeTag; 1499 | const OscError oscError = OscMessageGetTimeTag(oscMessage, &oscTimeTag); 1500 | *float32 = (float) oscTimeTag.value; 1501 | return oscError; 1502 | } 1503 | case OscTypeTagDouble: 1504 | { 1505 | Double64 double64; 1506 | const OscError oscError = OscMessageGetDouble(oscMessage, &double64); 1507 | *float32 = (float) double64; 1508 | return oscError; 1509 | } 1510 | case OscTypeTagCharacter: 1511 | { 1512 | char character; 1513 | const OscError oscError = OscMessageGetCharacter(oscMessage, &character); 1514 | *float32 = (float) character; 1515 | return oscError; 1516 | } 1517 | case OscTypeTagTrue: 1518 | { 1519 | *float32 = (float) true; 1520 | return OscErrorNone; 1521 | } 1522 | case OscTypeTagFalse: 1523 | { 1524 | *float32 = (float) false; 1525 | return OscErrorNone; 1526 | } 1527 | case OscTypeTagNil: 1528 | { 1529 | *float32 = 0.0f; 1530 | return OscErrorNone; 1531 | } 1532 | case OscTypeTagInfinitum: 1533 | { 1534 | *float32 = 1.0f / 0.0f; 1535 | return OscErrorNone; 1536 | } 1537 | default: 1538 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1539 | } 1540 | return OscErrorNone; 1541 | } 1542 | 1543 | /** 1544 | * @brief Interprets the next argument in the OSC message as a string even if 1545 | * the argument is of another type. 1546 | * 1547 | * The argument provided must be either a string, blob, alternate string, or 1548 | * character. The internal index oscTypeTagStringIndex will only be incremented 1549 | * to the next argument if this function is successful. 1550 | * 1551 | * Example use: 1552 | * @code 1553 | * char string[128]; 1554 | * OscMessageGetArgumentAsString(&oscMessage, string, sizeof(string)); 1555 | * printf("Value = %s", string); 1556 | * @endcode 1557 | * 1558 | * @param oscMessage OSC message. 1559 | * @param destination String argument. 1560 | * @param destinationSize Size of the destination that cannot be exceeded. 1561 | * @return Error code (0 if successful). 1562 | */ 1563 | OscError OscMessageGetArgumentAsString(OscMessage * const oscMessage, char * const destination, const size_t destinationSize) { 1564 | if (OscMessageIsArgumentAvailable(oscMessage) == false) { 1565 | return OscErrorNoArgumentsAvailable; 1566 | } 1567 | switch (OscMessageGetArgumentType(oscMessage)) { 1568 | case OscTypeTagString: 1569 | { 1570 | return OscMessageGetString(oscMessage, destination, destinationSize); 1571 | } 1572 | case OscTypeTagBlob: 1573 | { 1574 | size_t blobSize; 1575 | const OscError oscError = OscMessageGetBlob(oscMessage, &blobSize, destination, destinationSize); 1576 | if (oscError != 0) { 1577 | return oscError; 1578 | } 1579 | if (destination[blobSize - 1] != '\0') { // if blob not null terminated 1580 | if (blobSize >= destinationSize) { 1581 | return OscErrorDestinationTooSmall; // error: destination too small 1582 | } 1583 | destination[blobSize] = '\0'; 1584 | } 1585 | return OscErrorNone; 1586 | } 1587 | case OscTypeTagAlternateString: 1588 | { 1589 | return OscMessageGetString(oscMessage, destination, destinationSize); 1590 | } 1591 | case OscTypeTagCharacter: 1592 | { 1593 | char character; 1594 | const OscError oscError = OscMessageGetCharacter(oscMessage, &character); 1595 | if (oscError != 0) { 1596 | return oscError; 1597 | } 1598 | if (destinationSize < 2) { 1599 | return OscErrorDestinationTooSmall; // error: destination too small 1600 | } 1601 | destination[0] = character; 1602 | destination[1] = '\0'; // null terminate string 1603 | return OscErrorNone; 1604 | } 1605 | default: 1606 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1607 | } 1608 | return OscErrorNone; 1609 | } 1610 | 1611 | /** 1612 | * @brief Interprets the next argument in the OSC message as a blob even if 1613 | * the argument is of another type. 1614 | * 1615 | * The argument provided must be either a string, blob, alternate string, or 1616 | * character. The internal index oscTypeTagStringIndex will only be incremented 1617 | * to the next argument if this function is successful. 1618 | * 1619 | * Example use: 1620 | * @code 1621 | * char byteArray[128]; 1622 | * size_t numberOfBytes; 1623 | * OscMessageGetArgumentAsBlob(&oscMessage, &numberOfBytes, byteArray, sizeof(byteArray)); 1624 | * unsigned int index = 0; 1625 | * while(index <= numberOfBytes) { 1626 | * printf("%u,", (unsigned int)byteArray[index]); 1627 | * } 1628 | * @endcode 1629 | * 1630 | * @param oscMessage OSC message. 1631 | * @param blobSize Blob argument size (number of bytes). 1632 | * @param destination Blob argument (byte array). 1633 | * @param destinationSize Size of the destination that cannot be exceeded. 1634 | * @return Error code (0 if successful). 1635 | */ 1636 | OscError OscMessageGetArgumentAsBlob(OscMessage * const oscMessage, size_t * const blobSize, char * const destination, const size_t destinationSize) { 1637 | if (OscMessageIsArgumentAvailable(oscMessage) == false) { 1638 | return OscErrorNoArgumentsAvailable; 1639 | } 1640 | switch (OscMessageGetArgumentType(oscMessage)) { 1641 | case OscTypeTagString: 1642 | case OscTypeTagAlternateString: 1643 | { 1644 | const OscError oscError = OscMessageGetString(oscMessage, destination, destinationSize); 1645 | if (oscError != 0) { 1646 | return oscError; 1647 | } 1648 | *blobSize = strlen(destination); 1649 | return OscErrorNone; 1650 | } 1651 | case OscTypeTagBlob: 1652 | { 1653 | return OscMessageGetBlob(oscMessage, blobSize, destination, destinationSize); 1654 | } 1655 | case OscTypeTagCharacter: 1656 | { 1657 | char character; 1658 | const OscError oscError = OscMessageGetCharacter(oscMessage, &character); 1659 | if (oscError != 0) { 1660 | return oscError; 1661 | } 1662 | if (destinationSize < 1) { 1663 | return OscErrorDestinationTooSmall; 1664 | } 1665 | destination[0] = character; 1666 | *blobSize = 1; 1667 | return OscErrorNone; 1668 | } 1669 | default: 1670 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1671 | } 1672 | return OscErrorNone; 1673 | } 1674 | 1675 | /** 1676 | * @brief Interprets the next argument in the OSC message as an int64 even if 1677 | * the argument is of another type. 1678 | * 1679 | * The argument provided must be of a numerical type: int32, float32, int64, 1680 | * OSC time tag, 64-bit double, character, boolean, nil, or infinitum. The 1681 | * internal index oscTypeTagStringIndex will only be incremented to the next 1682 | * argument if this function is successful. 1683 | * 1684 | * Example use: 1685 | * @code 1686 | * int64_t int64; 1687 | * OscMessageGetArgumentAsInt64(&oscMessage, &int64); 1688 | * printf("Value = %d", int64); 1689 | * @endcode 1690 | * 1691 | * @param oscMessage OSC message. 1692 | * @param int64 64-bit integer argument. 1693 | * @return Error code (0 if successful). 1694 | */ 1695 | OscError OscMessageGetArgumentAsInt64(OscMessage * const oscMessage, int64_t * const int64) { 1696 | if (OscMessageIsArgumentAvailable(oscMessage) == false) { 1697 | return OscErrorNoArgumentsAvailable; 1698 | } 1699 | switch (OscMessageGetArgumentType(oscMessage)) { 1700 | case OscTypeTagInt32: 1701 | { 1702 | int32_t int32; 1703 | const OscError oscError = OscMessageGetInt32(oscMessage, &int32); 1704 | *int64 = (int64_t) int32; 1705 | return oscError; 1706 | } 1707 | case OscTypeTagFloat32: 1708 | { 1709 | float float32; 1710 | const OscError oscError = OscMessageGetFloat32(oscMessage, &float32); 1711 | *int64 = (int64_t) float32; 1712 | return oscError; 1713 | } 1714 | case OscTypeTagInt64: 1715 | { 1716 | return OscMessageGetInt64(oscMessage, int64); 1717 | } 1718 | case OscTypeTagTimeTag: 1719 | { 1720 | OscTimeTag oscTimeTag; 1721 | const OscError oscError = OscMessageGetTimeTag(oscMessage, &oscTimeTag); 1722 | *int64 = (int64_t) oscTimeTag.value; 1723 | return oscError; 1724 | } 1725 | case OscTypeTagDouble: 1726 | { 1727 | Double64 double64; 1728 | const OscError oscError = OscMessageGetDouble(oscMessage, &double64); 1729 | *int64 = (int64_t) double64; 1730 | return oscError; 1731 | } 1732 | case OscTypeTagCharacter: 1733 | { 1734 | char character; 1735 | const OscError oscError = OscMessageGetCharacter(oscMessage, &character); 1736 | *int64 = (int64_t) character; 1737 | return oscError; 1738 | } 1739 | case OscTypeTagTrue: 1740 | { 1741 | *int64 = (int64_t) true; 1742 | return OscErrorNone; 1743 | } 1744 | case OscTypeTagFalse: 1745 | { 1746 | *int64 = (int64_t) false; 1747 | return OscErrorNone; 1748 | } 1749 | case OscTypeTagNil: 1750 | { 1751 | *int64 = (int64_t) 0; 1752 | return OscErrorNone; 1753 | } 1754 | case OscTypeTagInfinitum: 1755 | { 1756 | *int64 = INT64_MAX; 1757 | return OscErrorNone; 1758 | } 1759 | default: 1760 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1761 | } 1762 | return OscErrorNone; 1763 | } 1764 | 1765 | /** 1766 | * @brief Interprets the next argument in the OSC message as an OSC Time Tag 1767 | * even if the argument is of another type. 1768 | * 1769 | * The argument provided must be of a numerical type: int32, float32, int64, 1770 | * OSC time tag, 64-bit double, character, boolean, nil, or infinitum. The 1771 | * internal index oscTypeTagStringIndex will only be incremented to the next 1772 | * argument if this function is successful. 1773 | * 1774 | * Example use: 1775 | * @code 1776 | * OscTimeTag oscTimeTag; 1777 | * OscMessageGetArgumentAsTimeTag(&oscMessage, &oscTimeTag); 1778 | * printf("Value = %u", (unsigned int)oscTimeTag.dwordStruct.seconds); 1779 | * @endcode 1780 | * 1781 | * @param oscMessage OSC message. 1782 | * @param oscTimeTag OSC time tag argument. 1783 | * @return Error code (0 if successful). 1784 | */ 1785 | OscError OscMessageGetArgumentAsTimeTag(OscMessage * const oscMessage, OscTimeTag * const oscTimeTag) { 1786 | if (OscMessageIsArgumentAvailable(oscMessage) == false) { 1787 | return OscErrorNoArgumentsAvailable; 1788 | } 1789 | switch (OscMessageGetArgumentType(oscMessage)) { 1790 | case OscTypeTagInt32: 1791 | { 1792 | int32_t int32; 1793 | const OscError oscError = OscMessageGetInt32(oscMessage, &int32); 1794 | oscTimeTag->value = (uint64_t) int32; 1795 | return oscError; 1796 | } 1797 | case OscTypeTagFloat32: 1798 | { 1799 | float float32; 1800 | const OscError oscError = OscMessageGetFloat32(oscMessage, &float32); 1801 | oscTimeTag->value = (uint64_t) float32; 1802 | return oscError; 1803 | } 1804 | case OscTypeTagInt64: 1805 | { 1806 | int64_t int64; 1807 | const OscError oscError = OscMessageGetInt64(oscMessage, &int64); 1808 | oscTimeTag->value = (uint64_t) int64; 1809 | return oscError; 1810 | } 1811 | case OscTypeTagTimeTag: 1812 | { 1813 | return OscMessageGetTimeTag(oscMessage, oscTimeTag); 1814 | } 1815 | case OscTypeTagDouble: 1816 | { 1817 | Double64 double64; 1818 | const OscError oscError = OscMessageGetDouble(oscMessage, &double64); 1819 | oscTimeTag->value = (uint64_t) double64; 1820 | return oscError; 1821 | } 1822 | case OscTypeTagCharacter: 1823 | { 1824 | char character; 1825 | const OscError oscError = OscMessageGetCharacter(oscMessage, &character); 1826 | oscTimeTag->value = (uint64_t) character; 1827 | return oscError; 1828 | } 1829 | case OscTypeTagTrue: 1830 | { 1831 | oscTimeTag->value = (uint64_t) true; 1832 | return OscErrorNone; 1833 | } 1834 | case OscTypeTagFalse: 1835 | { 1836 | oscTimeTag->value = (uint64_t) false; 1837 | return OscErrorNone; 1838 | } 1839 | case OscTypeTagNil: 1840 | { 1841 | oscTimeTag->value = (uint64_t) 0; 1842 | return OscErrorNone; 1843 | } 1844 | case OscTypeTagInfinitum: 1845 | { 1846 | oscTimeTag->value = (uint64_t) INT64_MAX; 1847 | return OscErrorNone; 1848 | } 1849 | default: 1850 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1851 | } 1852 | return OscErrorNone; 1853 | } 1854 | 1855 | /** 1856 | * @brief Interprets the next argument in the OSC message as a 64-bit double 1857 | * even if the argument is of another type. 1858 | * 1859 | * The argument provided must be of a numerical type: int32, float32, int64, 1860 | * OSC time tag, 64-bit double, character, boolean, nil, or infinitum. The 1861 | * internal index oscTypeTagStringIndex will only be incremented to the next 1862 | * argument if this function is successful. 1863 | * 1864 | * Example use: 1865 | * @code 1866 | * double double64; 1867 | * OscMessageGetArgumentAsDouble(&oscMessage, &double64); 1868 | * printf("Value = %f", double64); 1869 | * @endcode 1870 | * 1871 | * @param oscMessage OSC message. 1872 | * @param double64 64-bit double argument. 1873 | * @return Error code (0 if successful). 1874 | */ 1875 | OscError OscMessageGetArgumentAsDouble(OscMessage * const oscMessage, Double64 * const double64) { 1876 | if (OscMessageIsArgumentAvailable(oscMessage) == false) { 1877 | return OscErrorNoArgumentsAvailable; 1878 | } 1879 | switch (OscMessageGetArgumentType(oscMessage)) { 1880 | case OscTypeTagInt32: 1881 | { 1882 | int32_t int32; 1883 | const OscError oscError = OscMessageGetInt32(oscMessage, &int32); 1884 | *double64 = (Double64) int32; 1885 | return oscError; 1886 | } 1887 | case OscTypeTagFloat32: 1888 | { 1889 | float float32; 1890 | const OscError oscError = OscMessageGetFloat32(oscMessage, &float32); 1891 | *double64 = (Double64) float32; 1892 | return oscError; 1893 | } 1894 | case OscTypeTagInt64: 1895 | { 1896 | int64_t int64; 1897 | const OscError oscError = OscMessageGetInt64(oscMessage, &int64); 1898 | *double64 = (Double64) int64; 1899 | return oscError; 1900 | } 1901 | case OscTypeTagTimeTag: 1902 | { 1903 | OscTimeTag oscTimeTag; 1904 | const OscError oscError = OscMessageGetTimeTag(oscMessage, &oscTimeTag); 1905 | *double64 = (Double64) oscTimeTag.value; 1906 | return oscError; 1907 | } 1908 | case OscTypeTagDouble: 1909 | { 1910 | return OscMessageGetDouble(oscMessage, double64); 1911 | } 1912 | case OscTypeTagCharacter: 1913 | { 1914 | char character; 1915 | const OscError oscError = OscMessageGetCharacter(oscMessage, &character); 1916 | *double64 = (Double64) character; 1917 | return oscError; 1918 | } 1919 | case OscTypeTagTrue: 1920 | { 1921 | *double64 = (Double64) true; 1922 | return OscErrorNone; 1923 | } 1924 | case OscTypeTagFalse: 1925 | { 1926 | *double64 = (Double64) false; 1927 | return OscErrorNone; 1928 | } 1929 | case OscTypeTagNil: 1930 | { 1931 | *double64 = (Double64) 0; 1932 | return OscErrorNone; 1933 | } 1934 | case OscTypeTagInfinitum: 1935 | { 1936 | *double64 = (Double64) 1 / (Double64) 0; 1937 | return OscErrorNone; 1938 | } 1939 | default: 1940 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 1941 | } 1942 | return OscErrorNone; 1943 | } 1944 | 1945 | /** 1946 | * @brief Interprets the next argument in the OSC message as a character even if 1947 | * the argument is of another type. 1948 | * 1949 | * The argument provided must be of a numerical type: int32, float32, int64, 1950 | * OSC time tag, 64-bit double, character, boolean, nil, or infinitum. The 1951 | * internal index oscTypeTagStringIndex will only be incremented to the next 1952 | * argument if this function is successful. 1953 | * 1954 | * Example use: 1955 | * @code 1956 | * char character; 1957 | * OscMessageGetArgumentAsCharacter(&oscMessage, &character); 1958 | * printf("Value = %c", character); 1959 | * @endcode 1960 | * 1961 | * @param oscMessage OSC message. 1962 | * @param character Character argument. 1963 | * @return Error code (0 if successful). 1964 | */ 1965 | OscError OscMessageGetArgumentAsCharacter(OscMessage * const oscMessage, char * const character) { 1966 | if (OscMessageIsArgumentAvailable(oscMessage) == false) { 1967 | return OscErrorNoArgumentsAvailable; 1968 | } 1969 | switch (OscMessageGetArgumentType(oscMessage)) { 1970 | case OscTypeTagInt32: 1971 | { 1972 | int32_t int32; 1973 | const OscError oscError = OscMessageGetInt32(oscMessage, &int32); 1974 | *character = (char) int32; 1975 | return oscError; 1976 | } 1977 | case OscTypeTagFloat32: 1978 | { 1979 | float float32; 1980 | const OscError oscError = OscMessageGetFloat32(oscMessage, &float32); 1981 | *character = (char) float32; 1982 | return oscError; 1983 | } 1984 | case OscTypeTagInt64: 1985 | { 1986 | int64_t int64; 1987 | const OscError oscError = OscMessageGetInt64(oscMessage, &int64); 1988 | *character = (char) int64; 1989 | return oscError; 1990 | } 1991 | case OscTypeTagTimeTag: 1992 | { 1993 | OscTimeTag oscTimeTag; 1994 | const OscError oscError = OscMessageGetTimeTag(oscMessage, &oscTimeTag); 1995 | *character = (char) oscTimeTag.value; 1996 | return oscError; 1997 | } 1998 | case OscTypeTagDouble: 1999 | { 2000 | Double64 double64; 2001 | const OscError oscError = OscMessageGetDouble(oscMessage, &double64); 2002 | *character = (char) double64; 2003 | return oscError; 2004 | } 2005 | case OscTypeTagCharacter: 2006 | { 2007 | return OscMessageGetCharacter(oscMessage, character); 2008 | } 2009 | case OscTypeTagTrue: 2010 | { 2011 | *character = (char) true; 2012 | return OscErrorNone; 2013 | } 2014 | case OscTypeTagFalse: 2015 | { 2016 | *character = (char) false; 2017 | return OscErrorNone; 2018 | } 2019 | case OscTypeTagNil: 2020 | { 2021 | *character = (char) 0; 2022 | return OscErrorNone; 2023 | } 2024 | case OscTypeTagInfinitum: 2025 | { 2026 | *character = CHAR_MAX; 2027 | return OscErrorNone; 2028 | } 2029 | default: 2030 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 2031 | } 2032 | return OscErrorNone; 2033 | } 2034 | 2035 | /** 2036 | * @brief Interprets the next argument in the OSC message as a 32-bit RGBA 2037 | * colour even if the argument is of another type. 2038 | * 2039 | * The argument provided must be either a blob or 32-bit RGBA colour. The 2040 | * internal index oscTypeTagStringIndex will only be incremented to the next 2041 | * argument if this function is successful. 2042 | * 2043 | * Example use: 2044 | * @code 2045 | * RgbaColour rgbaColour; 2046 | * OscMessageGetArgumentAsRgbaColour(&oscMessage, &rgbaColour); 2047 | * printf("Value = %u,%u,%u,%u", rgbaColour.red, rgbaColour.green, rgbaColour.blue, rgbaColour.alpha); 2048 | * @endcode 2049 | * 2050 | * @param oscMessage OSC message. 2051 | * @param rgbaColour 32-bit RGBA colour argument. 2052 | * @return Error code (0 if successful). 2053 | */ 2054 | OscError OscMessageGetArgumentAsRgbaColour(OscMessage * const oscMessage, RgbaColour * const rgbaColour) { 2055 | if (OscMessageIsArgumentAvailable(oscMessage) == false) { 2056 | return OscErrorNoArgumentsAvailable; 2057 | } 2058 | switch (OscMessageGetArgumentType(oscMessage)) { 2059 | case OscTypeTagBlob: 2060 | { 2061 | size_t blobSize; 2062 | const OscError oscError = OscMessageGetBlob(oscMessage, &blobSize, (char *) &rgbaColour, sizeof (RgbaColour)); 2063 | if (oscError != 0) { 2064 | return oscError; 2065 | } 2066 | if (blobSize != sizeof (RgbaColour)) { 2067 | return OscErrorUnexpectedEndOfSource; // error: not enough bytes in blob 2068 | } 2069 | return OscErrorNone; 2070 | } 2071 | case OscTypeTagRgbaColour: 2072 | { 2073 | return OscMessageGetRgbaColour(oscMessage, rgbaColour); 2074 | } 2075 | default: 2076 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 2077 | } 2078 | return OscErrorNone; 2079 | } 2080 | 2081 | /** 2082 | * @brief Interprets the next argument in the OSC message as a 4 byte MIDI 2083 | * message even if the argument is of another type. 2084 | * 2085 | * The argument provided must be either a blob or 4 byte MIDI message. The 2086 | * internal index oscTypeTagStringIndex will only be incremented to the next 2087 | * argument if this function is successful. 2088 | * 2089 | * Example use: 2090 | * @code 2091 | * MidiMessage midiMessage; 2092 | * OscMessageGetArgumentAsMidiMessage(&oscMessage, &midiMessage); 2093 | * printf("Value = %u,%u,%u,%u", midiMessage.portID, midiMessage.status, midiMessage.data1, midiMessage.data2); 2094 | * @endcode 2095 | * 2096 | * @param oscMessage OSC message. 2097 | * @param midiMessage 4 byte MIDI message argument. 2098 | * @return Error code (0 if successful). 2099 | */ 2100 | OscError OscMessageGetArgumentAsMidiMessage(OscMessage * const oscMessage, MidiMessage * const midiMessage) { 2101 | if (OscMessageIsArgumentAvailable(oscMessage) == false) { 2102 | return OscErrorNoArgumentsAvailable; 2103 | } 2104 | switch (OscMessageGetArgumentType(oscMessage)) { 2105 | case OscTypeTagBlob: 2106 | { 2107 | size_t blobSize; 2108 | const OscError oscError = OscMessageGetBlob(oscMessage, &blobSize, (char *) &midiMessage, sizeof (MidiMessage)); 2109 | if (oscError != 0) { 2110 | return oscError; 2111 | } 2112 | if (blobSize != sizeof (MidiMessage)) { 2113 | return OscErrorUnexpectedEndOfSource; // error: not enough bytes in blob 2114 | } 2115 | return OscErrorNone; 2116 | } 2117 | case OscTypeTagMidiMessage: 2118 | { 2119 | return OscMessageGetMidiMessage(oscMessage, midiMessage); 2120 | } 2121 | default: 2122 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 2123 | } 2124 | return OscErrorNone; 2125 | } 2126 | 2127 | /** 2128 | * @brief Interprets the next argument in the OSC message as a boolean even if 2129 | * the argument is of another type. 2130 | * 2131 | * The argument provided must be of a numerical type: int32, float32, int64, 2132 | * OSC time tag, 64-bit double, character, boolean, nil, or infinitum. The 2133 | * internal index oscTypeTagStringIndex will only be incremented to the next 2134 | * argument if this function is successful. 2135 | * 2136 | * Example use: 2137 | * @code 2138 | * bool boolean; 2139 | * OscMessageGetArgumentAsBool(&oscMessage, &boolean); 2140 | * printf("Value = %u", boolean); 2141 | * @endcode 2142 | * 2143 | * @param oscMessage OSC message. 2144 | * @param boolean Boolean argument. 2145 | * @return Error code (0 if successful). 2146 | */ 2147 | OscError OscMessageGetArgumentAsBool(OscMessage * const oscMessage, bool * const boolean) { 2148 | if (OscMessageIsArgumentAvailable(oscMessage) == false) { 2149 | return OscErrorNoArgumentsAvailable; 2150 | } 2151 | switch (OscMessageGetArgumentType(oscMessage)) { 2152 | case OscTypeTagInt32: 2153 | { 2154 | int32_t int32; 2155 | const OscError oscError = OscMessageGetInt32(oscMessage, &int32); 2156 | *boolean = (bool) int32; 2157 | return oscError; 2158 | } 2159 | case OscTypeTagFloat32: 2160 | { 2161 | float float32; 2162 | const OscError oscError = OscMessageGetFloat32(oscMessage, &float32); 2163 | *boolean = (bool) float32; 2164 | return oscError; 2165 | } 2166 | case OscTypeTagInt64: 2167 | { 2168 | int64_t int64; 2169 | const OscError oscError = OscMessageGetInt64(oscMessage, &int64); 2170 | *boolean = (bool) int64; 2171 | return oscError; 2172 | } 2173 | case OscTypeTagTimeTag: 2174 | { 2175 | OscTimeTag oscTimeTag; 2176 | const OscError oscError = OscMessageGetTimeTag(oscMessage, &oscTimeTag); 2177 | *boolean = (bool) oscTimeTag.value; 2178 | return oscError; 2179 | } 2180 | case OscTypeTagDouble: 2181 | { 2182 | Double64 double64; 2183 | const OscError oscError = OscMessageGetDouble(oscMessage, &double64); 2184 | *boolean = (bool) double64; 2185 | return oscError; 2186 | } 2187 | case OscTypeTagCharacter: 2188 | { 2189 | char character; 2190 | const OscError oscError = OscMessageGetCharacter(oscMessage, &character); 2191 | *boolean = (bool) character; 2192 | return oscError; 2193 | } 2194 | case OscTypeTagTrue: 2195 | { 2196 | *boolean = true; 2197 | return OscErrorNone; 2198 | } 2199 | case OscTypeTagFalse: 2200 | { 2201 | *boolean = false; 2202 | return OscErrorNone; 2203 | } 2204 | case OscTypeTagNil: 2205 | { 2206 | *boolean = (bool) 0; 2207 | return OscErrorNone; 2208 | } 2209 | case OscTypeTagInfinitum: 2210 | { 2211 | *boolean = true; 2212 | return OscErrorNone; 2213 | } 2214 | default: 2215 | return OscErrorUnexpectedArgumentType; // error: unexpected argument type 2216 | } 2217 | return OscErrorNone; 2218 | } 2219 | 2220 | //------------------------------------------------------------------------------ 2221 | // End of file 2222 | -------------------------------------------------------------------------------- /Osc99/OscMessage.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscMessage.h 3 | * @author Seb Madgwick 4 | * @brief Functions and structures for constructing and deconstructing OSC 5 | * messages. 6 | * 7 | * MAX_OSC_ADDRESS_PATTERN_LENGTH and MAX_NUMBER_OF_ARGUMENTS may be modified as 8 | * required by the user application. 9 | * 10 | * See http://opensoundcontrol.org/spec-1_0 11 | */ 12 | 13 | #ifndef OSC_MESSAGE_H 14 | #define OSC_MESSAGE_H 15 | 16 | //------------------------------------------------------------------------------ 17 | // Includes 18 | 19 | #include "OscCommon.h" 20 | #include "OscError.h" 21 | #include 22 | #include 23 | #include 24 | 25 | //------------------------------------------------------------------------------ 26 | // Definitions 27 | 28 | /** 29 | * @brief Minimum size (number of bytes) of an OSC message as per the OSC 30 | * specification. The size is 8 bytes which includes the terminating null 31 | * character. 32 | */ 33 | #define MIN_OSC_MESSAGE_SIZE (sizeof("/\0\0\0,\0\0")) 34 | 35 | /** 36 | * @brief Maximum size (number of bytes) of an OSC message equal to the maximum 37 | * packet size permitted by the transport layer. 38 | */ 39 | #define MAX_OSC_MESSAGE_SIZE (MAX_TRANSPORT_SIZE) 40 | 41 | /** 42 | * @brief Maximum string length (excludes terminating null characters) of an OSC 43 | * address pattern. This value may be modified as required by the user 44 | * application. 45 | */ 46 | #define MAX_OSC_ADDRESS_PATTERN_LENGTH (64) 47 | 48 | /** 49 | * @brief Maximum number of arguments that may be contained within an OSC 50 | * message. This value may be modified as required by the user application. 51 | */ 52 | #define MAX_NUMBER_OF_ARGUMENTS (16) 53 | 54 | /** 55 | * @brief Maximum length of an OSC type tag string (includes comma but not 56 | * terminating null characters). 57 | */ 58 | #define MAX_OSC_TYPE_TAG_STRING_LENGTH (1 + MAX_NUMBER_OF_ARGUMENTS) 59 | 60 | /** 61 | * @brief Maximum combined size (number of bytes) of all arguments that may be 62 | * contained within an OSC message. The calculation assumes the worst case of 63 | * and extra 4 null characters for both the OSC address pattern and the OSC type 64 | * tag string. 65 | */ 66 | #define MAX_ARGUMENTS_SIZE (MAX_OSC_MESSAGE_SIZE - (MAX_OSC_ADDRESS_PATTERN_LENGTH + 4) - (MAX_OSC_TYPE_TAG_STRING_LENGTH + 4)) 67 | 68 | /** 69 | * @brief OSC message structure. Structure members are used internally and 70 | * should not be used by the user application. 71 | */ 72 | typedef struct { 73 | char oscAddressPattern[MAX_OSC_ADDRESS_PATTERN_LENGTH + 1]; // must be first member so that first byte of structure is equal to '/'. Null terminated. 74 | char oscTypeTagString[MAX_OSC_TYPE_TAG_STRING_LENGTH + 1]; // includes comma. Null terminated 75 | char arguments[MAX_ARGUMENTS_SIZE]; 76 | size_t oscAddressPatternLength; // does not include null characters 77 | size_t oscTypeTagStringLength; // includes comma but not null characters 78 | size_t argumentsSize; 79 | unsigned int oscTypeTagStringIndex; 80 | unsigned int argumentsIndex; 81 | } OscMessage; 82 | 83 | /** 84 | * @brief OSC type tag string characters indicating argument type. 85 | */ 86 | typedef enum { 87 | OscTypeTagInt32 = 'i', 88 | OscTypeTagFloat32 = 'f', 89 | OscTypeTagString = 's', 90 | OscTypeTagBlob = 'b', 91 | OscTypeTagInt64 = 'h', 92 | OscTypeTagTimeTag = 't', 93 | OscTypeTagDouble = 'd', 94 | OscTypeTagAlternateString = 'S', 95 | OscTypeTagCharacter = 'c', 96 | OscTypeTagRgbaColour = 'r', 97 | OscTypeTagMidiMessage = 'm', 98 | OscTypeTagTrue = 'T', 99 | OscTypeTagFalse = 'F', 100 | OscTypeTagNil = 'N', 101 | OscTypeTagInfinitum = 'I', 102 | OscTypeTagBeginArray = '[', 103 | OscTypeTagEndArray = ']', 104 | } OscTypeTag; 105 | 106 | //------------------------------------------------------------------------------ 107 | // Function prototypes 108 | 109 | // Message construction 110 | OscError OscMessageInitialise(OscMessage * const oscMessage, const char * oscAddressPattern); 111 | OscError OscMessageSetAddressPattern(OscMessage * const oscMessage, const char * oscAddressPattern); 112 | OscError OscMessageAppendAddressPattern(OscMessage * const oscMessage, const char * appendedParts); 113 | OscError OscMessageAddInt32(OscMessage * const oscMessage, const int32_t int32); 114 | OscError OscMessageAddFloat32(OscMessage * const oscMessage, const float float32); 115 | OscError OscMessageAddString(OscMessage * const oscMessage, const char * string); 116 | OscError OscMessageAddBlob(OscMessage * const oscMessage, const char * const source, const size_t numberOfBytes); 117 | OscError OscMessageAddInt64(OscMessage * const oscMessage, const uint64_t int64); 118 | OscError OscMessageAddTimeTag(OscMessage * const oscMessage, const OscTimeTag oscTimeTag); 119 | OscError OscMessageAddDouble(OscMessage * const oscMessage, const Double64 double64); 120 | OscError OscMessageAddAlternateString(OscMessage * const oscMessage, const char * string); 121 | OscError OscMessageAddCharacter(OscMessage * const oscMessage, const char asciiChar); 122 | OscError OscMessageAddRgbaColour(OscMessage * const oscMessage, const RgbaColour rgbaColour); 123 | OscError OscMessageAddMidiMessage(OscMessage * const oscMessage, const MidiMessage midiMessage); 124 | OscError OscMessageAddBool(OscMessage * const oscMessage, const bool boolean); 125 | OscError OscMessageAddNil(OscMessage * const oscMessage); 126 | OscError OscMessageAddInfinitum(OscMessage * const oscMessage); 127 | OscError OscMessageAddBeginArray(OscMessage * const oscMessage); 128 | OscError OscMessageAddEndArray(OscMessage * const oscMessage); 129 | size_t OscMessageGetSize(const OscMessage * const oscMessage); 130 | OscError OscMessageToCharArray(const OscMessage * const oscMessage, size_t * const oscMessageSize, char * const destination, const size_t destinationSize); 131 | 132 | // Message deconstruction 133 | OscError OscMessageInitialiseFromCharArray(OscMessage * const oscMessage, const char * const source, const size_t size); 134 | bool OscMessageIsArgumentAvailable(OscMessage * const oscMessage); 135 | OscTypeTag OscMessageGetArgumentType(OscMessage * const oscMessage); 136 | OscError OscMessageSkipArgument(OscMessage * const oscMessage); 137 | OscError OscMessageGetInt32(OscMessage * const oscMessage, int32_t * const int32); 138 | OscError OscMessageGetFloat32(OscMessage * const oscMessage, float * const float32); 139 | OscError OscMessageGetString(OscMessage * const oscMessage, char * const destination, const size_t destinationSize); 140 | OscError OscMessageGetBlob(OscMessage * const oscMessage, size_t * const blobSize, char * const destination, const size_t destinationSize); 141 | OscError OscMessageGetInt64(OscMessage * const oscMessage, int64_t * const int64); 142 | OscError OscMessageGetTimeTag(OscMessage * const oscMessage, OscTimeTag * const oscTimeTag); 143 | OscError OscMessageGetDouble(OscMessage * const oscMessage, Double64 * const double64); 144 | OscError OscMessageGetCharacter(OscMessage * const oscMessage, char * const character); 145 | OscError OscMessageGetRgbaColour(OscMessage * const oscMessage, RgbaColour * const rgbaColour); 146 | OscError OscMessageGetMidiMessage(OscMessage * const oscMessage, MidiMessage * const midiMessage); 147 | OscError OscMessageGetArgumentAsInt32(OscMessage * const oscMessage, int32_t * const int32); 148 | OscError OscMessageGetArgumentAsFloat32(OscMessage * const oscMessage, float * const float32); 149 | OscError OscMessageGetArgumentAsString(OscMessage * const oscMessage, char * const destination, const size_t destinationSize); 150 | OscError OscMessageGetArgumentAsBlob(OscMessage * const oscMessage, size_t * const blobSize, char * const destination, const size_t destinationSize); 151 | OscError OscMessageGetArgumentAsInt64(OscMessage * const oscMessage, int64_t * const int64); 152 | OscError OscMessageGetArgumentAsTimeTag(OscMessage * const oscMessage, OscTimeTag * const oscTimeTag); 153 | OscError OscMessageGetArgumentAsDouble(OscMessage * const oscMessage, Double64 * const double64); 154 | OscError OscMessageGetArgumentAsCharacter(OscMessage * const oscMessage, char * const character); 155 | OscError OscMessageGetArgumentAsRgbaColour(OscMessage * const oscMessage, RgbaColour * const rgbaColour); 156 | OscError OscMessageGetArgumentAsMidiMessage(OscMessage * const oscMessage, MidiMessage * const midiMessage); 157 | OscError OscMessageGetArgumentAsBool(OscMessage * const oscMessage, bool * const boolean); 158 | 159 | #endif 160 | 161 | //------------------------------------------------------------------------------ 162 | // End of file 163 | -------------------------------------------------------------------------------- /Osc99/OscPacket.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscPacket.c 3 | * @author Seb Madgwick 4 | * @brief Functions and structures for constructing and deconstructing OSC 5 | * packets. 6 | * See http://opensoundcontrol.org/spec-1_0 7 | */ 8 | 9 | //------------------------------------------------------------------------------ 10 | // Includes 11 | 12 | #include "OscPacket.h" 13 | #include 14 | 15 | //------------------------------------------------------------------------------ 16 | // Function prototypes 17 | 18 | static OscError DeconstructContents(OscPacket * const oscPacket, const OscTimeTag * const oscTimeTag, const void * const oscContents, const size_t contentsSize); 19 | 20 | //------------------------------------------------------------------------------ 21 | // Functions 22 | 23 | /** 24 | * @brief Initialises an OSC packet. 25 | * 26 | * An OSC packet must be initialised before use. This function can be used to 27 | * initialise an OSC packet before writing to the contents and size members 28 | * directly and is typically of use when constructing an OSC packet from 29 | * received bytes. 30 | * 31 | * Example use: 32 | * @code 33 | * OscPacket oscPacket; 34 | * OscPacketInitialise(&oscPacket); 35 | * const char source[] = "/example\0\0\0\0,\0\0"; // string terminating null character is part of OSC message 36 | * unsigned int index = 0; 37 | * while(index++ < sizeof(source)) { 38 | * oscPacket.contents[index] = source[index]; 39 | * } 40 | * oscPacket.size = sizeof(source); 41 | * @endcode 42 | * 43 | * @param oscPacket OSC packet to be initialised. 44 | */ 45 | void OscPacketInitialise(OscPacket * const oscPacket) { 46 | oscPacket->size = 0; 47 | oscPacket->processMessage = NULL; 48 | } 49 | 50 | /** 51 | * @brief Initialises an OSC packet from either OSC message or OSC 52 | * bundle. 53 | * 54 | * An OSC packet must be initialised before use. This function is used to 55 | * initialise an OSC packet from either OSC message or OSC bundle and is 56 | * typically of use when constructing an OSC packet for transmission. 57 | * 58 | * Example use: 59 | * @code 60 | * OscMessage oscMessage; 61 | * OscMessageInitialise(&oscMessage, "/example"); 62 | * OscPacket oscPacket; 63 | * OscPacketInitialiseFromContents(&oscPacket, &oscMessage); 64 | * @endcode 65 | * 66 | * @param oscPacket OSC packet to be initialised. 67 | * @param oscContents OSC message or OSC bundle. 68 | * @return Error code (0 if successful). 69 | */ 70 | OscError OscPacketInitialiseFromContents(OscPacket * const oscPacket, const void * const oscContents) { 71 | oscPacket->processMessage = NULL; 72 | if (OscContentsIsMessage(oscContents) == true) { 73 | return OscMessageToCharArray((OscMessage *) oscContents, &oscPacket->size, oscPacket->contents, MAX_OSC_PACKET_SIZE); 74 | } 75 | if (OscContentsIsBundle(oscContents) == true) { 76 | return OscBundleToCharArray((OscBundle *) oscContents, &oscPacket->size, oscPacket->contents, MAX_OSC_PACKET_SIZE); 77 | } 78 | return OscErrorInvalidContents; // error: invalid or uninitialised OSC contents 79 | } 80 | 81 | /** 82 | * @brief Initialises an OSC packet from byte array. 83 | * 84 | * An OSC packet must be initialised before use. This function is used to 85 | * initialise an OSC packet from a byte array and is typically of use when 86 | * constructing an OSC packet from received bytes. 87 | * 88 | * Example use: 89 | * @code 90 | * OscPacket oscPacket; 91 | * const char source[] = "/example\0\0\0\0,\0\0"; // string terminating null character is part of OSC message 92 | * OscPacketInitialiseFromCharArray(&oscPacket, source, sizeof(source)); 93 | * @endcode 94 | * 95 | * @param oscPacket OSC packet to be initialised. 96 | * @param source Byte array. 97 | * @param numberOfBytes Number of bytes in byte array. 98 | * @return Error code (0 if successful). 99 | */ 100 | OscError OscPacketInitialiseFromCharArray(OscPacket * const oscPacket, const char * const source, const size_t numberOfBytes) { 101 | oscPacket->size = 0; 102 | if (numberOfBytes > MAX_OSC_PACKET_SIZE) { 103 | return OscErrorPacketSizeTooLarge; // error: size exceeds maximum packet size 104 | } 105 | while (oscPacket->size < numberOfBytes) { 106 | oscPacket->contents[oscPacket->size] = source[oscPacket->size]; 107 | oscPacket->size++; 108 | } 109 | oscPacket->processMessage = NULL; 110 | return OscErrorNone; 111 | } 112 | 113 | /** 114 | * @brief Processes the OSC packet to provide each OSC message contained within 115 | * the packet to the user application with the associated OSC time tag (if the 116 | * message is contained within a bundle). 117 | * 118 | * A ProcessMessage function must be implemented within the application and 119 | * assigned to the OSC packet structure after initialisation. The 120 | * ProcessMessage function will be called for each OSC message found within the 121 | * OSC packet. 122 | * 123 | * Example use: 124 | * @code 125 | * void ProcessMessage(const OscTimeTag * const oscTimeTag, OscMessage * const oscMessage) { 126 | * } 127 | * 128 | * void Main() { 129 | * OscPacket oscPacket; 130 | * const char source[] = "/example\0\0\0\0,\0\0\0"; 131 | * OscPacketInitialiseFromCharArray(&oscPacket, source, sizeof(source) - 1); 132 | * oscPacket.processMessage = ProcessPacket; 133 | * OscPacketProcessMessages(&oscPacket); 134 | * } 135 | * @endcode 136 | * 137 | * @param oscPacket OSC packet to be processed. 138 | * @return Error code (0 if successful). 139 | */ 140 | OscError OscPacketProcessMessages(OscPacket * const oscPacket) { 141 | if (oscPacket->processMessage == NULL) { 142 | return OscErrorCallbackFunctionUndefined; // error: user function undefined 143 | } 144 | return DeconstructContents(oscPacket, NULL, oscPacket->contents, oscPacket->size); 145 | } 146 | 147 | /** 148 | * @brief Recursively deconstructs the OSC contents to provide each OSC message 149 | * to the user application with the associated OSC time tag (if the message is 150 | * contained within a bundle). 151 | * 152 | * This is an internal function and cannot be called by the user application. 153 | * 154 | * @param oscPacket OSC packet. 155 | * @param oscTimeTag OSC time tag of the bundle containing the OSC contents. 156 | * Must be NULL if the contents is not within an OSC bundle. 157 | * @param oscContents OSC contents to be deconstructed. 158 | * @param contentsSize Size of the OSC contents. 159 | * @return Error code (0 if successful). 160 | */ 161 | static OscError DeconstructContents(OscPacket * const oscPacket, const OscTimeTag * const oscTimeTag, const void * const oscContents, const size_t contentsSize) { 162 | if (contentsSize == 0) { 163 | return OscErrorContentsEmpty; // error: contents empty 164 | } 165 | 166 | // Contents is an OSC message 167 | if (OscContentsIsMessage(oscContents) == true) { 168 | OscMessage oscMessage; 169 | const OscError oscError = OscMessageInitialiseFromCharArray(&oscMessage, oscContents, contentsSize); 170 | if (oscError != OscErrorNone) { 171 | return oscError; // error: message initialisation failed 172 | } 173 | oscPacket->processMessage(oscTimeTag, &oscMessage); 174 | return OscErrorNone; 175 | } 176 | 177 | // Contents is an OSC bundle 178 | if (OscContentsIsBundle(oscContents) == true) { 179 | OscBundle oscBundle; 180 | OscError oscError = OscBundleInitialiseFromCharArray(&oscBundle, oscContents, contentsSize); 181 | if (oscError != OscErrorNone) { 182 | return oscError; // error: bundle initialisation failed 183 | } 184 | do { 185 | OscBundleElement oscBundleElement; 186 | if (OscBundleIsBundleElementAvailable(&oscBundle) == false) { 187 | break; // no more bundle elements 188 | } 189 | oscError = OscBundleGetBundleElement(&oscBundle, &oscBundleElement); 190 | if (oscError != OscErrorNone) { 191 | return oscError; // error: get bundle element failed 192 | } 193 | oscError = DeconstructContents(oscPacket, &oscBundle.oscTimeTag, oscBundleElement.contents, oscBundleElement.size.int32); // recursive deconstruction 194 | if (oscError != OscErrorNone) { 195 | return oscError; // error: contents deconstruction failed 196 | } 197 | } while (true); 198 | return OscErrorNone; 199 | } 200 | 201 | return OscErrorInvalidContents; // error: invalid or uninitialised contents 202 | } 203 | 204 | //------------------------------------------------------------------------------ 205 | // End of file 206 | -------------------------------------------------------------------------------- /Osc99/OscPacket.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscPacket.h 3 | * @author Seb Madgwick 4 | * @brief Functions and structures for constructing and deconstructing OSC 5 | * packets. 6 | * See http://opensoundcontrol.org/spec-1_0 7 | */ 8 | 9 | #ifndef OSC_PACKET_H 10 | #define OSC_PACKET_H 11 | 12 | //------------------------------------------------------------------------------ 13 | // Includes 14 | 15 | #include "OscBundle.h" 16 | #include "OscCommon.h" 17 | #include "OscError.h" 18 | #include "OscMessage.h" 19 | #include 20 | 21 | //------------------------------------------------------------------------------ 22 | // Definitions 23 | 24 | /** 25 | * @brief Maximum OSC packet size. The OSC packet size is limited by the 26 | * maximum packet size permitted by the transport layer. 27 | */ 28 | #define MAX_OSC_PACKET_SIZE (MAX_TRANSPORT_SIZE) 29 | 30 | /** 31 | * @brief OSC packet structure. Structure members are used internally and 32 | * should not be used by the user application. 33 | */ 34 | typedef struct { 35 | char contents[MAX_OSC_PACKET_SIZE]; 36 | size_t size; 37 | void ( *processMessage)(const OscTimeTag * const oscTimeTag, OscMessage * const oscMessage); 38 | } OscPacket; 39 | 40 | //------------------------------------------------------------------------------ 41 | // Function prototypes 42 | 43 | void OscPacketInitialise(OscPacket * const oscPacket); 44 | OscError OscPacketInitialiseFromContents(OscPacket * const oscPacket, const void * const oscContents); 45 | OscError OscPacketInitialiseFromCharArray(OscPacket * const oscPacket, const char * const source, const size_t numberOfBytes); 46 | OscError OscPacketProcessMessages(OscPacket * const oscPacket); 47 | 48 | #endif 49 | 50 | //------------------------------------------------------------------------------ 51 | // End of file 52 | -------------------------------------------------------------------------------- /Osc99/OscSlip.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscSlip.c 3 | * @author Seb Madgwick 4 | * @brief Functions and structures for encoding and decoding OSC packets using 5 | * the SLIP protocol. 6 | * See http://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol 7 | */ 8 | 9 | //------------------------------------------------------------------------------ 10 | // Includes 11 | 12 | #include "OscSlip.h" 13 | #include 14 | 15 | //------------------------------------------------------------------------------ 16 | // Definitions 17 | 18 | #define SLIP_END ((char)0xC0) 19 | #define SLIP_ESC ((char)0xDB) 20 | #define SLIP_ESC_END ((char)0xDC) 21 | #define SLIP_ESC_ESC ((char)0xDD) 22 | 23 | //------------------------------------------------------------------------------ 24 | // Functions 25 | 26 | /** 27 | * @brief Encodes an OSC packet as a SLIP packet. 28 | * 29 | * The OSC packet is encoded as a SLIP packet and written to the destination 30 | * address. The size of the encoded SLIP packet is written to the 31 | * destinationSize address. If the destination is too small to contain the 32 | * encoded SLIP packet then the written size will be 0. 33 | * 34 | * Example use: 35 | * @code 36 | * char slipPacket[1024]; 37 | * size slipPacketSize; 38 | * OscSlipEncodePacket(&oscPacket, slipPacket, &slipPacketSize, sizeof(slipPacket)); 39 | * @endcode 40 | * 41 | * @param oscPacket OSC packet to be encoded. 42 | * @param destination Destination address of the OSC SLIP packet. 43 | * @param destinationSize Size of the destination. 44 | * @return Error code (0 if successful). 45 | */ 46 | OscError OscSlipEncodePacket(const OscPacket * const oscPacket, size_t * const slipPacketSize, char * const destination, const size_t destinationSize) { 47 | *slipPacketSize = 0; // size will be 0 if function unsuccessful 48 | unsigned int encodedPacketSize = 0; 49 | unsigned int packetIndex; 50 | for (packetIndex = 0; packetIndex < oscPacket->size; packetIndex++) { 51 | if ((encodedPacketSize + 1) > destinationSize) { 52 | return OscErrorDestinationTooSmall; // error: destination too small 53 | } 54 | switch (oscPacket->contents[packetIndex]) { 55 | case SLIP_END: 56 | destination[encodedPacketSize++] = SLIP_ESC; 57 | destination[encodedPacketSize++] = SLIP_ESC_END; 58 | break; 59 | case SLIP_ESC: 60 | destination[encodedPacketSize++] = SLIP_ESC; 61 | destination[encodedPacketSize++] = SLIP_ESC_ESC; 62 | break; 63 | default: 64 | destination[encodedPacketSize++] = oscPacket->contents[packetIndex]; 65 | } 66 | } 67 | destination[encodedPacketSize++] = SLIP_END; 68 | *slipPacketSize = encodedPacketSize; 69 | return OscErrorNone; 70 | } 71 | 72 | /** 73 | * @brief Initialises an OSC SLIP decoder structure. 74 | * 75 | * An OSC SLIP decoder structure must be initialised before use. A 76 | * ProcessPacket function must be implemented within the application and 77 | * assigned to the OSC SLIP decoder structure after initialisation. 78 | * 79 | * Example use: 80 | * @code 81 | * void ProcessPacket(OscPacket * const oscPacket){ 82 | * } 83 | * 84 | * void Main() { 85 | * OscSlipDecoder oscSlipDecoder; 86 | * OscSlipDecoderInitialise(&oscSlipDecoder); 87 | * oscSlipDecoder.processPacket = ProcessPacket; 88 | * } 89 | * @endcode 90 | * 91 | * @param oscSlipDecoder Address OSC SLIP decoder structure. 92 | */ 93 | void OscSlipDecoderInitialise(OscSlipDecoder * const oscSlipDecoder) { 94 | oscSlipDecoder->bufferIndex = 0; 95 | oscSlipDecoder->processPacket = NULL; 96 | } 97 | 98 | /** 99 | * @brief Processes byte received within serial stream. 100 | * 101 | * This function should be called for each consecutive byte received within a 102 | * serial stream. Each byte is added to SLIP decoder receive buffer. If the 103 | * received byte is the last byte of a SLIP packet then the SLIP packet is 104 | * decoded and parsed to the application as an OSC packet via the ProcessPacket 105 | * function. The decoded packet will be discarded if a ProcessPacket function 106 | * has not been assigned. 107 | * 108 | * Example use: 109 | * @code 110 | * while(MySerialDataReady()){ 111 | * OscSlipDecoderProcessByte(&oscSlipDecoder, MySerialGetByte()); 112 | * } 113 | * @endcode 114 | * 115 | * @param oscSlipDecoder Address OSC SLIP decoder structure. 116 | * @param byte Byte received within serial stream. 117 | * @return Error code (0 if successful). 118 | */ 119 | OscError OscSlipDecoderProcessByte(OscSlipDecoder * const oscSlipDecoder, const char byte) { 120 | 121 | // Add byte to buffer 122 | oscSlipDecoder->buffer[oscSlipDecoder->bufferIndex] = byte; 123 | 124 | // Increment index with overflow 125 | if (++oscSlipDecoder->bufferIndex >= OSC_SLIP_DECODER_BUFFER_SIZE) { 126 | oscSlipDecoder->bufferIndex = 0; 127 | return OscErrorEncodedSlipPacketTooLong; // error: SLIP packet is too long 128 | } 129 | 130 | // Return if byte not END byte 131 | if (byte != SLIP_END) { 132 | return OscErrorNone; 133 | } 134 | 135 | // Reset index 136 | oscSlipDecoder->bufferIndex = 0; 137 | 138 | // Decode packet 139 | OscPacket oscPacket; 140 | OscPacketInitialise(&oscPacket); 141 | unsigned int index = 0; 142 | while (oscSlipDecoder->buffer[index] != SLIP_END) { 143 | if (oscSlipDecoder->buffer[index] == SLIP_ESC) { 144 | switch (oscSlipDecoder->buffer[++index]) { 145 | case SLIP_ESC_END: 146 | oscPacket.contents[oscPacket.size++] = SLIP_END; 147 | break; 148 | case SLIP_ESC_ESC: 149 | oscPacket.contents[oscPacket.size++] = SLIP_ESC; 150 | break; 151 | default: 152 | return OscErrorUnexpectedByteAfterSlipEsc; // error: unexpected byte value 153 | } 154 | } else { 155 | oscPacket.contents[oscPacket.size++] = oscSlipDecoder->buffer[index]; 156 | } 157 | if (oscPacket.size > MAX_OSC_PACKET_SIZE) { 158 | return OscErrorDecodedSlipPacketTooLong; // error: decoded packet too large 159 | } 160 | index++; 161 | } 162 | 163 | // Call user function 164 | if (oscSlipDecoder->processPacket == NULL) { 165 | return OscErrorCallbackFunctionUndefined; // error: user function undefined 166 | } 167 | oscSlipDecoder->processPacket(&oscPacket); 168 | return OscErrorNone; 169 | } 170 | 171 | /** 172 | * @brief Clears the SLIP decoder receive buffer. 173 | * 174 | * Example use: 175 | * @code 176 | * OscSlipDecoderClearBuffer(&oscSlipDecoder); 177 | * @endcode 178 | * 179 | * @param oscSlipDecoder Address OSC SLIP decoder structure. 180 | */ 181 | void OscSlipDecoderClearBuffer(OscSlipDecoder * const oscSlipDecoder) { 182 | oscSlipDecoder->bufferIndex = 0; 183 | } 184 | 185 | //------------------------------------------------------------------------------ 186 | // End of file 187 | -------------------------------------------------------------------------------- /Osc99/OscSlip.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OscSlip.h 3 | * @author Seb Madgwick 4 | * @brief Functions and structures for encoding and decoding OSC packets using 5 | * the SLIP protocol. 6 | * See http://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol 7 | */ 8 | 9 | #ifndef OSC_SLIP_H 10 | #define OSC_SLIP_H 11 | 12 | //------------------------------------------------------------------------------ 13 | // Includes 14 | 15 | #include "OscCommon.h" 16 | #include "OscError.h" 17 | #include "OscPacket.h" 18 | 19 | //------------------------------------------------------------------------------ 20 | // Definitions 21 | 22 | /** 23 | * @brief OSC SLIP decoder buffer size. If a packet size exceeds the buffer 24 | * size then all bytes in the decoder buffer will be discarded. 25 | */ 26 | #define OSC_SLIP_DECODER_BUFFER_SIZE (MAX_TRANSPORT_SIZE) 27 | 28 | /** 29 | * @brief OSC SLIP decoder structure. Structure members are used internally and 30 | * should not be used by the user application. 31 | */ 32 | typedef struct { 33 | char buffer[OSC_SLIP_DECODER_BUFFER_SIZE]; 34 | unsigned int bufferIndex; 35 | void ( *processPacket)(OscPacket * const oscPacket); 36 | } OscSlipDecoder; 37 | 38 | //------------------------------------------------------------------------------ 39 | // Function prototypes 40 | 41 | OscError OscSlipEncodePacket(const OscPacket * const oscPacket, size_t * const slipPacketSize, char * const destination, const size_t destinationSize); 42 | void OscSlipDecoderInitialise(OscSlipDecoder * const oscSlipDecoder); 43 | OscError OscSlipDecoderProcessByte(OscSlipDecoder * const oscSlipDecoder, const char byte); 44 | void OscSlipDecoderClearBuffer(OscSlipDecoder * const oscSlipDecoder); 45 | 46 | #endif 47 | 48 | //------------------------------------------------------------------------------ 49 | // End of file 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OSC99 2 | 3 | OSC99 is a portable ANSI C99 compliant OSC library developed for use with embedded systems. OSC99 implements the [OSC 1.0 specification](http://opensoundcontrol.org/spec-1_0) including all optional argument types. The library also includes a [SLIP](https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol) module for encoding and decoding OSC packets via unframed protocols such as UART/serial as required by the [OSC 1.1 specification](http://opensoundcontrol.org/spec-1_1). 4 | 5 | The following definitions may be modified in OscCommon.h as required by the user application: `LITTLE_ENDIAN_PLATFORM`, `MAX_TRANSPORT_SIZE`, `OSC_ERROR_MESSAGES_ENABLED`. 6 | --------------------------------------------------------------------------------