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