annotate data/osc/OSCQueue.cpp @ 1678:1078f0ef3012 single-point

Make it possible to start queue without port (even when compiled in)
author Chris Cannam
date Thu, 28 Mar 2019 13:37:09 +0000
parents 813dadf7c086
children c077a97d055f
rev   line source
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@1218 31 #include <unistd.h>
Chris@1218 32
Chris@1665 33 #include <QThread>
Chris@1665 34
Chris@320 35 void
Chris@320 36 OSCQueue::oscError(int num, const char *msg, const char *path)
Chris@320 37 {
Chris@843 38 cerr << "ERROR: OSCQueue::oscError: liblo server error " << num
Chris@1429 39 << " in path " << path << ": " << msg << endl;
Chris@320 40 }
Chris@320 41
Chris@320 42 int
Chris@320 43 OSCQueue::oscMessageHandler(const char *path, const char *types, lo_arg **argv,
Chris@320 44 int argc, lo_message, void *user_data)
Chris@320 45 {
Chris@320 46 OSCQueue *queue = static_cast<OSCQueue *>(user_data);
Chris@320 47
Chris@320 48 int target;
Chris@320 49 int targetData;
Chris@320 50 QString method;
Chris@320 51
Chris@320 52 if (!queue->parseOSCPath(path, target, targetData, method)) {
Chris@1429 53 return 1;
Chris@320 54 }
Chris@320 55
Chris@320 56 OSCMessage message;
Chris@320 57 message.setTarget(target);
Chris@320 58 message.setTargetData(targetData);
Chris@320 59 message.setMethod(method);
Chris@320 60
Chris@320 61 int i = 0;
Chris@320 62
Chris@320 63 while (types && i < argc && types[i]) {
Chris@320 64
Chris@320 65 char type = types[i];
Chris@320 66 lo_arg *arg = argv[i];
Chris@320 67
Chris@320 68 switch (type) {
Chris@320 69 case 'i': message.addArg(arg->i); break;
Chris@320 70 // This conversion fails to compile in 64-bit environments
Chris@320 71 // at present, and we don't use the h type anyway so we
Chris@320 72 // can safely omit it
Chris@320 73 // case 'h': message.addArg(arg->h); break;
Chris@320 74 case 'f': message.addArg(arg->f); break;
Chris@320 75 case 'd': message.addArg(arg->d); break;
Chris@320 76 case 'c': message.addArg(arg->c); break;
Chris@320 77 case 't': message.addArg(arg->i); break;
Chris@320 78 case 's': message.addArg(&arg->s); break;
Chris@843 79 default: cerr << "WARNING: OSCQueue::oscMessageHandler: "
Chris@320 80 << "Unsupported OSC type '" << type << "'"
Chris@843 81 << endl;
Chris@320 82 break;
Chris@320 83 }
Chris@320 84
Chris@1429 85 ++i;
Chris@320 86 }
Chris@320 87
Chris@320 88 queue->postMessage(message);
Chris@320 89 return 0;
Chris@320 90 }
Chris@320 91
Chris@320 92 #endif
Chris@320 93
Chris@1678 94 OSCQueue::OSCQueue(bool withNetworkPort) :
Chris@320 95 #ifdef HAVE_LIBLO
Chris@1582 96 m_thread(nullptr),
Chris@320 97 #endif
Chris@1678 98 m_withPort(withNetworkPort),
Chris@320 99 m_buffer(OSC_MESSAGE_QUEUE_SIZE)
Chris@320 100 {
Chris@408 101 Profiler profiler("OSCQueue::OSCQueue");
Chris@408 102
Chris@320 103 #ifdef HAVE_LIBLO
Chris@1678 104 if (m_withPort) {
Chris@1678 105 m_thread = lo_server_thread_new(nullptr, oscError);
Chris@320 106
Chris@1678 107 lo_server_thread_add_method(m_thread, nullptr, nullptr,
Chris@1678 108 oscMessageHandler, this);
Chris@320 109
Chris@1678 110 lo_server_thread_start(m_thread);
Chris@320 111
Chris@1678 112 SVDEBUG << "OSCQueue::OSCQueue: Started OSC thread, URL is "
Chris@1678 113 << lo_server_thread_get_url(m_thread) << endl;
Chris@1678 114
Chris@1678 115 cout << "OSCQueue::OSCQueue: Base OSC URL is "
Chris@1678 116 << lo_server_thread_get_url(m_thread) << endl;
Chris@1678 117 }
Chris@1678 118 #else
Chris@1678 119 if (m_withPort) {
Chris@1678 120 SVDEBUG << "OSCQueue::OSCQueue: Note: OSC port support not "
Chris@1678 121 << "compiled in; not opening port, falling back to "
Chris@1678 122 << "internal-only queue" << endl;
Chris@1678 123 m_withPort = false;
Chris@1678 124 }
Chris@320 125 #endif
Chris@320 126 }
Chris@320 127
Chris@320 128 OSCQueue::~OSCQueue()
Chris@320 129 {
Chris@320 130 #ifdef HAVE_LIBLO
Chris@320 131 if (m_thread) {
Chris@320 132 lo_server_thread_stop(m_thread);
Chris@320 133 }
Chris@320 134 #endif
Chris@320 135
Chris@320 136 while (m_buffer.getReadSpace() > 0) {
Chris@320 137 delete m_buffer.readOne();
Chris@320 138 }
Chris@320 139 }
Chris@320 140
Chris@320 141 bool
Chris@320 142 OSCQueue::isOK() const
Chris@320 143 {
Chris@1678 144 if (!m_withPort) {
Chris@1678 145 return true;
Chris@1678 146 } else {
Chris@320 147 #ifdef HAVE_LIBLO
Chris@1678 148 return (m_thread != nullptr);
Chris@320 149 #else
Chris@1678 150 return false;
Chris@320 151 #endif
Chris@1678 152 }
Chris@320 153 }
Chris@320 154
Chris@320 155 QString
Chris@320 156 OSCQueue::getOSCURL() const
Chris@320 157 {
Chris@320 158 QString url = "";
Chris@320 159 #ifdef HAVE_LIBLO
Chris@1678 160 if (m_thread) {
Chris@1678 161 url = lo_server_thread_get_url(m_thread);
Chris@1678 162 }
Chris@320 163 #endif
Chris@320 164 return url;
Chris@320 165 }
Chris@320 166
Chris@929 167 int
Chris@320 168 OSCQueue::getMessagesAvailable() const
Chris@320 169 {
Chris@320 170 return m_buffer.getReadSpace();
Chris@320 171 }
Chris@320 172
Chris@320 173 OSCMessage
Chris@320 174 OSCQueue::readMessage()
Chris@320 175 {
Chris@320 176 OSCMessage *message = m_buffer.readOne();
Chris@320 177 OSCMessage rmessage = *message;
Chris@320 178 delete message;
Chris@1665 179 SVDEBUG << "OSCQueue::readMessage: In thread "
Chris@1665 180 << QThread::currentThreadId() << ": message follows:\n"
Chris@1665 181 << rmessage.toString() << endl;
Chris@320 182 return rmessage;
Chris@320 183 }
Chris@320 184
Chris@320 185 void
Chris@320 186 OSCQueue::postMessage(OSCMessage message)
Chris@320 187 {
Chris@320 188 int count = 0, max = 5;
Chris@320 189 while (m_buffer.getWriteSpace() == 0) {
Chris@320 190 if (count == max) {
Chris@843 191 cerr << "ERROR: OSCQueue::postMessage: OSC message queue is full and not clearing -- abandoning incoming message" << endl;
Chris@320 192 return;
Chris@320 193 }
Chris@843 194 cerr << "WARNING: OSCQueue::postMessage: OSC message queue (capacity " << m_buffer.getSize() << " is full!" << endl;
Chris@690 195 SVDEBUG << "Waiting for something to be processed" << endl;
Chris@320 196 #ifdef _WIN32
Chris@320 197 Sleep(1);
Chris@320 198 #else
Chris@320 199 sleep(1);
Chris@320 200 #endif
Chris@320 201 count++;
Chris@320 202 }
Chris@320 203
Chris@320 204 OSCMessage *mp = new OSCMessage(message);
Chris@320 205 m_buffer.write(&mp, 1);
Chris@690 206 SVDEBUG << "OSCQueue::postMessage: Posted OSC message: target "
Chris@1678 207 << message.getTarget() << ", target data "
Chris@1678 208 << message.getTargetData() << ", method "
Chris@1678 209 << message.getMethod() << endl;
Chris@320 210 emit messagesAvailable();
Chris@320 211 }
Chris@320 212
Chris@320 213 bool
Chris@320 214 OSCQueue::parseOSCPath(QString path, int &target, int &targetData,
Chris@320 215 QString &method)
Chris@320 216 {
Chris@320 217 while (path.startsWith("/")) {
Chris@1429 218 path = path.right(path.length()-1);
Chris@320 219 }
Chris@320 220
Chris@320 221 int i = 0;
Chris@320 222
Chris@320 223 bool ok = false;
Chris@320 224 target = path.section('/', i, i).toInt(&ok);
Chris@320 225
Chris@320 226 if (!ok) {
Chris@320 227 target = 0;
Chris@320 228 } else {
Chris@320 229 ++i;
Chris@320 230 targetData = path.section('/', i, i).toInt(&ok);
Chris@320 231 if (!ok) {
Chris@320 232 targetData = 0;
Chris@320 233 } else {
Chris@320 234 ++i;
Chris@320 235 }
Chris@320 236 }
Chris@320 237
Chris@320 238 method = path.section('/', i, -1);
Chris@320 239
Chris@320 240 if (method.contains('/')) {
Chris@843 241 cerr << "ERROR: OSCQueue::parseOSCPath: malformed path \""
Chris@686 242 << path << "\" (should be target/data/method or "
Chris@320 243 << "target/method or method, where target and data "
Chris@843 244 << "are numeric)" << endl;
Chris@320 245 return false;
Chris@320 246 }
Chris@320 247
Chris@1678 248 SVDEBUG << "OSCQueue::parseOSCPath: good path \"" << path
Chris@1678 249 << "\"" << endl;
Chris@320 250
Chris@320 251 return true;
Chris@320 252 }
Chris@320 253