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