1 /*
2 Copyright (C) 2008, 2009 Andres Cabrera
3 mantaraya36@gmail.com
4
5 This file is part of CsoundQt.
6
7 CsoundQt is free software; you can redistribute it
8 and/or modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 CsoundQt is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with Csound; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA
21 */
22
23 #ifdef USE_QT5
24 #include <QtConcurrent>
25 #endif
26
27 #include <QThread>
28
29 #ifdef Q_OS_WIN
30 #include <ole2.h> // for OleInitialize() FLTK bug workaround
31 #endif
32
33 #ifdef CSOUND6
34 #include "csound_standard_types.h"
35 #endif
36
37 #include "csoundengine.h"
38 #include "widgetlayout.h"
39 #include "console.h"
40 #include "qutescope.h" // Needed for passing the ud to the scope for display data
41 #include "qutegraph.h" // Needed for passing the ud to the graph for display data
42 #include "midihandler.h"
43
44 // #define QDEBUG qDebug() << __FUNCTION__ << ":"
45
CsoundEngine(ConfigLists * configlists)46 CsoundEngine::CsoundEngine(ConfigLists *configlists) :
47 m_options(configlists)
48 {
49 QMutexLocker locker(&m_playMutex);
50 ud = new CsoundUserData();
51 ud->csEngine = this;
52 ud->csound = nullptr;
53 ud->perfThread = nullptr;
54 ud->flags = QCS_NO_FLAGS;
55 ud->mouseValues.resize(6); // For _MouseX _MouseY _MouseRelX _MouseRelY _MouseBut1 and _MouseBut2 channels
56 ud->wl = nullptr;
57 ud->midiBuffer = nullptr;
58 ud->virtualMidiBuffer = nullptr;
59 ud->playMutex = &m_playMutex;
60 #ifdef QCS_PYTHONQT
61 ud->m_pythonCallback = "";
62 #endif
63 m_consoleBufferSize = 0;
64 m_recording = false;
65 #ifndef QCS_DESTROY_CSOUND
66 ud->csound=csoundCreate( (void *) ud);
67 #ifdef CSOUND6
68 ud->midiBuffer = csoundCreateCircularBuffer(ud->csound, 1024, sizeof(unsigned char));
69 Q_ASSERT(ud->midiBuffer);
70 #endif
71 #endif
72 eventQueue.resize(QCS_MAX_EVENTS);
73 eventTimeStamps.resize(QCS_MAX_EVENTS);
74 eventQueueSize = 0;
75 m_refreshTime = QCS_QUEUETIMER_DEFAULT_TIME; // TODO Eventually allow this to be changed
76 ud->msgRefreshTime = m_refreshTime*1000;
77 ud->runDispatcher = true;
78 m_msgUpdateThread = QtConcurrent::run(messageListDispatcher, (void *) ud);
79 #ifdef QCS_DEBUGGER
80 m_debugging = false;
81 #endif
82 }
83
~CsoundEngine()84 CsoundEngine::~CsoundEngine()
85 {
86 disconnect(SIGNAL(passMessages(QString)),0,0);
87 disconnect(this, 0,0,0);
88 ud->runDispatcher = false;
89 m_msgUpdateThread.waitForFinished(); // Join the message thread
90 stop();
91 #ifndef QCS_DESTROY_CSOUND
92 csoundDestroyCircularBuffer(ud->csound, ud->midiBuffer);
93 csoundDestroy(ud->csound);
94 #endif
95 delete ud;
96 }
97
98 #ifndef CSOUND6
99
messageCallbackThread(CSOUND * csound,int,const char * fmt,va_list args)100 void CsoundEngine::messageCallbackThread(CSOUND *csound,
101 int /*attr*/,
102 const char *fmt,
103 va_list args)
104 {
105
106 CsoundUserData *ud = (CsoundUserData *) csoundGetHostData(csound);
107 qDebug() << msg;
108 if (!(ud->flags & QCS_NO_CONSOLE_MESSAGES)) {
109 QString msg;
110 msg = msg.vsprintf(fmt, args);
111 if (msg.isEmpty()) {
112 return;
113 }
114 ud->csEngine->queueMessage(msg);
115 }
116 }
117 #endif
118
119 #ifndef CSOUND6
outputValueCallback(CSOUND * csound,const char * channelName,MYFLT value)120 void CsoundEngine::outputValueCallback (CSOUND *csound,
121 const char *channelName,
122 MYFLT value)
123 {
124 // Called by the csound running engine when 'outvalue' opcode is used
125 // To pass data from Csound to CsoundQt
126 CsoundUserData *ud = (CsoundUserData *) csoundGetHostData(csound);
127 QString name = QString(channelName);
128 if (name.startsWith('$')) {
129 QString channelName = name;
130 channelName.chop(name.size() - (int) value + 1);
131 QString sValue = name;
132 sValue = sValue.right(name.size() - (int) value);
133 channelName.remove(0,1);
134 ud->csEngine->passOutString(channelName, sValue);
135 }
136 else {
137 ud->csEngine->passOutValue(name, value);
138 }
139 }
140
inputValueCallback(CSOUND * csound,const char * channelName,MYFLT * value)141 void CsoundEngine::inputValueCallback(CSOUND *csound,
142 const char *channelName,
143 MYFLT *value)
144 {
145 // Called by the csound running engine when 'invalue' opcode is used
146 // To pass data from qutecsound to Csound
147 CsoundUserData *ud = (CsoundUserData *) csoundGetHostData(csound);
148 QString name = QString(channelName);
149 if (name.startsWith('$')) { // channel is a string channel
150 char *string = (char *) value;
151 QString newValue = ud->wl->getStringForChannel(name.mid(1));
152 int maxlen = csoundGetStrVarMaxLen(csound);
153 strncpy(string, newValue.toLocal8Bit(), maxlen);
154 }
155 else { // Not a string channel
156 //FIXME check if mouse tracking is active, and move this from here
157 if (name == "_MouseX") {
158 *value = (MYFLT) ud->mouseValues[0];
159 }
160 else if (name == "_MouseY") {
161 *value = (MYFLT) ud->mouseValues[1];
162 }
163 else if(name == "_MouseRelX") {
164 *value = (MYFLT) ud->mouseValues[2];
165 }
166 else if(name == "_MouseRelY") {
167 *value = (MYFLT) ud->mouseValues[3];
168 }
169 else if(name == "_MouseBut1") {
170 *value = (MYFLT) ud->mouseValues[4];
171 }
172 else if(name == "_MouseBut2") {
173 *value = (MYFLT) ud->mouseValues[5];
174 }
175 else {
176 *value = (MYFLT) ud->wl->getValueForChannel(name);
177 }
178 }
179 }
180 #else
outputValueCallback(CSOUND * csound,const char * channelName,void * channelValuePtr,const void * channelType)181 void CsoundEngine::outputValueCallback (CSOUND *csound,
182 const char *channelName,
183 void *channelValuePtr,
184 const void *channelType)
185 {
186 // Called by the csound running engine when 'outvalue' opcode is used
187 // To pass data from Csound to CsoundQt
188 CsoundUserData *ud = (CsoundUserData *) csoundGetHostData(csound);
189 if (channelType == &CS_VAR_TYPE_S) {
190 ud->csEngine->passOutString(channelName, (const char *) channelValuePtr);
191 }
192 else if (channelType == &CS_VAR_TYPE_K){
193 ud->csEngine->passOutValue(channelName, *((MYFLT *)channelValuePtr));
194 } else {
195 QDEBUG << "Unsupported type";
196 }
197 }
198
inputValueCallback(CSOUND * csound,const char * channelName,void * channelValuePtr,const void * channelType)199 void CsoundEngine::inputValueCallback (CSOUND *csound,
200 const char *channelName,
201 void *channelValuePtr,
202 const void *channelType)
203 {
204 // Called by the csound running engine when 'invalue' opcode is used
205 // To pass data from CsoundQt to Csound
206 CsoundUserData *ud = (CsoundUserData *) csoundGetHostData(csound);
207 if (channelType == &CS_VAR_TYPE_S) { // channel is a string channel
208 char *string = (char *) channelValuePtr;
209 QString newValue = ud->wl->getStringForChannel(channelName);
210 int maxlen = csoundGetChannelDatasize(csound, channelName);
211 if (newValue.size() > 0) {
212 strncpy(string, newValue.toLocal8Bit(), maxlen - 1);
213 string[newValue.size()] = '\0';
214 } else {
215 string[0] = '\0';
216 }
217 }
218 else if (channelType == &CS_VAR_TYPE_K) { // Not a string channel
219 //FIXME check if mouse tracking is active, and move this from here
220 MYFLT *value = (MYFLT *) channelValuePtr;
221 if(!strcmp(channelName, "_Mouse")) {
222 const char *suffix = &channelName[6];
223 if (!strcmp(suffix, "X")) {
224 *value = (MYFLT) ud->mouseValues[0];
225 }
226 else if (!strcmp(suffix, "Y")) {
227 *value = (MYFLT) ud->mouseValues[1];
228 }
229 else if(!strcmp(suffix, "RelX")) {
230 *value = (MYFLT) ud->mouseValues[2];
231 }
232 else if(!strcmp(suffix, "RelY")) {
233 *value = (MYFLT) ud->mouseValues[3];
234 }
235 else if(!strcmp(suffix, "But1")) {
236 *value = (MYFLT) ud->mouseValues[4];
237 }
238 else if(!strcmp(suffix, "But2")) {
239 *value = (MYFLT) ud->mouseValues[5];
240 }
241 }
242 else {
243 // QString name(channelName);
244 *value = (MYFLT) ud->wl->getValueForChannel(channelName);
245 }
246 } else {
247 QDEBUG << "Unsupported type";
248 }
249 }
250
midiInOpenCb(CSOUND * csound,void ** ud,const char * devName)251 int CsoundEngine::midiInOpenCb(CSOUND *csound, void **ud, const char *devName)
252 {
253 CsoundUserData *userData = (CsoundUserData *) csoundGetHostData(csound);
254 Q_UNUSED(devName);
255 Q_ASSERT(userData->midiBuffer);
256 if (userData) {
257 *ud = userData;
258 } else {
259 qDebug() << "Error! userData not set for midiInOpenCb";
260 return CSOUND_ERROR;
261 }
262 return CSOUND_SUCCESS;
263 }
264
midiReadCb(CSOUND * csound,void * ud_,unsigned char * buf,int nBytes)265 int CsoundEngine::midiReadCb(CSOUND *csound, void *ud_, unsigned char *buf, int nBytes)
266 {
267 CsoundUserData *ud = (CsoundUserData *) ud_;
268 Q_UNUSED(csound);
269 int count, countVirtual;
270 count = csoundReadCircularBuffer(ud->csound, ud->midiBuffer, buf, nBytes);
271 countVirtual = csoundReadCircularBuffer(ud->csound, ud->virtualMidiBuffer, buf + count, nBytes - count);
272 return count + countVirtual;
273 }
274
midiInCloseCb(CSOUND * csound,void * ud)275 int CsoundEngine::midiInCloseCb(CSOUND *csound, void *ud)
276 {
277 Q_UNUSED(csound);
278 Q_UNUSED(ud);
279 return CSOUND_SUCCESS;
280 }
281
midiOutOpenCb(CSOUND * csound,void ** ud,const char * devName)282 int CsoundEngine::midiOutOpenCb(CSOUND *csound, void **ud, const char *devName)
283 {
284 CsoundUserData *userData = (CsoundUserData *) csoundGetHostData(csound);
285 Q_UNUSED(devName);
286 Q_ASSERT(userData->midiBuffer);
287 if (userData) {
288 *ud = userData;
289 } else {
290 QDEBUG << "Error! userData not set for midiInOpenCb";
291 return CSOUND_ERROR;
292 }
293 return CSOUND_SUCCESS;
294
295 }
296
midiWriteCb(CSOUND * csound,void * ud_,const unsigned char * buf,int nBytes)297 int CsoundEngine::midiWriteCb(CSOUND *csound, void *ud_, const unsigned char *buf, int nBytes)
298 {
299 CsoundUserData *userData = (CsoundUserData *) ud_;
300 std::vector<unsigned char> message;
301 Q_UNUSED(csound);
302 message.push_back(*buf++);
303 nBytes--;
304 while (nBytes--) {
305 if (*buf & 0x80) {
306 userData->midiHandler->sendMidiOut(&message);
307 message.clear();
308 }
309 message.push_back(*buf++);
310 }
311 if (message.size() > 0) {
312 userData->midiHandler->sendMidiOut(&message);
313 }
314 return 0;
315 }
316
midiOutCloseCb(CSOUND * csound,void * ud)317 int CsoundEngine::midiOutCloseCb(CSOUND *csound, void *ud)
318 {
319 (void) csound;
320 (void) ud;
321 return 0;
322 }
323
midiErrorStringCb(int)324 const char *CsoundEngine::midiErrorStringCb(int)
325 {
326
327 return nullptr;
328 }
329
queueMidiIn(std::vector<unsigned char> * message)330 void CsoundEngine::queueMidiIn(std::vector< unsigned char > *message)
331 {
332 if (ud->midiBuffer) {
333 csoundWriteCircularBuffer(ud->csound, ud->midiBuffer, message->data(), message->size()* sizeof(unsigned char));
334 }
335 }
336
queueVirtualMidiIn(std::vector<unsigned char> & message)337 void CsoundEngine::queueVirtualMidiIn(std::vector< unsigned char > &message)
338 {
339 if (ud->virtualMidiBuffer) {
340 csoundWriteCircularBuffer(ud->csound, ud->virtualMidiBuffer, message.data(), message.size()* sizeof(unsigned char));
341 }
342 }
343
sendMidiOut(QVector<unsigned char> & message)344 void CsoundEngine::sendMidiOut(QVector<unsigned char> &message)
345 {
346 (void) message;
347 }
348
349 #endif
350
makeGraphCallback(CSOUND * csound,WINDAT * windat,const char *)351 void CsoundEngine::makeGraphCallback(CSOUND *csound, WINDAT *windat, const char * /*name*/)
352 {
353 CsoundUserData *ud = (CsoundUserData *) csoundGetHostData(csound);
354 // Csound reuses windat, so it is not guaranteed to be unique
355 // name seems not to be used.
356 ud->wl->appendCurve(windat);
357 }
358
drawGraphCallback(CSOUND * csound,WINDAT * windat)359 void CsoundEngine::drawGraphCallback(CSOUND *csound, WINDAT *windat)
360 {
361 CsoundUserData *udata = (CsoundUserData *) csoundGetHostData(csound);
362 // This callback paints data on curves
363 udata->wl->updateCurve(windat);
364 }
365
killGraphCallback(CSOUND * csound,WINDAT * windat)366 void CsoundEngine::killGraphCallback(CSOUND *csound, WINDAT *windat)
367 {
368 // When is this callback called??
369 CsoundUserData *udata = (CsoundUserData *) csoundGetHostData(csound);
370 udata->wl->killCurve(windat);
371 }
372
exitGraphCallback(CSOUND * csound)373 int CsoundEngine::exitGraphCallback(CSOUND *csound)
374 {
375 CsoundUserData *udata = (CsoundUserData *) csoundGetHostData(csound);
376 return udata->wl->killCurves(csound);
377 }
378
keyEventCallback(void * userData,void * p,unsigned int type)379 int CsoundEngine::keyEventCallback(void *userData,
380 void *p,
381 unsigned int type)
382 {
383 if (type != CSOUND_CALLBACK_KBD_EVENT && type != CSOUND_CALLBACK_KBD_TEXT) {
384 return 1;
385 }
386 CsoundUserData *ud = (CsoundUserData *) userData;
387 // WidgetLayout *wl = (WidgetLayout *) ud->wl;
388 int *value = (int *) p;
389 int key = ud->csEngine->popKeyPressEvent();
390 if (key >= 0) {
391 *value = key;
392 qDebug() << "Pressed: " << key;
393 }
394 else if (type & CSOUND_CALLBACK_KBD_EVENT) {
395 key = ud->csEngine->popKeyReleaseEvent();
396 if (key >= 0) {
397 *value = key | 0x10000;
398 qDebug() << "Released: " << key;
399 }
400 }
401 return 0;
402 }
403
csThread(void * data)404 void CsoundEngine::csThread(void *data)
405 {
406 CsoundUserData* udata = (CsoundUserData*)data;
407 if (!(udata->flags & QCS_NO_COPY_BUFFER)) {
408 MYFLT *outputBuffer = csoundGetSpout(udata->csound);
409 long numSamples = udata->outputBufferSize*udata->numChnls;
410 udata->audioOutputBuffer.putManyScaled(outputBuffer, numSamples,
411 1.0/udata->zerodBFS);
412 // for (int i = 0; i < udata->outputBufferSize*udata->numChnls; i++) {
413 // udata->audioOutputBuffer.put(outputBuffer[i]/ udata->zerodBFS);
414 // }
415 }
416 // udata->wl->getValues(&udata->channelNames,
417 // &udata->values,
418 // &udata->stringValues);
419 if (udata->enableWidgets) {
420 // csoundDeleteChannelList(udata->csound, *channelList);
421 writeWidgetValues(udata);
422 readWidgetValues(udata);
423 }
424 if (!(udata->flags & QCS_NO_RT_EVENTS)) {
425 udata->csEngine->processEventQueue();
426 }
427 #ifdef QCS_PYTHONQT
428 if (!(udata->flags & QCS_NO_PYTHON_CALLBACK)) {
429 if (!udata->m_pythonCallback.isEmpty()) {
430 if (udata->m_pythonCallbackCounter >= udata->m_pythonCallbackSkip) {
431 udata->m_pythonConsole->evaluate(udata->m_pythonCallback, false);
432 udata->m_pythonCallbackCounter = 0;
433 }
434 else {
435 udata->m_pythonCallbackCounter++;
436 }
437 }
438 }
439 #endif
440 }
441
readWidgetValues(CsoundUserData * ud)442 void CsoundEngine::readWidgetValues(CsoundUserData *ud)
443 {
444 MYFLT* pvalue;
445
446 if (ud->wl->valueMutex.tryLock()) {
447 QHash<QString, double>::const_iterator i;
448 QHash<QString, double>::const_iterator end = ud->wl->newValues.constEnd();
449 for (i = ud->wl->newValues.constBegin(); i != end; ++i) {
450 if(csoundGetChannelPtr(ud->csound, &pvalue, i.key().toLocal8Bit().constData(),
451 CSOUND_INPUT_CHANNEL | CSOUND_CONTROL_CHANNEL) == 0) {
452 *pvalue = (MYFLT) i.value();
453 }
454 }
455 ud->wl->newValues.clear();
456 ud->wl->valueMutex.unlock();
457 }
458 if (ud->wl->stringValueMutex.tryLock()) {
459 QHash<QString, QString>::const_iterator i;
460 QHash<QString, QString>::const_iterator end = ud->wl->newStringValues.constEnd();
461 for (i = ud->wl->newStringValues.constBegin(); i != end; ++i) {
462 csoundSetStringChannel(ud->csound, i.key().toLocal8Bit().data(),
463 i.value().toLocal8Bit().data());
464 }
465 ud->wl->newStringValues.clear();
466 ud->wl->stringValueMutex.unlock();
467 }
468 }
469
writeWidgetValues(CsoundUserData * ud)470 void CsoundEngine::writeWidgetValues(CsoundUserData *ud)
471 {
472 MYFLT* pvalue;
473 for (int i = 0; i < ud->outputChannelNames.size(); i++) {
474 if (ud->outputChannelNames[i] != ""
475 && csoundGetChannelPtr(ud->csound, &pvalue,
476 ud->outputChannelNames[i].toLocal8Bit().constData(),
477 CSOUND_OUTPUT_CHANNEL | CSOUND_CONTROL_CHANNEL) == 0) {
478 if(ud->previousOutputValues[i] != *pvalue) {
479 ud->wl->setValue(ud->outputChannelNames[i],*pvalue);
480 ud->previousOutputValues[i] = *pvalue;
481 }
482 }
483 }
484 for (int i = 0; i < ud->outputStringChannelNames.size(); i++) {
485 if (ud->outputStringChannelNames[i] != ""
486 && csoundGetChannelPtr(ud->csound, &pvalue,
487 ud->outputStringChannelNames[i].toLocal8Bit().constData(),
488 CSOUND_OUTPUT_CHANNEL | CSOUND_STRING_CHANNEL) == 0) {
489 char chanString[2048]; // large enough for long strings in displays
490 csoundGetStringChannel(ud->csound, ud->outputStringChannelNames[i].toLocal8Bit().constData(),
491 chanString);
492 if(ud->previousStringOutputValues[i] != QString(chanString)) {
493 ud->wl->setValue(ud->outputStringChannelNames[i],QString(chanString));
494 ud->previousStringOutputValues[i] = QString(chanString);
495 }
496 }
497 }
498 }
499
setWidgetLayout(WidgetLayout * wl)500 void CsoundEngine::setWidgetLayout(WidgetLayout *wl)
501 {
502 ud->wl = wl;
503 // connect(wl, SIGNAL(destroyed()), this, SLOT(widgetLayoutDestroyed()));
504 // Key presses on widget layout and console are passed to the engine
505 connect(wl, SIGNAL(keyPressed(int)),
506 this, SLOT(keyPressForCsound(int)));
507 connect(wl, SIGNAL(keyReleased(int)),
508 this, SLOT(keyReleaseForCsound(int)));
509
510 // Register scopes and graphs to pass them the engine's user data
511 connect(wl, SIGNAL(registerScope(QuteScope*)),
512 this,SLOT(registerScope(QuteScope*)));
513 connect(wl, SIGNAL(registerGraph(QuteGraph*)),
514 this,SLOT(registerGraph(QuteGraph*)));
515 connect(wl, SIGNAL(requestCsoundUserData(QuteWidget*)),
516 this, SLOT(requestCsoundUserData(QuteWidget*)));
517 connect(this, SIGNAL(passMessages(QString)), wl, SLOT(appendMessage(QString)), Qt::UniqueConnection);
518 }
519
setMidiHandler(MidiHandler * mh)520 void CsoundEngine::setMidiHandler(MidiHandler *mh)
521 {
522 ud->midiHandler = mh;
523 }
524
enableWidgets(bool enable)525 void CsoundEngine::enableWidgets(bool enable)
526 {
527 ud->enableWidgets = enable;
528 }
529
registerConsole(ConsoleWidget * c)530 void CsoundEngine::registerConsole(ConsoleWidget *c)
531 {
532 consoles.append(c);
533 connect(this,SIGNAL(passMessages(QString)), c, SLOT(appendMessage(QString)), Qt::UniqueConnection);
534 }
535
getErrorLines()536 QList<QPair<int, QString> > CsoundEngine::getErrorLines()
537 {
538 QList<QPair<int, QString> > list;
539 if (consoles.size() > 0) {
540 QList<int> lines = consoles[0]->errorLines;
541 QStringList texts = consoles[0]->errorTexts;
542 for (int i = 0; i < lines.size(); i++) {
543 list.append(QPair<int, QString>(lines[i], texts[i]));
544 }
545 }
546 return list;
547 }
548
setConsoleBufferSize(int size)549 void CsoundEngine::setConsoleBufferSize(int size)
550 {
551 m_consoleBufferSize = size;
552 }
553
getAnsiKeySequence(int key)554 QList <int> CsoundEngine::getAnsiKeySequence(int key) // convert sepcial keys (Qt::Key) like Esc, arrows, F1 etc to ANSI escape key sequence for Csound
555 {
556 QList <int> keyArray;
557 if (key < 0xff) {
558 keyArray << key;
559 } else switch (key) {
560 case Qt::Key_Escape: keyArray << 27; break;
561 case Qt::Key_Tab: keyArray << 9; break;
562 case Qt::Key_Backspace: keyArray << 127; break;
563 case Qt::Key_Up: keyArray << 27 << 91 << 65; break;
564 case Qt::Key_Down: keyArray << 27 << 91 << 66; break;
565 case Qt::Key_Right: keyArray << 27 << 91 << 67; break;
566 case Qt::Key_Left: keyArray << 27 << 91 << 68; break;
567 case Qt::Key_Home: keyArray << 27 << 91 << 72; break;
568 case Qt::Key_End: keyArray << 27 << 91 << 70; break;
569 case Qt::Key_PageUp: keyArray << 27 << 53 << 126; break;
570 case Qt::Key_PageDown: keyArray << 27 << 52 << 126; break;
571 case Qt::Key_F1: keyArray << 27 << 91 << 80; break;
572 case Qt::Key_F2: keyArray << 27 << 79 << 81; break;
573 case Qt::Key_F3: keyArray << 27 << 79 << 82; break;
574 case Qt::Key_F4: keyArray << 27 << 79 << 83; break;
575 case Qt::Key_F5: keyArray << 27 << 91 << 49 << 53 << 126; break;
576 case Qt::Key_F6: keyArray << 27 << 91 << 49 << 55 << 126; break;
577 case Qt::Key_F7: keyArray << 27 << 91 << 49 << 56 << 126; break;
578 case Qt::Key_F8: keyArray << 27 << 91 << 49 << 57 << 126; break;
579 case Qt::Key_F9: keyArray << 27 << 91 << 50 << 48 << 126; break;
580 case Qt::Key_F10: keyArray << 27 << 91 << 50 << 49 << 126; break;
581 case Qt::Key_F11: keyArray << 27 << 91 << 50 << 51 << 126; break;
582 case Qt::Key_F12: keyArray << 27 << 91 << 50 << 52 << 126; break;
583 case Qt::Key_Insert: keyArray << 27 << 91 << 50 << 126; break;
584 case Qt::Key_Delete: keyArray << 27 << 91 << 51 << 126; break;
585
586 default: qDebug()<<"Key " << key << " ignored."; //keyArray << key;
587 }
588 return keyArray;
589 }
590
keyPressForCsound(int key)591 void CsoundEngine::keyPressForCsound(int key)
592 {
593 keyMutex.lock();
594 keyPressBuffer << getAnsiKeySequence(key);
595 keyMutex.unlock();
596 }
597
keyReleaseForCsound(int key)598 void CsoundEngine::keyReleaseForCsound(int key) // NB! I did not change this to int since seems Csound actually does not use it?
599 {
600 keyMutex.lock();
601 keyReleaseBuffer << key;
602 keyMutex.unlock();
603 }
604
requestCsoundUserData(QuteWidget * widget)605 void CsoundEngine::requestCsoundUserData(QuteWidget *widget) {
606 widget->setCsoundUserData(ud);
607 }
608
registerScope(QuteScope * scope)609 void CsoundEngine::registerScope(QuteScope *scope)
610 {
611 scope->setUd(ud);
612 }
613
registerGraph(QuteGraph * graph)614 void CsoundEngine::registerGraph(QuteGraph *graph)
615 {
616 graph->setUd(ud);
617 }
618
evaluate(QString code)619 void CsoundEngine::evaluate(QString code)
620 {
621 CSOUND *csound = getCsound();
622 if (csound) {
623 csoundCompileOrc(csound, code.toLatin1());
624 queueMessage(tr("Csound code evaluated.\n"));
625 } else {
626 queueMessage(tr("Csound is not running. Code not evaluated."));
627 }
628 }
629
630 //void CsoundEngine::unregisterScope(QuteScope *scope)
631 //{
632 // // TODO is it necessary to unregiter scopes?
633 // qDebug() << "Not implemented";
634 //}
635
popKeyPressEvent()636 int CsoundEngine::popKeyPressEvent()
637 {
638 int value = -1;
639 if (keyMutex.tryLock()) {
640 if (!keyPressBuffer.isEmpty()) {
641 value = keyPressBuffer.takeFirst();
642 }
643
644 keyMutex.unlock();
645 }
646 return value;
647 }
648
popKeyReleaseEvent()649 int CsoundEngine::popKeyReleaseEvent()
650 {
651 int value = -1;
652 if (keyMutex.tryLock()) {
653 if (!keyReleaseBuffer.isEmpty()) {
654 value = keyReleaseBuffer.takeFirst();
655 }
656 keyMutex.unlock();
657 }
658 return value;
659 }
660
processEventQueue()661 void CsoundEngine::processEventQueue()
662 {
663 // This function should only be called when Csound is running
664 eventMutex.lock();
665 while (eventQueueSize > 0) {
666 eventQueueSize--;
667 m_playMutex.lock();
668 #ifdef QCS_DESTROY_CSOUND
669 if (ud->perfThread) {
670 //ScoreEvent is not working
671 // ud->perfThread->ScoreEvent(0, type, eventElements.size(), pFields);
672 // qDebug() << eventQueue[eventQueueSize];
673 ud->perfThread->InputMessage(eventQueue[eventQueueSize].toLatin1());
674 }
675 else {
676 QDEBUG << "WARNING: ud->perfThread is NULL";
677 }
678 #else
679 if (ud->perfThread) {
680 ud->perfThread->InputMessage(eventQueue[eventQueueSize].toLatin1());
681 }
682 #endif
683 m_playMutex.unlock();
684 }
685 eventMutex.unlock();
686 }
687
passOutValue(QString channelName,double value)688 void CsoundEngine::passOutValue(QString channelName, double value)
689 {
690 ud->wl->newValue(QPair<QString, double>(channelName, value));
691 }
692
passOutString(QString channelName,QString value)693 void CsoundEngine::passOutString(QString channelName, QString value)
694 {
695 // qDebug() ;
696 ud->wl->newValue(QPair<QString, QString>(channelName, value));
697 }
698
play(CsoundOptions * options)699 int CsoundEngine::play(CsoundOptions *options)
700 {
701 QMutexLocker locker(&m_playMutex);
702 if (ud->perfThread && (ud->perfThread->GetStatus() == 0)) {
703 // GetStatus == 0 means playing
704 // ud->perfThread->TogglePause(); // no need for that when there is Pause button
705 QDEBUG << "Already playing";
706 return 0;
707 }
708 if (options) {
709 m_options = *options;
710 }
711 locker.unlock();
712 if(options->checkSyntaxOnly) {
713 return checkSyntax();
714 }
715 if(options->checkSyntaxBeforeRun) {
716 QDEBUG << "Checking Syntax";
717 int ret = checkSyntax();
718 if(ret != 0) {
719 QDEBUG << "Syntax check failed with return code:" << ret;
720 return ret;
721 }
722 QDEBUG << "Syntax check ok, running csound";
723 }
724 return runCsound();
725 }
726
stop()727 void CsoundEngine::stop()
728 {
729 stopRecording();
730 stopCsound();
731 }
732
pause()733 void CsoundEngine::pause()
734 {
735 QMutexLocker locker(&m_playMutex);
736 if (ud->perfThread && (ud->perfThread->GetStatus() == 0)) {
737 //ud->perfThread->Pause();
738 ud->perfThread->TogglePause();
739 }
740 }
741
startRecording(int sampleformat,QString fileName)742 int CsoundEngine::startRecording(int sampleformat, QString fileName)
743 {
744 qDebug("start recording (%i-bit samples): %s",
745 (sampleformat + 2) * 8,
746 fileName.toLocal8Bit().constData());
747 // clip instead of wrap when converting floats to ints
748 #ifdef PERFTHREAD_RECORD
749 // perfthread record API is only available with csound >= 6.04
750 if (ud->perfThread) {
751 m_recording = true;
752 ud->perfThread->Record(fileName.toLocal8Bit().constData(),
753 (sampleformat + 2) * 8);
754 }
755 #endif
756 return 0;
757 }
758
stopRecording()759 void CsoundEngine::stopRecording()
760 {
761 m_recording = false;
762
763 #ifdef PERFTHREAD_RECORD
764 if (ud->perfThread) {
765 ud->perfThread->StopRecord();
766 }
767 //qDebug("Recording stopped.");
768 #endif
769 }
770
queueEvent(QString eventLine,int delay)771 void CsoundEngine::queueEvent(QString eventLine, int delay)
772 {
773 // TODO: implement delayed events
774 (void) delay;
775 // qDebug("CsoundEngine::queueEvent %s", eventLine.toStdString().c_str());
776 if (!isRunning()) {
777 QMutexLocker lock(&m_messageMutex);
778 messageQueue << tr("Csound is not running! Event ignored.\n");
779 return;
780 }
781 if (eventQueueSize < QCS_MAX_EVENTS) {
782 eventMutex.lock();
783 eventQueue[eventQueueSize] = eventLine;
784 eventQueueSize++;
785 eventMutex.unlock();
786 }
787 else {
788 qDebug("Warning: event queue full, event not processed");
789 }
790 }
791
checkSyntax()792 int CsoundEngine::checkSyntax() {
793 QDEBUG << "$$$ checkSyntax 0";
794 QMutexLocker locker(&m_playMutex);
795 QDEBUG << "$$$ checkSyntax 1";
796
797 CsoundOptions options(m_options);
798 options.checkSyntaxOnly = true;
799
800 ud->csound = csoundCreate((void *) ud);
801 QDEBUG << "$$$ checkSyntax 2";
802
803 eventQueueSize = 0;
804 ud->msgRefreshTime = m_refreshTime*1000;
805 QDir::setCurrent(m_options.fileName1);
806 for (int i = 0; i < consoles.size(); i++) {
807 consoles[i]->reset();
808 }
809 csoundCreateMessageBuffer(ud->csound, 0);
810
811 char const **argv;// since there was change in Csound API
812 argv = (const char **) calloc(33, sizeof(char*));
813
814 int argc = options.generateCmdLine((char **)argv);
815
816 ud->result = csoundCompile(ud->csound, argc, argv);
817
818 for (int i = 0; i < argc; i++) {
819 qDebug() << argv[i];
820 free((char *) argv[i]);
821 }
822 free(argv);
823 int out;
824 if (ud->result != 256) {
825 qDebug() << "Csound syntax check failed! " << ud->result;
826 flushQueues();
827 locker.unlock(); // otherwise csoundStop will freeze
828 stop();
829 emit (errorLines(getErrorLines()));
830
831 out = -3; // compilation error
832 } else {
833 // this might still have failed...
834 QDEBUG << "Syntax check ok, return code: " << ud->result;
835 out = 0; // OK
836 }
837 // csoundDestroyMessageBuffer(ud->csound);
838 // csoundCleanup(ud->csound);
839 // csoundReset(ud->csound);
840 return out;
841 }
842
843
runCsound()844 int CsoundEngine::runCsound()
845 {
846 QMutexLocker locker(&m_playMutex);
847 #ifdef MACOSX_PRE_SNOW
848 // Remember menu bar to set it after FLTK grabs it
849 menuBarHandle = GetMenuBar();
850 #endif
851 #ifdef Q_OS_WIN
852 // Call OleInitialize twice to keep the FLTK opcodes from reducing the COM
853 // reference count to zero.
854 // OleInitialize(NULL); // Do not initialize here but in CsoundQt onbject
855 // OleInitialize(NULL);
856 #endif
857 eventQueueSize = 0;
858 // Flush events gathered while idle.
859 ud->audioOutputBuffer.allZero();
860 ud->msgRefreshTime = m_refreshTime*1000;
861 QDir::setCurrent(m_options.fileName1);
862 for (int i = 0; i < consoles.size(); i++) {
863 consoles[i]->reset();
864 }
865 #ifdef QCS_DESTROY_CSOUND
866 ud->csound=csoundCreate((void *) ud);
867 ud->midiBuffer = csoundCreateCircularBuffer(ud->csound, 1024, sizeof(unsigned char));
868 Q_ASSERT(ud->midiBuffer);
869 ud->virtualMidiBuffer = csoundCreateCircularBuffer(ud->csound, 1024, sizeof(unsigned char));
870 Q_ASSERT(ud->virtualMidiBuffer);
871 // csoundFlushCircularBuffer(ud->csound, ud->midiBuffer);
872 #endif
873 #ifdef QCS_DEBUGGER
874 if(m_debugging) {
875 csoundDebuggerInit(ud->csound);
876 foreach(QVariantList bp, m_startBreakpoints) {
877 Q_ASSERT(bp.size() > 1);
878 if (bp[0].toString() == "instr") {
879 Q_ASSERT(bp.size() == 3);
880 csoundSetInstrumentBreakpoint(ud->csound, bp[1].toDouble(), bp[2].toInt());
881 } else if (bp[0].toString() == "line") {
882 Q_ASSERT(bp.size() == 4);
883 csoundSetBreakpoint(ud->csound, bp[2].toInt(), bp[1].toInt(), bp[3].toInt());
884 } else {
885 qDebug() << "Wrong breakpoint format";
886 }
887 }
888 csoundSetBreakpointCallback(ud->csound, &CsoundEngine::breakpointCallback, (void *) this);
889 }
890 #endif
891 if(!m_options.useCsoundMidi) {
892 csoundSetHostImplementedMIDIIO(ud->csound, 1);
893 csoundSetExternalMidiInOpenCallback(ud->csound, &midiInOpenCb);
894 csoundSetExternalMidiReadCallback(ud->csound, &midiReadCb);
895 csoundSetExternalMidiInCloseCallback(ud->csound, &midiInCloseCb);
896 csoundSetExternalMidiOutOpenCallback(ud->csound, &midiOutOpenCb);
897 csoundSetExternalMidiWriteCallback(ud->csound, &midiWriteCb);
898 csoundSetExternalMidiOutCloseCallback(ud->csound, &midiOutCloseCb);
899 csoundSetExternalMidiErrorStringCallback(ud->csound, &midiErrorStringCb);
900 }
901 csoundCreateMessageBuffer(ud->csound, 0);
902
903 if (m_options.enableFLTK) {
904 // Disable FLTK graphs, but allow FLTK widgets.
905 int *var = (int*) csoundQueryGlobalVariable(ud->csound, "FLTK_Flags");
906 if (var) {
907 *var = 4;
908 } else {
909 if (csoundCreateGlobalVariable(ud->csound, "FLTK_Flags", sizeof(int)) != CSOUND_SUCCESS) {
910 qDebug() << "Error creating the FTLK_Flags variable";
911 } else {
912 int *var = (int*) csoundQueryGlobalVariable(ud->csound, "FLTK_Flags");
913 if (var) {
914 *var = 4;
915 } else {
916 qDebug() << "Error reading the FTLK_Flags variable";
917 }
918 }
919 }
920 }
921 else {
922 csoundSetGlobalEnv("CS_OMIT_LIBS", "fluidOpcodes,virtual,widgets");
923 int *var = (int*) csoundQueryGlobalVariable(ud->csound, "FLTK_Flags");
924 if (var) {
925 *var = 3;
926 } else {
927 qDebug() << "Error reading the FTLK_Flags variable";
928 }
929 }
930 csoundRegisterKeyboardCallback(ud->csound,
931 &CsoundEngine::keyEventCallback,
932 (void *) ud, CSOUND_CALLBACK_KBD_EVENT | CSOUND_CALLBACK_KBD_TEXT);
933 // necessary to put something into the buffer, otherwise sensekey complains when
934 // not started from terminal
935 csoundKeyPress(getCsound(),'\0');
936
937 #ifdef QCS_RTMIDI
938 if (!m_options.useCsoundMidi) {
939 csoundSetOption(ud->csound, const_cast<char *>("-+rtmidi=hostbased"));
940 csoundSetOption(ud->csound, const_cast<char *>("-M0"));
941 csoundSetOption(ud->csound, const_cast<char *>("-Q0"));
942 }
943 #endif
944 csoundSetIsGraphable(ud->csound, true);
945 csoundSetMakeGraphCallback(ud->csound, &CsoundEngine::makeGraphCallback);
946 csoundSetDrawGraphCallback(ud->csound, &CsoundEngine::drawGraphCallback);
947 csoundSetKillGraphCallback(ud->csound, &CsoundEngine::killGraphCallback);
948 csoundSetExitGraphCallback(ud->csound, &CsoundEngine::exitGraphCallback);
949 if (!m_options.fileName1.endsWith(".html", Qt::CaseInsensitive)) {
950 #if CS_APIVERSION>=4
951 char const **argv;// since there was change in Csound API
952 argv = (const char **) calloc(33, sizeof(char*));
953 #else
954 char **argv;
955 argv = (char **) calloc(33, sizeof(char*));
956 #endif
957
958 int argc = m_options.generateCmdLine((char **)argv);
959
960 qDebug() << "------------ Compiling csd...";
961 ud->result=csoundCompile(ud->csound,argc,argv);
962 for (int i = 0; i < argc; i++) {
963 qDebug() << argv[i];
964 free((char *) argv[i]);
965 }
966 free(argv);
967 if (ud->result != CSOUND_SUCCESS) {
968 qDebug() << "Csound compile failed! " << ud->result;
969 // Commenting out flushQues fixes the crash.
970 // Investigate closer, if it must be here
971 // seems that messages are outputted into console anyway...
972 flushQueues(); // the line was here in some earlier version. Otherwise errormessaged won't be processed by Console::appendMessage()
973 locker.unlock(); // otherwise csoundStop will freeze
974 stop();
975 emit (errorLines(getErrorLines()));
976 return -3;
977 }
978 qDebug() << "------- Compiling ok";
979 }
980 qDebug() << "------- 2";
981
982 ud->zerodBFS = csoundGet0dBFS(ud->csound);
983 ud->sampleRate = csoundGetSr(ud->csound);
984 ud->numChnls = csoundGetNchnls(ud->csound);
985 ud->outputBufferSize = csoundGetKsmps(ud->csound);
986 if (ud->enableWidgets) {
987 setupChannels();
988 }
989 // Do not run the performance thread if the piece is an HTML file,
990 // the HTML code must do that.
991 if (!m_options.fileName1.endsWith(".html", Qt::CaseInsensitive)) {
992 ud->perfThread = new CsoundPerformanceThread(ud->csound);
993 ud->perfThread->SetProcessCallback(CsoundEngine::csThread, (void*)ud);
994 ud->perfThread->Play();
995 }
996 return 0;
997 }
998
stopCsound()999 void CsoundEngine::stopCsound()
1000 {
1001 // perfThread->ScoreEvent(0, 'e', 0, 0);
1002 // m_playMutex.lock();
1003 QMutexLocker locker(&m_playMutex);
1004 if(!ud->perfThread)
1005 return;
1006
1007 CsoundPerformanceThread *pt = ud->perfThread;
1008
1009 pt->Stop();
1010
1011 unsigned int waitTime = 100;
1012
1013 for(int msecs = 2000; msecs > 0; msecs -= waitTime) {
1014 int status = pt->GetStatus();
1015 if(status > 0) {
1016 QDEBUG << "Csound stopped OK";
1017 break;
1018 }
1019 if(status < 0) {
1020 QDEBUG << "Error when stopping csound";
1021 break;
1022 }
1023 if(status == 0) {
1024 QDEBUG << "Csound still playing, trying again in " << waitTime << "msecs";
1025 QThread::msleep(waitTime);
1026 // pt->Stop();
1027 }
1028 }
1029
1030
1031
1032 if(pt->GetStatus() <= 0) {
1033 QDEBUG << "Csound's performance thread failed to stop, stopping csound";
1034 locker.unlock();
1035 csoundMutex.lock();
1036 pt->SetProcessCallback(nullptr, nullptr);
1037 QThread::msleep(200);
1038 QDEBUG << "Destroying csound...";
1039 // delete pt;
1040 csoundDestroy(ud->csound);
1041 QDEBUG << "Destroyed ok";
1042 ud->perfThread = nullptr;
1043 csoundMutex.unlock();
1044 emit stopSignal();
1045 QDEBUG << "Stopped OK...";
1046 return;
1047 }
1048
1049 QDEBUG << "Joining...";
1050 pt->Join();
1051 QDEBUG << "Joined.";
1052
1053 ud->perfThread = NULL;
1054 delete pt;
1055
1056 QDEBUG << "Cleaning up csound...";
1057
1058 this->cleanupCsound();
1059
1060 QDEBUG << "Clean up OK";
1061
1062
1063 #ifdef QCS_DEBUGGER
1064 stopDebug();
1065 #endif
1066
1067 #ifdef MACOSX_PRE_SNOW
1068 // Put menu bar back
1069 SetMenuBar(menuBarHandle);
1070 #endif
1071 QDEBUG << "emiting stopSignal...";
1072
1073 locker.unlock();
1074 emit stopSignal();
1075
1076 QDEBUG << "Exiting stopCsound";
1077 }
1078
cleanupCsound()1079 void CsoundEngine::cleanupCsound()
1080 {
1081 if(ud->csound == nullptr) {
1082 QDEBUG << "csound is null";
1083 return;
1084 }
1085 QMutexLocker locker(&csoundMutex);
1086 csoundSetIsGraphable(ud->csound, 0);
1087 csoundSetMakeGraphCallback(ud->csound, nullptr);
1088 csoundSetDrawGraphCallback(ud->csound, nullptr);
1089 csoundSetKillGraphCallback(ud->csound, nullptr);
1090 csoundSetExitGraphCallback(ud->csound, nullptr);
1091 csoundRemoveKeyboardCallback(ud->csound, &CsoundEngine::keyEventCallback);
1092 #ifdef QCS_DEBUGGER
1093 if(m_debugging) {
1094 csoundDebuggerClean(ud->csound);
1095 }
1096 #endif
1097
1098 csoundCleanup(ud->csound);
1099 flushQueues();
1100 csoundDestroyMessageBuffer(ud->csound);
1101
1102 #ifdef QCS_DESTROY_CSOUND
1103 csoundDestroyCircularBuffer(ud->csound, ud->midiBuffer);
1104 ud->midiBuffer = nullptr;
1105 csoundDestroyCircularBuffer(ud->csound, ud->virtualMidiBuffer);
1106 ud->virtualMidiBuffer = nullptr;
1107 csoundDestroy(ud->csound);
1108 ud->csound = nullptr;
1109 #else
1110 csoundReset(ud->csound);
1111 #endif
1112 }
1113
setupChannels()1114 void CsoundEngine::setupChannels()
1115 {
1116 ud->inputChannelNames.clear();
1117 ud->outputChannelNames.clear();
1118 ud->outputStringChannelNames.clear();
1119 ud->previousOutputValues.clear();
1120 ud->previousStringOutputValues.clear();
1121 csoundSetInputChannelCallback(ud->csound, &CsoundEngine::inputValueCallback);
1122 csoundSetOutputChannelCallback(ud->csound, &CsoundEngine::outputValueCallback);
1123 // For chnget/chnset
1124 controlChannelInfo_t *channelList;
1125 int numChannels = csoundListChannels(ud->csound, &channelList);
1126 controlChannelInfo_t *entry = channelList;
1127
1128 MYFLT *pvalue;
1129 QVector<QuteWidget *> widgets = ud->wl->getWidgets();
1130 // Set channels values for existing channels (i.e. those declared with chn_*
1131 // in the csound header
1132 for (int i = 0; i < numChannels; i++) {
1133 int chanType = csoundGetChannelPtr(ud->csound, &pvalue, entry->name,
1134 0);
1135 if (chanType & CSOUND_INPUT_CHANNEL) {
1136 if ((chanType & CSOUND_CHANNEL_TYPE_MASK) == CSOUND_CONTROL_CHANNEL) {
1137 ud->wl->valueMutex.lock();
1138 foreach (QuteWidget *w, widgets) {
1139 if (w->getChannelName() == QString(entry->name)) {
1140 ud->wl->newValues.insert(w->getChannelName(), w->getValue());
1141 }
1142 if (w->getChannel2Name() == QString(entry->name)) {
1143 ud->wl->newValues.insert(w->getChannel2Name(), w->getValue2());
1144 }
1145 }
1146 ud->wl->valueMutex.unlock();
1147 } else if ((chanType & CSOUND_CHANNEL_TYPE_MASK) == CSOUND_STRING_CHANNEL) {
1148 ud->wl->stringValueMutex.lock();
1149 foreach (QuteWidget *w, widgets) {
1150 if (w->getChannelName() == QString(entry->name)) {
1151 ud->wl->newStringValues.insert(w->getChannelName(), w->getStringValue());
1152 }
1153 }
1154 ud->wl->stringValueMutex.unlock();
1155 }
1156 }
1157 if (chanType & CSOUND_OUTPUT_CHANNEL) { // Channels can be input and output at the same time
1158 if ((chanType & CSOUND_CHANNEL_TYPE_MASK) == CSOUND_CONTROL_CHANNEL) {
1159 ud->outputChannelNames << QString(entry->name);
1160 ud->previousOutputValues << 0;
1161 foreach (QuteWidget *w, widgets) {
1162 if (w->getChannelName() == QString(entry->name)) {
1163 ud->previousOutputValues.last() = w->getValue();
1164 continue;
1165 }
1166 if (w->getChannel2Name() == QString(entry->name)) {
1167 ud->previousOutputValues.last() = w->getValue2();
1168 continue;
1169 }
1170 }
1171 } else if ((chanType & CSOUND_CHANNEL_TYPE_MASK) == CSOUND_STRING_CHANNEL) {
1172 ud->outputStringChannelNames << QString(entry->name);
1173 ud->previousStringOutputValues << "";
1174 foreach (QuteWidget *w, widgets) {
1175 if (w->getChannelName() == QString(entry->name)) {
1176 ud->previousStringOutputValues.last() = w->getStringValue();
1177 continue;
1178 }
1179 }
1180 }
1181 }
1182 entry++;
1183 }
1184 csoundDeleteChannelList(ud->csound, channelList);
1185
1186 // Force creation of string channels for _Browse widgets
1187 foreach (QuteWidget *w, widgets) {
1188 if (w->getChannelName().startsWith("_Browse")) {
1189 csoundGetChannelPtr(ud->csound, &pvalue, w->getChannelName().toLocal8Bit(),
1190 CSOUND_INPUT_CHANNEL | CSOUND_OUTPUT_CHANNEL | CSOUND_STRING_CHANNEL);
1191 ud->wl->newStringValues.insert(w->getChannelName(), w->getStringValue());
1192 }
1193 }
1194 }
1195
messageListDispatcher(void * data)1196 void CsoundEngine::messageListDispatcher(void *data)
1197 {
1198 CsoundUserData *ud_local = (CsoundUserData *) data;
1199 while (ud_local->runDispatcher) {
1200 ud_local->playMutex->lock();
1201 if (ud_local->perfThread && (ud_local->perfThread->GetStatus() != 0)) {
1202 // In case score has ended
1203 ud_local->playMutex->unlock();
1204 ud_local->csEngine->stop();
1205 } else {
1206 ud_local->playMutex->unlock();
1207 }
1208 CSOUND *csound = ud_local->csEngine->getCsound();
1209 if (csound) {
1210 if (ud_local->wl) {
1211 ud_local->wl->getMouseValues(&ud_local->mouseValues);
1212 }
1213 int count = csoundGetMessageCnt(csound);
1214 ud_local->csEngine->m_messageMutex.lock();
1215 for (int i = 0; i< count; i++) {
1216 ud_local->csEngine->messageQueue << csoundGetFirstMessage(csound);
1217 // FIXME: Is this thread safe?
1218 csoundPopFirstMessage(csound);
1219 }
1220 ud_local->csEngine->m_messageMutex.unlock();
1221 }
1222
1223 int counter = 0;
1224 ud_local->csEngine->m_messageMutex.lock();
1225 while ((ud_local->csEngine->m_consoleBufferSize <= 0 ||
1226 counter++ < ud_local->csEngine->m_consoleBufferSize)) {
1227 if (ud_local->csEngine->messageQueue.isEmpty()) {
1228 break;
1229 }
1230 QString msg = ud_local->csEngine->messageQueue.takeFirst();
1231 // Must use signals to make things thread safe
1232 emit ud_local->csEngine->passMessages(msg);
1233 }
1234 if (!ud_local->csEngine->messageQueue.isEmpty()
1235 && ud_local->csEngine->m_consoleBufferSize > 0
1236 && counter >= ud_local->csEngine->m_consoleBufferSize) {
1237 ud_local->csEngine->messageQueue.clear();
1238 ud_local->csEngine->messageQueue << "\nCsoundQt: Message buffer overflow. Messages discarded!\n";
1239 }
1240 ud_local->csEngine->m_messageMutex.unlock();
1241 QThread::usleep(ud_local->msgRefreshTime);
1242 }
1243 }
1244
flushQueues()1245 void CsoundEngine::flushQueues()
1246 {
1247 m_messageMutex.lock();
1248 int count = csoundGetMessageCnt(ud->csound);
1249 for (int i = 0; i < count; i++) {
1250 messageQueue << csoundGetFirstMessage(ud->csound);
1251 csoundPopFirstMessage(ud->csound);
1252 }
1253
1254 while (!messageQueue.isEmpty()) {
1255 QString msg = messageQueue.takeFirst();
1256 ConsoleWidget *console = nullptr;
1257 for (int i = 0; i < consoles.size(); i++) {
1258 console = consoles[i];
1259 console->appendMessage(msg);
1260 }
1261 ud->wl->appendMessage(msg);
1262
1263 }
1264 m_messageMutex.unlock();
1265 ud->wl->flushGraphBuffer();
1266 }
1267
queueMessage(QString message)1268 void CsoundEngine::queueMessage(QString message)
1269 {
1270 m_messageMutex.lock();
1271 messageQueue << message;
1272 m_messageMutex.unlock();
1273 }
1274
isRunning()1275 bool CsoundEngine::isRunning()
1276 {
1277 if(!m_playMutex.tryLock(1)) {
1278 qDebug() << "Could not acquire lock";
1279 return false;
1280 }
1281 auto out = (ud->perfThread && (ud->perfThread->GetStatus() == 0));
1282 m_playMutex.unlock();
1283 return out;
1284 }
1285
isRecording()1286 bool CsoundEngine::isRecording()
1287 {
1288 return m_recording;
1289 }
1290
getCsound()1291 CSOUND *CsoundEngine::getCsound()
1292 {
1293 return ud->csound;
1294 }
1295
1296 #ifdef QCS_PYTHONQT
registerProcessCallback(QString func,int skipPeriods)1297 void CsoundEngine::registerProcessCallback(QString func, int skipPeriods)
1298 {
1299 ud->m_pythonCallback = func;
1300 ud->m_pythonCallbackSkip = skipPeriods;
1301 }
1302
setPythonConsole(PythonConsole * pc)1303 void CsoundEngine::setPythonConsole(PythonConsole *pc)
1304 {
1305 ud->m_pythonConsole = pc;
1306 }
1307 #endif
1308
1309 #ifdef QCS_DEBUGGER
1310
breakpointCallback(CSOUND * csound,debug_bkpt_info_t * bkpt_info,void * udata)1311 void CsoundEngine::breakpointCallback(CSOUND *csound, debug_bkpt_info_t *bkpt_info, void *udata)
1312 {
1313 debug_instr_t *debug_instr = bkpt_info->breakpointInstr;
1314 CsoundEngine *cs = (CsoundEngine *) udata;
1315 // Copy variable list
1316 debug_variable_t* vp = bkpt_info->instrVarList;
1317 cs->variableMutex.lock();
1318 cs->m_varList.clear();
1319 while (vp) {
1320 if (vp->name[0] != '#') {
1321 QVariantList varDetails;
1322 varDetails << vp->name;
1323 if (strcmp(vp->typeName, "i") == 0
1324 || strcmp(vp->typeName, "k") == 0
1325 || strcmp(vp->typeName, "r") == 0) {
1326 varDetails << *((MYFLT *) vp->data);
1327 } else if(strcmp(vp->typeName, "S") == 0) {
1328 varDetails << (char *) vp->data;
1329 } else if (strcmp(vp->typeName, "a") == 0) {
1330 MYFLT *data = (MYFLT *) vp->data;
1331 varDetails << *data << *(data + 1)
1332 << *(data + 2)<< *(data + 3);
1333 } else {
1334 varDetails << QVariant();
1335 }
1336 varDetails << vp->typeName;
1337 cs->m_varList << varDetails;
1338 }
1339 vp = vp->next;
1340 }
1341 cs->variableMutex.unlock();
1342 if (bkpt_info->currentOpcode) {
1343 cs->m_currentLine.store(bkpt_info->currentOpcode->line);
1344 } else {
1345 cs->m_currentLine.store(debug_instr->line);
1346 }
1347
1348 debug_instr_t *debug_instr_list = bkpt_info->instrListHead;
1349 //Copy active instrument list
1350 cs->instrumentMutex.lock();
1351 cs->m_instrumentList.clear();
1352 while (debug_instr_list) {
1353 QVariantList instance;
1354 instance << debug_instr_list->p1;
1355 instance << QString("%1 %2").arg(debug_instr_list->p2).arg(debug_instr_list->p3);
1356 instance << (qulonglong)debug_instr_list->kcounter;
1357 cs->m_instrumentList << instance;
1358 debug_instr_list = debug_instr_list->next;
1359 }
1360 cs->instrumentMutex.unlock();
1361
1362 emit cs->breakpointReached();
1363 }
1364
setDebug()1365 void CsoundEngine::setDebug()
1366 {
1367 if (isRunning()) {
1368 ud->perfThread->Pause();
1369 csoundDebuggerInit(ud->csound);
1370 csoundSetBreakpointCallback(ud->csound, &CsoundEngine::breakpointCallback, (void *) this);
1371 // ud->perfThread->Play();
1372 }
1373 m_debugging = true;
1374 }
1375
pauseDebug()1376 void CsoundEngine::pauseDebug()
1377 {
1378 if (isRunning() && m_debugging) {
1379 csoundDebugStop(ud->csound);
1380 }
1381 }
1382
continueDebug()1383 void CsoundEngine::continueDebug()
1384 {
1385 if (isRunning() && m_debugging) {
1386 qDebug() ;
1387 csoundDebugContinue(ud->csound);
1388 }
1389 }
1390
stopDebug()1391 void CsoundEngine::stopDebug()
1392 {
1393 if (isRunning() && m_debugging) {
1394 stop();
1395 }
1396 m_debugging = false;
1397 }
1398
addInstrumentBreakpoint(double instr,int skip)1399 void CsoundEngine::addInstrumentBreakpoint(double instr, int skip)
1400 {
1401 if (isRunning() && m_debugging) {
1402 csoundSetInstrumentBreakpoint(ud->csound, instr, skip);
1403 }
1404 }
1405
removeInstrumentBreakpoint(double instr)1406 void CsoundEngine::removeInstrumentBreakpoint(double instr)
1407 {
1408 if (isRunning() && m_debugging) {
1409 csoundRemoveInstrumentBreakpoint(ud->csound, instr);
1410 }
1411 }
1412
addBreakpoint(int line,int instr,int skip)1413 void CsoundEngine::addBreakpoint(int line, int instr, int skip)
1414 {
1415 if (isRunning() && m_debugging) {
1416 csoundSetBreakpoint(ud->csound, line, instr, skip);
1417 }
1418 }
1419
removeBreakpoint(int line,int instr)1420 void CsoundEngine::removeBreakpoint(int line, int instr)
1421 {
1422 if (isRunning() && m_debugging) {
1423 csoundRemoveBreakpoint(ud->csound, line, instr);
1424 }
1425 }
1426
setStartingBreakpoints(QVector<QVariantList> bps)1427 void CsoundEngine::setStartingBreakpoints(QVector<QVariantList> bps)
1428 {
1429 m_startBreakpoints = bps;
1430 }
1431
getVaribleList()1432 QVector<QVariantList> CsoundEngine::getVaribleList()
1433 {
1434 QVector<QVariantList> outList;
1435 variableMutex.lock();
1436 outList = m_varList;
1437 variableMutex.unlock();
1438 return outList;
1439 }
1440
getInstrumentList()1441 QVector<QVariantList> CsoundEngine::getInstrumentList()
1442 {
1443 QVector<QVariantList> outList;
1444 instrumentMutex.lock();
1445 outList = m_instrumentList;
1446 instrumentMutex.unlock();
1447 return outList;
1448 }
1449
getCurrentLine()1450 int CsoundEngine::getCurrentLine()
1451 {
1452 return m_currentLine.load();
1453 }
1454
1455 #endif
1456
getUserData()1457 CsoundUserData *CsoundEngine::getUserData()
1458 {
1459 return ud;
1460 }
1461
clearConsoles(void)1462 void CsoundEngine::clearConsoles(void) {
1463 for (int i = 1, n = consoles.size(); i < n; ++i) {
1464 consoles[i]->reset();
1465 }
1466 }
1467