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/interface/trace.h"
15
16extern 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
24namespace webrtc
25{
26
27AudioMixerManagerLinuxALSA::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
42AudioMixerManagerLinuxALSA::~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
56int32_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
70int32_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
112int32_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
163int32_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
256int32_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
359bool AudioMixerManagerLinuxALSA::SpeakerIsInitialized() const
360{
361    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__);
362
363    return (_outputMixerHandle != NULL);
364}
365
366bool AudioMixerManagerLinuxALSA::MicrophoneIsInitialized() const
367{
368    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s",
369                 __FUNCTION__);
370
371    return (_inputMixerHandle != NULL);
372}
373
374int32_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
404int32_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
438int32_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
472int32_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
592int32_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
609int32_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
624int32_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
639int32_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
679int32_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
722int32_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
736int32_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
776int32_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
819int32_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
835int32_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
865int32_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
881int32_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
896int32_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
1015int32_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
1050int32_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
1091int32_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
1124int32_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
1145int32_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
1211int32_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
1291void 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