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