annotate data/osc/OSCQueue.cpp @ 1833:21c792334c2e sensible-delimited-data-strings

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