1 /*
2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include <assert.h>
12 
13 #include "webrtc/modules/audio_device/linux/audio_mixer_manager_alsa_linux.h"
14 #include "webrtc/system_wrappers/include/trace.h"
15 
16 extern webrtc_adm_linux_alsa::AlsaSymbolTable AlsaSymbolTable;
17 
18 // Accesses ALSA functions through our late-binding symbol table instead of
19 // directly. This way we don't have to link to libalsa, which means our binary
20 // will work on systems that don't have it.
21 #define LATE(sym) \
22   LATESYM_GET(webrtc_adm_linux_alsa::AlsaSymbolTable, &AlsaSymbolTable, sym)
23 
24 namespace webrtc
25 {
26 
AudioMixerManagerLinuxALSA(const int32_t id)27 AudioMixerManagerLinuxALSA::AudioMixerManagerLinuxALSA(const int32_t id) :
28     _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
29     _id(id),
30     _outputMixerHandle(NULL),
31     _inputMixerHandle(NULL),
32     _outputMixerElement(NULL),
33     _inputMixerElement(NULL)
34 {
35     WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id,
36                  "%s constructed", __FUNCTION__);
37 
38     memset(_outputMixerStr, 0, kAdmMaxDeviceNameSize);
39     memset(_inputMixerStr, 0, kAdmMaxDeviceNameSize);
40 }
41 
~AudioMixerManagerLinuxALSA()42 AudioMixerManagerLinuxALSA::~AudioMixerManagerLinuxALSA()
43 {
44     WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id,
45                  "%s destructed", __FUNCTION__);
46 
47     Close();
48 
49     delete &_critSect;
50 }
51 
52 // ============================================================================
53 //                                    PUBLIC METHODS
54 // ============================================================================
55 
Close()56 int32_t AudioMixerManagerLinuxALSA::Close()
57 {
58     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
59                  __FUNCTION__);
60 
61     CriticalSectionScoped lock(&_critSect);
62 
63     CloseSpeaker();
64     CloseMicrophone();
65 
66     return 0;
67 
68 }
69 
CloseSpeaker()70 int32_t AudioMixerManagerLinuxALSA::CloseSpeaker()
71 {
72     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
73                  __FUNCTION__);
74 
75     CriticalSectionScoped lock(&_critSect);
76 
77     int errVal = 0;
78 
79     if (_outputMixerHandle != NULL)
80     {
81         WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
82                      "Closing playout mixer");
83         LATE(snd_mixer_free)(_outputMixerHandle);
84         if (errVal < 0)
85         {
86             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
87                          "     Error freeing playout mixer: %s",
88                          LATE(snd_strerror)(errVal));
89         }
90         errVal = LATE(snd_mixer_detach)(_outputMixerHandle, _outputMixerStr);
91         if (errVal < 0)
92         {
93             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
94                          "     Error detachinging playout mixer: %s",
95                          LATE(snd_strerror)(errVal));
96         }
97         errVal = LATE(snd_mixer_close)(_outputMixerHandle);
98         if (errVal < 0)
99         {
100             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
101                          "     Error snd_mixer_close(handleMixer) errVal=%d",
102                          errVal);
103         }
104         _outputMixerHandle = NULL;
105         _outputMixerElement = NULL;
106     }
107     memset(_outputMixerStr, 0, kAdmMaxDeviceNameSize);
108 
109     return 0;
110 }
111 
CloseMicrophone()112 int32_t AudioMixerManagerLinuxALSA::CloseMicrophone()
113 {
114     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
115 
116     CriticalSectionScoped lock(&_critSect);
117 
118     int errVal = 0;
119 
120     if (_inputMixerHandle != NULL)
121     {
122         WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
123                      "Closing record mixer");
124 
125         LATE(snd_mixer_free)(_inputMixerHandle);
126         if (errVal < 0)
127         {
128             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
129                          "     Error freeing record mixer: %s",
130                          LATE(snd_strerror)(errVal));
131         }
132         WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
133                      "Closing record mixer 2");
134 
135         errVal = LATE(snd_mixer_detach)(_inputMixerHandle, _inputMixerStr);
136         if (errVal < 0)
137         {
138             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
139                          "     Error detachinging record mixer: %s",
140                          LATE(snd_strerror)(errVal));
141         }
142         WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
143                      "Closing record mixer 3");
144 
145         errVal = LATE(snd_mixer_close)(_inputMixerHandle);
146         if (errVal < 0)
147         {
148             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
149                          "     Error snd_mixer_close(handleMixer) errVal=%d",
150                          errVal);
151         }
152 
153         WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
154                      "Closing record mixer 4");
155         _inputMixerHandle = NULL;
156         _inputMixerElement = NULL;
157     }
158     memset(_inputMixerStr, 0, kAdmMaxDeviceNameSize);
159 
160     return 0;
161 }
162 
OpenSpeaker(char * deviceName)163 int32_t AudioMixerManagerLinuxALSA::OpenSpeaker(char* deviceName)
164 {
165     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
166                  "AudioMixerManagerLinuxALSA::OpenSpeaker(name=%s)", deviceName);
167 
168     CriticalSectionScoped lock(&_critSect);
169 
170     int errVal = 0;
171 
172     // Close any existing output mixer handle
173     //
174     if (_outputMixerHandle != NULL)
175     {
176         WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
177                      "Closing playout mixer");
178 
179         LATE(snd_mixer_free)(_outputMixerHandle);
180         if (errVal < 0)
181         {
182             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
183                          "     Error freeing playout mixer: %s",
184                          LATE(snd_strerror)(errVal));
185         }
186         errVal = LATE(snd_mixer_detach)(_outputMixerHandle, _outputMixerStr);
187         if (errVal < 0)
188         {
189             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
190                          "     Error detachinging playout mixer: %s",
191                          LATE(snd_strerror)(errVal));
192         }
193         errVal = LATE(snd_mixer_close)(_outputMixerHandle);
194         if (errVal < 0)
195         {
196             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
197                          "     Error snd_mixer_close(handleMixer) errVal=%d",
198                          errVal);
199         }
200     }
201     _outputMixerHandle = NULL;
202     _outputMixerElement = NULL;
203 
204     errVal = LATE(snd_mixer_open)(&_outputMixerHandle, 0);
205     if (errVal < 0)
206     {
207         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
208                      "snd_mixer_open(&_outputMixerHandle, 0) - error");
209         return -1;
210     }
211 
212     char controlName[kAdmMaxDeviceNameSize] = { 0 };
213     GetControlName(controlName, deviceName);
214 
215     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
216                  "     snd_mixer_attach(_outputMixerHandle, %s)", controlName);
217 
218     errVal = LATE(snd_mixer_attach)(_outputMixerHandle, controlName);
219     if (errVal < 0)
220     {
221         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
222                      "     snd_mixer_attach(_outputMixerHandle, %s) error: %s",
223                      controlName, LATE(snd_strerror)(errVal));
224         _outputMixerHandle = NULL;
225         return -1;
226     }
227     strcpy(_outputMixerStr, controlName);
228 
229     errVal = LATE(snd_mixer_selem_register)(_outputMixerHandle, NULL, NULL);
230     if (errVal < 0)
231     {
232         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
233                      "     snd_mixer_selem_register(_outputMixerHandle,"
234                      " NULL, NULL), error: %s",
235                      LATE(snd_strerror)(errVal));
236         _outputMixerHandle = NULL;
237         return -1;
238     }
239 
240     // Load and find the proper mixer element
241     if (LoadSpeakerMixerElement() < 0)
242     {
243         return -1;
244     }
245 
246     if (_outputMixerHandle != NULL)
247     {
248         WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
249                      "  the output mixer device is now open (0x%x)",
250                      _outputMixerHandle);
251     }
252 
253     return 0;
254 }
255 
OpenMicrophone(char * deviceName)256 int32_t AudioMixerManagerLinuxALSA::OpenMicrophone(char *deviceName)
257 {
258     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
259                  "AudioMixerManagerLinuxALSA::OpenMicrophone(name=%s)",
260                  deviceName);
261 
262     CriticalSectionScoped lock(&_critSect);
263 
264     int errVal = 0;
265 
266     // Close any existing input mixer handle
267     //
268     if (_inputMixerHandle != NULL)
269     {
270         WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
271                      "Closing record mixer");
272 
273         LATE(snd_mixer_free)(_inputMixerHandle);
274         if (errVal < 0)
275         {
276             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
277                          "     Error freeing record mixer: %s",
278                          LATE(snd_strerror)(errVal));
279         }
280         WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
281                      "Closing record mixer");
282 
283         errVal = LATE(snd_mixer_detach)(_inputMixerHandle, _inputMixerStr);
284         if (errVal < 0)
285         {
286             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
287                          "     Error detachinging record mixer: %s",
288                          LATE(snd_strerror)(errVal));
289         }
290         WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
291                      "Closing record mixer");
292 
293         errVal = LATE(snd_mixer_close)(_inputMixerHandle);
294         if (errVal < 0)
295         {
296             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
297                          "     Error snd_mixer_close(handleMixer) errVal=%d",
298                          errVal);
299         }
300         WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
301                      "Closing record mixer");
302     }
303     _inputMixerHandle = NULL;
304     _inputMixerElement = NULL;
305 
306     errVal = LATE(snd_mixer_open)(&_inputMixerHandle, 0);
307     if (errVal < 0)
308     {
309         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
310                      "     snd_mixer_open(&_inputMixerHandle, 0) - error");
311         return -1;
312     }
313 
314     char controlName[kAdmMaxDeviceNameSize] = { 0 };
315     GetControlName(controlName, deviceName);
316 
317     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
318                  "     snd_mixer_attach(_inputMixerHandle, %s)", controlName);
319 
320     errVal = LATE(snd_mixer_attach)(_inputMixerHandle, controlName);
321     if (errVal < 0)
322     {
323         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
324                      "     snd_mixer_attach(_inputMixerHandle, %s) error: %s",
325                      controlName, LATE(snd_strerror)(errVal));
326 
327         _inputMixerHandle = NULL;
328         return -1;
329     }
330     strcpy(_inputMixerStr, controlName);
331 
332     errVal = LATE(snd_mixer_selem_register)(_inputMixerHandle, NULL, NULL);
333     if (errVal < 0)
334     {
335         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
336                      "     snd_mixer_selem_register(_inputMixerHandle,"
337                      " NULL, NULL), error: %s",
338                      LATE(snd_strerror)(errVal));
339 
340         _inputMixerHandle = NULL;
341         return -1;
342     }
343     // Load and find the proper mixer element
344     if (LoadMicMixerElement() < 0)
345     {
346         return -1;
347     }
348 
349     if (_inputMixerHandle != NULL)
350     {
351         WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
352                      "  the input mixer device is now open (0x%x)",
353                      _inputMixerHandle);
354     }
355 
356     return 0;
357 }
358 
SpeakerIsInitialized() const359 bool AudioMixerManagerLinuxALSA::SpeakerIsInitialized() const
360 {
361     WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__);
362 
363     return (_outputMixerHandle != NULL);
364 }
365 
MicrophoneIsInitialized() const366 bool AudioMixerManagerLinuxALSA::MicrophoneIsInitialized() const
367 {
368     WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s",
369                  __FUNCTION__);
370 
371     return (_inputMixerHandle != NULL);
372 }
373 
SetSpeakerVolume(uint32_t volume)374 int32_t AudioMixerManagerLinuxALSA::SetSpeakerVolume(
375     uint32_t volume)
376 {
377     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
378                  "AudioMixerManagerLinuxALSA::SetSpeakerVolume(volume=%u)",
379                  volume);
380 
381     CriticalSectionScoped lock(&_critSect);
382 
383     if (_outputMixerElement == NULL)
384     {
385         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
386                      "  no avaliable output mixer element exists");
387         return -1;
388     }
389 
390     int errVal =
391         LATE(snd_mixer_selem_set_playback_volume_all)(_outputMixerElement,
392                                                       volume);
393     if (errVal < 0)
394     {
395         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
396                      "     Error changing master volume: %s",
397                      LATE(snd_strerror)(errVal));
398         return -1;
399     }
400 
401     return (0);
402 }
403 
SpeakerVolume(uint32_t & volume) const404 int32_t AudioMixerManagerLinuxALSA::SpeakerVolume(
405     uint32_t& volume) const
406 {
407 
408     if (_outputMixerElement == NULL)
409     {
410         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
411                      "  no avaliable output mixer element exists");
412         return -1;
413     }
414 
415     long int vol(0);
416 
417     int
418         errVal = LATE(snd_mixer_selem_get_playback_volume)(
419             _outputMixerElement,
420             (snd_mixer_selem_channel_id_t) 0,
421             &vol);
422     if (errVal < 0)
423     {
424         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
425                      "Error getting outputvolume: %s",
426                      LATE(snd_strerror)(errVal));
427         return -1;
428     }
429     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
430                  "     AudioMixerManagerLinuxALSA::SpeakerVolume() => vol=%i",
431                  vol);
432 
433     volume = static_cast<uint32_t> (vol);
434 
435     return 0;
436 }
437 
MaxSpeakerVolume(uint32_t & maxVolume) const438 int32_t AudioMixerManagerLinuxALSA::MaxSpeakerVolume(
439     uint32_t& maxVolume) const
440 {
441 
442     if (_outputMixerElement == NULL)
443     {
444         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
445                      "  no avilable output mixer element exists");
446         return -1;
447     }
448 
449     long int minVol(0);
450     long int maxVol(0);
451 
452     int errVal =
453         LATE(snd_mixer_selem_get_playback_volume_range)(_outputMixerElement,
454                                                         &minVol, &maxVol);
455 
456     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
457                  "     Playout hardware volume range, min: %d, max: %d",
458                  minVol, maxVol);
459 
460     if (maxVol <= minVol)
461     {
462         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
463                      "     Error getting get_playback_volume_range: %s",
464                      LATE(snd_strerror)(errVal));
465     }
466 
467     maxVolume = static_cast<uint32_t> (maxVol);
468 
469     return 0;
470 }
471 
MinSpeakerVolume(uint32_t & minVolume) const472 int32_t AudioMixerManagerLinuxALSA::MinSpeakerVolume(
473     uint32_t& minVolume) const
474 {
475 
476     if (_outputMixerElement == NULL)
477     {
478         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
479                      "  no avaliable output mixer element exists");
480         return -1;
481     }
482 
483     long int minVol(0);
484     long int maxVol(0);
485 
486     int errVal =
487         LATE(snd_mixer_selem_get_playback_volume_range)(_outputMixerElement,
488                                                         &minVol, &maxVol);
489 
490     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
491                  "     Playout hardware volume range, min: %d, max: %d",
492                  minVol, maxVol);
493 
494     if (maxVol <= minVol)
495     {
496         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
497                      "     Error getting get_playback_volume_range: %s",
498                      LATE(snd_strerror)(errVal));
499     }
500 
501     minVolume = static_cast<uint32_t> (minVol);
502 
503     return 0;
504 }
505 
506 // TL: Have done testnig with these but they don't seem reliable and
507 // they were therefore not added
508 /*
509  // ----------------------------------------------------------------------------
510  //    SetMaxSpeakerVolume
511  // ----------------------------------------------------------------------------
512 
513  int32_t AudioMixerManagerLinuxALSA::SetMaxSpeakerVolume(
514      uint32_t maxVolume)
515  {
516 
517  if (_outputMixerElement == NULL)
518  {
519  WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
520  "  no avaliable output mixer element exists");
521  return -1;
522  }
523 
524  long int minVol(0);
525  long int maxVol(0);
526 
527  int errVal = snd_mixer_selem_get_playback_volume_range(
528  _outputMixerElement, &minVol, &maxVol);
529  if ((maxVol <= minVol) || (errVal != 0))
530  {
531  WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
532   "     Error getting playback volume range: %s", snd_strerror(errVal));
533  }
534 
535  maxVol = maxVolume;
536  errVal = snd_mixer_selem_set_playback_volume_range(
537  _outputMixerElement, minVol, maxVol);
538  WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
539   "     Playout hardware volume range, min: %d, max: %d", minVol, maxVol);
540  if (errVal != 0)
541  {
542  WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
543   "     Error setting playback volume range: %s", snd_strerror(errVal));
544  return -1;
545  }
546 
547  return 0;
548  }
549 
550  // ----------------------------------------------------------------------------
551  //    SetMinSpeakerVolume
552  // ----------------------------------------------------------------------------
553 
554  int32_t AudioMixerManagerLinuxALSA::SetMinSpeakerVolume(
555      uint32_t minVolume)
556  {
557 
558  if (_outputMixerElement == NULL)
559  {
560  WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
561  "  no avaliable output mixer element exists");
562  return -1;
563  }
564 
565  long int minVol(0);
566  long int maxVol(0);
567 
568  int errVal = snd_mixer_selem_get_playback_volume_range(
569  _outputMixerElement, &minVol, &maxVol);
570  if ((maxVol <= minVol) || (errVal != 0))
571  {
572  WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
573   "     Error getting playback volume range: %s", snd_strerror(errVal));
574  }
575 
576  minVol = minVolume;
577  errVal = snd_mixer_selem_set_playback_volume_range(
578  _outputMixerElement, minVol, maxVol);
579  WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
580  "     Playout hardware volume range, min: %d, max: %d", minVol, maxVol);
581  if (errVal != 0)
582  {
583  WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
584  "     Error setting playback volume range: %s", snd_strerror(errVal));
585  return -1;
586  }
587 
588  return 0;
589  }
590  */
591 
SpeakerVolumeStepSize(uint16_t & stepSize) const592 int32_t AudioMixerManagerLinuxALSA::SpeakerVolumeStepSize(
593     uint16_t& stepSize) const
594 {
595 
596     if (_outputMixerHandle == NULL)
597     {
598         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
599                      "  no avaliable output mixer exists");
600         return -1;
601     }
602 
603     // The step size is always 1 for ALSA
604     stepSize = 1;
605 
606     return 0;
607 }
608 
SpeakerVolumeIsAvailable(bool & available)609 int32_t AudioMixerManagerLinuxALSA::SpeakerVolumeIsAvailable(
610     bool& available)
611 {
612     if (_outputMixerElement == NULL)
613     {
614         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
615                      "  no avaliable output mixer element exists");
616         return -1;
617     }
618 
619     available = LATE(snd_mixer_selem_has_playback_volume)(_outputMixerElement);
620 
621     return 0;
622 }
623 
SpeakerMuteIsAvailable(bool & available)624 int32_t AudioMixerManagerLinuxALSA::SpeakerMuteIsAvailable(
625     bool& available)
626 {
627     if (_outputMixerElement == NULL)
628     {
629         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
630                      "  no avaliable output mixer element exists");
631         return -1;
632     }
633 
634     available = LATE(snd_mixer_selem_has_playback_switch)(_outputMixerElement);
635 
636     return 0;
637 }
638 
SetSpeakerMute(bool enable)639 int32_t AudioMixerManagerLinuxALSA::SetSpeakerMute(bool enable)
640 {
641     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
642                  "AudioMixerManagerLinuxALSA::SetSpeakerMute(enable=%u)",
643                  enable);
644 
645     CriticalSectionScoped lock(&_critSect);
646 
647     if (_outputMixerElement == NULL)
648     {
649         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
650                      "  no avaliable output mixer element exists");
651         return -1;
652     }
653 
654     // Ensure that the selected speaker destination has a valid mute control.
655     bool available(false);
656     SpeakerMuteIsAvailable(available);
657     if (!available)
658     {
659         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
660                      "  it is not possible to mute the speaker");
661         return -1;
662     }
663 
664     // Note value = 0 (off) means muted
665     int errVal =
666         LATE(snd_mixer_selem_set_playback_switch_all)(_outputMixerElement,
667                                                       !enable);
668     if (errVal < 0)
669     {
670         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
671                      "     Error setting playback switch: %s",
672                      LATE(snd_strerror)(errVal));
673         return -1;
674     }
675 
676     return (0);
677 }
678 
SpeakerMute(bool & enabled) const679 int32_t AudioMixerManagerLinuxALSA::SpeakerMute(bool& enabled) const
680 {
681 
682     if (_outputMixerElement == NULL)
683     {
684         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
685                      "  no avaliable output mixer exists");
686         return -1;
687     }
688 
689     // Ensure that the selected speaker destination has a valid mute control.
690     bool available =
691         LATE(snd_mixer_selem_has_playback_switch)(_outputMixerElement);
692     if (!available)
693     {
694         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
695                      "  it is not possible to mute the speaker");
696         return -1;
697     }
698 
699     int value(false);
700 
701     // Retrieve one boolean control value for a specified mute-control
702     //
703     int
704         errVal = LATE(snd_mixer_selem_get_playback_switch)(
705             _outputMixerElement,
706             (snd_mixer_selem_channel_id_t) 0,
707             &value);
708     if (errVal < 0)
709     {
710         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
711                      "     Error getting playback switch: %s",
712                      LATE(snd_strerror)(errVal));
713         return -1;
714     }
715 
716     // Note value = 0 (off) means muted
717     enabled = (bool) !value;
718 
719     return 0;
720 }
721 
MicrophoneMuteIsAvailable(bool & available)722 int32_t AudioMixerManagerLinuxALSA::MicrophoneMuteIsAvailable(
723     bool& available)
724 {
725     if (_inputMixerElement == NULL)
726     {
727         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
728                      "  no avaliable input mixer element exists");
729         return -1;
730     }
731 
732     available = LATE(snd_mixer_selem_has_capture_switch)(_inputMixerElement);
733     return 0;
734 }
735 
SetMicrophoneMute(bool enable)736 int32_t AudioMixerManagerLinuxALSA::SetMicrophoneMute(bool enable)
737 {
738     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
739                  "AudioMixerManagerLinuxALSA::SetMicrophoneMute(enable=%u)",
740                  enable);
741 
742     CriticalSectionScoped lock(&_critSect);
743 
744     if (_inputMixerElement == NULL)
745     {
746         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
747                      "  no avaliable input mixer element exists");
748         return -1;
749     }
750 
751     // Ensure that the selected microphone destination has a valid mute control.
752     bool available(false);
753     MicrophoneMuteIsAvailable(available);
754     if (!available)
755     {
756         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
757                      "  it is not possible to mute the microphone");
758         return -1;
759     }
760 
761     // Note value = 0 (off) means muted
762     int errVal =
763         LATE(snd_mixer_selem_set_capture_switch_all)(_inputMixerElement,
764                                                      !enable);
765     if (errVal < 0)
766     {
767         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
768                      "     Error setting capture switch: %s",
769                      LATE(snd_strerror)(errVal));
770         return -1;
771     }
772 
773     return (0);
774 }
775 
MicrophoneMute(bool & enabled) const776 int32_t AudioMixerManagerLinuxALSA::MicrophoneMute(bool& enabled) const
777 {
778 
779     if (_inputMixerElement == NULL)
780     {
781         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
782                      "  no avaliable input mixer exists");
783         return -1;
784     }
785 
786     // Ensure that the selected microphone destination has a valid mute control.
787     bool available =
788         LATE(snd_mixer_selem_has_capture_switch)(_inputMixerElement);
789     if (!available)
790     {
791         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
792                      "  it is not possible to mute the microphone");
793         return -1;
794     }
795 
796     int value(false);
797 
798     // Retrieve one boolean control value for a specified mute-control
799     //
800     int
801         errVal = LATE(snd_mixer_selem_get_capture_switch)(
802             _inputMixerElement,
803             (snd_mixer_selem_channel_id_t) 0,
804             &value);
805     if (errVal < 0)
806     {
807         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
808                      "     Error getting capture switch: %s",
809                      LATE(snd_strerror)(errVal));
810         return -1;
811     }
812 
813     // Note value = 0 (off) means muted
814     enabled = (bool) !value;
815 
816     return 0;
817 }
818 
MicrophoneBoostIsAvailable(bool & available)819 int32_t AudioMixerManagerLinuxALSA::MicrophoneBoostIsAvailable(
820     bool& available)
821 {
822     if (_inputMixerHandle == NULL)
823     {
824         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
825                      "  no avaliable input mixer exists");
826         return -1;
827     }
828 
829     // Microphone boost cannot be enabled through ALSA Simple Mixer Interface
830     available = false;
831 
832     return 0;
833 }
834 
SetMicrophoneBoost(bool enable)835 int32_t AudioMixerManagerLinuxALSA::SetMicrophoneBoost(bool enable)
836 {
837     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
838                  "AudioMixerManagerLinuxALSA::SetMicrophoneBoost(enable=%u)",
839                  enable);
840 
841     CriticalSectionScoped lock(&_critSect);
842 
843     if (_inputMixerHandle == NULL)
844     {
845         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
846                      "  no avaliable input mixer exists");
847         return -1;
848     }
849 
850     // Ensure that the selected microphone destination has a valid mute control.
851     bool available(false);
852     MicrophoneMuteIsAvailable(available);
853     if (!available)
854     {
855         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
856                      "  it is not possible to enable microphone boost");
857         return -1;
858     }
859 
860     // It is assumed that the call above fails!
861 
862     return (0);
863 }
864 
MicrophoneBoost(bool & enabled) const865 int32_t AudioMixerManagerLinuxALSA::MicrophoneBoost(bool& enabled) const
866 {
867 
868     if (_inputMixerHandle == NULL)
869     {
870         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
871                      "  no avaliable input mixer exists");
872         return -1;
873     }
874 
875     // Microphone boost cannot be enabled on this platform!
876     enabled = false;
877 
878     return 0;
879 }
880 
MicrophoneVolumeIsAvailable(bool & available)881 int32_t AudioMixerManagerLinuxALSA::MicrophoneVolumeIsAvailable(
882     bool& available)
883 {
884     if (_inputMixerElement == NULL)
885     {
886         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
887                      "  no avaliable input mixer element exists");
888         return -1;
889     }
890 
891     available = LATE(snd_mixer_selem_has_capture_volume)(_inputMixerElement);
892 
893     return 0;
894 }
895 
SetMicrophoneVolume(uint32_t volume)896 int32_t AudioMixerManagerLinuxALSA::SetMicrophoneVolume(
897     uint32_t volume)
898 {
899     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
900                  "AudioMixerManagerLinuxALSA::SetMicrophoneVolume(volume=%u)",
901                  volume);
902 
903     CriticalSectionScoped lock(&_critSect);
904 
905     if (_inputMixerElement == NULL)
906     {
907         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
908                      "  no avaliable input mixer element exists");
909         return -1;
910     }
911 
912     int
913         errVal =
914             LATE(snd_mixer_selem_set_capture_volume_all)(_inputMixerElement,
915                                                          volume);
916     if (errVal < 0)
917     {
918         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
919                      "     Error changing microphone volume: %s",
920                      LATE(snd_strerror)(errVal));
921         return -1;
922     }
923 
924     return (0);
925 }
926 
927 // TL: Have done testnig with these but they don't seem reliable and
928 // they were therefore not added
929 /*
930  // ----------------------------------------------------------------------------
931  //    SetMaxMicrophoneVolume
932  // ----------------------------------------------------------------------------
933 
934  int32_t AudioMixerManagerLinuxALSA::SetMaxMicrophoneVolume(
935      uint32_t maxVolume)
936  {
937 
938  if (_inputMixerElement == NULL)
939  {
940  WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
941   "  no avaliable output mixer element exists");
942  return -1;
943  }
944 
945  long int minVol(0);
946  long int maxVol(0);
947 
948  int errVal = snd_mixer_selem_get_capture_volume_range(_inputMixerElement,
949   &minVol, &maxVol);
950  if ((maxVol <= minVol) || (errVal != 0))
951  {
952  WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
953   "     Error getting capture volume range: %s", snd_strerror(errVal));
954  }
955 
956  maxVol = (long int)maxVolume;
957  printf("min %d max %d", minVol, maxVol);
958  errVal = snd_mixer_selem_set_capture_volume_range(_inputMixerElement, minVol, maxVol);
959  WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
960  "     Capture hardware volume range, min: %d, max: %d", minVol, maxVol);
961  if (errVal != 0)
962  {
963  WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
964   "     Error setting capture volume range: %s", snd_strerror(errVal));
965  return -1;
966  }
967 
968  return 0;
969  }
970 
971  // ----------------------------------------------------------------------------
972  //    SetMinMicrophoneVolume
973  // ----------------------------------------------------------------------------
974 
975  int32_t AudioMixerManagerLinuxALSA::SetMinMicrophoneVolume(
976  uint32_t minVolume)
977  {
978 
979  if (_inputMixerElement == NULL)
980  {
981  WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
982   "  no avaliable output mixer element exists");
983  return -1;
984  }
985 
986  long int minVol(0);
987  long int maxVol(0);
988 
989  int errVal = snd_mixer_selem_get_capture_volume_range(
990  _inputMixerElement, &minVol, &maxVol);
991  if (maxVol <= minVol)
992  {
993  //maxVol = 255;
994  WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
995   "     Error getting capture volume range: %s", snd_strerror(errVal));
996  }
997 
998  printf("min %d max %d", minVol, maxVol);
999  minVol = (long int)minVolume;
1000  errVal = snd_mixer_selem_set_capture_volume_range(
1001  _inputMixerElement, minVol, maxVol);
1002  WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1003   "     Capture hardware volume range, min: %d, max: %d", minVol, maxVol);
1004  if (errVal != 0)
1005  {
1006  WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1007   "     Error setting capture volume range: %s", snd_strerror(errVal));
1008  return -1;
1009  }
1010 
1011  return 0;
1012  }
1013  */
1014 
MicrophoneVolume(uint32_t & volume) const1015 int32_t AudioMixerManagerLinuxALSA::MicrophoneVolume(
1016     uint32_t& volume) const
1017 {
1018 
1019     if (_inputMixerElement == NULL)
1020     {
1021         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1022                      "  no avaliable input mixer element exists");
1023         return -1;
1024     }
1025 
1026     long int vol(0);
1027 
1028     int
1029         errVal =
1030             LATE(snd_mixer_selem_get_capture_volume)(
1031                 _inputMixerElement,
1032                 (snd_mixer_selem_channel_id_t) 0,
1033                 &vol);
1034     if (errVal < 0)
1035     {
1036         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1037                      "Error getting inputvolume: %s",
1038                      LATE(snd_strerror)(errVal));
1039         return -1;
1040     }
1041     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1042                  "     AudioMixerManagerLinuxALSA::MicrophoneVolume() => vol=%i",
1043                  vol);
1044 
1045     volume = static_cast<uint32_t> (vol);
1046 
1047     return 0;
1048 }
1049 
MaxMicrophoneVolume(uint32_t & maxVolume) const1050 int32_t AudioMixerManagerLinuxALSA::MaxMicrophoneVolume(
1051     uint32_t& maxVolume) const
1052 {
1053 
1054     if (_inputMixerElement == NULL)
1055     {
1056         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1057                      "  no avaliable input mixer element exists");
1058         return -1;
1059     }
1060 
1061     long int minVol(0);
1062     long int maxVol(0);
1063 
1064     // check if we have mic volume at all
1065     if (!LATE(snd_mixer_selem_has_capture_volume)(_inputMixerElement))
1066     {
1067         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1068                      "     No microphone volume available");
1069         return -1;
1070     }
1071 
1072     int errVal =
1073         LATE(snd_mixer_selem_get_capture_volume_range)(_inputMixerElement,
1074                                                        &minVol, &maxVol);
1075 
1076     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1077                  "     Microphone hardware volume range, min: %d, max: %d",
1078                  minVol, maxVol);
1079     if (maxVol <= minVol)
1080     {
1081         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1082                      "     Error getting microphone volume range: %s",
1083                      LATE(snd_strerror)(errVal));
1084     }
1085 
1086     maxVolume = static_cast<uint32_t> (maxVol);
1087 
1088     return 0;
1089 }
1090 
MinMicrophoneVolume(uint32_t & minVolume) const1091 int32_t AudioMixerManagerLinuxALSA::MinMicrophoneVolume(
1092     uint32_t& minVolume) const
1093 {
1094 
1095     if (_inputMixerElement == NULL)
1096     {
1097         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1098                      "  no avaliable input mixer element exists");
1099         return -1;
1100     }
1101 
1102     long int minVol(0);
1103     long int maxVol(0);
1104 
1105     int errVal =
1106         LATE(snd_mixer_selem_get_capture_volume_range)(_inputMixerElement,
1107                                                        &minVol, &maxVol);
1108 
1109     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1110                  "     Microphone hardware volume range, min: %d, max: %d",
1111                  minVol, maxVol);
1112     if (maxVol <= minVol)
1113     {
1114         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1115                      "     Error getting microphone volume range: %s",
1116                      LATE(snd_strerror)(errVal));
1117     }
1118 
1119     minVolume = static_cast<uint32_t> (minVol);
1120 
1121     return 0;
1122 }
1123 
MicrophoneVolumeStepSize(uint16_t & stepSize) const1124 int32_t AudioMixerManagerLinuxALSA::MicrophoneVolumeStepSize(
1125     uint16_t& stepSize) const
1126 {
1127 
1128     if (_inputMixerHandle == NULL)
1129     {
1130         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1131                      "  no avaliable input mixer exists");
1132         return -1;
1133     }
1134 
1135     // The step size is always 1 for ALSA
1136     stepSize = 1;
1137 
1138     return 0;
1139 }
1140 
1141 // ============================================================================
1142 //                                 Private Methods
1143 // ============================================================================
1144 
LoadMicMixerElement() const1145 int32_t AudioMixerManagerLinuxALSA::LoadMicMixerElement() const
1146 {
1147     int errVal = LATE(snd_mixer_load)(_inputMixerHandle);
1148     if (errVal < 0)
1149     {
1150         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1151                      "snd_mixer_load(_inputMixerHandle), error: %s",
1152                      LATE(snd_strerror)(errVal));
1153         _inputMixerHandle = NULL;
1154         return -1;
1155     }
1156 
1157     snd_mixer_elem_t *elem = NULL;
1158     snd_mixer_elem_t *micElem = NULL;
1159     unsigned mixerIdx = 0;
1160     const char *selemName = NULL;
1161 
1162     // Find and store handles to the right mixer elements
1163     for (elem = LATE(snd_mixer_first_elem)(_inputMixerHandle); elem; elem
1164         = LATE(snd_mixer_elem_next)(elem), mixerIdx++)
1165     {
1166         if (LATE(snd_mixer_selem_is_active)(elem))
1167         {
1168             selemName = LATE(snd_mixer_selem_get_name)(elem);
1169             if (strcmp(selemName, "Capture") == 0) // "Capture", "Mic"
1170             {
1171                 _inputMixerElement = elem;
1172                 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice,
1173                              _id, "     Capture element set");
1174             } else if (strcmp(selemName, "Mic") == 0)
1175             {
1176                 micElem = elem;
1177                 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice,
1178                              _id, "     Mic element found");
1179             }
1180         }
1181 
1182         if (_inputMixerElement)
1183         {
1184             // Use the first Capture element that is found
1185             // The second one may not work
1186             break;
1187         }
1188     }
1189 
1190     if (_inputMixerElement == NULL)
1191     {
1192         // We didn't find a Capture handle, use Mic.
1193         if (micElem != NULL)
1194         {
1195             _inputMixerElement = micElem;
1196             WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1197                          "     Using Mic as capture volume.");
1198         } else
1199         {
1200             _inputMixerElement = NULL;
1201             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1202                          "Could not find capture volume on the mixer.");
1203 
1204             return -1;
1205         }
1206     }
1207 
1208     return 0;
1209 }
1210 
LoadSpeakerMixerElement() const1211 int32_t AudioMixerManagerLinuxALSA::LoadSpeakerMixerElement() const
1212 {
1213     int errVal = LATE(snd_mixer_load)(_outputMixerHandle);
1214     if (errVal < 0)
1215     {
1216         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1217                      "     snd_mixer_load(_outputMixerHandle), error: %s",
1218                      LATE(snd_strerror)(errVal));
1219         _outputMixerHandle = NULL;
1220         return -1;
1221     }
1222 
1223     snd_mixer_elem_t *elem = NULL;
1224     snd_mixer_elem_t *masterElem = NULL;
1225     snd_mixer_elem_t *speakerElem = NULL;
1226     unsigned mixerIdx = 0;
1227     const char *selemName = NULL;
1228 
1229     // Find and store handles to the right mixer elements
1230     for (elem = LATE(snd_mixer_first_elem)(_outputMixerHandle); elem; elem
1231         = LATE(snd_mixer_elem_next)(elem), mixerIdx++)
1232     {
1233         if (LATE(snd_mixer_selem_is_active)(elem))
1234         {
1235             selemName = LATE(snd_mixer_selem_get_name)(elem);
1236             WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1237                          "snd_mixer_selem_get_name %d: %s =%x", mixerIdx,
1238                          selemName, elem);
1239 
1240             // "Master", "PCM", "Wave", "Master Mono", "PC Speaker", "PCM", "Wave"
1241             if (strcmp(selemName, "PCM") == 0)
1242             {
1243                 _outputMixerElement = elem;
1244                 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice,
1245                              _id, "     PCM element set");
1246             } else if (strcmp(selemName, "Master") == 0)
1247             {
1248                 masterElem = elem;
1249                 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice,
1250                              _id, "     Master element found");
1251             } else if (strcmp(selemName, "Speaker") == 0)
1252             {
1253                 speakerElem = elem;
1254                 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice,
1255                              _id, "     Speaker element found");
1256             }
1257         }
1258 
1259         if (_outputMixerElement)
1260         {
1261             // We have found the element we want
1262             break;
1263         }
1264     }
1265 
1266     // If we didn't find a PCM Handle, use Master or Speaker
1267     if (_outputMixerElement == NULL)
1268     {
1269         if (masterElem != NULL)
1270         {
1271             _outputMixerElement = masterElem;
1272             WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1273                          "     Using Master as output volume.");
1274         } else if (speakerElem != NULL)
1275         {
1276             _outputMixerElement = speakerElem;
1277             WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1278                          "     Using Speaker as output volume.");
1279         } else
1280         {
1281             _outputMixerElement = NULL;
1282             WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1283                          "Could not find output volume in the mixer.");
1284             return -1;
1285         }
1286     }
1287 
1288     return 0;
1289 }
1290 
GetControlName(char * controlName,char * deviceName) const1291 void AudioMixerManagerLinuxALSA::GetControlName(char* controlName,
1292                                                 char* deviceName) const
1293 {
1294     // Example
1295     // deviceName: "front:CARD=Intel,DEV=0"
1296     // controlName: "hw:CARD=Intel"
1297     char* pos1 = strchr(deviceName, ':');
1298     char* pos2 = strchr(deviceName, ',');
1299     if (!pos2)
1300     {
1301         // Can also be default:CARD=Intel
1302         pos2 = &deviceName[strlen(deviceName)];
1303     }
1304     if (pos1 && pos2)
1305     {
1306         strcpy(controlName, "hw");
1307         int nChar = (int) (pos2 - pos1);
1308         strncpy(&controlName[2], pos1, nChar);
1309         controlName[2 + nChar] = '\0';
1310     } else
1311     {
1312         strcpy(controlName, deviceName);
1313     }
1314 
1315 }
1316 
1317 }
1318