MakingThings
  • 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.