svcore  1.9
OSCQueue.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     
00008     This program is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU General Public License as
00010     published by the Free Software Foundation; either version 2 of the
00011     License, or (at your option) any later version.  See the file
00012     COPYING included with this distribution for more information.
00013 */
00014 
00015 /*
00016    This is a modified version of a source file from the 
00017    Rosegarden MIDI and audio sequencer and notation editor.
00018    This file copyright 2000-2006 Chris Cannam and QMUL.
00019 */
00020 
00021 #include "OSCQueue.h"
00022 
00023 #include "base/Profiler.h"
00024 
00025 #include <iostream>
00026 #include <unistd.h>
00027 
00028 #define OSC_MESSAGE_QUEUE_SIZE 1023
00029 
00030 #ifdef HAVE_LIBLO
00031 
00032 void
00033 OSCQueue::oscError(int num, const char *msg, const char *path)
00034 {
00035     cerr << "ERROR: OSCQueue::oscError: liblo server error " << num
00036               << " in path " << path << ": " << msg << endl;
00037 }
00038 
00039 int
00040 OSCQueue::oscMessageHandler(const char *path, const char *types, lo_arg **argv,
00041                             int argc, lo_message, void *user_data)
00042 {
00043     OSCQueue *queue = static_cast<OSCQueue *>(user_data);
00044 
00045     int target;
00046     int targetData;
00047     QString method;
00048 
00049     if (!queue->parseOSCPath(path, target, targetData, method)) {
00050         return 1;
00051     }
00052 
00053     OSCMessage message;
00054     message.setTarget(target);
00055     message.setTargetData(targetData);
00056     message.setMethod(method);
00057 
00058     int i = 0;
00059 
00060     while (types && i < argc && types[i]) {
00061 
00062         char type = types[i];
00063         lo_arg *arg = argv[i];
00064 
00065         switch (type) {
00066         case 'i': message.addArg(arg->i); break;
00067             // This conversion fails to compile in 64-bit environments
00068             // at present, and we don't use the h type anyway so we
00069             // can safely omit it
00070 //        case 'h': message.addArg(arg->h); break;
00071         case 'f': message.addArg(arg->f); break;
00072         case 'd': message.addArg(arg->d); break;
00073         case 'c': message.addArg(arg->c); break;
00074         case 't': message.addArg(arg->i); break;
00075         case 's': message.addArg(&arg->s); break;
00076         default:  cerr << "WARNING: OSCQueue::oscMessageHandler: "
00077                             << "Unsupported OSC type '" << type << "'" 
00078                             << endl;
00079             break;
00080         }
00081 
00082         ++i;
00083     }
00084 
00085     queue->postMessage(message);
00086     return 0;
00087 }
00088 
00089 #endif
00090    
00091 OSCQueue::OSCQueue() :
00092 #ifdef HAVE_LIBLO
00093     m_thread(0),
00094 #endif
00095     m_buffer(OSC_MESSAGE_QUEUE_SIZE)
00096 {
00097     Profiler profiler("OSCQueue::OSCQueue");
00098 
00099 #ifdef HAVE_LIBLO
00100     m_thread = lo_server_thread_new(NULL, oscError);
00101 
00102     lo_server_thread_add_method(m_thread, NULL, NULL,
00103                                 oscMessageHandler, this);
00104 
00105     lo_server_thread_start(m_thread);
00106 
00107     cout << "OSCQueue::OSCQueue: Base OSC URL is "
00108               << lo_server_thread_get_url(m_thread) << endl;
00109 #endif
00110 }
00111 
00112 OSCQueue::~OSCQueue()
00113 {
00114 #ifdef HAVE_LIBLO
00115     if (m_thread) {
00116         lo_server_thread_stop(m_thread);
00117     }
00118 #endif
00119 
00120     while (m_buffer.getReadSpace() > 0) {
00121         delete m_buffer.readOne();
00122     }
00123 }
00124 
00125 bool
00126 OSCQueue::isOK() const
00127 {
00128 #ifdef HAVE_LIBLO
00129     return (m_thread != 0);
00130 #else
00131     return false;
00132 #endif
00133 }
00134 
00135 QString
00136 OSCQueue::getOSCURL() const
00137 {
00138     QString url = "";
00139 #ifdef HAVE_LIBLO
00140     url = lo_server_thread_get_url(m_thread);
00141 #endif
00142     return url;
00143 }
00144 
00145 int
00146 OSCQueue::getMessagesAvailable() const
00147 {
00148     return m_buffer.getReadSpace();
00149 }
00150 
00151 OSCMessage
00152 OSCQueue::readMessage()
00153 {
00154     OSCMessage *message = m_buffer.readOne();
00155     OSCMessage rmessage = *message;
00156     delete message;
00157     return rmessage;
00158 }
00159 
00160 void
00161 OSCQueue::postMessage(OSCMessage message)
00162 {
00163     int count = 0, max = 5;
00164     while (m_buffer.getWriteSpace() == 0) {
00165         if (count == max) {
00166             cerr << "ERROR: OSCQueue::postMessage: OSC message queue is full and not clearing -- abandoning incoming message" << endl;
00167             return;
00168         }
00169         cerr << "WARNING: OSCQueue::postMessage: OSC message queue (capacity " << m_buffer.getSize() << " is full!" << endl;
00170         SVDEBUG << "Waiting for something to be processed" << endl;
00171 #ifdef _WIN32
00172         Sleep(1);
00173 #else
00174         sleep(1);
00175 #endif
00176         count++;
00177     }
00178 
00179     OSCMessage *mp = new OSCMessage(message);
00180     m_buffer.write(&mp, 1);
00181     SVDEBUG << "OSCQueue::postMessage: Posted OSC message: target "
00182               << message.getTarget() << ", target data " << message.getTargetData()
00183               << ", method " << message.getMethod() << endl;
00184     emit messagesAvailable();
00185 }
00186 
00187 bool
00188 OSCQueue::parseOSCPath(QString path, int &target, int &targetData,
00189                        QString &method)
00190 {
00191     while (path.startsWith("/")) {
00192         path = path.right(path.length()-1);
00193     }
00194 
00195     int i = 0;
00196 
00197     bool ok = false;
00198     target = path.section('/', i, i).toInt(&ok);
00199 
00200     if (!ok) {
00201         target = 0;
00202     } else {
00203         ++i;
00204         targetData = path.section('/', i, i).toInt(&ok);
00205         if (!ok) {
00206             targetData = 0;
00207         } else {
00208             ++i;
00209         }
00210     }
00211 
00212     method = path.section('/', i, -1);
00213 
00214     if (method.contains('/')) {
00215         cerr << "ERROR: OSCQueue::parseOSCPath: malformed path \""
00216                   << path << "\" (should be target/data/method or "
00217                   << "target/method or method, where target and data "
00218                   << "are numeric)" << endl;
00219         return false;
00220     }
00221 
00222     SVDEBUG << "OSCQueue::parseOSCPath: good path \"" << path              << "\"" << endl;
00223 
00224     return true;
00225 }
00226