svcore  1.9
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 <unistd.h>
27 
28 #define OSC_MESSAGE_QUEUE_SIZE 1023
29 
30 #ifdef HAVE_LIBLO
31 
32 void
33 OSCQueue::oscError(int num, const char *msg, const char *path)
34 {
35  cerr << "ERROR: OSCQueue::oscError: liblo server error " << num
36  << " in path " << path << ": " << msg << endl;
37 }
38 
39 int
40 OSCQueue::oscMessageHandler(const char *path, const char *types, lo_arg **argv,
41  int argc, lo_message, void *user_data)
42 {
43  OSCQueue *queue = static_cast<OSCQueue *>(user_data);
44 
45  int target;
46  int targetData;
47  QString method;
48 
49  if (!queue->parseOSCPath(path, target, targetData, method)) {
50  return 1;
51  }
52 
53  OSCMessage message;
54  message.setTarget(target);
55  message.setTargetData(targetData);
56  message.setMethod(method);
57 
58  int i = 0;
59 
60  while (types && i < argc && types[i]) {
61 
62  char type = types[i];
63  lo_arg *arg = argv[i];
64 
65  switch (type) {
66  case 'i': message.addArg(arg->i); break;
67  // This conversion fails to compile in 64-bit environments
68  // at present, and we don't use the h type anyway so we
69  // can safely omit it
70 // case 'h': message.addArg(arg->h); break;
71  case 'f': message.addArg(arg->f); break;
72  case 'd': message.addArg(arg->d); break;
73  case 'c': message.addArg(arg->c); break;
74  case 't': message.addArg(arg->i); break;
75  case 's': message.addArg(&arg->s); break;
76  default: cerr << "WARNING: OSCQueue::oscMessageHandler: "
77  << "Unsupported OSC type '" << type << "'"
78  << endl;
79  break;
80  }
81 
82  ++i;
83  }
84 
85  queue->postMessage(message);
86  return 0;
87 }
88 
89 #endif
90 
92 #ifdef HAVE_LIBLO
93  m_thread(0),
94 #endif
95  m_buffer(OSC_MESSAGE_QUEUE_SIZE)
96 {
97  Profiler profiler("OSCQueue::OSCQueue");
98 
99 #ifdef HAVE_LIBLO
100  m_thread = lo_server_thread_new(NULL, oscError);
101 
102  lo_server_thread_add_method(m_thread, NULL, NULL,
103  oscMessageHandler, this);
104 
105  lo_server_thread_start(m_thread);
106 
107  cout << "OSCQueue::OSCQueue: Base OSC URL is "
108  << lo_server_thread_get_url(m_thread) << endl;
109 #endif
110 }
111 
113 {
114 #ifdef HAVE_LIBLO
115  if (m_thread) {
116  lo_server_thread_stop(m_thread);
117  }
118 #endif
119 
120  while (m_buffer.getReadSpace() > 0) {
121  delete m_buffer.readOne();
122  }
123 }
124 
125 bool
127 {
128 #ifdef HAVE_LIBLO
129  return (m_thread != 0);
130 #else
131  return false;
132 #endif
133 }
134 
135 QString
137 {
138  QString url = "";
139 #ifdef HAVE_LIBLO
140  url = lo_server_thread_get_url(m_thread);
141 #endif
142  return url;
143 }
144 
145 int
147 {
148  return m_buffer.getReadSpace();
149 }
150 
153 {
154  OSCMessage *message = m_buffer.readOne();
155  OSCMessage rmessage = *message;
156  delete message;
157  return rmessage;
158 }
159 
160 void
162 {
163  int count = 0, max = 5;
164  while (m_buffer.getWriteSpace() == 0) {
165  if (count == max) {
166  cerr << "ERROR: OSCQueue::postMessage: OSC message queue is full and not clearing -- abandoning incoming message" << endl;
167  return;
168  }
169  cerr << "WARNING: OSCQueue::postMessage: OSC message queue (capacity " << m_buffer.getSize() << " is full!" << endl;
170  SVDEBUG << "Waiting for something to be processed" << endl;
171 #ifdef _WIN32
172  Sleep(1);
173 #else
174  sleep(1);
175 #endif
176  count++;
177  }
178 
179  OSCMessage *mp = new OSCMessage(message);
180  m_buffer.write(&mp, 1);
181  SVDEBUG << "OSCQueue::postMessage: Posted OSC message: target "
182  << message.getTarget() << ", target data " << message.getTargetData()
183  << ", method " << message.getMethod() << endl;
184  emit messagesAvailable();
185 }
186 
187 bool
188 OSCQueue::parseOSCPath(QString path, int &target, int &targetData,
189  QString &method)
190 {
191  while (path.startsWith("/")) {
192  path = path.right(path.length()-1);
193  }
194 
195  int i = 0;
196 
197  bool ok = false;
198  target = path.section('/', i, i).toInt(&ok);
199 
200  if (!ok) {
201  target = 0;
202  } else {
203  ++i;
204  targetData = path.section('/', i, i).toInt(&ok);
205  if (!ok) {
206  targetData = 0;
207  } else {
208  ++i;
209  }
210  }
211 
212  method = path.section('/', i, -1);
213 
214  if (method.contains('/')) {
215  cerr << "ERROR: OSCQueue::parseOSCPath: malformed path \""
216  << path << "\" (should be target/data/method or "
217  << "target/method or method, where target and data "
218  << "are numeric)" << endl;
219  return false;
220  }
221 
222  SVDEBUG << "OSCQueue::parseOSCPath: good path \"" << path << "\"" << endl;
223 
224  return true;
225 }
226 
RingBuffer< OSCMessage * > m_buffer
Definition: OSCQueue.h:65
OSCMessage readMessage()
Definition: OSCQueue.cpp:152
void addArg(QVariant arg)
Definition: OSCMessage.cpp:36
virtual ~OSCQueue()
Definition: OSCQueue.cpp:112
int getTarget() const
Definition: OSCMessage.h:39
int getMessagesAvailable() const
Definition: OSCQueue.cpp:146
void messagesAvailable()
#define OSC_MESSAGE_QUEUE_SIZE
Definition: OSCQueue.cpp:28
T readOne(int R=0)
Read one sample from the buffer, for reader R.
Definition: RingBuffer.h:392
lo_server_thread m_thread
Definition: OSCQueue.h:55
QString getMethod() const
Definition: OSCMessage.h:45
OSCQueue()
Definition: OSCQueue.cpp:91
int write(const T *source, int n)
Write n samples to the buffer.
Definition: RingBuffer.h:491
QString getOSCURL() const
Definition: OSCQueue.cpp:136
#define SVDEBUG
Definition: Debug.h:42
static int oscMessageHandler(const char *, const char *, lo_arg **, int, lo_message, void *)
Definition: OSCQueue.cpp:40
void setMethod(QString method)
Definition: OSCMessage.h:44
void setTarget(const int &target)
Definition: OSCMessage.h:38
static void oscError(int, const char *, const char *)
Definition: OSCQueue.cpp:33
void postMessage(OSCMessage)
Definition: OSCQueue.cpp:161
int getTargetData() const
Definition: OSCMessage.h:42
void setTargetData(const int &targetData)
Definition: OSCMessage.h:41
int getReadSpace(int R=0) const
Return the amount of data available for reading by reader R, in samples.
Definition: RingBuffer.h:274
int getWriteSpace() const
Return the amount of space available for writing, in samples.
Definition: RingBuffer.h:292
bool isOK() const
Definition: OSCQueue.cpp:126
bool parseOSCPath(QString path, int &target, int &targetData, QString &method)
Definition: OSCQueue.cpp:188
Profile point instance class.
Definition: Profiler.h:86
int getSize() const
Return the total capacity of the ring buffer in samples.
Definition: RingBuffer.h:220