Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
osc_sys.cpp
00001 /* 00002 * osc_sys.cpp 00003 * adapting Make controller's OSC to mBed platform 00004 * Pehr Hovey 00005 */ 00006 /********************************************************************************* 00007 00008 Copyright 2006-2009 MakingThings 00009 00010 Licensed under the Apache License, 00011 Version 2.0 (the "License"); you may not use this file except in compliance 00012 with the License. You may obtain a copy of the License at 00013 00014 http://www.apache.org/licenses/LICENSE-2.0 00015 00016 Unless required by applicable law or agreed to in writing, software distributed 00017 under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 00018 CONDITIONS OF ANY KIND, either express or implied. See the License for 00019 the specific language governing permissions and limitations under the License. 00020 00021 *********************************************************************************/ 00022 00023 /** @defgroup OSC OSC 00024 Communicate with the Make Controller Kit via OSC. 00025 00026 \section osc OSC 00027 "Open Sound Control (OSC) is a protocol for communication among computers, sound synthesizers, 00028 and other multimedia devices that is optimized for modern networking technology." 00029 With OSC implemented on the Make Controller Kit, it can already talk to 00030 a wide variety of environments and devices like Java, Max/MSP, Pd, Flash, Processing, SuperCollider, 00031 and many others. 00032 00033 OSC is based on the notion of \b messages, which are composed of an address, and the data to 00034 be sent to that address. The address looks a lot like a URL that you might type into 00035 your internet browser. Each element in the address is like a directory, with other 00036 elements inside it, and each element is separated from the next by a slash (/). Each OSC 00037 message must also start with a slash. 00038 00039 \par Example: 00040 For instance, the OSC address 00041 \code /make/controller/kit \endcode 00042 says, "start in the 'make' directory, go down to the 'controller' directory, and 00043 finally to the 'kit' directory. 00044 00045 Any number of \b argument \b values can be sent to that address by including them in the message 00046 after the address. These values can be integers (ints), floats, or strings. 00047 00048 \par Example: 00049 If we wanted to send the value 35.4 to the address above, we would create the message 00050 \code /make/controller/kit 35.4 \endcode 00051 with a space between the address and the data.\n\n 00052 Additional data can be added, each separated by a space 00053 \code /make/controller/kit 35.4 lawn 12 \endcode 00054 00055 \section osc_mck OSC & the Make Controller Kit 00056 Many devices on the Make Controller Kit can be addressed via OSC. In sending messages to them, 00057 we need to know the OSC address, and the appropriate argument values to send. 00058 00059 The Make Controller Kit is orgranized, for OSC, into \b subsystems. Each subsystem has one or more 00060 \b devices, and each device has one or more \b properties. To address a particular device, you'll 00061 need to create an OSC message specifying the address in that format: 00062 \code /subsystem/device/property \endcode 00063 Each of the modules above provide the details for each of the subsystems on the board, along with their 00064 devices and properties. A simple example is given below. 00065 00066 \par Example: 00067 To create an OSC message to turn an LED on, first identify the appropriate \b subsystem. 00068 In this case, the subsystem is called \b appled.\n\n 00069 There are 4 LEDs, so we need to specify which one to control. 00070 The LEDs are numbered 0 -3, so choosing the first LED means the \b device value is 0.\n\n 00071 The \b property of the LED that turns it on and off is its 'state'.\n\n 00072 Lastly, we must specify what the state should actually be, by including an \b argument value after the address. 00073 To turn it on, this value should be 1. 0 would turn it off.\n 00074 The complete OSC message looks like 00075 \code /appled/0/state 1 \endcode 00076 */ 00077 00078 00079 #include "osc_sys.h" 00080 #include "lwip/udp.h" 00081 00082 00083 #include <string.h> 00084 #include <stdio.h> 00085 00086 #include <ctype.h> 00087 #include <stdarg.h> 00088 00089 00090 00091 int Osc_Quicky( int channel, char* preamble, char* string ); 00092 char* Osc_WritePaddedString( char* buffer, int* length, char* string ); 00093 char* Osc_WritePaddedBlob( char* buffer, int* length, char* blob, int blen ); 00094 char* Osc_WriteTimetag( char* buffer, int* length, int a, int b ); 00095 int Osc_EndianSwap( int a ); 00096 int Osc_ReceiveMessage( int channel, char* message, int length ); 00097 char* Osc_CreateBundle( char* buffer, int* length, int a, int b ); 00098 int Osc_PropertyLookup( char** properties, char* property ); 00099 int Osc_ReadInt( char* buffer ); 00100 float Osc_ReadFloat( char* buffer ); 00101 00102 00103 //void Osc_AsyncTask( void* p ); 00104 00105 void Osc_ResetChannel( OscChannel* ch ); 00106 int Osc_SendPacketInternal( OscChannel* ch ); 00107 00108 int Osc_SendMessage( int channel, char* message, int length ); 00109 int Osc_ReceiveMessage( int channel, char* message, int length ); 00110 int Osc_Poll( int channel, char* buffer, int maxLength, int* length ); 00111 00112 //osc_patternmatch.c 00113 bool Osc_PatternMatch(const char * pattern, const char * test); 00114 00115 int Osc_Quicky( int channel, char* preamble, char* string ); 00116 char* Osc_WritePaddedString( char* buffer, int* length, char* string ); 00117 char* Osc_WriteTimetag( char* buffer, int* length, int a, int b ); 00118 int Osc_EndianSwap( int a ); 00119 00120 char* Osc_CreateBundle( char* buffer, int* length, int a, int b ); 00121 char* Osc_CreateMessageInternal( char* bp, int* length, char* address, char* format, va_list args ); 00122 int Osc_CreateMessageToBuf( char* bp, int* length, char* address, char* format, ... ); 00123 00124 int Osc_ReadInt( char* buffer ); 00125 float Osc_ReadFloat( char* buffer ); 00126 int Osc_UdpPacketSend( char* packet, int length, struct ip_addr * replyAddress, int replyPort ); 00127 00128 int OscBusy; 00129 00130 //Structures 00131 00132 00133 00134 typedef struct OscSubsystem_ 00135 { 00136 const char* name; 00137 int (*receiveMessage)( int channel, char* buffer, int length ); 00138 int (*async)( int channel ); 00139 }OscSubsystem; 00140 00141 typedef struct Osc_ 00142 { 00143 int users; 00144 int running; 00145 int subsystemHighest; 00146 00147 OscChannel* channel[ OSC_CHANNEL_COUNT ]; 00148 OscSubsystem* subsystem[ OSC_SUBSYSTEM_COUNT ]; 00149 int registeredSubsystems; //counter 00150 char scratch1[ OSC_SCRATCH_SIZE ], scratch2[ OSC_SCRATCH_SIZE ]; 00151 //UDP stuff 00152 struct udp_pcb * osc_pcb; //used to send packets 00153 00154 }OscStruct;//OscStruct is the type name 00155 00156 OscStruct* Osc; //The allocated Osc struct that we refer to 00157 00158 00159 void Osc_SystemInit( struct udp_pcb * pcb ) 00160 { 00161 00162 Osc = (OscStruct *) malloc( sizeof( OscStruct )); 00163 Osc->subsystemHighest = 0; 00164 Osc->registeredSubsystems = 0; 00165 00166 int i; 00167 for( i = 0; i < OSC_CHANNEL_COUNT; i++ ) 00168 Osc->channel[ i ] = NULL; 00169 for( i = 0; i < OSC_SUBSYSTEM_COUNT; i++ ) 00170 Osc->subsystem[ i ] = NULL; 00171 00172 Osc->users = 1; 00173 Osc->running = true; 00174 00175 Osc_UdpInit(OSC_CHANNEL_UDP, pcb); 00176 00177 return; 00178 } 00179 00180 OscChannel * Osc_GetChannel(int channel){ 00181 if(channel < OSC_CHANNEL_COUNT) 00182 return Osc->channel[channel]; 00183 else 00184 return NULL; 00185 00186 } 00187 //Initialize the UDP channel 00188 void Osc_UdpInit( int channel, struct udp_pcb * pcb) 00189 { 00190 Osc->osc_pcb = pcb; 00191 Osc->channel[ channel ] = (OscChannel *) malloc( sizeof( OscChannel )); 00192 OscChannel *ch = Osc->channel[ channel ]; //get the struct so we can assign things to it 00193 00194 Osc_SetReplyPort( channel, UDP_BROADCAST_PORT); //broadcast by default 00195 Osc_SetReplyAddress( channel, IP_ADDR_BROADCAST); //broadcast by default 00196 ch->sendMessage = Osc_UdpPacketSend; //the function used to send packets 00197 Osc_ResetChannel( ch ); 00198 00199 ch->running = true; 00200 //will get data from the udp_recv callback (external right now) 00201 } 00202 00203 int Osc_UdpPacketSend( char* packet, int length, struct ip_addr * replyAddress, int replyPort ) 00204 { 00205 if ( replyAddress != 0 && replyPort != 0 ) 00206 { 00207 //Use LWIP raw API to send a packet 00208 //make packet 00209 struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT,length,PBUF_RAM); 00210 memcpy (p->payload, packet, length); 00211 //send packet 00212 int retval = udp_sendto(Osc->osc_pcb, p, replyAddress, replyPort); 00213 //free packet 00214 pbuf_free(p); 00215 00216 return retval; 00217 } 00218 else 00219 return CONTROLLER_ERROR_NO_ADDRESS; 00220 } 00221 00222 00223 void Osc_AsyncTask( void* p ) 00224 { 00225 // (void)p; 00226 // int channel; 00227 // int i; 00228 // OscSubsystem* sub; 00229 // int newMsgs = 0; 00230 // while( 1 ) 00231 // { 00232 // channel = System_GetAsyncDestination( ); 00233 // if( channel >= 0 ) 00234 // { 00235 // for( i = 0; i < Osc->registeredSubsystems; i++ ) 00236 // { 00237 // sub = Osc->subsystem[ i ]; 00238 // if( sub->async != NULL ) 00239 // newMsgs += (sub->async)( channel ); 00240 // } 00241 // if( newMsgs > 0 ) 00242 // { 00243 // Osc_SendPacket( channel ); 00244 // newMsgs = 0; 00245 // } 00246 // Sleep( System_GetAutoSendInterval( ) ); 00247 // } 00248 // else 00249 // Sleep( 1000 ); 00250 // } 00251 } 00252 00253 int Osc_SetReplyAddress( int channel, struct ip_addr * replyAddress ) 00254 { 00255 if ( channel < 0 || channel >= OSC_CHANNEL_COUNT ) 00256 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00257 00258 OscChannel* ch = Osc->channel[ channel ]; 00259 00260 ch->replyAddress = replyAddress; 00261 00262 return CONTROLLER_OK; 00263 } 00264 00265 int Osc_SetReplyPort( int channel, int replyPort ) 00266 { 00267 if ( channel < 0 || channel >= OSC_CHANNEL_COUNT ) 00268 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00269 00270 OscChannel* ch = Osc->channel[ channel ]; 00271 00272 ch->replyPort = replyPort; 00273 00274 return CONTROLLER_OK; 00275 } 00276 00277 void Osc_ResetChannel( OscChannel* ch ) 00278 { 00279 ch->bufferPointer = ch->buffer; 00280 ch->bufferRemaining = OSC_MAX_MESSAGE_OUT; 00281 ch->messages = 0; 00282 } 00283 00284 int Osc_SendMessage( int channel, char* message, int length ) 00285 { 00286 (void)channel; 00287 (void)message; 00288 (void)length; 00289 return CONTROLLER_OK; 00290 } 00291 00292 int Osc_ReceivePacket( int channel, char* packet, int length ) 00293 { 00294 // Got a packet. Unpacket. 00295 int status = -1; 00296 switch ( *packet ) 00297 { 00298 case '/': 00299 status = Osc_ReceiveMessage( channel, packet, length ); 00300 break; 00301 case '#': 00302 if ( strcmp( packet, "#bundle" ) == 0 ) 00303 { 00304 // skip bundle text and timetag 00305 packet += 16; 00306 length -= 16; 00307 while ( length > 0 ) 00308 { 00309 // read the length (pretend packet is a pointer to integer) 00310 int messageLength = Osc_EndianSwap( *((int*)packet) ); 00311 packet += 4; 00312 length -= 4; 00313 if ( messageLength <= length ) 00314 Osc_ReceivePacket( channel, packet, messageLength ); 00315 length -= messageLength; 00316 packet += messageLength; 00317 } 00318 } 00319 break; 00320 default: 00321 // Something else? 00322 Osc_CreateMessage( channel, "/error", ",s", "Packet Error" ); 00323 break; 00324 } 00325 00326 return Osc_SendPacket( channel ); 00327 } 00328 00329 int Osc_ReceiveMessage( int channel, char* message, int length ) 00330 { 00331 // Got a packet. Unpacket. 00332 00333 // Confirm it's a message 00334 if ( *message == '/' ) 00335 { 00336 if( strlen(message) > (unsigned int)length ) 00337 return CONTROLLER_ERROR_BAD_DATA; 00338 00339 int i; 00340 char* nextChar = message + 1; // first, try to see if it was a "help" query 00341 if( *nextChar == '\0' || *nextChar == ' ' ) 00342 { 00343 for ( i = 0; i < Osc->registeredSubsystems; i++ ) 00344 { 00345 OscSubsystem* sub = Osc->subsystem[ i ]; 00346 Osc_CreateMessage( channel, "/", ",s", sub->name ); 00347 } 00348 return CONTROLLER_OK; 00349 } 00350 00351 char* nextSlash = strchr( message + 1, '/' ); 00352 if ( nextSlash != NULL ) 00353 *nextSlash = 0; 00354 00355 int count = 0; 00356 for ( i = 0; i < Osc->registeredSubsystems; i++ ) 00357 { 00358 OscSubsystem* sub = Osc->subsystem[ i ]; 00359 if ( Osc_PatternMatch( message + 1, sub->name ) ) 00360 { 00361 count++; 00362 if ( nextSlash ) 00363 (sub->receiveMessage)( channel, nextSlash + 1, length - ( nextSlash - message ) - 1 ); 00364 else 00365 { 00366 char* noNextSlash = message + strlen(message); 00367 (sub->receiveMessage)( channel, noNextSlash, 0 ); 00368 } 00369 } 00370 } 00371 if ( count == 0 ) 00372 { 00373 00374 snprintf( Osc->scratch1, OSC_SCRATCH_SIZE, "No Subsystem Match - %s", message + 1 ); 00375 Osc_CreateMessage( channel, "/error", ",s", Osc->scratch1 ); 00376 00377 } 00378 } 00379 else 00380 { 00381 return CONTROLLER_ERROR_BAD_DATA; 00382 } 00383 return CONTROLLER_OK; 00384 } 00385 00386 int Osc_SendPacket( int channel ) 00387 { 00388 if ( channel < 0 || channel >= OSC_CHANNEL_COUNT ) 00389 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00390 00391 OscChannel* ch = Osc->channel[ channel ]; 00392 00393 if ( ch->messages == 0 ) 00394 return CONTROLLER_OK; 00395 00396 00397 int ret = Osc_SendPacketInternal( ch ); 00398 printf("Sent packets for channel # %d, ret=%d \r\n",channel,ret); 00399 00400 return ret; 00401 } 00402 00403 int Osc_SendPacketInternal( OscChannel* ch ) 00404 { 00405 //printf("Trying to send packets - we have %d msgs\r\n",ch->messages); 00406 if ( ch->messages == 0 ) 00407 return CONTROLLER_OK; 00408 00409 // set the buffer and length up 00410 char* buffer = ch->buffer; 00411 int length = OSC_MAX_MESSAGE_OUT - ch->bufferRemaining; 00412 00413 // see if we can dispense with the bundle business 00414 if ( ch->messages == 1 ) 00415 { 00416 // skip 8 bytes of "#bundle" and 8 bytes of timetag and 4 bytes of size 00417 buffer += 20; 00418 // shorter too 00419 length -= 20; 00420 } 00421 //call the stored function to do the sending (Osc_UdpPacketSend if its UDP) 00422 (*ch->sendMessage)( buffer, length, ch->replyAddress, ch->replyPort ); 00423 00424 Osc_ResetChannel( ch ); 00425 00426 return CONTROLLER_OK; 00427 } 00428 00429 int Osc_Poll( int channel, char* buffer, int maxLength, int* length ) 00430 { 00431 (void)buffer; 00432 (void)maxLength; 00433 (void)length; 00434 (void)channel; 00435 return CONTROLLER_OK; 00436 } 00437 00438 int Osc_Quicky( int channel, char* preamble, char* string ) 00439 { 00440 return Osc_CreateMessage( channel, "/debug", ",ss", preamble, string ); 00441 } 00442 00443 /** @defgroup OSCAPI OSC API 00444 Make use of the OSC infrastructure for your own subsystems. 00445 You can use the existing OSC infrastructure to create your own OSC subsystems. It's expected that you'll have 00446 a few things lined up to use this API: 00447 -# The name of your subsystem 00448 -# The properties that your subsystem will support. This must be in an array with '0' as the last element. 00449 -# Getters and setters for each of those properties and functions to call them by property index. 00450 -# A function to call the correct helper when Osc calls you. 00451 -# Finally, once you've done all this, you'll need to add your subsystem to the list that Osc knows about. 00452 00453 \par Example 00454 So this might look something like: 00455 \code 00456 // our system name and a function to get it 00457 static char* MySubsystemOsc_Name = "my-system"; 00458 const char* MySubsystemOsc_GetName( void ) 00459 { 00460 return MySubsystemOsc_Name; 00461 } 00462 00463 // our property names 00464 static char* MySubsystemOsc_PropertyNames[] = { "prop0", "prop1", 0 }; // must end with a zero 00465 00466 // A getter and setter, each dealing with the property given to us by the OSC system 00467 int MyGPSOsc_PropertySet( int index, int property, int value ) 00468 { 00469 switch ( property ) 00470 { 00471 case 0: 00472 MySubsystem_SetProperty0( index, value ); 00473 break; 00474 case 1: 00475 MySubsystem_SetProperty1( index, value ); 00476 break; 00477 } 00478 return CONTROLLER_OK; 00479 } 00480 00481 int MySubsystemOsc_PropertyGet( int index, int property ) 00482 { 00483 int value; 00484 switch ( property ) 00485 { 00486 case 0: 00487 value = MySubsystem_GetProperty0( index ); 00488 break; 00489 case 1: 00490 value = MySubsystem_GetProperty1( index ); 00491 break; 00492 } 00493 return value; 00494 } 00495 00496 // this is called when the OSC system determines an incoming message is for you. 00497 int MySubsystemOsc_ReceiveMessage( int channel, char* message, int length ) 00498 { 00499 // depending on your subsystem, use one of the OSC helpers to parse the incoming message 00500 return Osc_IndexGeneralReceiverHelper( channel, message, length, 00501 MySubsystemOsc_Name, 00502 MySubsystemOsc_PropertySet, 00503 MySubsystemOsc_PropertyGet, 00504 MySubsystemOsc_PropertyNames ); 00505 } 00506 00507 // lastly, we'll need to register our system with OSC 00508 Run( ) // this is our startup task in make.c 00509 { 00510 // other startup stuff 00511 Osc_RegisterSubsystem( MySubsystemOsc_GetName(), MySubsystemOsc_ReceiveMessage, NULL ); 00512 } 00513 \endcode 00514 00515 Check the how-to at http://www.makingthings.com/documentation/how-to/create-your-own-osc-subsystem for details. 00516 00517 \ingroup Core 00518 */ 00519 00520 /** 00521 Register your subsystem with the OSC system. 00522 You'll usually want to do this on startup. 00523 @param name The name of your subsystem. 00524 @param subsystem_ReceiveMessage The function to call when an OSC message for this subsystem has arrived. 00525 @param subsystem_Async The function to be called by the OSC Async system. This is a task that will call you at regular intervals, 00526 if enabled, so you can check for status changes and send a message out automatically if you like. See the analog in source for 00527 an example. Pass in NULL if you don't want to use the Async system. 00528 \ingroup OSCAPI 00529 00530 \par Example 00531 \code 00532 Run( ) // this is our startup task in make.c 00533 { 00534 // other startup stuff 00535 Osc_RegisterSubsystem( MySubsystemOsc_GetName(), MySubsystemOsc_ReceiveMessage, NULL ); 00536 } 00537 \endcode 00538 */ 00539 int Osc_RegisterSubsystem( const char *name, int (*subsystem_ReceiveMessage)( int channel, char* buffer, int length ), int (*subsystem_Async)( int channel ) ) 00540 { 00541 int subsystem = Osc->registeredSubsystems; 00542 if ( Osc->registeredSubsystems++ > OSC_SUBSYSTEM_COUNT ) 00543 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00544 00545 Osc->subsystem[ subsystem ] = (OscSubsystem *) malloc( sizeof( OscSubsystem )); 00546 OscSubsystem* sub = Osc->subsystem[ subsystem ]; 00547 sub->name = name; 00548 sub->receiveMessage = subsystem_ReceiveMessage; 00549 sub->async = subsystem_Async; 00550 return CONTROLLER_OK; 00551 } 00552 00553 /** 00554 Send an error back via OSC from your subsystem. 00555 You'll usually want to call this when the OSC system calls you with a new message, you've tried to 00556 parse it, and you've gotten an error. 00557 @param channel An index for which OSC channel is being used (usually USB or Ethernet). Usually provided for you 00558 by the OSC system. 00559 @param subsystem The name of the subsystem sending the error. 00560 @param string The actual contents of the error message. 00561 \ingroup OSCAPI 00562 00563 \par Example 00564 \code 00565 // this is where OSC calls us when an incoming message for us has arrived 00566 int MySubsystemOsc_ReceiveMessage( int channel, char* message, int length ) 00567 { 00568 int status = Osc_IntReceiverHelper( channel, message, length, 00569 MySubsystemOsc_Name, 00570 MySubsystemOsc_PropertySet, MySubsystemOsc_PropertyGet, 00571 MySubsystemOsc_PropertyNames ); 00572 00573 if ( status != CONTROLLER_OK ) 00574 Osc_SubsystemError( channel, MySubsystemOsc_Name, "Oh no. Bad data." ); 00575 } 00576 \endcode 00577 */ 00578 int Osc_SubsystemError( int channel, char* subsystem, char* string ) 00579 { 00580 int retval; 00581 00582 snprintf( Osc->scratch1, OSC_SCRATCH_SIZE, "/%s/error", subsystem ); 00583 retval = Osc_CreateMessage( channel, Osc->scratch1, ",s", string ); 00584 00585 return retval; 00586 } 00587 00588 00589 /** 00590 Receive an OSC blob for a subsystem with no indexes. 00591 You'll usually want to call this when the OSC system calls you with a new message. 00592 @param channel An index for which OSC channel is being used (usually USB or Ethernet). Usually provided for you 00593 by the OSC system. 00594 @param message The OSC message being received. Usually provided for you by the OSC system. 00595 @param length The length of the incoming message. Usually provided for you by the OSC system. 00596 @param subsystemName The name of your subsystem. 00597 @param blobPropertySet A pointer to the function to be called in order to write a property of your subsystem. 00598 @param blobPropertyGet A pointer to the function to be called in order to read a property of your subsystem. 00599 @param blobPropertyNames An array of all the property names in your subsystem. 00600 \ingroup OSCAPI 00601 00602 \par Example 00603 \code 00604 // this is where OSC calls us when an incoming message for us has arrived 00605 int MySubsystemOsc_ReceiveMessage( int channel, char* message, int length ) 00606 { 00607 int status = Osc_BlobReceiverHelper( channel, message, length, 00608 MySubsystemOsc_Name, 00609 MySubsystemOsc_BlobPropertySet, MySubsystemOsc_BlobPropertyGet, 00610 MySubsystemOsc_BlobPropertyNames ); 00611 00612 if ( status != CONTROLLER_OK ) 00613 return Osc_SendError( channel, MySubsystemOsc_Name, status ); 00614 return CONTROLLER_OK; 00615 } 00616 \endcode 00617 */ 00618 int Osc_BlobReceiverHelper( int channel, char* message, int length, 00619 char* subsystemName, 00620 int (*blobPropertySet)( int property, uchar* buffer, int length ), 00621 int (*blobPropertyGet)( int property, uchar* buffer, int size ), 00622 char* blobPropertyNames[] ) 00623 { 00624 if ( message == NULL ) 00625 return CONTROLLER_ERROR_NO_PROPERTY; 00626 00627 int propertyIndex = Osc_PropertyLookup( blobPropertyNames, message ); 00628 if ( propertyIndex == -1 ) 00629 return CONTROLLER_ERROR_UNKNOWN_PROPERTY; 00630 00631 // Sometime after the address, the data tag begins - this is the description 00632 // of the data in the rest of the message. It starts with a comma. Return 00633 // where it is into 'type'. If there is no comma, this is bad. 00634 char* type = Osc_FindDataTag( message, length ); //typetag 00635 if ( type == NULL ) 00636 return CONTROLLER_ERROR_NO_TYPE_TAG; 00637 00638 // We can tell if there's data by seeing if the character after the comma 00639 // is a zero or not. 00640 if ( type[ 1 ] != 0 ) 00641 { 00642 if ( type[ 1 ] == 'b' ) 00643 { 00644 unsigned char *buffer; 00645 int size; 00646 int count = Osc_ExtractData( type, "b", &buffer, &size ); 00647 if ( count != 1 ) 00648 return CONTROLLER_ERROR_BAD_DATA; 00649 00650 (*blobPropertySet)( propertyIndex, buffer, size ); 00651 } 00652 else 00653 { 00654 if ( type[ 1 ] == 's' ) 00655 { 00656 unsigned char *buffer; 00657 int count = Osc_ExtractData( type, "s", &buffer ); 00658 if ( count != 1 ) 00659 return CONTROLLER_ERROR_BAD_DATA; 00660 00661 (*blobPropertySet)( propertyIndex, buffer, strlen( (char*)buffer ) ); 00662 } 00663 else 00664 return CONTROLLER_ERROR_BAD_DATA; 00665 } 00666 00667 } 00668 else 00669 { 00670 // No data, then. I guess it was a read. The XXXXOsc getters 00671 // take the channel number and use it to call 00672 // Osc_CreateMessage() which adds a new message to the outgoing 00673 // stack 00674 00675 int size = (*blobPropertyGet)( propertyIndex, (uchar*)Osc->scratch1, OSC_SCRATCH_SIZE ); 00676 snprintf( Osc->scratch2, OSC_SCRATCH_SIZE, "/%s/%s", subsystemName, blobPropertyNames[ propertyIndex ] ); 00677 Osc_CreateMessage( channel, Osc->scratch2, ",b", Osc->scratch1, size ); 00678 00679 } 00680 00681 return CONTROLLER_OK; 00682 } 00683 00684 /** 00685 Receive an OSC blob for a subsystem with indexes. 00686 You'll usually want to call this when the OSC system calls you with a new message. 00687 @param channel An index for which OSC channel is being used (usually USB or Ethernet). Usually provided for you 00688 by the OSC system. 00689 @param message The OSC message being received. Usually provided for you by the OSC system. 00690 @param length The length of the incoming message. Usually provided for you by the OSC system. 00691 @param indexCount The number of indexes in your subsystem. 00692 @param subsystemName The name of your subsystem. 00693 @param blobPropertySet A pointer to the function to be called in order to write a property of your subsystem. 00694 @param blobPropertyGet A pointer to the function to be called in order to read a property of your subsystem. 00695 @param blobPropertyNames An array of all the property names in your subsystem. 00696 \ingroup OSCAPI 00697 00698 \par Example 00699 \code 00700 // this is where OSC calls us when an incoming message for us has arrived 00701 int MySubsystemOsc_ReceiveMessage( int channel, char* message, int length ) 00702 { 00703 int status = Osc_IndexBlobReceiverHelper( channel, message, length, 2, 00704 MySubsystemOsc_Name, 00705 MySubsystemOsc_BlobPropertySet, MySubsystemOsc_BlobPropertyGet, 00706 MySubsystemOsc_BlobPropertyNames ); 00707 00708 if ( status != CONTROLLER_OK ) 00709 return Osc_SendError( channel, MySubsystemOsc_Name, status ); 00710 return CONTROLLER_OK; 00711 } 00712 \endcode 00713 */ 00714 int Osc_IndexBlobReceiverHelper( int channel, char* message, int length, 00715 int indexCount, char* subsystemName, 00716 int (*blobPropertySet)( int index, int property, uchar* buffer, int length ), 00717 int (*blobPropertyGet)( int index, int property, uchar* buffer, int length ), 00718 char* blobPropertyNames[] ) 00719 { 00720 // Look for the next slash - being the one that separates the index 00721 // from the property. Note that this won't go off on a search through the buffer 00722 // since there will soon be a string terminator (i.e. a 0) 00723 char* prop = strchr( message, '/' ); 00724 if ( prop == NULL ) 00725 return CONTROLLER_ERROR_BAD_FORMAT; 00726 00727 // Now that we know where the property is, we can see if we can find it. 00728 // This is a little cheap, since we're also implying that there are no 00729 // more address terms after the property. That is, if testing for "speed", while 00730 // "speed" would match, "speed/other_stuff" would not. 00731 int propertyIndex = Osc_PropertyLookup( blobPropertyNames, prop + 1 ); 00732 if ( propertyIndex == -1 ) 00733 return CONTROLLER_ERROR_UNKNOWN_PROPERTY; 00734 00735 // Here's where we try to understand what index we got. In the world of 00736 // OSC, this could be a pattern. So while we could get "0/speed" we could 00737 // also get "*/speed" or "[0-4]/speed". This is kind of a drag, but it is 00738 // quite nice from the user's perspective. 00739 // So to deal with this take a look at the text "0" or "{1,2}" or whatever 00740 // and produce either a nice integer in the simplest case or a set of bits 00741 // where each bit corresponds to one of the indicies. Clearly we don't have 00742 // to go crazy, since there are only a small finite number of them. 00743 // Osc_NumberMatch() does the work for us, producing either number = -1 and 00744 // bits == -1 if there was no index match, or number != -1 for there was a single 00745 // number, or bits != -1 if there were several. 00746 00747 // note that we tweak the string a bit here to make sure the next '/' is not 00748 // mixed up with this. Insert a string terminator. 00749 *prop = 0; 00750 00751 int bits; 00752 int number = Osc_NumberMatch( indexCount, message, &bits ); 00753 if ( number == -1 && bits == -1 ) 00754 return CONTROLLER_ERROR_ILLEGAL_INDEX; 00755 00756 // We tweaked the '/' before - now put it back 00757 *prop = '/'; 00758 00759 // Sometime after the address, the data tag begins - this is the description 00760 // of the data in the rest of the message. It starts with a comma. Return 00761 // where it is into 'type'. If there is no comma, this is bad. 00762 char* type = Osc_FindDataTag( message, length ); 00763 if ( type == NULL ) 00764 return CONTROLLER_ERROR_NO_TYPE_TAG; 00765 00766 // We can tell if there's data by seeing if the character after the comma 00767 // is a zero or not. 00768 if ( type[ 1 ] == 'b' || type[ 1 ] == 's' ) 00769 { 00770 // If there was blob or string data, it was a WRITE. 00771 // So, sort of scanf-like, go get the data. Here we pass in where the data is 00772 // thanks to the previous routine and then specify what we expect to find there 00773 // in tag terms (i.e. "b", "s"). Finally we pass in a set 00774 // of pointers to the data types we want to extract. Osc_ExtractData() 00775 // will rummage around in the message magically grabbing values for you, 00776 // reporting how many it got. It will grab convert strings if necessary. 00777 unsigned char *buffer; 00778 int size; 00779 int count = Osc_ExtractData( type, "b", &buffer, &size ); 00780 if ( count != 1 ) 00781 return CONTROLLER_ERROR_INCORRECT_DATA_TYPE; 00782 00783 // Now with the data we need to decide what to do with it. 00784 // Is there one or many here? 00785 if ( number != -1 ) 00786 (*blobPropertySet)( number, propertyIndex, buffer, size ); 00787 else 00788 { 00789 int index = 0; 00790 while ( bits > 0 && index < indexCount ) 00791 { 00792 if ( bits & 1 ) 00793 (*blobPropertySet)( index, propertyIndex, buffer, size ); 00794 bits >>= 1; 00795 index++; 00796 } 00797 } 00798 } 00799 else 00800 { 00801 // No data, then. I guess it was a read. The XXXXOsc getters 00802 // take the channel number and use it to call 00803 // Osc_CreateMessage() which adds a new message to the outgoing 00804 // stack 00805 if ( number != -1 ) 00806 { 00807 00808 int size = (*blobPropertyGet)( number, propertyIndex, (uchar*)Osc->scratch1, OSC_SCRATCH_SIZE ); 00809 snprintf( Osc->scratch2, OSC_SCRATCH_SIZE, "/%s/%d/%s", subsystemName, number, blobPropertyNames[ propertyIndex ] ); 00810 Osc_CreateMessage( channel, Osc->scratch2, ",b", Osc->scratch1, size ); 00811 00812 } 00813 else 00814 { 00815 int index = 0; 00816 while ( bits > 0 && index < indexCount ) 00817 { 00818 if ( bits & 1 ) 00819 { 00820 00821 int size = (*blobPropertyGet)( index, propertyIndex, (uchar*)Osc->scratch1, OSC_SCRATCH_SIZE ); 00822 snprintf( Osc->scratch2, OSC_SCRATCH_SIZE, "/%s/%d/%s", subsystemName, index, blobPropertyNames[ propertyIndex ] ); 00823 Osc_CreateMessage( channel, Osc->scratch2, ",b", Osc->scratch1, size ); 00824 00825 } 00826 bits >>= 1; 00827 index++; 00828 } 00829 } 00830 } 00831 00832 return CONTROLLER_OK; 00833 } 00834 00835 /** 00836 Receive an integer for a subsystem with no indexes. 00837 You'll usually want to call this when the OSC system calls you with a new message. 00838 @param channel An index for which OSC channel is being used (usually USB or Ethernet). Usually provided for you 00839 by the OSC system. 00840 @param message The OSC message being received. Usually provided for you by the OSC system. 00841 @param length The length of the incoming message. Usually provided for you by the OSC system. 00842 @param subsystemName The name of your subsystem. 00843 @param propertySet A pointer to the function to be called in order to write a property of your subsystem. 00844 @param propertyGet A pointer to the function to be called in order to read a property of your subsystem. 00845 @param propertyNames An array of all the property names in your subsystem. 00846 \ingroup OSCAPI 00847 00848 \par Example 00849 \code 00850 // this is where OSC calls us when an incoming message for us has arrived 00851 int MySubsystemOsc_ReceiveMessage( int channel, char* message, int length ) 00852 { 00853 int status = Osc_IntReceiverHelper( channel, message, length, 00854 MySubsystemOsc_Name, 00855 MySubsystemOsc_PropertySet, MySubsystemOsc_PropertyGet, 00856 MySubsystemOsc_PropertyNames ); 00857 00858 if ( status != CONTROLLER_OK ) 00859 return Osc_SendError( channel, MySubsystemOsc_Name, status ); 00860 return CONTROLLER_OK; 00861 } 00862 \endcode 00863 */ 00864 int Osc_IntReceiverHelper( int channel, char* message, int length, 00865 char* subsystemName, 00866 int (*propertySet)( int property, int value ), 00867 int (*propertyGet)( int property ), 00868 char* propertyNames[] ) 00869 { 00870 if( *message == '\0' || *message == ' ' ) // first, try to see if it was a property "help" query 00871 { 00872 int i = 0; 00873 while( true ) 00874 { 00875 if( propertyNames[i] != 0 ) 00876 { 00877 00878 snprintf( Osc->scratch1, OSC_SCRATCH_SIZE, "/%s", subsystemName ); 00879 Osc_CreateMessage( channel, Osc->scratch1, ",s", propertyNames[i] ); 00880 00881 i++; 00882 } 00883 else 00884 return CONTROLLER_OK; 00885 } 00886 } 00887 00888 int propertyIndex = Osc_PropertyLookup( propertyNames, message ); 00889 if ( propertyIndex == -1 ) 00890 return CONTROLLER_ERROR_UNKNOWN_PROPERTY; 00891 00892 /* 00893 int bits; 00894 int number = Osc_NumberMatch( indexCount, message, &bits ); 00895 if ( number == -1 && bits == -1 ) 00896 return Osc_SubsystemError( channel, subsystemName, "Bad index" ); 00897 */ 00898 00899 // Sometime after the address, the data tag begins - this is the description 00900 // of the data in the rest of the message. It starts with a comma. Return 00901 // where it is into 'type'. If there is no comma, this is bad. 00902 char* type = Osc_FindDataTag( message, length ); 00903 if ( type == NULL ) 00904 return CONTROLLER_ERROR_NO_TYPE_TAG; 00905 00906 // We can tell if there's data by seeing if the character after the comma 00907 // is a zero or not. 00908 if ( type[ 1 ] == 'i' || type[ 1 ] == 'f' ) 00909 { 00910 // If there was int or float data, it was a WRITE. 00911 // So, sort of scanff-like, go get the data. Here we pass in where the data is 00912 // thanks to the previous routine and then specify what we expect to find there 00913 // in tag terms (i.e. "i", "s", "f" and others). Finally we pass in a set 00914 // of pointers to the data types we want to extract. Osc_ExtractData() 00915 // will rummage around in the message magically grabbing values for you, 00916 // reporting how many it got. It will convert ints and floats if necessary. 00917 int value; 00918 int count = Osc_ExtractData( type, "i", &value ); 00919 if ( count != 1 ) 00920 return CONTROLLER_ERROR_BAD_DATA; 00921 00922 (*propertySet)( propertyIndex, value ); 00923 } 00924 else 00925 { 00926 // No data, then. I guess it was a read. The XXXXOsc getters 00927 // take the channel number and use it to call 00928 // Osc_CreateMessage() which adds a new message to the outgoing 00929 // stack 00930 int value = (*propertyGet)( propertyIndex ); 00931 00932 snprintf( Osc->scratch1, OSC_SCRATCH_SIZE, "/%s/%s", subsystemName, propertyNames[ propertyIndex ] ); 00933 Osc_CreateMessage( channel, Osc->scratch1, ",i", value ); 00934 00935 } 00936 00937 return CONTROLLER_OK; 00938 } 00939 00940 /** 00941 Receive data for a subsystem that receives a variety of different data types. 00942 An example of this kind of situation is the network system - you have a variety of different properties, 00943 several of which are both ints and strings. 00944 00945 You'll usually want to call this when the OSC system calls you with a new message. 00946 @param channel An index for which OSC channel is being used (usually USB or Ethernet). Usually provided for you 00947 by the OSC system. 00948 @param message The OSC message being received. Usually provided for you by the OSC system. 00949 @param length The length of the incoming message. Usually provided for you by the OSC system. 00950 @param subsystemName The name of your subsystem. 00951 @param propertySet A pointer to the function to be called in order to write a property of your subsystem. 00952 @param propertyGet A pointer to the function to be called in order to read a property of your subsystem. 00953 @param propertyNames An array of all the property names in your subsystem. 00954 \ingroup OSCAPI 00955 00956 \par Example 00957 \code 00958 // this is where OSC calls us when an incoming message for us has arrived 00959 int MySubsystemOsc_ReceiveMessage( int channel, char* message, int length ) 00960 { 00961 int status = Osc_GeneralReceiverHelper( channel, message, length, 00962 MySubsystemOsc_Name, 00963 MySubsystemOsc_PropertySet, MySubsystemOsc_PropertyGet, 00964 MySubsystemOsc_PropertyNames ); 00965 00966 if ( status != CONTROLLER_OK ) 00967 return Osc_SendError( channel, MySubsystemOsc_Name, status ); 00968 return CONTROLLER_OK; 00969 } 00970 \endcode 00971 */ 00972 int Osc_GeneralReceiverHelper( int channel, char* message, int length, 00973 char* subsystemName, 00974 int (*propertySet)( int property, char* typedata, int channel ), 00975 int (*propertyGet)( int property, int channel ), 00976 char* propertyNames[] ) 00977 { 00978 if ( message == NULL ) 00979 return CONTROLLER_ERROR_NO_PROPERTY; 00980 00981 if( *message == '\0' || *message == ' ' ) // first, try to see if it was a property "help" query 00982 { 00983 int i = 0; 00984 while( true ) 00985 { 00986 if( propertyNames[i] != 0 ) 00987 { 00988 00989 snprintf( Osc->scratch1, OSC_SCRATCH_SIZE, "/%s", subsystemName ); 00990 Osc_CreateMessage( channel, Osc->scratch1, ",s", propertyNames[i] ); 00991 00992 i++; 00993 } 00994 else 00995 return CONTROLLER_OK; 00996 } 00997 } 00998 00999 int propertyIndex = Osc_PropertyLookup( propertyNames, message ); 01000 if ( propertyIndex == -1 ) 01001 return CONTROLLER_ERROR_UNKNOWN_PROPERTY; 01002 01003 /* 01004 int bits; 01005 int number = Osc_NumberMatch( indexCount, message, &bits ); 01006 if ( number == -1 && bits == -1 ) 01007 return Osc_SubsystemError( channel, subsystemName, "Bad index" ); 01008 */ 01009 01010 // Sometime after the address, the data tag begins - this is the description 01011 // of the data in the rest of the message. It starts with a comma. Return 01012 // where it is into 'type'. If there is no comma, this is bad. 01013 char* type = Osc_FindDataTag( message, length ); 01014 if ( type == NULL ) 01015 return Osc_SubsystemError( channel, subsystemName, "No type tag" ); 01016 01017 // Debug( 1, "Osc General Type[1] = %d", type[ 1 ] ); 01018 01019 // We can tell if there's data by seeing if the character after the comma 01020 // is a zero or not. 01021 if ( type[ 1 ] != 0 ) 01022 { 01023 // If there was data, it was a WRITE. 01024 int status; 01025 status = (*propertySet)( propertyIndex, type, channel ); 01026 if ( status != CONTROLLER_OK ) 01027 return CONTROLLER_ERROR_BAD_DATA; 01028 } 01029 else 01030 { 01031 // No data, then. I guess it was a read. The XXXXOsc getters 01032 // take the channel number and use it to call 01033 // Osc_CreateMessage() which adds a new message to the outgoing 01034 // stack 01035 01036 (*propertyGet)( propertyIndex, channel ); 01037 } 01038 01039 return CONTROLLER_OK; 01040 } 01041 01042 /** 01043 Receive integers for a subsystem with multiple indexes. 01044 An example of this kind of situation is the analog in system - you have 8 channels (indexes) and you're only 01045 going to be receiving integers. 01046 01047 You'll usually want to call this when the OSC system calls you with a new message. 01048 @param channel An index for which OSC channel is being used (usually USB or Ethernet). Usually provided for you 01049 by the OSC system. 01050 @param message The OSC message being received. Usually provided for you by the OSC system. 01051 @param length The length of the incoming message. Usually provided for you by the OSC system. 01052 @param indexCount The number of indexes in your subsystem. 01053 @param subsystemName The name of your subsystem. 01054 @param propertySet A pointer to the function to be called in order to write a property of your subsystem. 01055 @param propertyGet A pointer to the function to be called in order to read a property of your subsystem. 01056 @param propertyNames An array of all the property names in your subsystem. 01057 \ingroup OSCAPI 01058 01059 \par Example 01060 \code 01061 // this is where OSC calls us when an incoming message for us has arrived 01062 int MySubsystemOsc_ReceiveMessage( int channel, char* message, int length ) 01063 { 01064 int status = Osc_GeneralReceiverHelper( channel, message, length, 01065 5, // our index count 01066 MySubsystemOsc_Name, 01067 MySubsystemOsc_PropertySet, MySubsystemOsc_PropertyGet, 01068 MySubsystemOsc_PropertyNames ); 01069 01070 if ( status != CONTROLLER_OK ) 01071 return Osc_SendError( channel, MySubsystemOsc_Name, status ); 01072 return CONTROLLER_OK; 01073 } 01074 \endcode 01075 */ 01076 01077 //validIndex is a func to tell if an index is valid... some indexes between 0 and indexCount may not be valid 01078 int Osc_IndexIntReceiverHelper( int channel, char* message, int length, 01079 int indexCount, bool (*validIndex)( int index ), char* subsystemName, 01080 int (*propertySet)( int index, int property, int value ), 01081 int (*propertyGet)( int index, int property ), 01082 char* propertyNames[] ) 01083 { 01084 01085 int i; 01086 if( *message == '\0' || *message == ' ' ) // first, try to see if it was an index "help" query 01087 { 01088 for ( i = 0; i < indexCount; i++ ) 01089 { 01090 if(validIndex == NULL || (*validIndex)(i)){ //use index validator if we have one 01091 //list each valid index 01092 snprintf( Osc->scratch1, OSC_SCRATCH_SIZE, "/%s", subsystemName ); 01093 Osc_CreateMessage( channel, Osc->scratch1, ",i", i ); 01094 } 01095 01096 } 01097 return CONTROLLER_OK; 01098 } 01099 //printf("IIReHelper looking for next slash\r\n"); 01100 // Look for the next slash - being the one that separates the index 01101 // from the property. Note that this won't go off on a search through the buffer 01102 // since there will soon be a string terminator (i.e. a 0) 01103 char* prop = strchr( message, '/' ); 01104 char* propHelp = NULL; 01105 if ( prop == NULL ){ //index only, no property found /led/5 01106 01107 // printf("IIRhelper no property found for msg %s\r\n",message); 01108 propHelp = strchr(message,'\0');//try to get ptr to end of string 01109 //propHelp = message + strlen(message);//go to end of string this might have issues 01110 01111 }else{ //there is a '/', but is there anything after it? 01112 01113 // if( *(prop+1) =='\0'){ 01114 // printf("IIRHelper - message ends in '/' so its malformed\r\n"); 01115 // return CONTROLLER_ERROR_BAD_FORMAT; 01116 // } 01117 // printf("IIRhelper found potential property '%s' for msg %s\r\n",prop+1,message); 01118 } 01119 01120 // Here's where we try to understand what index we got. In the world of 01121 // OSC, this could be a pattern. So while we could get "0/speed" we could 01122 // also get "*/speed" or "[0-4]/speed". This is kind of a drag, but it is 01123 // quite nice from the user's perspective. 01124 // So to deal with this take a look at the text "0" or "{1,2}" or whatever 01125 // and produce either a nice integer in the simplest case or a set of bits 01126 // where each bit corresponds to one of the indicies. Clearly we don't have 01127 // to go crazy, since there are only a small finite number of them. 01128 // Osc_NumberMatch() does the work for us, producing either number = -1 and 01129 // bits == -1 if there was no index match, or number != -1 for there was a single 01130 // number, or bits != -1 if there were several. 01131 01132 // note that we tweak the string a bit here to make sure the next '/' is not 01133 // mixed up with this. Insert a string terminator. 01134 if(prop!=NULL) 01135 *prop = 0; 01136 01137 //printf("IIRHelper number matching %s to figure out index\r\n",message); 01138 int bits; 01139 //Need the number even to list properties (for constructing OSC addr) 01140 int number = Osc_NumberMatch( indexCount, message, &bits ); 01141 if ( number == -1 && bits == -1 ) 01142 return CONTROLLER_ERROR_ILLEGAL_INDEX; 01143 01144 //printf("IIRHelper number matching returned num=%d bits=%d for message %s \r\n",number,bits,message); 01145 // We tweaked the '/' before - now put it back 01146 if(prop!=NULL) 01147 *prop = '/'; 01148 01149 //char* propHelp = prop + 1; //set propHelp to end of string 01150 // first, try to see if it was an index "help" query 01151 //print all properties if we do not have one 01152 if( *prop == '\0' || *prop == ' ' || prop==NULL ) 01153 { 01154 //printf("IIRHelper listing properties for message %s \r\n",message); 01155 i = 0; 01156 while( true ) 01157 { 01158 if( propertyNames[i] != 0 ) //list each valid property 01159 { 01160 01161 snprintf( Osc->scratch1, OSC_SCRATCH_SIZE, "/%s/%d", subsystemName, number ); 01162 Osc_CreateMessage( channel, Osc->scratch1, ",s", propertyNames[i] ); 01163 01164 i++; 01165 } 01166 else 01167 return CONTROLLER_OK; 01168 } 01169 } 01170 01171 // Now that we know where the property is, we can see if we can find it. 01172 // This is a little cheap, since we're also implying that there are no 01173 // more address terms after the property. That is, if testing for "speed", while 01174 // "speed" would match, "speed/other_stuff" would not. 01175 int propertyIndex = Osc_PropertyLookup( propertyNames, prop + 1 ); //do +1 to skip the '/' 01176 if ( propertyIndex == -1 ){ 01177 return CONTROLLER_ERROR_UNKNOWN_PROPERTY; 01178 }else{ 01179 // printf("IIRH: found prop index %d for property %s\r\n",propertyIndex,prop); 01180 } 01181 01182 01183 // Sometime after the address, the data tag begins - this is the description 01184 // of the data in the rest of the message. It starts with a comma. Return 01185 // where it is into 'type'. If there is no comma, this is bad. 01186 char* type = Osc_FindDataTag( message, length ); 01187 if ( type == NULL ) 01188 return CONTROLLER_ERROR_NO_TYPE_TAG; 01189 //else 01190 // printf("IIRHelper - found typetag %s\r\n",type); 01191 01192 // We can tell if there's data by seeing if the character after the comma 01193 // is a zero or not. 01194 if ( type[ 1 ] == 'i' || type[ 1 ] == 'f' ) 01195 { 01196 // If there was int or float data, it was a WRITE. 01197 // So, sort of scanff-like, go get the data. Here we pass in where the data is 01198 // thanks to the previous routine and then specify what we expect to find there 01199 // in tag terms (i.e. "i", "s", "f" and others). Finally we pass in a set 01200 // of pointers to the data types we want to extract. Osc_ExtractData() 01201 // will rummage around in the message magically grabbing values for you, 01202 // reporting how many it got. It will convert ints and floats if necessary. 01203 int value; 01204 int count = Osc_ExtractData( type, "i", &value ); 01205 if ( count != 1 ) 01206 return CONTROLLER_ERROR_INCORRECT_DATA_TYPE; 01207 01208 // Now with the data we need to decide what to do with it. 01209 // Is there one or many here? 01210 if ( number != -1 ) 01211 (*propertySet)( number, propertyIndex, value ); 01212 else 01213 { 01214 int index = 0; 01215 while ( bits > 0 && index < indexCount ) 01216 { 01217 if ( bits & 1 ) 01218 (*propertySet)( index, propertyIndex, value ); 01219 bits >>= 1; 01220 index++; 01221 } 01222 } 01223 } 01224 else 01225 { 01226 // No data, then. I guess it was a read. The XXXXOsc getters 01227 // take the channel number and use it to call 01228 // Osc_CreateMessage() which adds a new message to the outgoing 01229 // stack 01230 if ( number != -1 ) 01231 { 01232 int value = (*propertyGet)( number, propertyIndex ); 01233 01234 snprintf( Osc->scratch1, OSC_SCRATCH_SIZE, "/%s/%d/%s", subsystemName, number, propertyNames[ propertyIndex ] ); 01235 Osc_CreateMessage( channel, Osc->scratch1, ",i", value ); 01236 01237 } 01238 else 01239 { 01240 int index = 0; 01241 while ( bits > 0 && index < indexCount ) 01242 { 01243 if ( bits & 1 ) 01244 { 01245 int value = (*propertyGet)( index, propertyIndex ); 01246 01247 snprintf( Osc->scratch1, OSC_SCRATCH_SIZE, "/%s/%d/%s", subsystemName, index, propertyNames[ propertyIndex ] ); 01248 Osc_CreateMessage( channel, Osc->scratch1, ",i", value ); 01249 01250 } 01251 bits >>= 1; 01252 index++; 01253 } 01254 } 01255 } 01256 01257 return CONTROLLER_OK; 01258 } 01259 01260 int Osc_SendError( int channel, char* subsystemName, int error ) 01261 { 01262 char* errorText; 01263 switch ( error ) 01264 { 01265 case CONTROLLER_ERROR_UNKNOWN_PROPERTY: 01266 errorText = "Unknown Property"; 01267 break; 01268 case CONTROLLER_ERROR_NO_PROPERTY: 01269 errorText = "No Property"; 01270 break; 01271 case CONTROLLER_ERROR_INCORRECT_DATA_TYPE: 01272 errorText = "Incorrect Data Type"; 01273 break; 01274 case CONTROLLER_ERROR_ILLEGAL_INDEX: 01275 errorText = "Bad Index"; 01276 break; 01277 case CONTROLLER_ERROR_BAD_FORMAT: 01278 errorText = "Bad Format"; 01279 break; 01280 case CONTROLLER_ERROR_NO_TYPE_TAG: 01281 errorText = "No Type Tag"; 01282 break; 01283 case CONTROLLER_ERROR_BAD_DATA: 01284 errorText = "Bad Data"; 01285 break; 01286 default: 01287 errorText = "Error"; 01288 break; 01289 } 01290 return Osc_SubsystemError( channel, subsystemName, errorText ); 01291 } 01292 01293 // OSC_ExtractData takes a buffer (i.e. a point in the incoming OSC message) 01294 // And a format e.g. "i" "bb", etc. and unpacks them to the var args 01295 // The var args need to be pointers to memory ready to receive the values. 01296 // In the case of blobs, there need to be three parameters: char** buffer, 01297 // and int* size on the param list. The buffer gets a pointer into the 01298 // right place in the incoming buffer and the size value gets assigned 01299 int Osc_ExtractData( char* buffer, char* format, ... ) 01300 { 01301 // Set up to iterate through the arguments 01302 va_list args; 01303 va_start( args, format ); 01304 int count = 0; 01305 01306 // figure out where the data starts 01307 int tagLen = strlen( buffer ) + 1; 01308 int pad = tagLen % 4; 01309 if ( pad != 0 ) 01310 tagLen += ( 4 - pad ); 01311 char* data = buffer + tagLen; 01312 01313 // Going to be walking the tag string, the format string and the data 01314 char* fp; 01315 char* tp = buffer + 1; // need to skip the comma ',' 01316 bool cont = true; 01317 for ( fp = format; *fp && cont; fp++ ) 01318 { 01319 cont = false; 01320 switch ( *fp ) 01321 { 01322 case 'i': 01323 if ( *tp == 'i' ) 01324 { 01325 *(va_arg( args, int* )) = Osc_ReadInt( data ); 01326 data += 4; 01327 count++; 01328 cont = true; 01329 } 01330 if ( *tp == 'f' ) 01331 { 01332 *(va_arg( args, int* )) = (int)Osc_ReadFloat( data ); 01333 data += 4; 01334 count++; 01335 cont = true; 01336 } 01337 01338 break; 01339 case 'f': 01340 if ( *tp == 'f' ) 01341 { 01342 *(va_arg( args, float* )) = Osc_ReadFloat( data ); 01343 data += 4; 01344 count++; 01345 cont = true; 01346 } 01347 if ( *tp == 'i' ) 01348 { 01349 *(va_arg( args, float* )) = (float)Osc_ReadInt( data ); 01350 data += 4; 01351 count++; 01352 cont = true; 01353 } 01354 break; 01355 case 's': 01356 if ( *tp == 's' ) 01357 { 01358 *(va_arg( args, char** )) = data; 01359 int len = strlen( data ) + 1; 01360 int pad = len % 4; 01361 if ( pad != 0 ) 01362 len += ( 4 - pad ); 01363 data += len; 01364 count++; 01365 cont = true; 01366 } 01367 break; 01368 case 'b': 01369 if ( *tp == 'b' ) 01370 { 01371 int length = Osc_ReadInt( data ); 01372 data += 4; 01373 *(va_arg( args, char** )) = data; 01374 *(va_arg( args, int* )) = length; 01375 int pad = length % 4; 01376 if ( pad != 0 ) 01377 length += ( 4 - pad ); 01378 data += length; 01379 count++; 01380 cont = true; 01381 } 01382 else 01383 { 01384 if ( *tp == 's' ) 01385 { 01386 *(va_arg( args, char** )) = data; 01387 int len = strlen( data ) + 1; 01388 *(va_arg( args, int* )) = len; 01389 int pad = len % 4; 01390 if ( pad != 0 ) 01391 len += ( 4 - pad ); 01392 data += len; 01393 count++; 01394 cont = true; 01395 } 01396 } 01397 break; 01398 } 01399 tp++; 01400 } 01401 01402 //va_end( args ); 01403 01404 return count; 01405 } 01406 01407 int Osc_ReadInt( char* buffer ) 01408 { 01409 int v = *((int*)buffer); 01410 v = Osc_EndianSwap( v ); 01411 return v; 01412 } 01413 01414 float Osc_ReadFloat( char* buffer ) 01415 { 01416 int v = *((int*)buffer); 01417 v = Osc_EndianSwap( v ); 01418 return *(float*)&v; 01419 } 01420 01421 /** 01422 Osc_CreateMessage 01423 Must put the "," as the first format letter 01424 */ 01425 int Osc_CreateMessage( int channel, char* address, char* format, ... ) 01426 { 01427 if ( address == NULL || format == NULL || *format != ',' ) 01428 return CONTROLLER_ERROR_BAD_DATA; 01429 01430 if ( channel < 0 || channel >= OSC_CHANNEL_COUNT ) 01431 return CONTROLLER_ERROR_ILLEGAL_INDEX; 01432 01433 OscChannel* ch = Osc->channel[ channel ]; 01434 01435 if ( !ch->running ) 01436 return CONTROLLER_ERROR_SUBSYSTEM_INACTIVE; 01437 01438 if ( channel == OSC_CHANNEL_UDP && ch->replyAddress == 0 ) 01439 return CONTROLLER_ERROR_NO_ADDRESS; 01440 01441 // Check for sender 01442 if ( ch->sendMessage == NULL ) 01443 return CONTROLLER_ERROR_RESOURCE_MISSING; 01444 01445 01446 01447 if ( ch->bufferPointer == NULL ) 01448 Osc_ResetChannel( ch ); 01449 01450 // try to send this message - if there's a problem somewhere, 01451 // send the existing buffer - freeing up space, then try (once) again. 01452 int count = 0; 01453 char *bp; 01454 do 01455 { 01456 count++; 01457 01458 char* buffer = ch->bufferPointer; 01459 int length = ch->bufferRemaining; 01460 01461 bp = buffer; 01462 01463 // First message in the buffer? 01464 if ( bp == ch->buffer ) 01465 { 01466 bp = Osc_CreateBundle( bp, &length, 0, 0 ); 01467 if ( bp == NULL ) 01468 return CONTROLLER_ERROR_INSUFFICIENT_RESOURCES; 01469 } 01470 01471 // Make room for the new message 01472 int* lp = (int *)bp; 01473 bp += 4; 01474 length -= 4; 01475 01476 // remember the start of the message 01477 char* mp = bp; 01478 01479 if ( length > 0 ) 01480 { 01481 // Set up to iterate through the arguments 01482 va_list args; 01483 va_start( args, format ); 01484 01485 bp = Osc_CreateMessageInternal( bp, &length, address, format, args ); 01486 01487 //va_end( args ); 01488 } 01489 else 01490 bp = 0; 01491 01492 if ( bp != 0 ) 01493 { 01494 // Set the size 01495 *lp = Osc_EndianSwap( bp - mp ); 01496 01497 ch->bufferPointer = bp; 01498 ch->bufferRemaining = length; 01499 ch->messages++; 01500 } 01501 else 01502 { 01503 Osc_SendPacketInternal( ch ); 01504 } 01505 } while ( bp == 0 && count == 1 ); 01506 01507 01508 return ( bp != 0 ) ? CONTROLLER_OK : CONTROLLER_ERROR_ILLEGAL_PARAMETER_VALUE; 01509 } 01510 01511 int Osc_CreateMessageToBuf( char* bp, int* length, char* address, char* format, ... ) 01512 { 01513 if ( address == NULL || format == NULL || *format != ',' ) 01514 return CONTROLLER_ERROR_BAD_DATA; 01515 01516 va_list args; 01517 va_start( args, format ); 01518 01519 Osc_CreateMessageInternal( bp, length, address, format, args ); 01520 return CONTROLLER_OK; 01521 } 01522 01523 char* Osc_CreateMessageInternal( char* bp, int* length, char* address, char* format, va_list args ) 01524 { 01525 // do the address 01526 bp = Osc_WritePaddedString( bp, length, address ); 01527 if ( bp == NULL ) 01528 return 0; 01529 01530 // do the type 01531 bp = Osc_WritePaddedString( bp, length, format ); 01532 if ( bp == NULL ) 01533 return 0; 01534 01535 // Going to be walking the tag string, the format string and the data 01536 // skip the ',' comma 01537 char* fp; 01538 bool cont = true; 01539 for ( fp = format + 1; *fp && cont; fp++ ) 01540 { 01541 switch ( *fp ) 01542 { 01543 case 'i': 01544 *length -= 4; 01545 if ( *length >= 0 ) 01546 { 01547 int v = va_arg( args, int ); 01548 v = Osc_EndianSwap( v ); 01549 *((int*)bp) = v; 01550 bp += 4; 01551 } 01552 else 01553 cont = false; 01554 break; 01555 case 'f': 01556 *length -= 4; 01557 if ( *length >= 0 ) 01558 { 01559 int v; 01560 *((float*)&v) = (float)( va_arg( args, double ) ); 01561 v = Osc_EndianSwap( v ); 01562 *((int*)bp) = v; 01563 bp += 4; 01564 } 01565 else 01566 cont = false; 01567 break; 01568 case 's': 01569 { 01570 char* s = va_arg( args, char* ); 01571 bp = Osc_WritePaddedString( bp, length, s ); 01572 if ( bp == NULL ) 01573 cont = false; 01574 break; 01575 } 01576 case 'b': 01577 { 01578 char* b = va_arg( args, char* ); 01579 int blen = va_arg( args, int ); 01580 bp = Osc_WritePaddedBlob( bp, length, b, blen ); 01581 if ( bp == NULL ) 01582 cont = false; 01583 break; 01584 } 01585 default: 01586 cont = false; 01587 } 01588 } 01589 01590 return ( cont ) ? bp : NULL; 01591 } 01592 01593 char* Osc_CreateBundle( char* buffer, int* length, int a, int b ) 01594 { 01595 char *bp = buffer; 01596 01597 // do the bundle bit 01598 bp = Osc_WritePaddedString( bp, length, "#bundle" ); 01599 if ( bp == NULL ) 01600 return 0; 01601 01602 // do the timetag 01603 bp = Osc_WriteTimetag( bp, length, a, b ); 01604 if ( bp == NULL ) 01605 return 0; 01606 01607 return bp; 01608 } 01609 01610 01611 01612 int Osc_NumberMatch( int count, char* message, int* bits ) 01613 { 01614 int n = 0; 01615 int digits = 0; 01616 while ( isdigit( *message ) ) 01617 { 01618 digits++; 01619 n = n * 10 + ( *message++ - '0' ); 01620 } 01621 01622 *bits = -1; 01623 if ( n >= count ) 01624 return -1; 01625 01626 switch ( *message ) 01627 { 01628 case '*': 01629 case '?': 01630 case '[': 01631 case '{': 01632 { 01633 int i; 01634 int b = 0; 01635 char s[ 5 ]; 01636 for ( i = count - 1; i >=0 ; i-- ) 01637 { 01638 b <<= 1; 01639 sprintf( s, "%d", i ); 01640 if ( Osc_PatternMatch( message, s ) ) 01641 b |= 1; 01642 } 01643 *bits = b; 01644 return -1; 01645 } 01646 default: 01647 if ( digits == 0 ) 01648 return -1; 01649 return n; 01650 } 01651 } 01652 01653 // Looks the named property up, returning an index 01654 // Note that we need to be careful - there may be other stuff there in the string 01655 // Probably best to eventually do something better with it. 01656 int Osc_PropertyLookup( char** properties, char* property ) 01657 { 01658 char** p = properties; 01659 int index = 0; 01660 while (*p != NULL ) 01661 { 01662 if ( strcmp( property, *p++ ) == 0 ) 01663 return index; 01664 index++; 01665 } 01666 return -1; 01667 } 01668 01669 char *Osc_FindDataTag( char* message, int length ) 01670 { 01671 while ( *message != ',' && length-- > 0 ) 01672 message++; 01673 if ( length <= 0 ) 01674 return NULL; 01675 else 01676 return message; 01677 } 01678 01679 char* Osc_WritePaddedString( char* buffer, int* length, char* string ) 01680 { 01681 int tagLen = strlen( string ) + 1; 01682 int tagPadLen = tagLen; 01683 int pad = ( tagPadLen ) % 4; 01684 if ( pad != 0 ) 01685 tagPadLen += ( 4 - pad ); 01686 01687 *length -= tagPadLen; 01688 01689 if ( *length >= 0 ) 01690 { 01691 strcpy( buffer, string ); 01692 int i; 01693 buffer += tagLen; 01694 for ( i = tagLen; i < tagPadLen; i++ ) 01695 *buffer++ = 0; 01696 } 01697 else 01698 return NULL; 01699 01700 return buffer; 01701 } 01702 01703 char* Osc_WritePaddedBlob( char* buffer, int* length, char* blob, int blen ) 01704 { 01705 int i; 01706 int padLength = blen; 01707 int pad = ( padLength ) % 4; 01708 if ( pad != 0 ) 01709 padLength += ( 4 - pad ); 01710 01711 if ( *length < ( padLength + 4 ) ) 01712 return 0; 01713 01714 // add the length of the blob 01715 int l = Osc_EndianSwap( blen ); 01716 *((int*)buffer) = l; 01717 buffer += 4; 01718 *length -= 4; 01719 01720 memcpy( buffer, blob, blen ); 01721 buffer += blen; 01722 // reduce the remaining buffer size 01723 *length -= padLength; 01724 01725 for ( i = blen; i < padLength; i++ ) 01726 *buffer++ = 0; 01727 01728 return buffer; 01729 } 01730 01731 char* Osc_WriteTimetag( char* buffer, int* length, int a, int b ) 01732 { 01733 if ( *length < 8 ) 01734 return NULL; 01735 01736 *((int*)buffer) = Osc_EndianSwap( a ); 01737 buffer += 4; 01738 *((int*)buffer) = Osc_EndianSwap( b ); 01739 buffer += 4; 01740 *length -= 8; 01741 01742 return buffer; 01743 } 01744 01745 int Osc_EndianSwap( int a ) 01746 { 01747 return ( ( a & 0x000000FF ) << 24 ) | 01748 ( ( a & 0x0000FF00 ) << 8 ) | 01749 ( ( a & 0x00FF0000 ) >> 8 ) | 01750 ( ( a & 0xFF000000 ) >> 24 ); 01751 01752 } 01753 01754 01755 01756
Generated on Wed Jul 13 2022 01:51:44 by
1.7.2