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 <alsa/asoundlib.h>
9 #include <sys/poll.h>
10 
11 #include "ALSAAudio.h"
12 
13 #include "MainWindow.h"
14 #include "Global.h"
15 #include "User.h"
16 
17 #define NBLOCKS 8
18 
19 class ALSAEnumerator {
20 	public:
21 		QHash<QString,QString> qhInput;
22 		QHash<QString,QString> qhOutput;
23 		static QString getHint(void *hint, const char *id);
24 		ALSAEnumerator();
25 };
26 
27 static ALSAEnumerator *cards = NULL;
28 
29 class ALSAAudioInputRegistrar : public AudioInputRegistrar {
30 	public:
31 		ALSAAudioInputRegistrar();
32 		virtual AudioInput *create();
33 		virtual const QList<audioDevice> getDeviceChoices();
34 		virtual void setDeviceChoice(const QVariant &, Settings &);
35 		virtual bool canEcho(const QString &) const;
36 };
37 
38 
39 class ALSAAudioOutputRegistrar : public AudioOutputRegistrar {
40 	public:
41 		ALSAAudioOutputRegistrar();
42 		virtual AudioOutput *create();
43 		virtual const QList<audioDevice> getDeviceChoices();
44 		virtual void setDeviceChoice(const QVariant &, Settings &);
45 };
46 
47 class ALSAInit : public DeferInit {
48 	protected:
49 		ALSAAudioInputRegistrar *pairALSA;
50 		ALSAAudioOutputRegistrar *paorALSA;
51 	public:
52 		void initialize();
53 		void destroy();
54 };
55 
56 static ALSAInit aiInit;
57 QMutex qmALSA;
58 
initialize()59 void ALSAInit::initialize() {
60 	pairALSA = NULL;
61 	paorALSA = NULL;
62 	cards = NULL;
63 
64 	int card = -1;
65 	snd_card_next(&card);
66 	if (card != -1) {
67 		pairALSA = new ALSAAudioInputRegistrar();
68 		paorALSA = new ALSAAudioOutputRegistrar();
69 		cards = new ALSAEnumerator();
70 	} else {
71 		qWarning("ALSAInit: No cards found, not initializing");
72 	}
73 }
74 
destroy()75 void ALSAInit::destroy() {
76 	QMutexLocker qml(&qmALSA);
77 	delete pairALSA;
78 	delete paorALSA;
79 	delete cards;
80 }
81 
ALSAAudioInputRegistrar()82 ALSAAudioInputRegistrar::ALSAAudioInputRegistrar() : AudioInputRegistrar(QLatin1String("ALSA"),5) {
83 }
84 
create()85 AudioInput *ALSAAudioInputRegistrar::create() {
86 	return new ALSAAudioInput();
87 }
88 
getDeviceChoices()89 const QList<audioDevice> ALSAAudioInputRegistrar::getDeviceChoices() {
90 	QList<audioDevice> qlReturn;
91 
92 	QStringList qlInputDevs = cards->qhInput.keys();
93 	qSort(qlInputDevs);
94 
95 	if (qlInputDevs.contains(g.s.qsALSAInput)) {
96 		qlInputDevs.removeAll(g.s.qsALSAInput);
97 		qlInputDevs.prepend(g.s.qsALSAInput);
98 	}
99 
100 	foreach(const QString &dev, qlInputDevs) {
101 		QString t=QString::fromLatin1("[%1] %2").arg(dev, cards->qhInput.value(dev));
102 		qlReturn << audioDevice(t, dev);
103 	}
104 
105 	return qlReturn;
106 }
107 
setDeviceChoice(const QVariant & choice,Settings & s)108 void ALSAAudioInputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
109 	s.qsALSAInput = choice.toString();
110 }
111 
canEcho(const QString &) const112 bool ALSAAudioInputRegistrar::canEcho(const QString &) const {
113 	return false;
114 }
115 
ALSAAudioOutputRegistrar()116 ALSAAudioOutputRegistrar::ALSAAudioOutputRegistrar() : AudioOutputRegistrar(QLatin1String("ALSA"), 5) {
117 }
118 
create()119 AudioOutput *ALSAAudioOutputRegistrar::create() {
120 	return new ALSAAudioOutput();
121 }
122 
getDeviceChoices()123 const QList<audioDevice> ALSAAudioOutputRegistrar::getDeviceChoices() {
124 	QList<audioDevice> qlReturn;
125 
126 	QStringList qlOutputDevs = cards->qhOutput.keys();
127 	qSort(qlOutputDevs);
128 
129 	if (qlOutputDevs.contains(g.s.qsALSAOutput)) {
130 		qlOutputDevs.removeAll(g.s.qsALSAOutput);
131 		qlOutputDevs.prepend(g.s.qsALSAOutput);
132 	}
133 
134 	foreach(const QString &dev, qlOutputDevs) {
135 		QString t=QString::fromLatin1("[%1] %2").arg(dev, cards->qhOutput.value(dev));
136 		qlReturn << audioDevice(t, dev);
137 	}
138 
139 	return qlReturn;
140 }
141 
setDeviceChoice(const QVariant & choice,Settings & s)142 void ALSAAudioOutputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
143 	s.qsALSAOutput = choice.toString();
144 }
145 
ALSAEnumerator()146 ALSAEnumerator::ALSAEnumerator() {
147 	QMutexLocker qml(&qmALSA);
148 
149 	qhInput.insert(QLatin1String("default"), ALSAAudioInput::tr("Default ALSA Card"));
150 	qhOutput.insert(QLatin1String("default"), ALSAAudioOutput::tr("Default ALSA Card"));
151 
152 #if SND_LIB_VERSION >= 0x01000e
153 	void **hints = NULL;
154 	void **hint;
155 	snd_config_t *basic = NULL;
156 	int r;
157 
158 	snd_config_update();
159 	r = snd_config_search(snd_config, "defaults.namehint.extended", &basic);
160 	if ((r==0) && basic) {
161 		if (snd_config_set_ascii(basic, "on"))
162 			qWarning("ALSAEnumerator: Failed to set namehint");
163 	} else {
164 		qWarning("ALSAEnumerator: Namehint not found");
165 	}
166 
167 	r = snd_device_name_hint(-1, "pcm", &hints);
168 
169 	if (r || ! hints) {
170 		qWarning("ALSAEnumerator: snd_device_name_hint: %d", r);
171 	} else {
172 		hint = hints;
173 		while (*hint) {
174 			const QString name = getHint(*hint, "NAME");
175 			const QString ioid = getHint(*hint, "IOID");
176 			QString desc = getHint(*hint, "DESC");
177 
178 			desc.replace(QLatin1Char('\n'), QLatin1Char(' '));
179 
180 
181 			// ALSA, in it's infinite wisdom, claims "dmix" is an input/output device.
182 			// Since there seems to be no way to fetch the ctl interface for a matching device string
183 			// without actually opening it, we'll simply have to start guessing.
184 
185 			bool caninput = (ioid.isNull() || (ioid.compare(QLatin1String("Input"), Qt::CaseInsensitive)==0));
186 			bool canoutput = (ioid.isNull() || (ioid.compare(QLatin1String("Output"), Qt::CaseInsensitive)==0));
187 
188 			if (name.startsWith(QLatin1String("dmix:")))
189 				caninput = false;
190 			else if (name.startsWith(QLatin1String("dsnoop:")))
191 				canoutput = false;
192 
193 			if (caninput)
194 				qhInput.insert(name, desc);
195 			if (canoutput)
196 				qhOutput.insert(name, desc);
197 
198 			++hint;
199 		}
200 		snd_device_name_free_hint(hints);
201 	}
202 
203 	snd_config_update_free_global();
204 	snd_config_update();
205 #else
206 	int card=-1;
207 	snd_card_next(&card);
208 	while (card != -1) {
209 		char *name;
210 		snd_ctl_t *ctl=NULL;
211 		snd_card_get_longname(card, &name);
212 		QByteArray dev=QString::fromLatin1("hw:%1").arg(card).toUtf8();
213 		if (snd_ctl_open(&ctl, dev.data(), SND_CTL_READONLY) >= 0) {
214 			snd_pcm_info_t *info = NULL;
215 			snd_pcm_info_malloc(&info);
216 
217 			char *cname = NULL;
218 			snd_card_get_name(card, &cname);
219 
220 			int device = -1;
221 			snd_ctl_pcm_next_device(ctl, &device);
222 
223 			bool play = false;
224 			bool cap = false;
225 
226 			while (device != -1) {
227 				QString devname=QString::fromLatin1("hw:%1,%2").arg(card).arg(device);
228 				snd_pcm_info_set_device(info, device);
229 				snd_pcm_info_set_stream(info, SND_PCM_STREAM_CAPTURE);
230 				if (snd_ctl_pcm_info(ctl,info) == 0) {
231 					QString fname=QString::fromLatin1(snd_pcm_info_get_name(info));
232 					qhInput.insert(devname,fname);
233 					cap = true;
234 				}
235 
236 				snd_pcm_info_set_stream(info, SND_PCM_STREAM_PLAYBACK);
237 				if (snd_ctl_pcm_info(ctl,info) == 0) {
238 					QString fname=QString::fromLatin1(snd_pcm_info_get_name(info));
239 					qhOutput.insert(devname,fname);
240 					play = true;
241 				}
242 
243 				snd_ctl_pcm_next_device(ctl, &device);
244 			}
245 			if (play) {
246 				qhOutput.insert(QString::fromLatin1("dmix:CARD=%1").arg(card),QLatin1String(cname));
247 			}
248 			if (cap) {
249 				qhInput.insert(QString::fromLatin1("dsnoop:CARD=%1").arg(card),QLatin1String(cname));
250 			}
251 			snd_pcm_info_free(info);
252 			snd_ctl_close(ctl);
253 		}
254 		snd_card_next(&card);
255 	}
256 #endif
257 }
258 
getHint(void * hint,const char * id)259 QString ALSAEnumerator::getHint(void *hint, const char *id) {
260 	QString s;
261 #if SND_LIB_VERSION >= 0x01000e
262 	char *value = snd_device_name_get_hint(hint, id);
263 	if (value) {
264 		s = QLatin1String(value);
265 		free(value);
266 	}
267 #endif
268 	return s;
269 }
270 
271 
ALSAAudioInput()272 ALSAAudioInput::ALSAAudioInput() {
273 	bRunning = true;
274 }
275 
~ALSAAudioInput()276 ALSAAudioInput::~ALSAAudioInput() {
277 	// Signal input thread to end
278 	bRunning = false;
279 	wait();
280 }
281 
282 #define ALSA_ERRBAIL(x) if (!bOk) {} else if ((err=static_cast<int>(x)) < 0) { bOk = false; qWarning("ALSAAudio: %s: %s", #x, snd_strerror(err));}
283 #define ALSA_ERRCHECK(x) if (!bOk) {} else if ((err=static_cast<int>(x)) < 0) {qWarning("ALSAAudio: Non-critical: %s: %s", #x, snd_strerror(err));}
284 
run()285 void ALSAAudioInput::run() {
286 	QMutexLocker qml(&qmALSA);
287 	snd_pcm_sframes_t readblapp;
288 
289 	QByteArray device_name = g.s.qsALSAInput.toLatin1();
290 	snd_pcm_hw_params_t *hw_params = NULL;
291 	snd_pcm_t *capture_handle = NULL;
292 
293 	unsigned int rrate = SAMPLE_RATE;
294 	bool bOk = true;
295 
296 	int err = 0;
297 
298 	unsigned int iChannels = 1;
299 
300 	qWarning("ALSAAudioInput: Initing audiocapture %s.",device_name.data());
301 
302 	snd_pcm_hw_params_alloca(&hw_params);
303 
304 	ALSA_ERRBAIL(snd_pcm_open(&capture_handle, device_name.data(), SND_PCM_STREAM_CAPTURE, 0));
305 	ALSA_ERRCHECK(snd_pcm_hw_params_any(capture_handle, hw_params));
306 	ALSA_ERRBAIL(snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED));
307 	ALSA_ERRBAIL(snd_pcm_hw_params_set_format(capture_handle, hw_params, SND_PCM_FORMAT_S16));
308 	ALSA_ERRBAIL(snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &rrate, NULL));
309 	ALSA_ERRBAIL(snd_pcm_hw_params_set_channels_near(capture_handle, hw_params, &iChannels));
310 
311 	snd_pcm_uframes_t wantPeriod = (rrate * iFrameSize) / SAMPLE_RATE;
312 	snd_pcm_uframes_t wantBuff = wantPeriod * 8;
313 
314 	ALSA_ERRBAIL(snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params, &wantPeriod, NULL));
315 	ALSA_ERRBAIL(snd_pcm_hw_params_set_buffer_size_near(capture_handle, hw_params, &wantBuff));
316 	ALSA_ERRBAIL(snd_pcm_hw_params(capture_handle, hw_params));
317 
318 	qWarning("ALSAAudioInput: Actual buffer %d hz, %d channel %ld samples [%ld per period]",rrate,iChannels,wantBuff,wantPeriod);
319 
320 	ALSA_ERRBAIL(snd_pcm_hw_params_current(capture_handle, hw_params));
321 	ALSA_ERRBAIL(snd_pcm_hw_params_get_channels(hw_params, &iMicChannels));
322 	ALSA_ERRBAIL(snd_pcm_hw_params_get_rate(hw_params, &iMicFreq, NULL));
323 
324 #ifdef ALSA_VERBOSE
325 	snd_output_t *log;
326 	snd_output_stdio_attach(&log, stderr,0);
327 	if (capture_handle)
328 		snd_pcm_dump(capture_handle, log);
329 #endif
330 
331 	ALSA_ERRBAIL(snd_pcm_prepare(capture_handle));
332 	ALSA_ERRBAIL(snd_pcm_start(capture_handle));
333 
334 	if (! bOk) {
335 		if (capture_handle) {
336 			snd_pcm_drain(capture_handle);
337 			snd_pcm_close(capture_handle);
338 			capture_handle = NULL;
339 		}
340 		g.mw->msgBox(tr("Opening chosen ALSA Input failed: %1").arg(Qt::escape(QLatin1String(snd_strerror(err)))));
341 		return;
342 	}
343 
344 	eMicFormat = SampleShort;
345 	initializeMixer();
346 
347 	char inbuff[wantPeriod * iChannels * sizeof(short)];
348 
349 	qml.unlock();
350 
351 	while (bRunning) {
352 #ifdef ALSA_VERBOSE
353 		snd_pcm_status_malloc(&status);
354 		snd_pcm_status(capture_handle, status);
355 		snd_pcm_status_dump(status, log);
356 		snd_pcm_status_free(status);
357 #endif
358 		readblapp = snd_pcm_readi(capture_handle, inbuff, static_cast<int>(wantPeriod));
359 		if (readblapp == -ESTRPIPE) {
360 			qWarning("ALSAAudioInput: PCM suspended, trying to resume");
361 			while (bRunning && snd_pcm_resume(capture_handle) == -EAGAIN)
362 				msleep(1000);
363 			if ((err = snd_pcm_prepare(capture_handle)) < 0)
364 				qWarning("ALSAAudioInput: %s: %s", snd_strerror(static_cast<int>(readblapp)), snd_strerror(err));
365 		} else if (readblapp == -EPIPE) {
366 			err = snd_pcm_prepare(capture_handle);
367 			qWarning("ALSAAudioInput: %s: %s", snd_strerror(static_cast<int>(readblapp)), snd_strerror(err));
368 		} else if (readblapp < 0) {
369 			err = snd_pcm_prepare(capture_handle);
370 			qWarning("ALSAAudioInput: %s: %s", snd_strerror(static_cast<int>(readblapp)), snd_strerror(err));
371 		} else if (wantPeriod == static_cast<unsigned int>(readblapp)) {
372 			addMic(inbuff, static_cast<int>(readblapp));
373 		}
374 	}
375 
376 	snd_pcm_drop(capture_handle);
377 	snd_pcm_close(capture_handle);
378 
379 	qWarning("ALSAAudioInput: Releasing ALSA Mic.");
380 }
381 
ALSAAudioOutput()382 ALSAAudioOutput::ALSAAudioOutput() {
383 	qWarning("ALSAAudioOutput: Initialized");
384 	bRunning = true;
385 }
386 
~ALSAAudioOutput()387 ALSAAudioOutput::~ALSAAudioOutput() {
388 	bRunning = false;
389 	// Call destructor of all children
390 	wipe();
391 	// Wait for terminate
392 	wait();
393 	qWarning("ALSAAudioOutput: Destroyed");
394 }
395 
run()396 void ALSAAudioOutput::run() {
397 	QMutexLocker qml(&qmALSA);
398 	snd_pcm_t *pcm_handle = NULL;
399 	struct pollfd fds[16];
400 	int count;
401 	bool stillRun = true;
402 	int err = 0;
403 	bool bOk = true;
404 
405 
406 	snd_pcm_hw_params_t *hw_params = NULL;
407 	snd_pcm_sw_params_t *sw_params = NULL;
408 	QByteArray device_name = g.s.qsALSAOutput.toLatin1();
409 
410 	snd_pcm_hw_params_alloca(&hw_params);
411 	snd_pcm_sw_params_alloca(&sw_params);
412 
413 	ALSA_ERRBAIL(snd_pcm_open(&pcm_handle, device_name.data(), SND_PCM_STREAM_PLAYBACK, 0));
414 	ALSA_ERRCHECK(snd_pcm_hw_params_any(pcm_handle, hw_params));
415 
416 	if (g.s.doPositionalAudio()) {
417 		iChannels = 1;
418 		ALSA_ERRBAIL(snd_pcm_hw_params_get_channels_max(hw_params, &iChannels));
419 		if (iChannels > 9) {
420 			qWarning("ALSAAudioOutput: ALSA reports %d output channels. Clamping to 2.",iChannels);
421 			iChannels = 2;
422 		}
423 	} else {
424 		iChannels = 1;
425 	}
426 
427 	ALSA_ERRBAIL(snd_pcm_hw_params_set_access(pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED));
428 	ALSA_ERRBAIL(snd_pcm_hw_params_set_format(pcm_handle, hw_params, SND_PCM_FORMAT_S16));
429 	ALSA_ERRBAIL(snd_pcm_hw_params_set_channels_near(pcm_handle, hw_params, &iChannels));
430 	unsigned int rrate = SAMPLE_RATE;
431 	ALSA_ERRBAIL(snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, &rrate, NULL));
432 
433 	unsigned int iOutputSize = (iFrameSize * rrate) / SAMPLE_RATE;
434 
435 	snd_pcm_uframes_t period_size = iOutputSize;
436 	snd_pcm_uframes_t buffer_size = iOutputSize * (g.s.iOutputDelay + 1);
437 
438 	int dir = 1;
439 	ALSA_ERRBAIL(snd_pcm_hw_params_set_period_size_near(pcm_handle, hw_params, &period_size, &dir));
440 	ALSA_ERRBAIL(snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hw_params, &buffer_size));
441 
442 	ALSA_ERRBAIL(snd_pcm_hw_params(pcm_handle, hw_params));
443 	ALSA_ERRBAIL(snd_pcm_hw_params_current(pcm_handle, hw_params));
444 	ALSA_ERRBAIL(snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir));
445 	ALSA_ERRBAIL(snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size));
446 
447 	qWarning("ALSAAudioOutput: Actual buffer %d hz, %d channel %ld samples [%ld per period]",rrate,iChannels,buffer_size,period_size);
448 
449 	ALSA_ERRBAIL(snd_pcm_sw_params_current(pcm_handle, sw_params));
450 	ALSA_ERRBAIL(snd_pcm_sw_params_set_avail_min(pcm_handle, sw_params, period_size));
451 	ALSA_ERRBAIL(snd_pcm_sw_params_set_start_threshold(pcm_handle, sw_params, buffer_size - period_size));
452 	ALSA_ERRBAIL(snd_pcm_sw_params_set_stop_threshold(pcm_handle, sw_params, buffer_size));
453 	ALSA_ERRBAIL(snd_pcm_sw_params(pcm_handle, sw_params));
454 
455 #ifdef ALSA_VERBOSE
456 	snd_output_t *log;
457 	snd_output_stdio_attach(&log, stderr,0);
458 	if (pcm_handle)
459 		snd_pcm_dump(pcm_handle, log);
460 #endif
461 
462 	ALSA_ERRBAIL(snd_pcm_prepare(pcm_handle));
463 
464 	const unsigned int buffsize = static_cast<unsigned int>(period_size * iChannels);
465 
466 	float zerobuff[buffsize];
467 	float outbuff[buffsize];
468 
469 	for (unsigned int i=0;i<buffsize;i++)
470 		zerobuff[i]=0;
471 
472 	// Fill buffer
473 	if (bOk && pcm_handle)
474 		for (unsigned int i = 0; i < buffer_size / period_size; i++)
475 			snd_pcm_writei(pcm_handle, zerobuff, period_size);
476 
477 	if (! bOk) {
478 		g.mw->msgBox(tr("Opening chosen ALSA Output failed: %1").arg(Qt::escape(QLatin1String(snd_strerror(err)))));
479 		if (pcm_handle) {
480 			snd_pcm_close(pcm_handle);
481 			pcm_handle = NULL;
482 		}
483 		return;
484 	}
485 
486 	const unsigned int chanmasks[32] = {
487 		SPEAKER_FRONT_LEFT,
488 		SPEAKER_FRONT_RIGHT,
489 		SPEAKER_BACK_LEFT,
490 		SPEAKER_BACK_RIGHT,
491 		SPEAKER_FRONT_CENTER,
492 		SPEAKER_LOW_FREQUENCY,
493 		SPEAKER_SIDE_LEFT,
494 		SPEAKER_SIDE_RIGHT,
495 		SPEAKER_BACK_CENTER
496 	};
497 
498 	ALSA_ERRBAIL(snd_pcm_hw_params_current(pcm_handle, hw_params));
499 	ALSA_ERRBAIL(snd_pcm_hw_params_get_channels(hw_params, &iChannels));
500 	ALSA_ERRBAIL(snd_pcm_hw_params_get_rate(hw_params, &rrate, NULL));
501 	iMixerFreq = rrate;
502 	eSampleFormat = SampleShort;
503 
504 	qWarning("ALSAAudioOutput: Initializing %d channel, %d hz mixer", iChannels, iMixerFreq);
505 	initializeMixer(chanmasks);
506 
507 	count = snd_pcm_poll_descriptors_count(pcm_handle);
508 	snd_pcm_poll_descriptors(pcm_handle, fds, count);
509 
510 	qml.unlock();
511 
512 	while (bRunning && bOk) {
513 		poll(fds, count, 20);
514 		unsigned short revents;
515 
516 		snd_pcm_poll_descriptors_revents(pcm_handle, fds, count, &revents);
517 		if (revents & POLLERR) {
518 			snd_pcm_prepare(pcm_handle);
519 		} else if (revents & POLLOUT) {
520 			snd_pcm_sframes_t avail{};
521 			ALSA_ERRCHECK(avail = snd_pcm_avail_update(pcm_handle));
522 			while (avail >= static_cast<int>(period_size)) {
523 				stillRun = mix(outbuff, static_cast<int>(period_size));
524 				if (stillRun) {
525 					snd_pcm_sframes_t w = 0;
526 					ALSA_ERRCHECK(w=snd_pcm_writei(pcm_handle, outbuff, period_size));
527 					if (w < 0) {
528 						avail = w;
529 						break;
530 					}
531 				} else
532 					break;
533 				ALSA_ERRCHECK(avail = snd_pcm_avail_update(pcm_handle));
534 			}
535 
536 			if (avail == -EPIPE) {
537 				snd_pcm_drain(pcm_handle);
538 				ALSA_ERRCHECK(snd_pcm_prepare(pcm_handle));
539 				for (unsigned int i=0;i< buffer_size / period_size;++i)
540 					ALSA_ERRCHECK(snd_pcm_writei(pcm_handle, zerobuff, period_size));
541 			}
542 
543 			if (! stillRun) {
544 				snd_pcm_drain(pcm_handle);
545 
546 				while (bRunning && !mix(outbuff, static_cast<unsigned int>(period_size))) {
547 					this->msleep(10);
548 				}
549 
550 				if (! bRunning)
551 					break;
552 
553 				snd_pcm_prepare(pcm_handle);
554 
555 				// Fill one frame
556 				for (unsigned int i = 0; i < (buffer_size / period_size) - 1 ; i++)
557 					snd_pcm_writei(pcm_handle, zerobuff, period_size);
558 
559 				snd_pcm_writei(pcm_handle, outbuff, period_size);
560 			}
561 		}
562 	}
563 	snd_pcm_close(pcm_handle);
564 }
565