Chris@320: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@320: 
Chris@320: /*
Chris@320:     Sonic Visualiser
Chris@320:     An audio file viewer and annotation editor.
Chris@320:     Centre for Digital Music, Queen Mary, University of London.
Chris@320:     
Chris@320:     This program is free software; you can redistribute it and/or
Chris@320:     modify it under the terms of the GNU General Public License as
Chris@320:     published by the Free Software Foundation; either version 2 of the
Chris@320:     License, or (at your option) any later version.  See the file
Chris@320:     COPYING included with this distribution for more information.
Chris@320: */
Chris@320: 
Chris@320: /*
Chris@320:    This is a modified version of a source file from the 
Chris@320:    Rosegarden MIDI and audio sequencer and notation editor.
Chris@320:    This file copyright 2000-2006 Chris Cannam and QMUL.
Chris@320: */
Chris@320: 
Chris@320: #include "OSCQueue.h"
Chris@320: 
Chris@408: #include "base/Profiler.h"
Chris@408: 
Chris@320: #include <iostream>
Chris@320: 
Chris@320: #define OSC_MESSAGE_QUEUE_SIZE 1023
Chris@320: 
Chris@320: #ifdef HAVE_LIBLO
Chris@320: 
Chris@1218: #include <unistd.h>
Chris@1218: 
Chris@320: void
Chris@320: OSCQueue::oscError(int num, const char *msg, const char *path)
Chris@320: {
Chris@843:     cerr << "ERROR: OSCQueue::oscError: liblo server error " << num
Chris@843: 	      << " in path " << path << ": " << msg << endl;
Chris@320: }
Chris@320: 
Chris@320: int
Chris@320: OSCQueue::oscMessageHandler(const char *path, const char *types, lo_arg **argv,
Chris@320:                             int argc, lo_message, void *user_data)
Chris@320: {
Chris@320:     OSCQueue *queue = static_cast<OSCQueue *>(user_data);
Chris@320: 
Chris@320:     int target;
Chris@320:     int targetData;
Chris@320:     QString method;
Chris@320: 
Chris@320:     if (!queue->parseOSCPath(path, target, targetData, method)) {
Chris@320: 	return 1;
Chris@320:     }
Chris@320: 
Chris@320:     OSCMessage message;
Chris@320:     message.setTarget(target);
Chris@320:     message.setTargetData(targetData);
Chris@320:     message.setMethod(method);
Chris@320: 
Chris@320:     int i = 0;
Chris@320: 
Chris@320:     while (types && i < argc && types[i]) {
Chris@320: 
Chris@320:         char type = types[i];
Chris@320:         lo_arg *arg = argv[i];
Chris@320: 
Chris@320:         switch (type) {
Chris@320:         case 'i': message.addArg(arg->i); break;
Chris@320:             // This conversion fails to compile in 64-bit environments
Chris@320:             // at present, and we don't use the h type anyway so we
Chris@320:             // can safely omit it
Chris@320: //        case 'h': message.addArg(arg->h); break;
Chris@320:         case 'f': message.addArg(arg->f); break;
Chris@320:         case 'd': message.addArg(arg->d); break;
Chris@320:         case 'c': message.addArg(arg->c); break;
Chris@320:         case 't': message.addArg(arg->i); break;
Chris@320:         case 's': message.addArg(&arg->s); break;
Chris@843:         default:  cerr << "WARNING: OSCQueue::oscMessageHandler: "
Chris@320:                             << "Unsupported OSC type '" << type << "'" 
Chris@843:                             << endl;
Chris@320:             break;
Chris@320:         }
Chris@320: 
Chris@320: 	++i;
Chris@320:     }
Chris@320: 
Chris@320:     queue->postMessage(message);
Chris@320:     return 0;
Chris@320: }
Chris@320: 
Chris@320: #endif
Chris@320:    
Chris@320: OSCQueue::OSCQueue() :
Chris@320: #ifdef HAVE_LIBLO
Chris@320:     m_thread(0),
Chris@320: #endif
Chris@320:     m_buffer(OSC_MESSAGE_QUEUE_SIZE)
Chris@320: {
Chris@408:     Profiler profiler("OSCQueue::OSCQueue");
Chris@408: 
Chris@320: #ifdef HAVE_LIBLO
Chris@320:     m_thread = lo_server_thread_new(NULL, oscError);
Chris@320: 
Chris@320:     lo_server_thread_add_method(m_thread, NULL, NULL,
Chris@320:                                 oscMessageHandler, this);
Chris@320: 
Chris@320:     lo_server_thread_start(m_thread);
Chris@320: 
Chris@843:     cout << "OSCQueue::OSCQueue: Base OSC URL is "
Chris@843:               << lo_server_thread_get_url(m_thread) << endl;
Chris@320: #endif
Chris@320: }
Chris@320: 
Chris@320: OSCQueue::~OSCQueue()
Chris@320: {
Chris@320: #ifdef HAVE_LIBLO
Chris@320:     if (m_thread) {
Chris@320:         lo_server_thread_stop(m_thread);
Chris@320:     }
Chris@320: #endif
Chris@320: 
Chris@320:     while (m_buffer.getReadSpace() > 0) {
Chris@320:         delete m_buffer.readOne();
Chris@320:     }
Chris@320: }
Chris@320: 
Chris@320: bool
Chris@320: OSCQueue::isOK() const
Chris@320: {
Chris@320: #ifdef HAVE_LIBLO
Chris@320:     return (m_thread != 0);
Chris@320: #else
Chris@320:     return false;
Chris@320: #endif
Chris@320: }
Chris@320: 
Chris@320: QString
Chris@320: OSCQueue::getOSCURL() const
Chris@320: {
Chris@320:     QString url = "";
Chris@320: #ifdef HAVE_LIBLO
Chris@320:     url = lo_server_thread_get_url(m_thread);
Chris@320: #endif
Chris@320:     return url;
Chris@320: }
Chris@320: 
Chris@929: int
Chris@320: OSCQueue::getMessagesAvailable() const
Chris@320: {
Chris@320:     return m_buffer.getReadSpace();
Chris@320: }
Chris@320: 
Chris@320: OSCMessage
Chris@320: OSCQueue::readMessage()
Chris@320: {
Chris@320:     OSCMessage *message = m_buffer.readOne();
Chris@320:     OSCMessage rmessage = *message;
Chris@320:     delete message;
Chris@320:     return rmessage;
Chris@320: }
Chris@320: 
Chris@320: void
Chris@320: OSCQueue::postMessage(OSCMessage message)
Chris@320: {
Chris@320:     int count = 0, max = 5;
Chris@320:     while (m_buffer.getWriteSpace() == 0) {
Chris@320:         if (count == max) {
Chris@843:             cerr << "ERROR: OSCQueue::postMessage: OSC message queue is full and not clearing -- abandoning incoming message" << endl;
Chris@320:             return;
Chris@320:         }
Chris@843:         cerr << "WARNING: OSCQueue::postMessage: OSC message queue (capacity " << m_buffer.getSize() << " is full!" << endl;
Chris@690:         SVDEBUG << "Waiting for something to be processed" << endl;
Chris@320: #ifdef _WIN32
Chris@320:         Sleep(1);
Chris@320: #else
Chris@320:         sleep(1);
Chris@320: #endif
Chris@320:         count++;
Chris@320:     }
Chris@320: 
Chris@320:     OSCMessage *mp = new OSCMessage(message);
Chris@320:     m_buffer.write(&mp, 1);
Chris@690:     SVDEBUG << "OSCQueue::postMessage: Posted OSC message: target "
Chris@320:               << message.getTarget() << ", target data " << message.getTargetData()
Chris@687:               << ", method " << message.getMethod() << endl;
Chris@320:     emit messagesAvailable();
Chris@320: }
Chris@320: 
Chris@320: bool
Chris@320: OSCQueue::parseOSCPath(QString path, int &target, int &targetData,
Chris@320:                        QString &method)
Chris@320: {
Chris@320:     while (path.startsWith("/")) {
Chris@320: 	path = path.right(path.length()-1);
Chris@320:     }
Chris@320: 
Chris@320:     int i = 0;
Chris@320: 
Chris@320:     bool ok = false;
Chris@320:     target = path.section('/', i, i).toInt(&ok);
Chris@320: 
Chris@320:     if (!ok) {
Chris@320:         target = 0;
Chris@320:     } else {
Chris@320:         ++i;
Chris@320:         targetData = path.section('/', i, i).toInt(&ok);
Chris@320:         if (!ok) {
Chris@320:             targetData = 0;
Chris@320:         } else {
Chris@320:             ++i;
Chris@320:         }
Chris@320:     }
Chris@320: 
Chris@320:     method = path.section('/', i, -1);
Chris@320: 
Chris@320:     if (method.contains('/')) {
Chris@843:         cerr << "ERROR: OSCQueue::parseOSCPath: malformed path \""
Chris@686:                   << path << "\" (should be target/data/method or "
Chris@320:                   << "target/method or method, where target and data "
Chris@843:                   << "are numeric)" << endl;
Chris@320:         return false;
Chris@320:     }
Chris@320: 
Chris@690:     SVDEBUG << "OSCQueue::parseOSCPath: good path \"" << path              << "\"" << endl;
Chris@320: 
Chris@320:     return true;
Chris@320: }
Chris@320: