1 /*
2     Copyright (C) 2008-2016 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 
24 // Code based partly on CsoundHtmlWrapper by Michael Gogins https://github.com/gogins/gogins.github.io/tree/master/csound_html5.
25 // This class must be safe against calling with a null or uninitialized CsoundEngine.
26 
27 #include "csoundhtmlwrapper.h"
28 #include "csoundhtmlview.h"
29 #include <QApplication>
30 #include <QDebug>
31 
CsoundHtmlWrapper(QObject * parent)32 CsoundHtmlWrapper::CsoundHtmlWrapper(QObject *parent) :
33     QObject(parent),
34     m_csoundEngine(nullptr),
35     message_callback(nullptr),
36     csoundHtmlView(nullptr)
37 {
38 }
39 
setCsoundHtmlView(CsoundHtmlView * csoundHtmlView_)40 void CsoundHtmlWrapper::setCsoundHtmlView(CsoundHtmlView *csoundHtmlView_) {
41     csoundHtmlView = csoundHtmlView_;
42 }
43 
44 
getCsound()45 CSOUND *CsoundHtmlWrapper::getCsound()
46 {
47     if(!m_csoundEngine) {
48         return nullptr;
49     }
50     return m_csoundEngine->getCsound();
51 }
52 
getThread()53 CsoundPerformanceThread *CsoundHtmlWrapper::getThread()
54 {
55     if(!m_csoundEngine) {
56         return nullptr;
57     }
58     return m_csoundEngine->getUserData()->perfThread;
59 }
60 
getUserData()61 CsoundUserData *CsoundHtmlWrapper::getUserData()
62 {
63     if(!m_csoundEngine) {
64         return nullptr;
65     }
66     return m_csoundEngine->getUserData();
67 }
68 
setCsoundEngine(CsoundEngine * csEngine)69 void CsoundHtmlWrapper::setCsoundEngine(CsoundEngine *csEngine)
70 {
71     m_csoundEngine = csEngine;
72     if (m_csoundEngine != nullptr) {
73         auto csound = m_csoundEngine->getCsound();
74         if (csound != nullptr) {
75             //TODO: Csound crashes later -- not sure why.
76             //csoundSetMessageCallback(csound, CsoundHtmlWrapper::csoundMessageCallback_);
77         }
78     }
79 }
80 
compileCsd(const QString & filename)81 int CsoundHtmlWrapper::compileCsd(const QString &filename) {
82     if (!m_csoundEngine) {
83         return -1;
84     }
85 #if CS_APIVERSION>=4
86     return csoundCompileCsd(getCsound(), filename.toLocal8Bit());
87 #else
88     return csoundCompileCsd(getCsound(), filename.toLocal8Bit().data());
89 #endif
90 }
91 
compileCsdText(const QString & text)92 int CsoundHtmlWrapper::compileCsdText(const QString &text) {
93     if (!m_csoundEngine) {
94         return -1;
95     }
96     return csoundCompileCsdText(getCsound(), text.toLocal8Bit());
97 }
98 
compileOrc(const QString & text)99 int CsoundHtmlWrapper::compileOrc(const QString &text) {
100     if (!m_csoundEngine) {
101         return -1;
102     }
103     return csoundCompileOrc(getCsound(), text.toLocal8Bit());
104 }
105 
evalCode(const QString & text)106 double CsoundHtmlWrapper::evalCode(const QString &text) {
107     if (!m_csoundEngine) {
108         return -1;
109     }
110     return csoundEvalCode(getCsound(), text.toLocal8Bit());
111 }
112 
get0dBFS()113 double CsoundHtmlWrapper::get0dBFS() {
114     if (!m_csoundEngine) {
115         return -1;
116     }
117     return csoundGet0dBFS(getCsound());
118 }
119 
getApiVersion()120 int CsoundHtmlWrapper::getApiVersion() {
121     if (!m_csoundEngine) {
122         return -1;
123     }
124     return csoundGetAPIVersion();
125 }
126 
getControlChannel(const QString & name)127 double CsoundHtmlWrapper::getControlChannel(const QString &name) {
128     if (!m_csoundEngine) {
129         return -1;
130     }
131     int result = 0;
132     double value = csoundGetControlChannel(getCsound(), name.toLocal8Bit(), &result);
133     return value;
134 }
135 
getCurrentTimeSamples()136 qint64 CsoundHtmlWrapper::getCurrentTimeSamples() { // FIXME: unknown type int64_t qint64
137     if (!m_csoundEngine) {
138         return -1;
139     }
140     return csoundGetCurrentTimeSamples(getCsound());
141 }
142 
getEnv(const QString & name)143 QString CsoundHtmlWrapper::getEnv(const QString &name) { // not sure, if it works... test with setGlobalEnv
144     if (!m_csoundEngine) {
145         return QString();
146     }
147     return csoundGetEnv(getCsound(),name.toLocal8Bit());
148 }
149 
getKsmps()150 int CsoundHtmlWrapper::getKsmps() {
151     if (!m_csoundEngine) {
152         return -1;
153     }
154     return csoundGetKsmps(getCsound());
155 }
156 
getNchnls()157 int CsoundHtmlWrapper::getNchnls() {
158     if (!m_csoundEngine) {
159         return -1;
160     }
161     return csoundGetNchnls(getCsound());
162 }
163 
getNchnlsInput()164 int CsoundHtmlWrapper::getNchnlsInput() {
165     if (!m_csoundEngine) {
166         return -1;
167     }
168     return csoundGetNchnlsInput(getCsound());
169 }
170 
getOutputName()171 QString CsoundHtmlWrapper::getOutputName() {
172     if (!m_csoundEngine) {
173         return QString();
174     }
175     return QString(csoundGetOutputName(getCsound()));
176 }
177 
getScoreOffsetSeconds()178 double CsoundHtmlWrapper::getScoreOffsetSeconds() {
179     if (!m_csoundEngine) {
180         return -1;
181     }
182     return csoundGetScoreOffsetSeconds(getCsound());
183 }
184 
getScoreTime()185 double CsoundHtmlWrapper::getScoreTime() {
186     if (!m_csoundEngine) {
187         return -1;
188     }
189     return csoundGetScoreTime(getCsound());
190 }
191 
getSr()192 int CsoundHtmlWrapper::getSr() {
193     if (!m_csoundEngine) {
194         return -1;
195     }
196     return csoundGetSr(getCsound());
197 }
198 
getStringChannel(const QString & name)199 QString CsoundHtmlWrapper::getStringChannel(const QString &name) {
200     if (!m_csoundEngine) {
201         return QString();
202     }
203     char buffer[0x100];
204     csoundGetStringChannel(getCsound(),name.toLocal8Bit(), buffer);
205     return QString(buffer);
206 }
207 
getVersion()208 int CsoundHtmlWrapper::getVersion() {
209     return csoundGetVersion();
210 }
211 
isPlaying()212 bool CsoundHtmlWrapper::isPlaying() {
213     if (!m_csoundEngine) {
214         return false;
215     }
216     if (!getThread()) {
217         return false;
218     }
219     return (getThread()->GetStatus() == 0);
220 }
221 
isScorePending()222 int CsoundHtmlWrapper::isScorePending() {
223     if (!m_csoundEngine) {
224         return -1;
225     }
226     return csoundIsScorePending(getCsound());
227 }
228 
message(const QString & text)229 void CsoundHtmlWrapper::message(const QString &text) {
230     if (!m_csoundEngine) {
231         return;
232     }
233     qDebug() << text;
234     if (isPlaying()) {
235         csoundMessage(getCsound(), "%s", text.toLocal8Bit().constData());
236     }
237 }
238 
perform()239 int CsoundHtmlWrapper::perform() {
240     if (!m_csoundEngine) {
241         return -1;
242     }
243     if (getThread()) {
244         qDebug() << "Stopping existing Csound performance thread.";
245         getThread()->Stop();
246         getThread()->Join();
247         delete getThread();
248     }
249     getUserData()->perfThread = new CsoundPerformanceThread(getCsound());
250     getThread()->SetProcessCallback(CsoundEngine::csThread, (void*)getUserData());
251     getThread()->Play();
252     return 0;
253 }
254 
readScore(const QString & text)255 int CsoundHtmlWrapper::readScore(const QString &text) {
256     if (!m_csoundEngine) {
257         return -1;
258     }
259     if (isPlaying()) {
260         return csoundReadScore(getCsound(), text.toLocal8Bit());
261     }
262     return -1;
263 }
264 
reset()265 void CsoundHtmlWrapper::reset() {
266     if (!m_csoundEngine) {
267         return;
268     }
269     csoundReset(getCsound());
270 }
271 
rewindScore()272 void CsoundHtmlWrapper::rewindScore() {
273     if (!m_csoundEngine) {
274         return;
275     }
276     csoundRewindScore(getCsound());
277 }
278 
runUtility(const QString & command,int argc,char ** argv)279 int CsoundHtmlWrapper::runUtility(const QString &command, int argc, char **argv) {
280     if (!m_csoundEngine) {
281         return -1;
282     }
283     return csoundRunUtility(getCsound(), command.toLocal8Bit(), argc, argv); // probably does not work from JS due char **
284 }
285 
scoreEvent(char type,const double * pFields,long numFields)286 int CsoundHtmlWrapper::scoreEvent(char type, const double *pFields, long numFields) {
287     if (!m_csoundEngine) {
288         return -1;
289     }
290     return csoundScoreEvent(getCsound(),type, pFields, numFields);
291 }
292 
setControlChannel(const QString & name,double value)293 void CsoundHtmlWrapper::setControlChannel(const QString &name, double value) {
294     if (!m_csoundEngine) {
295         return;
296     }
297     csoundSetControlChannel(getCsound(),name.toLocal8Bit(), value);
298 }
299 
setGlobalEnv(const QString & name,const QString & value)300 int CsoundHtmlWrapper::setGlobalEnv(const QString &name, const QString &value) {
301     if (!m_csoundEngine) {
302         return -1;
303     }
304     return csoundSetGlobalEnv(name.toLocal8Bit(), value.toLocal8Bit());
305 }
306 
setInput(const QString & name)307 void CsoundHtmlWrapper::setInput(const QString &name){
308     if (!m_csoundEngine) {
309         return;
310     }
311 #if CS_APIVERSION>=4
312     csoundSetInput(getCsound(), name.toLocal8Bit());
313 #else
314     csoundSetInput(getCsound(), name.toLocal8Bit().data());
315 #endif
316 }
317 
setMessageCallback(QObject * callback)318 void CsoundHtmlWrapper::setMessageCallback(QObject *callback){
319     qDebug();
320     callback->dumpObjectInfo();
321 }
322 
setOption(const QString & name)323 int CsoundHtmlWrapper::setOption(const QString &name){
324     if (!m_csoundEngine) {
325         return -1;
326     }
327 #if CS_APIVERSION>=4
328     return csoundSetOption(getCsound(), name.toLocal8Bit());
329 #else
330     return csoundSetOption(getCsound(), name.toLocal8Bit().data());
331 #endif
332 }
333 
setOutput(const QString & name,const QString & type,const QString & format)334 void CsoundHtmlWrapper::setOutput(const QString &name, const QString &type, const QString &format){
335     if (!m_csoundEngine) {
336         return;
337     }
338 #if CS_APIVERSION>=4
339     csoundSetOutput(getCsound(), name.toLocal8Bit(), type.toLocal8Bit(), format.toLocal8Bit());
340 #else
341     csoundSetOutput(getCsound(), name.toLocal8Bit().data(), type.toLocal8Bit().data(), format.toLocal8Bit().data());
342 #endif
343 }
344 
setScoreOffsetSeconds(double value)345 void CsoundHtmlWrapper::setScoreOffsetSeconds(double value){
346     if (!m_csoundEngine) {
347         return;
348     }
349     csoundSetScoreOffsetSeconds(getCsound(), value);
350 }
351 
setScorePending(bool value)352 void CsoundHtmlWrapper::setScorePending(bool value){
353     if (!m_csoundEngine) {
354         return;
355     }
356     csoundSetScorePending(getCsound(),(int) value);
357 }
358 
setStringChannel(const QString & name,const QString & value)359 void CsoundHtmlWrapper::setStringChannel(const QString &name, const QString &value){
360     if (!m_csoundEngine) {
361         return;
362     }
363     csoundSetStringChannel(getCsound(),  name.toLocal8Bit(), value.toLocal8Bit().data());
364 }
365 
start()366 int CsoundHtmlWrapper::start(){
367     if (!m_csoundEngine) {
368         return -1;
369     }
370     return csoundStart(getCsound());
371 }
372 
stop()373 void CsoundHtmlWrapper::stop(){
374     if (!m_csoundEngine) {
375         return;
376     }
377     csoundStop(getCsound());
378 }
379 
tableGet(int table_number,int index)380 double CsoundHtmlWrapper::tableGet(int table_number, int index){
381     if (!m_csoundEngine) {
382         return -1;
383     }
384     return csoundTableGet(getCsound(), table_number, index);
385 }
386 
tableLength(int table_number)387 int CsoundHtmlWrapper::tableLength(int table_number){
388     if (!m_csoundEngine) {
389         return -1;
390     }
391     return csoundTableLength(getCsound(), table_number);
392 }
393 
tableSet(int table_number,int index,double value)394 void CsoundHtmlWrapper::tableSet(int table_number, int index, double value){
395     if (!m_csoundEngine) {
396         return;
397     }
398     csoundTableSet(getCsound(), table_number, index, value);
399 }
400 
401 
csoundMessageCallback_(CSOUND * csound,int attributes,const char * format,va_list args)402 void CsoundHtmlWrapper::csoundMessageCallback_(CSOUND *csound,
403                                          int attributes,
404                                          const char *format,
405                                          va_list args) {
406         return reinterpret_cast<CsoundHtmlWrapper *>(csoundGetHostData(csound))->csoundMessageCallback(attributes, format, args);
407 }
408 
csoundMessageCallback(int attributes,const char * format,va_list args)409 void CsoundHtmlWrapper::csoundMessageCallback(int attributes,
410                            const char *format,
411                            va_list args)
412 {
413     (void) attributes;
414 #ifdef  USE_QT_GT_54
415     QString message = QString::vasprintf(format, args);
416 #else
417     QString message;
418     message.sprintf(format, args); // NB! Should pass but not tested!
419 #endif
420     qDebug() << message;
421 //    if (!console->isHidden()) { // otherwise crash on exit
422 //        passMessages(message);
423 //    }
424     for (int i = 0, n = message.length(); i < n; i++) {
425         auto c = message[i];
426         if (c == '\n') {
427             QString code = "console.log(\"" + csoundMessageBuffer + "\\n\");";
428             if (csoundHtmlView != nullptr) {
429 #ifdef USE_WEBKIT
430 				csoundHtmlView->webView->page()->mainFrame()->evaluateJavaScript(code);
431 #else
432 				csoundHtmlView->webView->page()->runJavaScript(code);
433 #endif
434 
435             }
436             csoundMessageBuffer.clear();
437         } else {
438             csoundMessageBuffer.append(c);
439         }
440     }
441 }
442