1 // Copyright 2005-2019 The Mumble Developers. All rights reserved.
2 // Use of this source code is governed by a BSD-style license
3 // that can be found in the LICENSE file at the root of the
4 // Mumble source tree or at <https://www.mumble.info/LICENSE>.
5 
6 #include "mumble_pch.hpp"
7 
8 #include "ASIOInput.h"
9 
10 #include "MainWindow.h"
11 #include "Global.h"
12 
13 // From os_win.cpp.
14 extern HWND mumble_mw_hwnd;
15 
16 class ASIOAudioInputRegistrar : public AudioInputRegistrar {
17 	public:
18 		ASIOAudioInputRegistrar();
19 		virtual AudioInput *create();
20 		virtual const QList<audioDevice> getDeviceChoices();
21 		virtual void setDeviceChoice(const QVariant &, Settings &);
22 		virtual bool canEcho(const QString &) const;
23 
24 };
25 
ASIOAudioInputRegistrar()26 ASIOAudioInputRegistrar::ASIOAudioInputRegistrar() : AudioInputRegistrar(QLatin1String("ASIO")) {
27 }
28 
create()29 AudioInput *ASIOAudioInputRegistrar::create() {
30 	return new ASIOInput();
31 }
getDeviceChoices()32 const QList<audioDevice> ASIOAudioInputRegistrar::getDeviceChoices() {
33 	QList<audioDevice> qlReturn;
34 	return qlReturn;
35 }
36 
canEcho(const QString &) const37 bool ASIOAudioInputRegistrar::canEcho(const QString &) const {
38 	return true;
39 }
40 
setDeviceChoice(const QVariant &,Settings &)41 void ASIOAudioInputRegistrar::setDeviceChoice(const QVariant &, Settings &) {
42 }
43 
ASIOConfigDialogNew(Settings & st)44 static ConfigWidget *ASIOConfigDialogNew(Settings &st) {
45 	return new ASIOConfig(st);
46 }
47 
48 class ASIOInit : public DeferInit {
49 		ASIOAudioInputRegistrar *airASIO;
50 		ConfigRegistrar *crASIO;
51 	public:
ASIOInit()52 		ASIOInit() : airASIO(NULL), crASIO(NULL) {}
53 		void initialize();
54 		void destroy();
55 };
56 
initialize()57 void ASIOInit::initialize() {
58 	HKEY hkDevs;
59 	HKEY hk;
60 	FILETIME ft;
61 
62 	airASIO = NULL;
63 	crASIO = NULL;
64 
65 	bool bFound = false;
66 
67 	if (!g.s.bASIOEnable) {
68 		qWarning("ASIOInput: ASIO forcefully disabled via 'asio/enable' config option.");
69 		return;
70 	}
71 
72 	// List of devices known to misbehave or be totally useless
73 	QStringList blacklist;
74 	blacklist << QLatin1String("{a91eaba1-cf4c-11d3-b96a-00a0c9c7b61a}"); // ASIO DirectX
75 	blacklist << QLatin1String("{e3186861-3a74-11d1-aef8-0080ad153287}"); // ASIO Multimedia
76 #ifdef QT_NO_DEBUG
77 	blacklist << QLatin1String("{232685c6-6548-49d8-846d-4141a3ef7560}"); // ASIO4ALL
78 #endif
79 	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\ASIO", 0, KEY_READ, &hkDevs) == ERROR_SUCCESS) {
80 		DWORD idx = 0;
81 		DWORD keynamelen = 255;
82 		WCHAR keyname[255];
83 		while (RegEnumKeyEx(hkDevs, idx++, keyname, &keynamelen, NULL, NULL, NULL, &ft)  == ERROR_SUCCESS) {
84 			QString name=QString::fromUtf16(reinterpret_cast<ushort *>(keyname),keynamelen);
85 			if (RegOpenKeyEx(hkDevs, keyname, 0, KEY_READ, &hk) == ERROR_SUCCESS) {
86 				DWORD dtype = REG_SZ;
87 				WCHAR wclsid[255];
88 				DWORD datasize = 255;
89 				CLSID clsid;
90 				if (RegQueryValueEx(hk, L"CLSID", 0, &dtype, reinterpret_cast<BYTE *>(wclsid), &datasize) == ERROR_SUCCESS) {
91 					if (datasize > 76)
92 						datasize = 76;
93 					QString qsCls = QString::fromUtf16(reinterpret_cast<ushort *>(wclsid), datasize / 2).toLower().trimmed();
94 					if (! blacklist.contains(qsCls.toLower()) && ! FAILED(CLSIDFromString(wclsid, &clsid))) {
95 						bFound = true;
96 					}
97 				}
98 				RegCloseKey(hk);
99 			}
100 			keynamelen = 255;
101 		}
102 		RegCloseKey(hkDevs);
103 	}
104 
105 	if (bFound) {
106 		airASIO = new ASIOAudioInputRegistrar();
107 		crASIO = new ConfigRegistrar(2002, ASIOConfigDialogNew);
108 	} else {
109 		qWarning("ASIO: No valid devices found, disabling");
110 	}
111 }
112 
destroy()113 void ASIOInit::destroy() {
114 	delete airASIO;
115 	delete crASIO;
116 }
117 
118 static class ASIOInit asioinit;
119 
120 ASIOInput *ASIOInput::aiSelf;
121 
ASIOConfig(Settings & st)122 ASIOConfig::ASIOConfig(Settings &st) : ConfigWidget(st) {
123 	setupUi(this);
124 
125 	// List of devices known to misbehave or be totally useless
126 	QStringList blacklist;
127 	blacklist << QLatin1String("{a91eaba1-cf4c-11d3-b96a-00a0c9c7b61a}"); // ASIO DirectX
128 	blacklist << QLatin1String("{e3186861-3a74-11d1-aef8-0080ad153287}"); // ASIO Multimedia
129 #ifdef QT_NO_DEBUG
130 	blacklist << QLatin1String("{232685c6-6548-49d8-846d-4141a3ef7560}"); // ASIO4ALL
131 #endif
132 	HKEY hkDevs;
133 	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\ASIO", 0, KEY_READ, &hkDevs) == ERROR_SUCCESS) {
134 		const DWORD keynamebufsize = 255;
135 		WCHAR keyname[keynamebufsize];
136 
137 		FILETIME ft;
138 		DWORD idx = 0;
139 		DWORD keynamelen = keynamebufsize;
140 		while (RegEnumKeyEx(hkDevs, idx++, keyname, &keynamelen, NULL, NULL, NULL, &ft) == ERROR_SUCCESS) {
141 			QString name=QString::fromUtf16(reinterpret_cast<ushort *>(keyname), keynamelen);
142 			HKEY hk;
143 			if (RegOpenKeyEx(hkDevs, keyname, 0, KEY_READ, &hk) == ERROR_SUCCESS) {
144 				DWORD dtype = REG_SZ;
145 				WCHAR wclsid[255];
146 				DWORD datasize = 255;
147 				if (RegQueryValueEx(hk, L"CLSID", 0, &dtype, reinterpret_cast<BYTE *>(wclsid), &datasize) == ERROR_SUCCESS) {
148 					if (datasize > 76)
149 						datasize = 76;
150 					QString qsCls = QString::fromUtf16(reinterpret_cast<ushort *>(wclsid), datasize / 2).toLower().trimmed();
151 					CLSID clsid;
152 					if (! blacklist.contains(qsCls) && ! FAILED(CLSIDFromString(wclsid, &clsid))) {
153 						ASIODev ad(name, qsCls);
154 						qlDevs << ad;
155 					}
156 				}
157 				RegCloseKey(hk);
158 			}
159 			keynamelen = keynamebufsize;
160 		}
161 		RegCloseKey(hkDevs);
162 	}
163 
164 	bOk = false;
165 
166 	ASIODev ad;
167 
168 	foreach(ad, qlDevs) {
169 		qcbDevice->addItem(ad.first, QVariant(ad.second));
170 	}
171 
172 	if (qlDevs.count() == 0) {
173 		qpbQuery->setEnabled(false);
174 		qpbConfig->setEnabled(false);
175 	}
176 }
177 
on_qpbQuery_clicked()178 void ASIOConfig::on_qpbQuery_clicked() {
179 	QString qsCls = qcbDevice->itemData(qcbDevice->currentIndex()).toString();
180 	CLSID clsid;
181 	IASIO *iasio;
182 
183 	clearQuery();
184 
185 	CLSIDFromString(const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(qsCls.utf16())), &clsid);
186 	if (CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, clsid, reinterpret_cast<void **>(&iasio)) == S_OK) {
187 		SleepEx(10, false);
188 		if (iasio->init(mumble_mw_hwnd)) {
189 			SleepEx(10, false);
190 			char buff[512];
191 			memset(buff, 0, 512);
192 
193 			iasio->getDriverName(buff);
194 			SleepEx(10, false);
195 
196 			long ver = iasio->getDriverVersion();
197 			SleepEx(10, false);
198 
199 			ASIOSampleRate srate = 0.0;
200 			iasio->setSampleRate(48000.0);
201 			iasio->getSampleRate(&srate);
202 			SleepEx(10, false);
203 
204 			long minSize, maxSize, prefSize, granSize;
205 			iasio->getBufferSize(&minSize, &maxSize, &prefSize, &granSize);
206 			SleepEx(10, false);
207 
208 			QString str = tr("%1 (version %2)").arg(QLatin1String(buff)).arg(ver);
209 			qlName->setText(str);
210 
211 			str = tr("%1 -> %2 samples buffer, with %3 sample resolution (%4 preferred) at %5 Hz").arg(minSize).arg(maxSize).arg(granSize).arg(prefSize).arg(srate,0,'f',0);
212 
213 			qlBuffers->setText(str);
214 
215 			long ichannels, ochannels;
216 			iasio->getChannels(&ichannels, &ochannels);
217 			SleepEx(10, false);
218 			long cnum;
219 
220 			bool match = (s.qsASIOclass == qsCls);
221 
222 			for (cnum=0;cnum<ichannels;cnum++) {
223 				ASIOChannelInfo aci;
224 				aci.channel = cnum;
225 				aci.isInput = true;
226 				iasio->getChannelInfo(&aci);
227 				SleepEx(10, false);
228 				switch (aci.type) {
229 					case ASIOSTFloat32LSB:
230 					case ASIOSTInt32LSB:
231 					case ASIOSTInt24LSB:
232 					case ASIOSTInt16LSB: {
233 							QListWidget *widget = qlwUnused;
234 							QVariant v = static_cast<int>(cnum);
235 							if (match && s.qlASIOmic.contains(v))
236 								widget = qlwMic;
237 							else if (match && s.qlASIOspeaker.contains(v))
238 								widget = qlwSpeaker;
239 							QListWidgetItem *item = new QListWidgetItem(QLatin1String(aci.name), widget);
240 							item->setData(Qt::UserRole, static_cast<int>(cnum));
241 						}
242 						break;
243 					default:
244 						qWarning("ASIOInput: Channel %ld %s (Unusable format %ld)", cnum, aci.name,aci.type);
245 				}
246 			}
247 
248 			bOk = true;
249 		} else {
250 			SleepEx(10, false);
251 			char err[255];
252 			iasio->getErrorMessage(err);
253 			SleepEx(10, false);
254 			QMessageBox::critical(this, QLatin1String("Mumble"), tr("ASIO Initialization failed: %1").arg(Qt::escape(QLatin1String(err))), QMessageBox::Ok, QMessageBox::NoButton);
255 		}
256 		iasio->Release();
257 	} else {
258 		QMessageBox::critical(this, QLatin1String("Mumble"), tr("Failed to instantiate ASIO driver"), QMessageBox::Ok, QMessageBox::NoButton);
259 	}
260 }
261 
on_qpbConfig_clicked()262 void ASIOConfig::on_qpbConfig_clicked() {
263 	QString qsCls = qcbDevice->itemData(qcbDevice->currentIndex()).toString();
264 	CLSID clsid;
265 	IASIO *iasio;
266 
267 	CLSIDFromString(const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(qsCls.utf16())), &clsid);
268 	if (CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, clsid, reinterpret_cast<void **>(&iasio)) == S_OK) {
269 		SleepEx(10, false);
270 		if (iasio->init(mumble_mw_hwnd)) {
271 			SleepEx(10, false);
272 			iasio->controlPanel();
273 			SleepEx(10, false);
274 		} else {
275 			SleepEx(10, false);
276 			char err[255];
277 			iasio->getErrorMessage(err);
278 			SleepEx(10, false);
279 			QMessageBox::critical(this, QLatin1String("Mumble"), tr("ASIO Initialization failed: %1").arg(Qt::escape(QLatin1String(err))), QMessageBox::Ok, QMessageBox::NoButton);
280 		}
281 		iasio->Release();
282 	} else {
283 		QMessageBox::critical(this, QLatin1String("Mumble"), tr("Failed to instantiate ASIO driver"), QMessageBox::Ok, QMessageBox::NoButton);
284 	}
285 }
286 
on_qcbDevice_activated(int)287 void ASIOConfig::on_qcbDevice_activated(int) {
288 	clearQuery();
289 }
290 
on_qpbAddMic_clicked()291 void ASIOConfig::on_qpbAddMic_clicked() {
292 	int row = qlwUnused->currentRow();
293 	if (row < 0)
294 		return;
295 	qlwMic->addItem(qlwUnused->takeItem(row));
296 }
297 
on_qpbRemMic_clicked()298 void ASIOConfig::on_qpbRemMic_clicked() {
299 	int row = qlwMic->currentRow();
300 	if (row < 0)
301 		return;
302 	qlwUnused->addItem(qlwMic->takeItem(row));
303 }
304 
on_qpbAddSpeaker_clicked()305 void ASIOConfig::on_qpbAddSpeaker_clicked() {
306 	int row = qlwUnused->currentRow();
307 	if (row < 0)
308 		return;
309 	qlwSpeaker->addItem(qlwUnused->takeItem(row));
310 }
311 
on_qpbRemSpeaker_clicked()312 void ASIOConfig::on_qpbRemSpeaker_clicked() {
313 	int row = qlwSpeaker->currentRow();
314 	if (row < 0)
315 		return;
316 	qlwUnused->addItem(qlwSpeaker->takeItem(row));
317 }
318 
title() const319 QString ASIOConfig::title() const {
320 	return tr("ASIO");
321 }
322 
icon() const323 QIcon ASIOConfig::icon() const {
324 	return QIcon(QLatin1String("skin:config_asio.png"));
325 }
326 
save() const327 void ASIOConfig::save() const {
328 	if (! bOk)
329 		return;
330 
331 	s.qsASIOclass = qcbDevice->itemData(qcbDevice->currentIndex()).toString();
332 
333 	QList<QVariant> list;
334 
335 	for (int i=0;i<qlwMic->count();i++) {
336 		QListWidgetItem *item = qlwMic->item(i);
337 		list << item->data(Qt::UserRole);
338 	}
339 
340 	s.qlASIOmic = list;
341 
342 	list.clear();
343 
344 	for (int i=0;i<qlwSpeaker->count();i++) {
345 		QListWidgetItem *item = qlwSpeaker->item(i);
346 		list << item->data(Qt::UserRole);
347 	}
348 
349 	s.qlASIOspeaker = list;
350 }
351 
load(const Settings & r)352 void ASIOConfig::load(const Settings &r) {
353 	int i = 0;
354 	ASIODev ad;
355 	foreach(ad, qlDevs) {
356 		if (ad.second == r.qsASIOclass) {
357 			loadComboBox(qcbDevice, i);
358 		}
359 		i++;
360 	}
361 	s.qlASIOmic = r.qlASIOmic;
362 	s.qlASIOspeaker = r.qlASIOspeaker;
363 
364 	qlName->setText(QString());
365 	qlBuffers->setText(QString());
366 	qlwMic->clear();
367 	qlwUnused->clear();
368 	qlwSpeaker->clear();
369 }
370 
clearQuery()371 void ASIOConfig::clearQuery() {
372 	bOk = false;
373 	qlName->setText(QString());
374 	qlBuffers->setText(QString());
375 	qlwMic->clear();
376 	qlwUnused->clear();
377 	qlwSpeaker->clear();
378 }
379 
ASIOInput()380 ASIOInput::ASIOInput() {
381 	QString qsCls = g.s.qsASIOclass;
382 	CLSID clsid;
383 
384 	iasio = NULL;
385 	abiInfo = NULL;
386 	aciInfo = NULL;
387 
388 	// Sanity check things first.
389 
390 	iNumMic=g.s.qlASIOmic.count();
391 	iNumSpeaker=g.s.qlASIOspeaker.count();
392 
393 	if ((iNumMic == 0) || (iNumSpeaker == 0)) {
394 		QMessageBox::warning(NULL, QLatin1String("Mumble"), tr("You need to select at least one microphone and one speaker source to use ASIO. "
395 		                     "If you just need microphone sampling, use DirectSound."),  QMessageBox::Ok, QMessageBox::NoButton);
396 		return;
397 	}
398 
399 	CLSIDFromString(const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(qsCls.utf16())), &clsid);
400 	if (CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, clsid, reinterpret_cast<void **>(&iasio)) == S_OK) {
401 		if (iasio->init(NULL)) {
402 			iasio->setSampleRate(48000.0);
403 			ASIOSampleRate srate = 0.0;
404 			iasio->getSampleRate(&srate);
405 
406 			if (srate <= 0.0)
407 				return;
408 
409 			long minSize, maxSize, prefSize, granSize;
410 			iasio->getBufferSize(&minSize, &maxSize, &prefSize, &granSize);
411 
412 			bool halfit = true;
413 
414 			double wbuf = (srate / 100.0);
415 			long wantBuf = lround(wbuf);
416 			lBufSize = wantBuf;
417 
418 			if (static_cast<double>(wantBuf) == wbuf) {
419 				qWarning("ASIOInput: Exact buffer match possible.");
420 				if ((wantBuf >= minSize) && (wantBuf <= maxSize)) {
421 					if (wantBuf == minSize)
422 						halfit = false;
423 					else if ((granSize >= 1) && (((wantBuf-minSize)%granSize)==0))
424 						halfit = false;
425 				}
426 			}
427 
428 			if (halfit) {
429 				if (granSize == 0) {
430 					qWarning("ASIOInput: Single buffer size");
431 					lBufSize = minSize;
432 				} else {
433 					long target = wantBuf / 2;
434 					lBufSize = target;
435 					while (lBufSize < target) {
436 						if (granSize < 0)
437 							lBufSize *= 2;
438 						else
439 							lBufSize += granSize;
440 					}
441 				}
442 				qWarning("ASIOInput: Buffer mismatch mode. Wanted %li, got %li", wantBuf, lBufSize);
443 			}
444 
445 
446 			abiInfo = new ASIOBufferInfo[iNumMic + iNumSpeaker];
447 			aciInfo = new ASIOChannelInfo[iNumMic + iNumSpeaker];
448 
449 			int i, idx = 0;
450 			for (i=0;i<iNumMic;i++) {
451 				abiInfo[idx].isInput = true;
452 				abiInfo[idx].channelNum = g.s.qlASIOmic[i].toInt();
453 
454 				aciInfo[idx].channel = abiInfo[idx].channelNum;
455 				aciInfo[idx].isInput = true;
456 				iasio->getChannelInfo(&aciInfo[idx]);
457 				SleepEx(10, false);
458 
459 				idx++;
460 			}
461 			for (i=0;i<iNumSpeaker;i++) {
462 				abiInfo[idx].isInput = true;
463 				abiInfo[idx].channelNum = g.s.qlASIOspeaker[i].toInt();
464 
465 				aciInfo[idx].channel = abiInfo[idx].channelNum;
466 				aciInfo[idx].isInput = true;
467 				iasio->getChannelInfo(&aciInfo[idx]);
468 				SleepEx(10, false);
469 
470 				idx++;
471 			}
472 
473 			iEchoChannels = iNumSpeaker;
474 			iMicChannels = iNumMic;
475 			iEchoFreq = iMicFreq = iroundf(srate);
476 
477 			initializeMixer();
478 
479 			ASIOCallbacks asioCallbacks;
480 			ZeroMemory(&asioCallbacks, sizeof(asioCallbacks));
481 			asioCallbacks.bufferSwitch = &bufferSwitch;
482 			asioCallbacks.sampleRateDidChange = &sampleRateChanged;
483 			asioCallbacks.asioMessage = &asioMessages;
484 			asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
485 
486 			if (iasio->createBuffers(abiInfo, idx, lBufSize, &asioCallbacks) == ASE_OK) {
487 				bRunning = true;
488 				return;
489 			}
490 		}
491 	}
492 
493 	if (iasio) {
494 		iasio->Release();
495 		iasio = NULL;
496 	}
497 
498 	QMessageBox::critical(NULL, QLatin1String("Mumble"), tr("Opening selected ASIO device failed. No input will be done."),
499 	                      QMessageBox::Ok, QMessageBox::NoButton);
500 
501 }
502 
~ASIOInput()503 ASIOInput::~ASIOInput() {
504 	qwDone.wakeAll();
505 	wait();
506 	if (iasio) {
507 		iasio->stop();
508 		iasio->disposeBuffers();
509 		iasio->Release();
510 		iasio = NULL;
511 	}
512 
513 	delete [] abiInfo;
514 	abiInfo = NULL;
515 
516 	delete [] aciInfo;
517 	aciInfo = NULL;
518 }
519 
run()520 void ASIOInput::run() {
521 	QMutex m;
522 	m.lock();
523 	if (iasio) {
524 		aiSelf = this;
525 		iasio->start();
526 		qwDone.wait(&m);
527 	}
528 }
529 
bufferSwitchTimeInfo(ASIOTime *,long index,ASIOBool)530 ASIOTime *ASIOInput::bufferSwitchTimeInfo(ASIOTime *, long index, ASIOBool) {
531 	aiSelf->bufferReady(index);
532 	return 0L;
533 }
534 
535 void
addBuffer(ASIOSampleType sampType,int interleave,void * src,float * RESTRICT dst)536 ASIOInput::addBuffer(ASIOSampleType sampType, int interleave, void *src, float * RESTRICT dst) {
537 	switch (sampType) {
538 		case ASIOSTInt16LSB: {
539 				const float m = 1.0f / 32768.f;
540 				const short * RESTRICT buf=static_cast<short *>(src);
541 				for (int i=0;i<lBufSize;i++)
542 					dst[i*interleave]=buf[i] * m;
543 			}
544 			break;
545 		case ASIOSTInt32LSB: {
546 				const float m = 1.0f / 2147483648.f;
547 				const int * RESTRICT buf=static_cast<int *>(src);
548 				for (int i=0;i<lBufSize;i++)
549 					dst[i*interleave]=buf[i] * m;
550 			}
551 			break;
552 		case ASIOSTInt24LSB: {
553 				const float m = 1.0f / static_cast<float>(0x7FFFFFFF - 0xFF);
554 				const unsigned char * RESTRICT buf=static_cast<unsigned char *>(src);
555 				for (int i=0;i<lBufSize;i++)
556 					dst[i * interleave] = (buf[i*3] << 24 | buf[i*3+1] << 16 | buf[i*3+2] << 8) * m;
557 			}
558 			break;
559 		case ASIOSTFloat32LSB: {
560 				const float * RESTRICT buf=static_cast<float *>(src);
561 				for (int i=0;i<lBufSize;i++)
562 					dst[i*interleave]=buf[i];
563 			}
564 			break;
565 	}
566 }
567 
568 void
bufferReady(long buffindex)569 ASIOInput::bufferReady(long buffindex) {
570 	STACKVAR(float, buffer, lBufSize * qMax(iNumMic,iNumSpeaker));
571 
572 	for (int c=0;c<iNumSpeaker;++c)
573 		addBuffer(aciInfo[iNumMic+c].type, iNumSpeaker, abiInfo[iNumMic+c].buffers[buffindex], buffer+c);
574 	addEcho(buffer, lBufSize);
575 
576 	for (int c=0;c<iNumMic;++c)
577 		addBuffer(aciInfo[c].type, iNumMic, abiInfo[c].buffers[buffindex], buffer+c);
578 	addMic(buffer, lBufSize);
579 }
580 
bufferSwitch(long index,ASIOBool processNow)581 void ASIOInput::bufferSwitch(long index, ASIOBool processNow) {
582 	ASIOTime  timeInfo;
583 	memset(&timeInfo, 0, sizeof(timeInfo));
584 
585 	if (aiSelf->iasio->getSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK)
586 		timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
587 
588 	bufferSwitchTimeInfo(&timeInfo, index, processNow);
589 }
590 
sampleRateChanged(ASIOSampleRate)591 void ASIOInput::sampleRateChanged(ASIOSampleRate) {
592 	qFatal("ASIOInput: sampleRateChanged");
593 }
594 
asioMessages(long selector,long value,void *,double *)595 long ASIOInput::asioMessages(long selector, long value, void*, double*) {
596 	long ret = 0;
597 	switch (selector) {
598 		case kAsioSelectorSupported:
599 			if (value == kAsioResetRequest
600 			        || value == kAsioEngineVersion
601 			        || value == kAsioResyncRequest
602 			        || value == kAsioLatenciesChanged
603 			        || value == kAsioSupportsTimeInfo
604 			        || value == kAsioSupportsTimeCode
605 			        || value == kAsioSupportsInputMonitor)
606 				ret = 1L;
607 			break;
608 		case kAsioResetRequest:
609 			qFatal("ASIOInput: kAsioResetRequest");
610 			ret = 1L;
611 			break;
612 		case kAsioResyncRequest:
613 			ret = 1L;
614 			break;
615 		case kAsioLatenciesChanged:
616 			ret = 1L;
617 			break;
618 		case kAsioEngineVersion:
619 			ret = 2L;
620 			break;
621 		case kAsioSupportsTimeInfo:
622 			ret = 1;
623 			break;
624 		case kAsioSupportsTimeCode:
625 			ret = 0;
626 			break;
627 	}
628 	return ret;
629 }
630