Pehr Hovey / Mbed 2 deprecated mbed_osc

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers osc_sys.cpp Source File

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