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
|