| Chris@320 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@320 | 2 | 
| Chris@320 | 3 /* | 
| Chris@320 | 4     Sonic Visualiser | 
| Chris@320 | 5     An audio file viewer and annotation editor. | 
| Chris@320 | 6     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@320 | 7 | 
| Chris@320 | 8     This program is free software; you can redistribute it and/or | 
| Chris@320 | 9     modify it under the terms of the GNU General Public License as | 
| Chris@320 | 10     published by the Free Software Foundation; either version 2 of the | 
| Chris@320 | 11     License, or (at your option) any later version.  See the file | 
| Chris@320 | 12     COPYING included with this distribution for more information. | 
| Chris@320 | 13 */ | 
| Chris@320 | 14 | 
| Chris@320 | 15 /* | 
| Chris@320 | 16    This is a modified version of a source file from the | 
| Chris@320 | 17    Rosegarden MIDI and audio sequencer and notation editor. | 
| Chris@320 | 18    This file copyright 2000-2006 Chris Cannam and QMUL. | 
| Chris@320 | 19 */ | 
| Chris@320 | 20 | 
| Chris@320 | 21 #include "OSCQueue.h" | 
| Chris@320 | 22 | 
| Chris@408 | 23 #include "base/Profiler.h" | 
| Chris@408 | 24 | 
| Chris@320 | 25 #include <iostream> | 
| Chris@320 | 26 | 
| Chris@320 | 27 #define OSC_MESSAGE_QUEUE_SIZE 1023 | 
| Chris@320 | 28 | 
| Chris@320 | 29 #ifdef HAVE_LIBLO | 
| Chris@320 | 30 | 
| Chris@320 | 31 void | 
| Chris@320 | 32 OSCQueue::oscError(int num, const char *msg, const char *path) | 
| Chris@320 | 33 { | 
| Chris@320 | 34     std::cerr << "ERROR: OSCQueue::oscError: liblo server error " << num | 
| Chris@320 | 35 	      << " in path " << path << ": " << msg << std::endl; | 
| Chris@320 | 36 } | 
| Chris@320 | 37 | 
| Chris@320 | 38 int | 
| Chris@320 | 39 OSCQueue::oscMessageHandler(const char *path, const char *types, lo_arg **argv, | 
| Chris@320 | 40                             int argc, lo_message, void *user_data) | 
| Chris@320 | 41 { | 
| Chris@320 | 42     OSCQueue *queue = static_cast<OSCQueue *>(user_data); | 
| Chris@320 | 43 | 
| Chris@320 | 44     int target; | 
| Chris@320 | 45     int targetData; | 
| Chris@320 | 46     QString method; | 
| Chris@320 | 47 | 
| Chris@320 | 48     if (!queue->parseOSCPath(path, target, targetData, method)) { | 
| Chris@320 | 49 	return 1; | 
| Chris@320 | 50     } | 
| Chris@320 | 51 | 
| Chris@320 | 52     OSCMessage message; | 
| Chris@320 | 53     message.setTarget(target); | 
| Chris@320 | 54     message.setTargetData(targetData); | 
| Chris@320 | 55     message.setMethod(method); | 
| Chris@320 | 56 | 
| Chris@320 | 57     int i = 0; | 
| Chris@320 | 58 | 
| Chris@320 | 59     while (types && i < argc && types[i]) { | 
| Chris@320 | 60 | 
| Chris@320 | 61         char type = types[i]; | 
| Chris@320 | 62         lo_arg *arg = argv[i]; | 
| Chris@320 | 63 | 
| Chris@320 | 64         switch (type) { | 
| Chris@320 | 65         case 'i': message.addArg(arg->i); break; | 
| Chris@320 | 66             // This conversion fails to compile in 64-bit environments | 
| Chris@320 | 67             // at present, and we don't use the h type anyway so we | 
| Chris@320 | 68             // can safely omit it | 
| Chris@320 | 69 //        case 'h': message.addArg(arg->h); break; | 
| Chris@320 | 70         case 'f': message.addArg(arg->f); break; | 
| Chris@320 | 71         case 'd': message.addArg(arg->d); break; | 
| Chris@320 | 72         case 'c': message.addArg(arg->c); break; | 
| Chris@320 | 73         case 't': message.addArg(arg->i); break; | 
| Chris@320 | 74         case 's': message.addArg(&arg->s); break; | 
| Chris@320 | 75         default:  std::cerr << "WARNING: OSCQueue::oscMessageHandler: " | 
| Chris@320 | 76                             << "Unsupported OSC type '" << type << "'" | 
| Chris@320 | 77                             << std::endl; | 
| Chris@320 | 78             break; | 
| Chris@320 | 79         } | 
| Chris@320 | 80 | 
| Chris@320 | 81 	++i; | 
| Chris@320 | 82     } | 
| Chris@320 | 83 | 
| Chris@320 | 84     queue->postMessage(message); | 
| Chris@320 | 85     return 0; | 
| Chris@320 | 86 } | 
| Chris@320 | 87 | 
| Chris@320 | 88 #endif | 
| Chris@320 | 89 | 
| Chris@320 | 90 OSCQueue::OSCQueue() : | 
| Chris@320 | 91 #ifdef HAVE_LIBLO | 
| Chris@320 | 92     m_thread(0), | 
| Chris@320 | 93 #endif | 
| Chris@320 | 94     m_buffer(OSC_MESSAGE_QUEUE_SIZE) | 
| Chris@320 | 95 { | 
| Chris@408 | 96     Profiler profiler("OSCQueue::OSCQueue"); | 
| Chris@408 | 97 | 
| Chris@320 | 98 #ifdef HAVE_LIBLO | 
| Chris@320 | 99     m_thread = lo_server_thread_new(NULL, oscError); | 
| Chris@320 | 100 | 
| Chris@320 | 101     lo_server_thread_add_method(m_thread, NULL, NULL, | 
| Chris@320 | 102                                 oscMessageHandler, this); | 
| Chris@320 | 103 | 
| Chris@320 | 104     lo_server_thread_start(m_thread); | 
| Chris@320 | 105 | 
| Chris@320 | 106     std::cout << "OSCQueue::OSCQueue: Base OSC URL is " | 
| Chris@320 | 107               << lo_server_thread_get_url(m_thread) << std::endl; | 
| Chris@320 | 108 #endif | 
| Chris@320 | 109 } | 
| Chris@320 | 110 | 
| Chris@320 | 111 OSCQueue::~OSCQueue() | 
| Chris@320 | 112 { | 
| Chris@320 | 113 #ifdef HAVE_LIBLO | 
| Chris@320 | 114     if (m_thread) { | 
| Chris@320 | 115         lo_server_thread_stop(m_thread); | 
| Chris@320 | 116     } | 
| Chris@320 | 117 #endif | 
| Chris@320 | 118 | 
| Chris@320 | 119     while (m_buffer.getReadSpace() > 0) { | 
| Chris@320 | 120         delete m_buffer.readOne(); | 
| Chris@320 | 121     } | 
| Chris@320 | 122 } | 
| Chris@320 | 123 | 
| Chris@320 | 124 bool | 
| Chris@320 | 125 OSCQueue::isOK() const | 
| Chris@320 | 126 { | 
| Chris@320 | 127 #ifdef HAVE_LIBLO | 
| Chris@320 | 128     return (m_thread != 0); | 
| Chris@320 | 129 #else | 
| Chris@320 | 130     return false; | 
| Chris@320 | 131 #endif | 
| Chris@320 | 132 } | 
| Chris@320 | 133 | 
| Chris@320 | 134 QString | 
| Chris@320 | 135 OSCQueue::getOSCURL() const | 
| Chris@320 | 136 { | 
| Chris@320 | 137     QString url = ""; | 
| Chris@320 | 138 #ifdef HAVE_LIBLO | 
| Chris@320 | 139     url = lo_server_thread_get_url(m_thread); | 
| Chris@320 | 140 #endif | 
| Chris@320 | 141     return url; | 
| Chris@320 | 142 } | 
| Chris@320 | 143 | 
| Chris@320 | 144 size_t | 
| Chris@320 | 145 OSCQueue::getMessagesAvailable() const | 
| Chris@320 | 146 { | 
| Chris@320 | 147     return m_buffer.getReadSpace(); | 
| Chris@320 | 148 } | 
| Chris@320 | 149 | 
| Chris@320 | 150 OSCMessage | 
| Chris@320 | 151 OSCQueue::readMessage() | 
| Chris@320 | 152 { | 
| Chris@320 | 153     OSCMessage *message = m_buffer.readOne(); | 
| Chris@320 | 154     OSCMessage rmessage = *message; | 
| Chris@320 | 155     delete message; | 
| Chris@320 | 156     return rmessage; | 
| Chris@320 | 157 } | 
| Chris@320 | 158 | 
| Chris@320 | 159 void | 
| Chris@320 | 160 OSCQueue::postMessage(OSCMessage message) | 
| Chris@320 | 161 { | 
| Chris@320 | 162     int count = 0, max = 5; | 
| Chris@320 | 163     while (m_buffer.getWriteSpace() == 0) { | 
| Chris@320 | 164         if (count == max) { | 
| Chris@320 | 165             std::cerr << "ERROR: OSCQueue::postMessage: OSC message queue is full and not clearing -- abandoning incoming message" << std::endl; | 
| Chris@320 | 166             return; | 
| Chris@320 | 167         } | 
| Chris@320 | 168         std::cerr << "WARNING: OSCQueue::postMessage: OSC message queue (capacity " << m_buffer.getSize() << " is full!" << std::endl; | 
| Chris@320 | 169         std::cerr << "Waiting for something to be processed" << std::endl; | 
| Chris@320 | 170 #ifdef _WIN32 | 
| Chris@320 | 171         Sleep(1); | 
| Chris@320 | 172 #else | 
| Chris@320 | 173         sleep(1); | 
| Chris@320 | 174 #endif | 
| Chris@320 | 175         count++; | 
| Chris@320 | 176     } | 
| Chris@320 | 177 | 
| Chris@320 | 178     OSCMessage *mp = new OSCMessage(message); | 
| Chris@320 | 179     m_buffer.write(&mp, 1); | 
| Chris@320 | 180     std::cerr << "OSCQueue::postMessage: Posted OSC message: target " | 
| Chris@320 | 181               << message.getTarget() << ", target data " << message.getTargetData() | 
| Chris@320 | 182               << ", method " << message.getMethod().toStdString() << std::endl; | 
| Chris@320 | 183     emit messagesAvailable(); | 
| Chris@320 | 184 } | 
| Chris@320 | 185 | 
| Chris@320 | 186 bool | 
| Chris@320 | 187 OSCQueue::parseOSCPath(QString path, int &target, int &targetData, | 
| Chris@320 | 188                        QString &method) | 
| Chris@320 | 189 { | 
| Chris@320 | 190     while (path.startsWith("/")) { | 
| Chris@320 | 191 	path = path.right(path.length()-1); | 
| Chris@320 | 192     } | 
| Chris@320 | 193 | 
| Chris@320 | 194     int i = 0; | 
| Chris@320 | 195 | 
| Chris@320 | 196     bool ok = false; | 
| Chris@320 | 197     target = path.section('/', i, i).toInt(&ok); | 
| Chris@320 | 198 | 
| Chris@320 | 199     if (!ok) { | 
| Chris@320 | 200         target = 0; | 
| Chris@320 | 201     } else { | 
| Chris@320 | 202         ++i; | 
| Chris@320 | 203         targetData = path.section('/', i, i).toInt(&ok); | 
| Chris@320 | 204         if (!ok) { | 
| Chris@320 | 205             targetData = 0; | 
| Chris@320 | 206         } else { | 
| Chris@320 | 207             ++i; | 
| Chris@320 | 208         } | 
| Chris@320 | 209     } | 
| Chris@320 | 210 | 
| Chris@320 | 211     method = path.section('/', i, -1); | 
| Chris@320 | 212 | 
| Chris@320 | 213     if (method.contains('/')) { | 
| Chris@320 | 214         std::cerr << "ERROR: OSCQueue::parseOSCPath: malformed path \"" | 
| Chris@320 | 215                   << path.toStdString() << "\" (should be target/data/method or " | 
| Chris@320 | 216                   << "target/method or method, where target and data " | 
| Chris@320 | 217                   << "are numeric)" << std::endl; | 
| Chris@320 | 218         return false; | 
| Chris@320 | 219     } | 
| Chris@320 | 220 | 
| Chris@320 | 221     std::cerr << "OSCQueue::parseOSCPath: good path \"" << path.toStdString() | 
| Chris@320 | 222               << "\"" << std::endl; | 
| Chris@320 | 223 | 
| Chris@320 | 224     return true; | 
| Chris@320 | 225 } | 
| Chris@320 | 226 |