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.