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 "PulseAudio.h"
9
10 #include "MainWindow.h"
11 #include "Timer.h"
12 #include "User.h"
13
14 // We define a global macro called 'g'. This can lead to issues when included code uses 'g' as a type or parameter name (like protobuf 3.7 does). As such, for now, we have to make this our last include.
15 #include "Global.h"
16
17 static const char *mumble_sink_input = "Mumble Speakers";
18 static const char *mumble_echo = "Mumble Speakers (Echo)";
19
20 static PulseAudioSystem *pasys = NULL;
21
22 #define NBLOCKS 8
23
24 class PulseAudioInputRegistrar : public AudioInputRegistrar {
25 public:
26 PulseAudioInputRegistrar();
27 virtual AudioInput *create();
28 virtual const QList<audioDevice> getDeviceChoices();
29 virtual void setDeviceChoice(const QVariant &, Settings &);
30 virtual bool canEcho(const QString &) const;
31 };
32
33
34 class PulseAudioOutputRegistrar : public AudioOutputRegistrar {
35 public:
36 PulseAudioOutputRegistrar();
37 virtual AudioOutput *create();
38 virtual const QList<audioDevice> getDeviceChoices();
39 virtual void setDeviceChoice(const QVariant &, Settings &);
40 bool canMuteOthers() const;
41 };
42
43 class PulseAudioInit : public DeferInit {
44 public:
45 PulseAudioInputRegistrar *airPulseAudio;
46 PulseAudioOutputRegistrar *aorPulseAudio;
initialize()47 void initialize() {
48 pasys = new PulseAudioSystem();
49 pasys->qmWait.lock();
50 pasys->qwcWait.wait(&pasys->qmWait, 1000);
51 pasys->qmWait.unlock();
52 if (pasys->bPulseIsGood) {
53 airPulseAudio = new PulseAudioInputRegistrar();
54 aorPulseAudio = new PulseAudioOutputRegistrar();
55 } else {
56 airPulseAudio = NULL;
57 aorPulseAudio = NULL;
58 delete pasys;
59 pasys = NULL;
60 }
61 };
destroy()62 void destroy() {
63 delete airPulseAudio;
64 delete aorPulseAudio;
65 delete pasys;
66 pasys = NULL;
67 };
68 };
69
70 static PulseAudioInit pulseinit;
71
PulseAudioSystem()72 PulseAudioSystem::PulseAudioSystem() {
73 pasInput = pasOutput = pasSpeaker = NULL;
74 bSourceDone=bSinkDone=bServerDone = false;
75 iDelayCache = 0;
76 bPositionalCache = false;
77 bAttenuating = false;
78 iRemainingOperations = 0;
79 bPulseIsGood = false;
80 iSinkId = -1;
81
82 pam = pa_threaded_mainloop_new();
83 pa_mainloop_api *api = pa_threaded_mainloop_get_api(pam);
84
85 pa_proplist *proplist;
86
87 proplist = pa_proplist_new();
88 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "Mumble");
89 pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "net.sourceforge.mumble.mumble");
90 pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "mumble");
91 pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "game");
92
93 pacContext = pa_context_new_with_proplist(api, NULL, proplist);
94 pa_proplist_free(proplist);
95
96 pa_context_set_subscribe_callback(pacContext, subscribe_callback, this);
97
98 pa_context_set_state_callback(pacContext, context_state_callback, this);
99 pa_context_connect(pacContext, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
100
101 pade = api->defer_new(api, defer_event_callback, this);
102 api->defer_enable(pade, false);
103
104 pa_threaded_mainloop_start(pam);
105
106 bRunning = true;
107 }
108
~PulseAudioSystem()109 PulseAudioSystem::~PulseAudioSystem() {
110 bRunning = false;
111 if (bAttenuating) {
112 qmWait.lock();
113 bAttenuating = false;
114 setVolumes();
115 bool success = qwcWait.wait(&qmWait, 1000);
116 if (! success) {
117 qWarning("PulseAudio: Shutdown timeout when attempting to restore volumes.");
118 }
119 qmWait.unlock();
120 }
121 pa_threaded_mainloop_stop(pam);
122 pa_context_disconnect(pacContext);
123 pa_context_unref(pacContext);
124 pa_threaded_mainloop_free(pam);
125 }
126
outputDevice() const127 QString PulseAudioSystem::outputDevice() const {
128 QString odev = g.s.qsPulseAudioOutput;
129 if (odev.isEmpty()) {
130 odev = qsDefaultOutput;
131 }
132 if (!qhOutput.contains(odev)) {
133 odev = qsDefaultOutput;
134 }
135 return odev;
136 }
137
inputDevice() const138 QString PulseAudioSystem::inputDevice() const {
139 QString idev = g.s.qsPulseAudioInput;
140 if (idev.isEmpty()) {
141 idev = qsDefaultInput;
142 }
143 if (!qhInput.contains(idev)) {
144 idev = qsDefaultInput;
145 }
146 return idev;
147 }
148
wakeup()149 void PulseAudioSystem::wakeup() {
150 pa_mainloop_api *api = pa_threaded_mainloop_get_api(pam);
151 api->defer_enable(pade, true);
152 }
153
wakeup_lock()154 void PulseAudioSystem::wakeup_lock() {
155 pa_threaded_mainloop_lock(pam);
156 pa_mainloop_api *api = pa_threaded_mainloop_get_api(pam);
157 api->defer_enable(pade, true);
158 pa_threaded_mainloop_unlock(pam);
159 }
160
defer_event_callback(pa_mainloop_api * a,pa_defer_event * e,void * userdata)161 void PulseAudioSystem::defer_event_callback(pa_mainloop_api *a, pa_defer_event *e, void *userdata) {
162 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
163 pas->eventCallback(a, e);
164 }
165
eventCallback(pa_mainloop_api * api,pa_defer_event *)166 void PulseAudioSystem::eventCallback(pa_mainloop_api *api, pa_defer_event *) {
167 api->defer_enable(pade, false);
168
169 if (! bSourceDone || ! bSinkDone || ! bServerDone)
170 return;
171
172 AudioInputPtr ai = g.ai;
173 AudioOutputPtr ao = g.ao;
174 AudioInput *raw_ai = ai.get();
175 AudioOutput *raw_ao = ao.get();
176 PulseAudioInput *pai = dynamic_cast<PulseAudioInput *>(raw_ai);
177 PulseAudioOutput *pao = dynamic_cast<PulseAudioOutput *>(raw_ao);
178
179 if (raw_ao) {
180 QString odev = outputDevice();
181 pa_stream_state ost = pasOutput ? pa_stream_get_state(pasOutput) : PA_STREAM_TERMINATED;
182 bool do_stop = false;
183 bool do_start = false;
184
185 if (! pao && (ost == PA_STREAM_READY)) {
186 do_stop = true;
187 } else if (pao) {
188 switch (ost) {
189 case PA_STREAM_TERMINATED: {
190 if (pasOutput)
191 pa_stream_unref(pasOutput);
192
193 pa_sample_spec pss = qhSpecMap.value(odev);
194 pa_channel_map pcm = qhChanMap.value(odev);
195 if ((pss.format != PA_SAMPLE_FLOAT32NE) && (pss.format != PA_SAMPLE_S16NE))
196 pss.format = PA_SAMPLE_FLOAT32NE;
197
198 pss.rate = SAMPLE_RATE;
199
200 if ((pss.channels == 0) || (! g.s.doPositionalAudio()))
201 pss.channels = 1;
202
203 pasOutput = pa_stream_new(pacContext, mumble_sink_input, &pss, (pss.channels == 1) ? NULL : &pcm);
204 pa_stream_set_state_callback(pasOutput, stream_callback, this);
205 pa_stream_set_write_callback(pasOutput, write_callback, this);
206
207 // Fallthrough
208 }
209 case PA_STREAM_UNCONNECTED:
210 do_start = true;
211 break;
212 case PA_STREAM_READY: {
213 if (g.s.iOutputDelay != iDelayCache) {
214 do_stop = true;
215 } else if (g.s.doPositionalAudio() != bPositionalCache) {
216 do_stop = true;
217 } else if (odev != qsOutputCache) {
218 do_stop = true;
219 }
220 break;
221 }
222 default:
223 break;
224 }
225 }
226 if (do_stop) {
227 qWarning("PulseAudio: Stopping output");
228 pa_stream_disconnect(pasOutput);
229 iSinkId = -1;
230 } else if (do_start) {
231 qWarning("PulseAudio: Starting output: %s", qPrintable(odev));
232 pa_buffer_attr buff;
233 const pa_sample_spec *pss = pa_stream_get_sample_spec(pasOutput);
234 const size_t sampleSize = (pss->format == PA_SAMPLE_FLOAT32NE) ? sizeof(float) : sizeof(short);
235 const unsigned int iBlockLen = pao->iFrameSize * pss->channels * static_cast<unsigned int>(sampleSize);
236 buff.tlength = iBlockLen * (g.s.iOutputDelay+1);
237 buff.minreq = iBlockLen;
238 buff.maxlength = -1;
239 buff.prebuf = -1;
240 buff.fragsize = iBlockLen;
241
242 iDelayCache = g.s.iOutputDelay;
243 bPositionalCache = g.s.doPositionalAudio();
244 qsOutputCache = odev;
245
246 pa_stream_connect_playback(pasOutput, qPrintable(odev), &buff, PA_STREAM_ADJUST_LATENCY, NULL, NULL);
247 pa_context_get_sink_info_by_name(pacContext, qPrintable(odev), sink_info_callback, this);
248 }
249 }
250
251 if (raw_ai) {
252 QString idev = inputDevice();
253 pa_stream_state ist = pasInput ? pa_stream_get_state(pasInput) : PA_STREAM_TERMINATED;
254 bool do_stop = false;
255 bool do_start = false;
256
257 if (! pai && (ist == PA_STREAM_READY)) {
258 do_stop = true;
259 } else if (pai) {
260 switch (ist) {
261 case PA_STREAM_TERMINATED: {
262 if (pasInput)
263 pa_stream_unref(pasInput);
264
265 pa_sample_spec pss = qhSpecMap.value(idev);
266 if ((pss.format != PA_SAMPLE_FLOAT32NE) && (pss.format != PA_SAMPLE_S16NE))
267 pss.format = PA_SAMPLE_FLOAT32NE;
268 pss.rate = SAMPLE_RATE;
269 pss.channels = 1;
270
271 pasInput = pa_stream_new(pacContext, "Microphone", &pss, NULL);
272 pa_stream_set_state_callback(pasInput, stream_callback, this);
273 pa_stream_set_read_callback(pasInput, read_callback, this);
274
275 // Fallthrough
276 }
277
278 case PA_STREAM_UNCONNECTED:
279 do_start = true;
280 break;
281 case PA_STREAM_READY: {
282 if (idev != qsInputCache) {
283 do_stop = true;
284 }
285 break;
286 }
287 default:
288 break;
289 }
290 }
291 if (do_stop) {
292 qWarning("PulseAudio: Stopping input");
293 pa_stream_disconnect(pasInput);
294 } else if (do_start) {
295 qWarning("PulseAudio: Starting input %s",qPrintable(idev));
296 pa_buffer_attr buff;
297 const pa_sample_spec *pss = pa_stream_get_sample_spec(pasInput);
298 const size_t sampleSize = (pss->format == PA_SAMPLE_FLOAT32NE) ? sizeof(float) : sizeof(short);
299 const unsigned int iBlockLen = pai->iFrameSize * pss->channels * static_cast<unsigned int>(sampleSize);
300 buff.tlength = iBlockLen;
301 buff.minreq = iBlockLen;
302 buff.maxlength = -1;
303 buff.prebuf = -1;
304 buff.fragsize = iBlockLen;
305
306 qsInputCache = idev;
307
308 pa_stream_connect_record(pasInput, qPrintable(idev), &buff, PA_STREAM_ADJUST_LATENCY);
309 }
310 }
311
312 if (raw_ai) {
313 QString odev = outputDevice();
314 QString edev = qhEchoMap.value(odev);
315 pa_stream_state est = pasSpeaker ? pa_stream_get_state(pasSpeaker) : PA_STREAM_TERMINATED;
316 bool do_stop = false;
317 bool do_start = false;
318
319 if ((! pai || ! g.s.doEcho()) && (est == PA_STREAM_READY)) {
320 do_stop = true;
321 } else if (pai && g.s.doEcho()) {
322 switch (est) {
323 case PA_STREAM_TERMINATED: {
324 if (pasSpeaker)
325 pa_stream_unref(pasSpeaker);
326
327 pa_sample_spec pss = qhSpecMap.value(edev);
328 pa_channel_map pcm = qhChanMap.value(edev);
329 if ((pss.format != PA_SAMPLE_FLOAT32NE) && (pss.format != PA_SAMPLE_S16NE))
330 pss.format = PA_SAMPLE_FLOAT32NE;
331 pss.rate = SAMPLE_RATE;
332 if ((pss.channels == 0) || (! g.s.bEchoMulti))
333 pss.channels = 1;
334
335 pasSpeaker = pa_stream_new(pacContext, mumble_echo, &pss, (pss.channels == 1) ? NULL : &pcm);
336 pa_stream_set_state_callback(pasSpeaker, stream_callback, this);
337 pa_stream_set_read_callback(pasSpeaker, read_callback, this);
338
339 // Fallthrough
340 }
341 case PA_STREAM_UNCONNECTED:
342 do_start = true;
343 break;
344 case PA_STREAM_READY: {
345 if (g.s.bEchoMulti != bEchoMultiCache) {
346 do_stop = true;
347 } else if (edev != qsEchoCache) {
348 do_stop = true;
349 }
350 break;
351 }
352 default:
353 break;
354 }
355 }
356 if (do_stop) {
357 qWarning("PulseAudio: Stopping echo");
358 pa_stream_disconnect(pasSpeaker);
359 } else if (do_start) {
360 qWarning("PulseAudio: Starting echo: %s", qPrintable(edev));
361 pa_buffer_attr buff;
362 const pa_sample_spec *pss = pa_stream_get_sample_spec(pasSpeaker);
363 const size_t sampleSize = (pss->format == PA_SAMPLE_FLOAT32NE) ? sizeof(float) : sizeof(short);
364 const unsigned int iBlockLen = pai->iFrameSize * pss->channels * static_cast<unsigned int>(sampleSize);
365 buff.tlength = iBlockLen;
366 buff.minreq = iBlockLen;
367 buff.maxlength = -1;
368 buff.prebuf = -1;
369 buff.fragsize = iBlockLen;
370
371 bEchoMultiCache = g.s.bEchoMulti;
372 qsEchoCache = edev;
373
374 pa_stream_connect_record(pasSpeaker, qPrintable(edev), &buff, PA_STREAM_ADJUST_LATENCY);
375 }
376 }
377 }
378
context_state_callback(pa_context * c,void * userdata)379 void PulseAudioSystem::context_state_callback(pa_context *c, void *userdata) {
380 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
381 pas->contextCallback(c);
382 }
383
subscribe_callback(pa_context *,pa_subscription_event_type evt,unsigned int,void * userdata)384 void PulseAudioSystem::subscribe_callback(pa_context *, pa_subscription_event_type evt, unsigned int, void *userdata) {
385 switch (evt & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
386 case PA_SUBSCRIPTION_EVENT_NEW:
387 case PA_SUBSCRIPTION_EVENT_REMOVE:
388 break;
389 default:
390 return;
391 }
392 switch (evt & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
393 case PA_SUBSCRIPTION_EVENT_SINK:
394 case PA_SUBSCRIPTION_EVENT_SOURCE:
395 break;
396 default:
397 return;
398 }
399 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
400 qWarning("PulseAudio: Sinks or inputs changed (inserted or removed sound card)");
401 pas->query();
402 }
403
sink_callback(pa_context *,const pa_sink_info * i,int eol,void * userdata)404 void PulseAudioSystem::sink_callback(pa_context *, const pa_sink_info *i, int eol, void *userdata) {
405 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
406 if (!i || eol) {
407 pas->bSinkDone = true;
408 pas->wakeup();
409 return;
410 }
411
412 const QString name = QString::fromUtf8(i->name);
413
414 pas->qhSpecMap.insert(name, i->sample_spec);
415 pas->qhChanMap.insert(name, i->channel_map);
416 pas->qhOutput.insert(name, QString::fromUtf8(i->description));
417 pas->qhEchoMap.insert(name, QString::fromUtf8(i->monitor_source_name));
418 }
419
source_callback(pa_context *,const pa_source_info * i,int eol,void * userdata)420 void PulseAudioSystem::source_callback(pa_context *, const pa_source_info *i, int eol, void *userdata) {
421 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
422 if (!i || eol) {
423 pas->bSourceDone = true;
424 pas->wakeup();
425 return;
426 }
427
428 const QString name = QString::fromUtf8(i->name);
429
430 pas->qhSpecMap.insert(name, i->sample_spec);
431 pas->qhChanMap.insert(name, i->channel_map);
432
433 pas->qhInput.insert(QString::fromUtf8(i->name), QString::fromUtf8(i->description));
434 }
435
server_callback(pa_context *,const pa_server_info * i,void * userdata)436 void PulseAudioSystem::server_callback(pa_context *, const pa_server_info *i, void *userdata) {
437 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
438
439 pas->qsDefaultInput = QString::fromUtf8(i->default_source_name);
440 pas->qsDefaultOutput = QString::fromUtf8(i->default_sink_name);
441
442 pas->bServerDone = true;
443 pas->wakeup();
444 }
445
sink_info_callback(pa_context *,const pa_sink_info * i,int eol,void * userdata)446 void PulseAudioSystem::sink_info_callback(pa_context *, const pa_sink_info *i, int eol, void *userdata) {
447 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
448 if (!i || eol) {
449 return;
450 }
451
452 pas->iSinkId = i->index;
453 }
454
455
stream_callback(pa_stream * s,void * userdata)456 void PulseAudioSystem::stream_callback(pa_stream *s, void *userdata) {
457 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
458 switch (pa_stream_get_state(s)) {
459 case PA_STREAM_FAILED:
460 qWarning("PulseAudio: Stream error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
461 break;
462 default:
463 break;
464 }
465 pas->wakeup();
466 }
467
read_callback(pa_stream * s,size_t bytes,void * userdata)468 void PulseAudioSystem::read_callback(pa_stream *s, size_t bytes, void *userdata) {
469 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
470
471 size_t length = bytes;
472 const void *data = NULL;
473 pa_stream_peek(s, &data, &length);
474 if (data == NULL && length > 0) {
475 qWarning("PulseAudio: pa_stream_peek reports no data at current read index.");
476 } else if (data == NULL && length == 0) {
477 qWarning("PulseAudio: pa_stream_peek reports empty memblockq.");
478 } else if (data == NULL || length == 0) {
479 qWarning("PulseAudio: invalid pa_stream_peek state encountered.");
480 return;
481 }
482
483 AudioInputPtr ai = g.ai;
484 PulseAudioInput *pai = dynamic_cast<PulseAudioInput *>(ai.get());
485 if (! pai) {
486 if (length > 0) {
487 pa_stream_drop(s);
488 }
489 pas->wakeup();
490 return;
491 }
492
493 const pa_sample_spec *pss = pa_stream_get_sample_spec(s);
494
495 if (s == pas->pasInput) {
496 if (!pa_sample_spec_equal(pss, &pai->pssMic)) {
497 pai->pssMic = *pss;
498 pai->iMicFreq = pss->rate;
499 pai->iMicChannels = pss->channels;
500 if (pss->format == PA_SAMPLE_FLOAT32NE)
501 pai->eMicFormat = PulseAudioInput::SampleFloat;
502 else
503 pai->eMicFormat = PulseAudioInput::SampleShort;
504 pai->initializeMixer();
505 }
506 if (data != NULL) {
507 pai->addMic(data, static_cast<unsigned int>(length) / pai->iMicSampleSize);
508 }
509 } else if (s == pas->pasSpeaker) {
510 if (!pa_sample_spec_equal(pss, &pai->pssEcho)) {
511 pai->pssEcho = *pss;
512 pai->iEchoFreq = pss->rate;
513 pai->iEchoChannels = pss->channels;
514 if (pss->format == PA_SAMPLE_FLOAT32NE)
515 pai->eEchoFormat = PulseAudioInput::SampleFloat;
516 else
517 pai->eEchoFormat = PulseAudioInput::SampleShort;
518 pai->initializeMixer();
519 }
520 if (data != NULL) {
521 pai->addEcho(data, static_cast<unsigned int>(length) / pai->iEchoSampleSize);
522 }
523 }
524
525 if (length > 0) {
526 pa_stream_drop(s);
527 }
528 }
529
write_callback(pa_stream * s,size_t bytes,void * userdata)530 void PulseAudioSystem::write_callback(pa_stream *s, size_t bytes, void *userdata) {
531 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
532 Q_ASSERT(s == pas->pasOutput);
533
534 AudioOutputPtr ao = g.ao;
535 PulseAudioOutput *pao = dynamic_cast<PulseAudioOutput *>(ao.get());
536
537 if (! pao) {
538 return;
539 }
540
541 const pa_sample_spec *pss = pa_stream_get_sample_spec(s);
542 const pa_channel_map *pcm = pa_stream_get_channel_map(pas->pasOutput);
543 if (!pa_sample_spec_equal(pss, &pao->pss) || !pa_channel_map_equal(pcm, &pao->pcm)) {
544 pao->pss = *pss;
545 pao->pcm = *pcm;
546 if (pss->format == PA_SAMPLE_FLOAT32NE)
547 pao->eSampleFormat = PulseAudioOutput::SampleFloat;
548 else
549 pao->eSampleFormat = PulseAudioOutput::SampleShort;
550 pao->iMixerFreq = pss->rate;
551 pao->iChannels = pss->channels;
552 unsigned int chanmasks[pss->channels];
553 for (int i=0;i<pss->channels;++i) {
554 unsigned int cm = 0;
555 switch (pcm->map[i]) {
556 case PA_CHANNEL_POSITION_LEFT:
557 cm = SPEAKER_FRONT_LEFT;
558 break;
559 case PA_CHANNEL_POSITION_RIGHT:
560 cm = SPEAKER_FRONT_RIGHT;
561 break;
562 case PA_CHANNEL_POSITION_CENTER:
563 cm = SPEAKER_FRONT_CENTER;
564 break;
565 case PA_CHANNEL_POSITION_REAR_LEFT:
566 cm = SPEAKER_BACK_LEFT;
567 break;
568 case PA_CHANNEL_POSITION_REAR_RIGHT:
569 cm = SPEAKER_BACK_RIGHT;
570 break;
571 case PA_CHANNEL_POSITION_REAR_CENTER:
572 cm = SPEAKER_BACK_CENTER;
573 break;
574 case PA_CHANNEL_POSITION_LFE:
575 cm = SPEAKER_LOW_FREQUENCY;
576 break;
577 case PA_CHANNEL_POSITION_SIDE_LEFT:
578 cm = SPEAKER_SIDE_LEFT;
579 break;
580 case PA_CHANNEL_POSITION_SIDE_RIGHT:
581 cm = SPEAKER_SIDE_RIGHT;
582 break;
583 case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
584 cm = SPEAKER_FRONT_LEFT_OF_CENTER;
585 break;
586 case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
587 cm = SPEAKER_FRONT_RIGHT_OF_CENTER;
588 break;
589 default:
590 cm = 0;
591 break;
592 }
593 chanmasks[i] = cm;
594 }
595 pao->initializeMixer(chanmasks);
596 }
597
598 const unsigned int iSampleSize = pao->iSampleSize;
599 const unsigned int samples = static_cast<unsigned int>(bytes) / iSampleSize;
600 bool oldAttenuation = pas->bAttenuating;
601
602 unsigned char buffer[bytes];
603 // do we have some mixed output?
604 if (pao->mix(buffer, samples)) {
605 // attenuate if instructed to or it's in settings
606 pas->bAttenuating = (g.bAttenuateOthers || g.s.bAttenuateOthers);
607
608 } else {
609 memset(buffer, 0, bytes);
610
611 // attenuate if intructed to (self-activated)
612 pas->bAttenuating = g.bAttenuateOthers;
613 }
614
615 // if the attenuation state has changed
616 if (oldAttenuation != pas->bAttenuating) {
617 pas->setVolumes();
618 }
619
620 pa_stream_write(s, buffer, iSampleSize * samples, NULL, 0, PA_SEEK_RELATIVE);
621 }
622
volume_sink_input_list_callback(pa_context * c,const pa_sink_input_info * i,int eol,void * userdata)623 void PulseAudioSystem::volume_sink_input_list_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) {
624 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
625
626 if (eol == 0) {
627 // If we're using the default of "enable attenuation on all ouputs" and output from an application is loopbacked,
628 // both the loopback and the application will be attenuated leading to double attenuation.
629 if (!g.s.bOnlyAttenuateSameOutput && pas->iSinkId > -1 && !strcmp(i->driver, "module-loopback.c")) {
630 return;
631 }
632 // If we're not attenuating different sinks and the input is not on this sink, don't attenuate. Or,
633 // if the input is a loopback module and connected to Mumble's sink, also ignore it (loopbacks are used to connect
634 // sinks). An attenuated loopback means an indirect application attenuation.
635 if (g.s.bOnlyAttenuateSameOutput && pas->iSinkId > -1) {
636 if (int(i->sink) != pas->iSinkId || (int(i->sink) == pas->iSinkId && !strcmp(i->driver, "module-loopback.c") && !g.s.bAttenuateLoopbacks)) {
637 return;
638 }
639 }
640 // ensure we're not attenuating ourselves!
641 if (strcmp(i->name, mumble_sink_input) != 0) {
642 // create a new entry
643 PulseAttenuation patt;
644 patt.index = i->index;
645 patt.name = QString::fromUtf8(i->name);
646 patt.stream_restore_id = QString::fromUtf8(pa_proplist_gets(i->proplist, "module-stream-restore.id"));
647 patt.normal_volume = i->volume;
648
649 // calculate the attenuated volume
650 pa_volume_t adj = static_cast<pa_volume_t>(PA_VOLUME_NORM * g.s.fOtherVolume);
651 pa_sw_cvolume_multiply_scalar(&patt.attenuated_volume, &i->volume, adj);
652
653 // set it on the sink input
654 pa_operation_unref(pa_context_set_sink_input_volume(c, i->index, &patt.attenuated_volume, NULL, NULL));
655
656 // store it
657 pas->qhVolumes[i->index] = patt;
658 }
659
660 } else if (eol < 0) {
661 qWarning("PulseAudio: Sink input introspection error.");
662 }
663 }
664
restore_sink_input_list_callback(pa_context * c,const pa_sink_input_info * i,int eol,void * userdata)665 void PulseAudioSystem::restore_sink_input_list_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata) {
666 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
667
668 if (eol == 0) {
669 // if we were tracking this specific sink previously
670 if (pas->qhVolumes.contains(i->index)) {
671 // and if it has the attenuated volume we applied to it
672 if (pa_cvolume_equal(&i->volume, &pas->qhVolumes[i->index].attenuated_volume) != 0) {
673 // mark it as matched
674 pas->qlMatchedSinks.append(i->index);
675
676 // reset the volume to normal
677 pas->iRemainingOperations++;
678 pa_operation_unref(pa_context_set_sink_input_volume(c, i->index, &pas->qhVolumes[i->index].normal_volume, restore_volume_success_callback, pas));
679 }
680
681 // otherwise, save for matching at the end of iteration
682 } else {
683 QString restore_id = QString::fromUtf8(pa_proplist_gets(i->proplist, "module-stream-restore.id"));
684 PulseAttenuation patt;
685 patt.index = i->index;
686 patt.normal_volume = i->volume;
687 pas->qhUnmatchedSinks[restore_id] = patt;
688 }
689
690 } else if (eol < 0) {
691 qWarning("PulseAudio: Sink input introspection error.");
692
693 } else {
694 // build a list of missing streams by iterating our active list
695 QHash<uint32_t, PulseAttenuation>::const_iterator it;
696 for (it = pas->qhVolumes.constBegin(); it != pas->qhVolumes.constEnd(); ++it) {
697 // skip if previously matched
698 if (pas->qlMatchedSinks.contains(it.key())) {
699 continue;
700 }
701
702 // check if the restore id matches. the only case where this would
703 // happen is if the application was reopened during attenuation.
704 if (pas->qhUnmatchedSinks.contains(it.value().stream_restore_id)) {
705 PulseAttenuation active_sink = pas->qhUnmatchedSinks[it.value().stream_restore_id];
706 // if the volume wasn't changed from our attenuation
707 if (pa_cvolume_equal(&active_sink.normal_volume, &it.value().attenuated_volume) != 0) {
708 // reset the volume to normal
709 pas->iRemainingOperations++;
710 pa_operation_unref(pa_context_set_sink_input_volume(c, active_sink.index, &it.value().normal_volume, restore_volume_success_callback, pas));
711 }
712 continue;
713 }
714
715 // at this point, we don't know what happened to the sink. add
716 // it to a list to check the stream restore database for.
717 pas->qhMissingSinks[it.value().stream_restore_id] = it.value();
718 }
719
720 // clean up
721 pas->qlMatchedSinks.clear();
722 pas->qhUnmatchedSinks.clear();
723 pas->qhVolumes.clear();
724
725 // if we had missing sinks, check the stream restore database
726 // to see if we can find and update them.
727 if (pas->qhMissingSinks.count() > 0) {
728 pas->iRemainingOperations++;
729 pa_operation_unref(pa_ext_stream_restore_read(c, stream_restore_read_callback, pas));
730 }
731
732 // trigger the volume completion callback;
733 // necessary so that shutdown actions are called
734 restore_volume_success_callback(c, 1, pas);
735 }
736 }
737
stream_restore_read_callback(pa_context * c,const pa_ext_stream_restore_info * i,int eol,void * userdata)738 void PulseAudioSystem::stream_restore_read_callback(pa_context *c, const pa_ext_stream_restore_info *i, int eol, void *userdata) {
739 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
740
741 if (eol == 0) {
742 QString name = QString::fromUtf8(i->name);
743
744 // were we looking for this restoration?
745 if (pas->qhMissingSinks.contains(name)) {
746 // make sure it still has the volume we gave it
747 if (pa_cvolume_equal(&pas->qhMissingSinks[name].attenuated_volume, &i->volume) != 0) {
748 // update the stream restore record
749 pa_ext_stream_restore_info restore = *i;
750 restore.volume = pas->qhMissingSinks[name].normal_volume;
751 pas->iRemainingOperations++;
752 pa_operation_unref(pa_ext_stream_restore_write(c, PA_UPDATE_REPLACE, &restore, 1, 1, restore_volume_success_callback, pas));
753 }
754
755 pas->qhMissingSinks.remove(name);
756 }
757
758 } else if (eol < 0) {
759 qWarning("PulseAudio: Couldn't read stream restore database.");
760 pas->qhMissingSinks.clear();
761
762 } else {
763 // verify missing list is empty
764 if (pas->qhMissingSinks.count() > 0) {
765 qWarning("PulseAudio: Failed to match %d stream(s).", pas->qhMissingSinks.count());
766 pas->qhMissingSinks.clear();
767 }
768
769 // trigger the volume completion callback;
770 // necessary so that shutdown actions are called
771 restore_volume_success_callback(c, 1, pas);
772 }
773 }
774
restore_volume_success_callback(pa_context *,int,void * userdata)775 void PulseAudioSystem::restore_volume_success_callback(pa_context *, int, void *userdata) {
776 PulseAudioSystem *pas = reinterpret_cast<PulseAudioSystem *>(userdata);
777
778 pas->iRemainingOperations--;
779
780 // if there are no more pending volume adjustments and we're shutting down,
781 // let the main thread know
782 if (! pas->bRunning && pas->iRemainingOperations == 0) {
783 pas->qwcWait.wakeAll();
784 }
785 }
786
query()787 void PulseAudioSystem::query() {
788 bSourceDone=bSinkDone=bServerDone = false;
789 qhInput.clear();
790 qhOutput.clear();
791 qhEchoMap.clear();
792 qhSpecMap.clear();
793 qhChanMap.clear();
794 qhInput.insert(QString(), tr("Default Input"));
795 qhOutput.insert(QString(), tr("Default Output"));
796 pa_operation_unref(pa_context_get_server_info(pacContext, server_callback, this));
797 pa_operation_unref(pa_context_get_sink_info_list(pacContext, sink_callback, this));
798 pa_operation_unref(pa_context_get_source_info_list(pacContext, source_callback, this));
799 wakeup();
800 }
801
setVolumes()802 void PulseAudioSystem::setVolumes() {
803 // set attenuation state and volumes
804 if (bAttenuating) {
805 // ensure the volume map is empty, otherwise it may be dangerous to change
806 if (qhVolumes.empty()) {
807 // set the new per-application volumes and store the old ones
808 pa_operation_unref(pa_context_get_sink_input_info_list(pacContext, volume_sink_input_list_callback, this));
809 }
810 // clear attenuation state and restore normal volumes
811 } else {
812 iRemainingOperations++;
813 pa_operation_unref(pa_context_get_sink_input_info_list(pacContext, restore_sink_input_list_callback, this));
814 }
815 }
816
contextCallback(pa_context * c)817 void PulseAudioSystem::contextCallback(pa_context *c) {
818 Q_ASSERT(c == pacContext);
819 switch (pa_context_get_state(c)) {
820 case PA_CONTEXT_READY:
821 bPulseIsGood = true;
822 pa_operation_unref(pa_context_subscribe(pacContext, PA_SUBSCRIPTION_MASK_SOURCE, NULL, this));
823 pa_operation_unref(pa_context_subscribe(pacContext, PA_SUBSCRIPTION_MASK_SINK, NULL, this));
824 query();
825 break;
826 case PA_CONTEXT_TERMINATED:
827 qWarning("PulseAudio: Forcibly disconnected from PulseAudio");
828 break;
829 case PA_CONTEXT_FAILED:
830 qWarning("PulseAudio: Connection failure: %s", pa_strerror(pa_context_errno(c)));
831 break;
832 default:
833 return;
834 }
835 qmWait.lock();
836 qwcWait.wakeAll();
837 qmWait.unlock();
838 }
839
PulseAudioInputRegistrar()840 PulseAudioInputRegistrar::PulseAudioInputRegistrar() : AudioInputRegistrar(QLatin1String("PulseAudio"), 10) {
841 }
842
create()843 AudioInput *PulseAudioInputRegistrar::create() {
844 return new PulseAudioInput();
845 }
846
getDeviceChoices()847 const QList<audioDevice> PulseAudioInputRegistrar::getDeviceChoices() {
848 QList<audioDevice> qlReturn;
849
850 QStringList qlInputDevs = pasys->qhInput.keys();
851 qSort(qlInputDevs);
852
853 if (qlInputDevs.contains(g.s.qsPulseAudioInput)) {
854 qlInputDevs.removeAll(g.s.qsPulseAudioInput);
855 qlInputDevs.prepend(g.s.qsPulseAudioInput);
856 }
857
858 foreach(const QString &dev, qlInputDevs) {
859 qlReturn << audioDevice(pasys->qhInput.value(dev), dev);
860 }
861
862 return qlReturn;
863 }
864
setDeviceChoice(const QVariant & choice,Settings & s)865 void PulseAudioInputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
866 s.qsPulseAudioInput = choice.toString();
867 }
868
canEcho(const QString & osys) const869 bool PulseAudioInputRegistrar::canEcho(const QString &osys) const {
870 return (osys == name);
871 }
872
PulseAudioOutputRegistrar()873 PulseAudioOutputRegistrar::PulseAudioOutputRegistrar() : AudioOutputRegistrar(QLatin1String("PulseAudio"), 10) {
874 }
875
create()876 AudioOutput *PulseAudioOutputRegistrar::create() {
877 return new PulseAudioOutput();
878 }
879
getDeviceChoices()880 const QList<audioDevice> PulseAudioOutputRegistrar::getDeviceChoices() {
881 QList<audioDevice> qlReturn;
882
883 QStringList qlOutputDevs = pasys->qhOutput.keys();
884 qSort(qlOutputDevs);
885
886 if (qlOutputDevs.contains(g.s.qsPulseAudioOutput)) {
887 qlOutputDevs.removeAll(g.s.qsPulseAudioOutput);
888 qlOutputDevs.prepend(g.s.qsPulseAudioOutput);
889 }
890
891 foreach(const QString &dev, qlOutputDevs) {
892 qlReturn << audioDevice(pasys->qhOutput.value(dev), dev);
893 }
894
895 return qlReturn;
896 }
897
setDeviceChoice(const QVariant & choice,Settings & s)898 void PulseAudioOutputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
899 s.qsPulseAudioOutput = choice.toString();
900 }
901
canMuteOthers() const902 bool PulseAudioOutputRegistrar::canMuteOthers() const {
903 return true;
904 }
905
PulseAudioInput()906 PulseAudioInput::PulseAudioInput() {
907 memset(&pssMic, 0, sizeof(pssMic));
908 memset(&pssEcho, 0, sizeof(pssEcho));
909 bRunning = true;
910 if (pasys)
911 pasys->wakeup_lock();
912 }
913
~PulseAudioInput()914 PulseAudioInput::~PulseAudioInput() {
915 bRunning = false;
916 qmMutex.lock();
917 qwcWait.wakeAll();
918 qmMutex.unlock();
919 wait();
920 if (pasys)
921 pasys->wakeup_lock();
922 }
923
run()924 void PulseAudioInput::run() {
925 qmMutex.lock();
926 while (bRunning)
927 qwcWait.wait(&qmMutex);
928 qmMutex.unlock();
929 }
930
PulseAudioOutput()931 PulseAudioOutput::PulseAudioOutput() {
932 memset(&pss, 0, sizeof(pss));
933 memset(&pcm, 0, sizeof(pcm));
934 bRunning = true;
935 if (pasys)
936 pasys->wakeup_lock();
937 }
938
~PulseAudioOutput()939 PulseAudioOutput::~PulseAudioOutput() {
940 bRunning = false;
941 qmMutex.lock();
942 qwcWait.wakeAll();
943 qmMutex.unlock();
944 wait();
945 if (pasys)
946 pasys->wakeup_lock();
947 }
948
run()949 void PulseAudioOutput::run() {
950 qmMutex.lock();
951 while (bRunning)
952 qwcWait.wait(&qmMutex);
953 qmMutex.unlock();
954 }
955