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 #include <QProcess>
24 #include <QFile>
25 #include <QTemporaryFile>
26 #include <QTextStream>
27 #include <QDir>
28 #include <QApplication>
29 #include <QDebug>
30 
31 #include "csound.h"
32 #include "configlists.h"
33 
34 typedef QPair<QString, QString> QStringPair;
35 
36 
ConfigLists()37 ConfigLists::ConfigLists()
38 {
39     fileTypeNames      << "wav" << "aiff" << "au" << "avr"
40                        << "caf" << "flac" << "htk" << "ircam"
41                        << "mat4" << "mat5" << "nis" << "paf"
42                        << "pvf" << "raw" << "sd2" << "sds"
43                        << "svx" << "voc" << "w64" << "wavex";
44 
45     fileTypeExtensions << "*.wav" << "*.aif;*.aiff" << "*.au" << "*.avr"
46                        << "*.caf" << "*.flac" << "*.htk;*.*" << "*.ircam;*.*"
47                        << "*.mat4;*.*" << "*.mat5;*.*" << "*.nis;*.*" << "*.paf;*.*"
48                        << "*.pvf" << "*.raw;*.*" << "*.sd2;*.*" << "*.sds;*.*"
49 					   << "*.svx;*.*" << "*.voc;*.*" << "*.w64;*.wav" << "*.wavex;*.wav";
50 
51     fileTypeLongNames  << "WAVE" << "AIFF" << "au" << "avr"
52                        << "CAF" << "FLAC" << "htk" << "ircam"
53                        << "mat4" << "mat5" << "nis" << "paf"
54                        << "pvf" << "Raw (Headerless)" << "Sound Designer II" << "sds"
55                        << "svx" << "voc" << "WAVE (w64)" << "WAVE (wavex)";
56 
57 	fileFormatFlags << "24bit" << "short"<< "uchar"
58 					<< "schar"<< "float"<< "long";
59 	fileFormatNames << "24 Bit" << "16 Bit (short)" << "unsigned 8-bit"
60 					<< "signed 8-bit" << "32 bit float"<< "long (32-bit)";
61 
62 	refreshModules();
63 
64     languages << "English" << "Spanish" << "German" << "French" << "Portuguese"
65               << "Italian"  << "Turkish"  << "Finnish" << "Russian" << "Korean"
66               << "Persian";
67 
68     languageCodes << "en" << "es" << "de" << "fr" << "pt"
69                   << "it" << "tr" << "fi" << "ru" << "kr"
70                   << "fa";
71 }
72 
73 
~ConfigLists()74 ConfigLists::~ConfigLists()
75 {
76 }
77 
msgCallback(CSOUND * csound,int attr,const char * fmt,va_list args)78 void ConfigLists::msgCallback(CSOUND *csound, int attr, const char *fmt, va_list args)
79 {
80 	Q_UNUSED(attr);
81 	QString *ud = (QString *) csoundGetHostData(csound);
82 	QString msg;
83 	msg = msg.vsprintf(fmt, args);
84 	if (msg.isEmpty()) {
85 		return;
86 	}
87 	ud->append(msg);
88 }
89 
refreshModules()90 void ConfigLists::refreshModules()
91 {
92     rtMidiNames.clear();
93 	rtAudioNames.clear();
94     CSOUND *csound = csoundCreate(nullptr);
95 	char *name, *type;
96 	int n = 0;
97 	while(!csoundGetModule(csound, n++, &name, &type)) {
98 		if (strcmp(type, "audio") == 0) {
99 			rtAudioNames << name;
100 		}
101 	}
102 	rtAudioNames << "null"; // add also none (-+rtaudio=null)
103 	n = 0;
104 	while(!csoundGetModule(csound, n++, &name, &type)) {
105 		if (strcmp(type, "midi") == 0) {
106 			rtMidiNames << name;
107 		}
108 	}
109     if (rtAudioNames.contains("jack")) {
110         rtMidiNames <<  "jack";
111 	}
112     rtMidiNames << "virtual" << "none";
113     csoundDestroy(csound);
114 }
115 
getMidiInputDevices(QString module)116 QHash<QString,QString> ConfigLists::getMidiInputDevices(QString module)
117 {
118 	// based on code by Steven Yi
119     QHash<QString,QString> deviceList;
120 #ifdef CSOUND6
121     CSOUND *cs = csoundCreate(nullptr);
122 	if (module=="jack") {
123         csoundSetOption(cs, "-+rtaudio=jack");
124         csoundSetOption(cs, "-+rtmidi=jack");
125 	}
126 	csoundSetMIDIModule(cs, module.toLatin1().data());
127     int i,newn, n = csoundGetMIDIDevList(cs, nullptr, 0);
128 	CS_MIDIDEVICE *devs = (CS_MIDIDEVICE *) malloc(n*sizeof(CS_MIDIDEVICE));
129 	newn = csoundGetMIDIDevList(cs,devs,0);
130 	if (newn != n) {
131 		qDebug()  << "Device number changed";
132 		return deviceList;
133 	}
134 	for (i = 0; i < n; i++) {
135 		qDebug() << "Device "<<i << devs[i].device_name;
136 		QString displayName, id;
137 		if (module=="jack") {
138 			displayName = devs[i].device_name;
139 			id = devs[i].device_name;
140 		} else {
141             displayName = QString("%1 (%2)")
142                     .arg(devs[i].device_name)
143                     .arg(devs[i].interface_name);
144 			id = QString(devs[i].device_id);
145 		}
146 		deviceList.insert(displayName, id);
147 	}
148 	free(devs);
149     csoundDestroy(cs);
150 #else
151 	if (module == "none") {
152 		return deviceList;
153 	}
154 	if (module == "alsa") {
155 		QProcess amidi;
156 		amidi.start("amidi", QStringList() << "-l");
157 		if (!amidi.waitForFinished())
158 			return deviceList;
159 
160 		QByteArray result = amidi.readAllStandardOutput();
161 		QString values = QString(result);
162 		QStringList st = values.split("\n");
163 		st.takeFirst(); // Remove first column lines
164 		for (int i = 0; i < st.size(); i++){
165 			QStringList parts = st[i].split(" ", QString::SkipEmptyParts);
166 			if (parts.size() > 0 && parts[0].contains("I")) {
167 				QString devname = parts[1]; // Devce name
168 				parts.takeFirst(); // Remove IO flags
169 				QString fullname = parts.join(" ") ; // Full name with description
170 				deviceList.insert(fullname, devname);
171 			}
172 		}
173 		deviceList.insert("All available devices ", "a");
174 	}
175 	else if (module == "virtual") {
176 		QString name = qApp->translate("Enabled", "Virtual MIDI keyboard Enabled");
177 		deviceList.insert(name, "0");
178 	}
179 	else { // if not alsa (i.e. winmm or portmidi)
180 		QFile file(":/test.csd");
181 		if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
182 			return deviceList;
183 		QString jackCSD = QString(file.readAll());
184 		QString tempText = jackCSD;
185 		tempText.replace("$SR", "441000");
186 		QTemporaryFile tempFile(QDir::tempPath() + QDir::separator() + "testcsdCsoundQtXXXXXX.csd");
187 		tempFile.open();
188 		QTextStream out(&tempFile);
189 		out << tempText;
190 		tempFile.close();
191 		tempFile.open();
192 
193 		QStringList flags;
194 		QString rtMidiFlag = "-+rtmidi=" + module;
195 		flags << "-+msg_color=false" << rtMidiFlag << "-otest"  << "-n"  << "-M999" << tempFile.fileName();
196 		QStringList messages = runCsoundInternally(flags);
197 
198 		QString startText, endText;
199 		if (module == "portmidi") {
200 			startText = "The available MIDI";
201 			endText = "*** PortMIDI";
202 		}
203 		else if (module == "winmm") {
204 			startText = "The available MIDI";
205 			endText = "rtmidi: input device number is out of range";
206 		}
207 		else if (module == "coremidi") {
208 			int index = messages.indexOf(QRegExp("[0-9]{1,1} MIDI sources in system\\s*"));
209             if (index >= 0) {
210                 for (int i = 0; i < messages[index].split(" ")[0].toInt(); i++) {
211                     deviceList.insert(QString::number(i), QString::number(i));
212                 }
213             }
214 		}
215 		else if (module == "alsaseq") {
216 			//FIXME parse alsaseq devices
217 		}
218 		if (startText == "" && endText == "") {
219 			return deviceList;
220 		}
221 		bool collect = false;
222 		foreach (QString line, messages) {
223 			if (collect) {
224 				if (endText.length() > 0 && line.indexOf(endText) >= 0) {
225 					collect = false;
226 				}
227 				else {
228 					if (line.indexOf(":") >= 0) {
229 //						qDebug()  << line;
230 						QString fullname = line.mid(line.indexOf(":") + 1).trimmed();
231 						QString devname = line.mid(0,line.indexOf(":")).trimmed();
232 						deviceList.insert(fullname, devname);
233 					}
234 				}
235 			}
236 			else if (line.indexOf(startText) >= 0) {
237 				collect = true;
238 			}
239 		}
240 	}
241 #endif
242 	qDebug()<<"Devices found: "<<deviceList;
243 	return deviceList;
244 }
245 
getMidiOutputDevices(QString module)246 QList<QPair<QString, QString> > ConfigLists::getMidiOutputDevices(QString module)
247 {
248 	QList<QPair<QString, QString> > deviceList;
249 #ifdef CSOUND6
250     CSOUND *cs = csoundCreate(nullptr);
251 	if (module=="jack") {
252         csoundSetOption(cs, "-+rtaudio=jack");
253         csoundSetOption(cs, "-+rtmidi=jack");
254 	}
255 	csoundSetMIDIModule(cs, module.toLatin1().data());
256     int i,newn, n = csoundGetMIDIDevList(cs, nullptr, 1);
257 	CS_MIDIDEVICE *devs = (CS_MIDIDEVICE *) malloc(n*sizeof(CS_MIDIDEVICE));
258 	newn = csoundGetMIDIDevList(cs,devs,1);
259 	if (newn != n) {
260 		qDebug()  << "Device number changed";
261 		return deviceList;
262 	}
263 	for (i = 0; i < n; i++) {
264         // qDebug() << devs[i].device_name;
265 		QString displayName, id;
266 		if (module=="jack") {
267 			displayName = devs[i].device_name;
268 			id = devs[i].device_name;
269 		} else {
270             displayName = QString("%1 (%2)")
271                     .arg(devs[i].device_name)
272                     .arg(devs[i].interface_name);
273 			id = QString(devs[i].device_id);
274 		}
275 
276         deviceList.append(QStringPair(displayName, id));
277 	}
278 	free(devs);
279     csoundDestroy(cs);
280 #else
281 	if (module == "none") {
282 		return deviceList;
283 	}
284 	if (module == "alsa") {
285 		QProcess amidi;
286 		amidi.start("amidi", QStringList() << "-l");
287 		if (!amidi.waitForFinished())
288 			return deviceList;
289 
290 		QByteArray result = amidi.readAllStandardOutput();
291 		QString values = QString(result);
292 		QStringList st = values.split("\n");
293 		st.takeFirst(); // Remove first column lines
294 		for (int i = 0; i < st.size(); i++){
295 			QStringList parts = st[i].split(" ", QString::SkipEmptyParts);
296 			if (parts.size() > 0 && parts[0].contains("O")) {
297 				QPair<QString, QString> device;
298 				device.second = parts[1]; // Devce name
299 				parts.takeFirst(); // Remove IO flags
300 				device.first = parts.join(" ") ; // Full name with description
301 				deviceList.append(device);
302 			}
303 		}
304 	}
305 	else if (module == "virtual") {
306 		// do nothing
307 	}
308 	else { // if not alsa (i.e. winmm or portmidi)
309 		QFile file(":/test.csd");
310 		if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
311 			return deviceList;
312 		QString jackCSD = QString(file.readAll());
313 		QString tempText = jackCSD;
314 		tempText.replace("$SR", "441000");
315 		QTemporaryFile tempFile(QDir::tempPath() + QDir::separator() + "testcsdCsoundQtXXXXXX.csd");
316 		tempFile.open();
317 		QTextStream out(&tempFile);
318 		out << tempText;
319 		tempFile.close();
320 		tempFile.open();
321 
322 		QStringList flags;
323 		flags << "-+msg_color=false" << "-otest"  << "-n"   << "-Q999" << tempFile.fileName();
324 		QStringList messages = runCsoundInternally(flags);
325 
326 		QString startText, endText;
327 		if (module == "portmidi") {
328 			startText = "The available MIDI";
329 			endText = "*** PortMIDI";
330 		}
331 		else if (module == "winmm") {
332 			startText = "The available MIDI";
333 			endText = "rtmidi: output device number is out of range";
334 		}
335 		if (startText == "" && endText == "") {
336 			return deviceList;
337 		}
338 		bool collect = false;
339 		foreach (QString line, messages) {
340 			if (collect) {
341 				if (endText.length() > 0 && line.indexOf(endText) >= 0) {
342 					collect = false;
343 				}
344 				else {
345 					if (line.indexOf(":") >= 0) {
346 						qDebug("%s", line.toLocal8Bit().constData());
347 						QPair<QString, QString> device;
348 						device.first = line.mid(line.indexOf(":") + 1).trimmed();
349 						device.second = line.mid(0,line.indexOf(":")).trimmed();
350 						deviceList.append(device);
351 					}
352 				}
353 			}
354 			else if (line.indexOf(startText) >= 0) {
355 				collect = true;
356 			}
357 		}
358 	}
359 #endif
360 	return deviceList;
361 }
362 
getAudioInputDevices(QString module)363 QList<QPair<QString, QString> > ConfigLists::getAudioInputDevices(QString module)
364 {
365     QList<QStringPair> deviceList;
366 #ifdef CSOUND6
367     CSOUND *cs = csoundCreate(nullptr);
368 	csoundSetRTAudioModule(cs, module.toLatin1().data());
369     int i,newn, n = csoundGetAudioDevList(cs, nullptr, 0);
370 	CS_AUDIODEVICE *devs = (CS_AUDIODEVICE *) malloc(n*sizeof(CS_AUDIODEVICE));
371 	newn = csoundGetAudioDevList(cs,devs,0);
372 	if (newn != n) {
373 		qDebug()  << "Device number changed";
374 		return deviceList;
375 	}
376 	for (i = 0; i < n; i++) {
377         // qDebug() << "evs[i].device_name;
378         deviceList.append(QStringPair(devs[i].device_name, devs[i].device_id));
379 	}
380 	free(devs);
381     csoundDestroy(cs);
382 #else
383 	if (module == "none") {
384 		return deviceList;
385 	}
386 	if (module == "alsa") {
387 		QFile f("/proc/asound/pcm");
388 		f.open(QIODevice::ReadOnly | QIODevice::Text);
389 		QString values = QString(f.readAll());
390 		QStringList st = values.split("\n");
391 		foreach (QString line, st) {
392 			if (line.indexOf("capture") >= 0) {
393 				QStringList parts = line.split(":");
394 
395 				QStringList cardId = parts[0].split("-");
396 				QString buffer = "";
397 				buffer.append(parts[1]).append(" : ").append(parts[2]);
398 
399 				QPair<QString, QString> device;
400 				device.first = buffer;
401 				device.second = "adc:hw:" + QString::number(cardId[0].toInt()) + "," + QString::number(cardId[1].toInt());
402 				deviceList.append(device);
403 			}
404 		}
405 	}
406 	else if (module == "jack") {
407 		QFile file(":/test.csd");
408 		if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
409 			return deviceList;
410 		QString jackCSD = QString(file.readAll());
411 		QString tempText = jackCSD;
412 		tempText.replace("$SR", "1000");
413 		QTemporaryFile tempFile(QDir::tempPath() + QDir::separator() + "testcsdCsoundQtXXXXXX.csd");
414 		tempFile.open();
415 		QTextStream out(&tempFile);
416 		out << tempText;
417 		tempFile.close();
418 		tempFile.open();
419 
420 		QStringList flags;
421 		QString previousLine = "";
422 		// -odac is needed otherwise csound segfaults
423 		flags << "-+msg_color=false" << "-+rtaudio=jack" << "-iadc:xxx" << "-odac:xxx" << "-B2048" <<  tempFile.fileName();
424 		QStringList messages = runCsoundInternally(flags);
425 
426 		QString sr = "";
427 		foreach (QString line, messages) { // Need to run again if sample rate does not match...
428 			if (line.indexOf("does not match JACK sample rate") >= 0) {
429 				sr = line.mid(line.lastIndexOf(" ") + 1);
430 			}
431 			if (line.endsWith("channel)\n") || line.endsWith("channels)\n")) {
432 				QStringList parts = previousLine.split("\"");
433 				QPair<QString, QString> device;
434 				device.first = parts[1];
435 				device.second = "adc:" + parts[1];
436 				deviceList.append(device);
437 			}
438 			previousLine = line;
439 		}
440 		if (sr == "") {
441 			return deviceList;
442 		}
443 
444 		tempText = jackCSD;
445 		tempText.replace("$SR", sr);
446 		out << tempText;
447 		tempFile.close();
448 		tempFile.open();
449 
450 		messages = runCsoundInternally(flags); // run with same flags as before
451 
452 		foreach (QString line, messages) {
453 			if (line.endsWith("channel)\n") || line.endsWith("channels)\n")) {
454 				QStringList parts = previousLine.split("\"");
455 				QPair<QString, QString> device;
456 				device.first = parts[1];
457 				device.second = "adc:" + parts[1];
458 				deviceList.append(device);
459 			}
460 			previousLine = line;
461 		}
462 	}  //ends if (module=="jack")
463 	else { // if not alsa or jack (i.e. coreaudio or portaudio)
464 		QFile file(":/test.csd");
465 		if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
466 			return deviceList;
467 		QString jackCSD = QString(file.readAll());
468 		QString tempText = jackCSD;
469 		tempText.replace("$SR", "1000");
470 		QTemporaryFile tempFile(QDir::tempPath() + QDir::separator() + "testcsdCsoundQtXXXXXX.csd");
471 		tempFile.open();
472 		QTextStream out(&tempFile);
473 		out << tempText;
474 		tempFile.close();
475 		tempFile.open();
476 
477 		QStringList flags;
478 		QString rtAudioFlag = "-+rtaudio=" + module + " ";
479 		flags << "-+msg_color=false" << rtAudioFlag << "-iadc999" << "-odac999" << "-d" << tempFile.fileName();
480 		QStringList messages = runCsoundInternally(flags);
481 
482 		QString startText, endText;
483 		if (module=="portaudio") {
484 			startText = "PortAudio: available";
485 			endText = "error:";
486 		}
487 		else if (module=="winmm" || module=="mme") {
488 			startText = "The available input devices are:";
489 			endText = "device number is out of range";
490 		}
491 		else if (module=="coreaudio") {
492 			startText = "CoreAudio Module: found";
493 			endText = "";
494 		}
495 		if (startText == "" && endText == "") {
496 			return deviceList;
497 		}
498 		bool collect = false;
499 		foreach (QString line, messages) {
500 			if (collect) {
501 				if (endText.length() > 0 && line.indexOf(endText) >= 0) {
502 					collect = false;
503 				}
504 				else {
505 					if (module == "coreaudio") {
506 						QString coreAudioMatch = "=> CoreAudio device";
507 
508 						if (line.indexOf(coreAudioMatch) >= 0) {
509 							line = line.mid(coreAudioMatch.length()).trimmed();
510 
511 							QPair<QString, QString> device;
512 							device.first = line.mid(line.indexOf(":")).trimmed();
513 							device.second = "adc" + line.left(line.indexOf(":"));
514 							deviceList.append(device);
515 						}
516 					} // if not coreaudio, i.e. portaudio
517 					else if (line.indexOf(":") >= 0) {
518 						QPair<QString, QString> device;
519 						device.first = line.mid(line.indexOf(":") + 1).trimmed();
520 						device.second = "adc" + line.left(line.indexOf(":")).trimmed();
521 						deviceList.append(device);
522 					}
523 				}
524 			}
525 			else if (line.indexOf(startText) >= 0) {
526 				collect = true;
527 			}
528 		}
529 	}
530 #endif
531 	return deviceList;
532 }
533 
534 
getAudioOutputDevices(QString module)535 QList<QPair<QString, QString> > ConfigLists::getAudioOutputDevices(QString module)
536 {
537     QList<QPair<QString, QString> > deviceList;
538 #ifdef CSOUND6
539     CSOUND *cs = csoundCreate(nullptr);
540 	csoundSetRTAudioModule(cs, module.toLatin1().data());
541     int i,newn, n = csoundGetAudioDevList(cs, nullptr, 1);
542 	CS_AUDIODEVICE *devs = (CS_AUDIODEVICE *) malloc(n*sizeof(CS_AUDIODEVICE));
543 	newn = csoundGetAudioDevList(cs,devs,1);
544 	if (newn != n) {
545 		qDebug()  << "OutputDevices Device number changed";
546 		return deviceList;
547 	}
548 	for (i = 0; i < n; i++) {
549         // qDebug()  << devs[i].device_name;
550         deviceList.append(QStringPair(devs[i].device_name, devs[i].device_id));
551 	}
552 	free(devs);
553     csoundDestroy(cs);
554 #else
555 	if (module == "none") {
556 		return deviceList;
557 	}
558 	if (module == "alsa") {
559 		QFile f("/proc/asound/pcm");
560 		f.open(QIODevice::ReadOnly | QIODevice::Text);
561 		QString values = QString(f.readAll());
562 		QStringList st = values.split("\n");
563 		foreach (QString line, st) {
564 			if (line.indexOf("playback") >= 0) {
565 				QStringList parts = line.split(":");
566 
567 				QStringList cardId = parts[0].split("-");
568 				QString buffer = "";
569 				buffer.append(parts[1]).append(" : ").append(parts[2]);
570 
571 				QPair<QString, QString> device;
572 				device.first = buffer;
573 				device.second = "dac:hw:" + QString::number(cardId[0].toInt()) + "," + QString::number(cardId[1].toInt());
574 				deviceList.append(device);
575 			}
576 		}
577 	}
578 	else if (module=="jack") {
579 		QFile file(":/test.csd");
580 		if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
581 			return deviceList;
582 		QString jackCSD = QString(file.readAll());
583 		QString tempText = jackCSD;
584 		tempText.replace("$SR", "1000");
585 		QTemporaryFile tempFile(QDir::tempPath() + QDir::separator() + "testcsdCsoundQtXXXXXX.csd");
586 		tempFile.open();
587 		QTextStream out(&tempFile);
588 		out << tempText;
589 		tempFile.close();
590 		tempFile.open();
591 
592 		QStringList flags;
593 		QString previousLine = "";
594 		flags << "-+msg_color=false" << "-+rtaudio=jack" << "-odac:xxx" << "-B2048" <<  tempFile.fileName();
595 		QStringList messages = runCsoundInternally(flags);
596 
597 		QString sr = "";
598 		foreach (QString line, messages) {
599 			if (line.indexOf("does not match JACK sample rate") >= 0) {
600 				sr = line.mid(line.lastIndexOf(" ") + 1);
601 			}
602 			if (line.endsWith("channel)\n") || line.endsWith("channels)\n")) {
603 				QStringList parts = previousLine.split("\"");
604 				QPair<QString, QString> device;
605 				device.first = parts[1];
606 				device.second = "dac:" + parts[1];
607 				deviceList.append(device);
608 			}
609 			previousLine = line;
610 		}
611 		if (sr == "") {
612 			return deviceList;
613 		}
614 
615 		tempText = jackCSD;
616 		tempText.replace("$SR", sr);
617 		out << tempText;
618 		tempFile.close();
619 		tempFile.open();
620 
621 		messages = runCsoundInternally(flags); // run with same flags as before
622 
623 		foreach (QString line, messages) {
624 			if (line.endsWith("channel)\n") || line.endsWith("channels)\n")) {
625 				QStringList parts = previousLine.split("\"");
626 				QPair<QString, QString> device;
627 				device.first = parts[1];
628 				device.second = "dac:" + parts[1];
629 				deviceList.append(device);
630 			}
631 			previousLine = line;
632 		}
633 	}  //ends if (module=="jack")
634 	else { // if not alsa or jack (i.e. coreaudio or portaudio)
635 		QFile file(":/test.csd");
636 		if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
637 			return deviceList;
638 		QString jackCSD = QString(file.readAll());
639 		QString tempText = jackCSD;
640 		tempText.replace("$SR", "1000");
641 		QTemporaryFile tempFile(QDir::tempPath() + QDir::separator() + "testcsdCsoundQtXXXXXX.csd");
642 		tempFile.open();
643 		QTextStream out(&tempFile);
644 		out << tempText;
645 		tempFile.close();
646 		tempFile.open();
647 
648 		QStringList flags;
649 		QString rtAudioFlag = "-+rtaudio=" + module + " ";
650 		flags << "-+msg_color=false" << rtAudioFlag << "-odac999" << tempFile.fileName();
651 		QStringList messages = runCsoundInternally(flags);
652 
653 		QString startText, endText;
654 		if (module=="portaudio") {
655 			startText = "PortAudio: available";
656 			endText = "error:";
657 		}
658 		else if (module=="winmm" || module=="mme") {
659 			startText = "The available output devices are:";
660 			endText = "device number is out of range";
661 		}
662 		else if (module=="coreaudio") {
663 			startText = "CoreAudio Module: found";
664 			endText = "";
665 		}
666 		if (startText == "" && endText == "") {
667 			return deviceList;
668 		}
669 		bool collect = false;
670 		foreach (QString line, messages) {
671 			if (collect) {
672 				if (endText.length() > 0 && line.indexOf(endText) >= 0) {
673 					collect = false;
674 				}
675 				else {
676 					if (module == "coreaudio") {
677 						QString coreAudioMatch = "=> CoreAudio device";
678 
679 						if (line.indexOf(coreAudioMatch) >= 0) {
680 							line = line.mid(coreAudioMatch.length()).trimmed();
681 
682 							QPair<QString, QString> device;
683 							device.first = line.mid(line.indexOf(":")).trimmed();
684 							device.second = "dac" + line.left(line.indexOf(":"));
685 							deviceList.append(device);
686 						}
687 					} // if not coreaudio, i.e. portaudio
688 					else if (line.indexOf(":") >= 0) {
689 						QPair<QString, QString> device;
690 						device.first = line.mid(line.indexOf(":") + 1).trimmed();
691 						device.second = "dac" + line.left(line.indexOf(":")).trimmed();
692 						deviceList.append(device);
693 					}
694 				}
695 			}
696 			else if (line.indexOf(startText) >= 0) {
697 				collect = true;
698 			}
699 		}
700 	}
701 #endif
702 	return deviceList;
703 }
704 
runCsoundInternally(QStringList flags)705 QStringList ConfigLists::runCsoundInternally(QStringList flags)
706 {
707 #if CS_APIVERSION>=4
708 	const char *argv[33];
709 #else
710 	char *argv[33];
711 #endif
712     int index = 1;
713     Q_ASSERT(flags.size() < 32);
714 	argv[0]  = (char *) calloc(7, sizeof(char));
715 	strncpy((char *) argv[0], "csound", 6);
716 
717 	foreach (QString flag, flags) {
718 		argv[index] = (char *) calloc(flag.size()+1, sizeof(char));
719 		strncpy((char *) argv[index], flag.toLatin1(), flag.size());
720 		index++;
721 	}
722     int argc = flags.size() + 1;
723 #ifdef MACOSX_PRE_SNOW
724 	//Remember menu bar to set it after FLTK grabs it
725 	menuBarHandle = GetMenuBar();
726 #endif
727 	CSOUND *csoundD;
728 	m_messages.clear();
729 	csoundD=csoundCreate(&m_messages);
730 
731 	csoundSetMessageCallback(csoundD, msgCallback);
732 	int result = csoundCompile(csoundD,argc,argv);
733 
734 	if(!result) {
735 		csoundPerform(csoundD);
736     } else {
737         qDebug() << "Error compiling.";
738         qDebug() << m_messages;
739     }
740 
741     // FIXME This crashes on Linux for portmidi! And messes up devices on OS X
742     csoundDestroy(csoundD);
743 
744 #ifdef MACOSX_PRE_SNOW
745 	// Put menu bar back
746 	SetMenuBar(menuBarHandle);
747 #endif
748 	return m_messages.split("\n");
749 }
750 
751 
isJackRunning()752 bool ConfigLists::isJackRunning() {
753     CSOUND *cs = csoundCreate(nullptr);
754     csoundSetRTAudioModule(cs, "jack");
755     int n = csoundGetAudioDevList(cs, nullptr, 1);
756     qDebug() << "isJackRunning" << n;
757     csoundDestroy(cs);
758     return n > 0;
759 }
760