- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

# osc.c

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

The Make Controller Kit is an open source project maintained by MakingThings.
MakingThings code is released under the Apache 2.0 license.
Bug tracker, development wiki and status can be found at http://dev.makingthings.com.
This document was last updated on 18 May 2009.