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 "webrtc/modules/audio_device/mac/audio_mixer_manager_mac.h"
12#include "webrtc/system_wrappers/interface/trace.h"
13
14#include <unistd.h>             // getpid()
15
16namespace webrtc {
17
18#define WEBRTC_CA_RETURN_ON_ERR(expr)                                     \
19    do {                                                                \
20        err = expr;                                                     \
21        if (err != noErr) {                                             \
22            logCAMsg(kTraceError, kTraceAudioDevice, _id,    \
23                "Error in " #expr, (const char *)&err);                 \
24            return -1;                                                  \
25        }                                                               \
26    } while(0)
27
28#define WEBRTC_CA_LOG_ERR(expr)                                           \
29    do {                                                                \
30        err = expr;                                                     \
31        if (err != noErr) {                                             \
32            logCAMsg(kTraceError, kTraceAudioDevice, _id,    \
33                "Error in " #expr, (const char *)&err);                 \
34        }                                                               \
35    } while(0)
36
37#define WEBRTC_CA_LOG_WARN(expr)                                           \
38    do {                                                                 \
39        err = expr;                                                      \
40        if (err != noErr) {                                              \
41            logCAMsg(kTraceWarning, kTraceAudioDevice, _id,  \
42                "Error in " #expr, (const char *)&err);                  \
43        }                                                                \
44    } while(0)
45
46AudioMixerManagerMac::AudioMixerManagerMac(const int32_t id) :
47    _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
48    _id(id),
49    _inputDeviceID(kAudioObjectUnknown),
50    _outputDeviceID(kAudioObjectUnknown),
51    _noInputChannels(0),
52    _noOutputChannels(0)
53{
54    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id,
55                 "%s constructed", __FUNCTION__);
56}
57
58AudioMixerManagerMac::~AudioMixerManagerMac()
59{
60    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id,
61                 "%s destructed", __FUNCTION__);
62
63    Close();
64
65    delete &_critSect;
66}
67
68// ============================================================================
69//	                                PUBLIC METHODS
70// ============================================================================
71
72int32_t AudioMixerManagerMac::Close()
73{
74    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
75                 __FUNCTION__);
76
77    CriticalSectionScoped lock(&_critSect);
78
79    CloseSpeaker();
80    CloseMicrophone();
81
82    return 0;
83
84}
85
86int32_t AudioMixerManagerMac::CloseSpeaker()
87{
88    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
89                 __FUNCTION__);
90
91    CriticalSectionScoped lock(&_critSect);
92
93    _outputDeviceID = kAudioObjectUnknown;
94    _noOutputChannels = 0;
95
96    return 0;
97}
98
99int32_t AudioMixerManagerMac::CloseMicrophone()
100{
101    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
102                 __FUNCTION__);
103
104    CriticalSectionScoped lock(&_critSect);
105
106    _inputDeviceID = kAudioObjectUnknown;
107    _noInputChannels = 0;
108
109    return 0;
110}
111
112int32_t AudioMixerManagerMac::OpenSpeaker(AudioDeviceID deviceID)
113{
114    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
115                 "AudioMixerManagerMac::OpenSpeaker(id=%d)", deviceID);
116
117    CriticalSectionScoped lock(&_critSect);
118
119    OSStatus err = noErr;
120    UInt32 size = 0;
121    pid_t hogPid = -1;
122
123    _outputDeviceID = deviceID;
124
125    // Check which process, if any, has hogged the device.
126    AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyHogMode,
127            kAudioDevicePropertyScopeOutput, 0 };
128
129    size = sizeof(hogPid);
130    WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID,
131            &propertyAddress, 0, NULL, &size, &hogPid));
132
133    if (hogPid == -1)
134    {
135        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
136                     " No process has hogged the input device");
137    }
138    // getpid() is apparently "always successful"
139    else if (hogPid == getpid())
140    {
141        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
142                     " Our process has hogged the input device");
143    } else
144    {
145        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
146                     " Another process (pid = %d) has hogged the input device",
147                     static_cast<int> (hogPid));
148
149        return -1;
150    }
151
152    // get number of channels from stream format
153    propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
154
155    // Get the stream format, to be able to read the number of channels.
156    AudioStreamBasicDescription streamFormat;
157    size = sizeof(AudioStreamBasicDescription);
158    memset(&streamFormat, 0, size);
159    WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID,
160            &propertyAddress, 0, NULL, &size, &streamFormat));
161
162    _noOutputChannels = streamFormat.mChannelsPerFrame;
163
164    return 0;
165}
166
167int32_t AudioMixerManagerMac::OpenMicrophone(AudioDeviceID deviceID)
168{
169    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
170                 "AudioMixerManagerMac::OpenMicrophone(id=%d)", deviceID);
171
172    CriticalSectionScoped lock(&_critSect);
173
174    OSStatus err = noErr;
175    UInt32 size = 0;
176    pid_t hogPid = -1;
177
178    _inputDeviceID = deviceID;
179
180    // Check which process, if any, has hogged the device.
181    AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyHogMode,
182            kAudioDevicePropertyScopeInput, 0 };
183    size = sizeof(hogPid);
184    WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID,
185            &propertyAddress, 0, NULL, &size, &hogPid));
186    if (hogPid == -1)
187    {
188        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
189                     " No process has hogged the input device");
190    }
191    // getpid() is apparently "always successful"
192    else if (hogPid == getpid())
193    {
194        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
195                     " Our process has hogged the input device");
196    } else
197    {
198        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
199                     " Another process (pid = %d) has hogged the input device",
200                     static_cast<int> (hogPid));
201
202        return -1;
203    }
204
205    // get number of channels from stream format
206    propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
207
208    // Get the stream format, to be able to read the number of channels.
209    AudioStreamBasicDescription streamFormat;
210    size = sizeof(AudioStreamBasicDescription);
211    memset(&streamFormat, 0, size);
212    WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID,
213            &propertyAddress, 0, NULL, &size, &streamFormat));
214
215    _noInputChannels = streamFormat.mChannelsPerFrame;
216
217    return 0;
218}
219
220bool AudioMixerManagerMac::SpeakerIsInitialized() const
221{
222    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s",
223                 __FUNCTION__);
224
225    return (_outputDeviceID != kAudioObjectUnknown);
226}
227
228bool AudioMixerManagerMac::MicrophoneIsInitialized() const
229{
230    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s",
231                 __FUNCTION__);
232
233    return (_inputDeviceID != kAudioObjectUnknown);
234}
235
236int32_t AudioMixerManagerMac::SetSpeakerVolume(uint32_t volume)
237{
238    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
239                 "AudioMixerManagerMac::SetSpeakerVolume(volume=%u)", volume);
240
241    CriticalSectionScoped lock(&_critSect);
242
243    if (_outputDeviceID == kAudioObjectUnknown)
244    {
245        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
246                     "  device ID has not been set");
247        return -1;
248    }
249
250    OSStatus err = noErr;
251    UInt32 size = 0;
252    bool success = false;
253
254    // volume range is 0.0 - 1.0, convert from 0 -255
255    const Float32 vol = (Float32)(volume / 255.0);
256
257    assert(vol <= 1.0 && vol >= 0.0);
258
259    // Does the capture device have a master volume control?
260    // If so, use it exclusively.
261    AudioObjectPropertyAddress propertyAddress = {
262            kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput,
263            0 };
264    Boolean isSettable = false;
265    err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
266                                        &isSettable);
267    if (err == noErr && isSettable)
268    {
269        size = sizeof(vol);
270        WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_outputDeviceID,
271                &propertyAddress, 0, NULL, size, &vol));
272
273        return 0;
274    }
275
276    // Otherwise try to set each channel.
277    for (UInt32 i = 1; i <= _noOutputChannels; i++)
278    {
279        propertyAddress.mElement = i;
280        isSettable = false;
281        err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
282                                            &isSettable);
283        if (err == noErr && isSettable)
284        {
285            size = sizeof(vol);
286            WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_outputDeviceID,
287                    &propertyAddress, 0, NULL, size, &vol));
288        }
289        success = true;
290    }
291
292    if (!success)
293    {
294        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
295                     " Unable to set a volume on any output channel");
296        return -1;
297    }
298
299    return 0;
300}
301
302int32_t AudioMixerManagerMac::SpeakerVolume(uint32_t& volume) const
303{
304
305    if (_outputDeviceID == kAudioObjectUnknown)
306    {
307        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
308                     "  device ID has not been set");
309        return -1;
310    }
311
312    OSStatus err = noErr;
313    UInt32 size = 0;
314    unsigned int channels = 0;
315    Float32 channelVol = 0;
316    Float32 vol = 0;
317
318    // Does the device have a master volume control?
319    // If so, use it exclusively.
320    AudioObjectPropertyAddress propertyAddress = {
321            kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput,
322            0 };
323    Boolean hasProperty = AudioObjectHasProperty(_outputDeviceID,
324                                                 &propertyAddress);
325    if (hasProperty)
326    {
327        size = sizeof(vol);
328        WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID,
329                &propertyAddress, 0, NULL, &size, &vol));
330
331        // vol 0.0 to 1.0 -> convert to 0 - 255
332        volume = static_cast<uint32_t> (vol * 255 + 0.5);
333    } else
334    {
335        // Otherwise get the average volume across channels.
336        vol = 0;
337        for (UInt32 i = 1; i <= _noOutputChannels; i++)
338        {
339            channelVol = 0;
340            propertyAddress.mElement = i;
341            hasProperty = AudioObjectHasProperty(_outputDeviceID,
342                                                 &propertyAddress);
343            if (hasProperty)
344            {
345                size = sizeof(channelVol);
346                WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID,
347                        &propertyAddress, 0, NULL, &size, &channelVol));
348
349                vol += channelVol;
350                channels++;
351            }
352        }
353
354        if (channels == 0)
355        {
356            WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
357                         " Unable to get a volume on any channel");
358            return -1;
359        }
360
361        assert(channels > 0);
362        // vol 0.0 to 1.0 -> convert to 0 - 255
363        volume = static_cast<uint32_t> (255 * vol / channels + 0.5);
364    }
365
366    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
367                 "     AudioMixerManagerMac::SpeakerVolume() => vol=%i", vol);
368
369    return 0;
370}
371
372int32_t
373AudioMixerManagerMac::MaxSpeakerVolume(uint32_t& maxVolume) const
374{
375
376    if (_outputDeviceID == kAudioObjectUnknown)
377    {
378        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
379                     "  device ID has not been set");
380        return -1;
381    }
382
383    // volume range is 0.0 to 1.0
384    // we convert that to 0 - 255
385    maxVolume = 255;
386
387    return 0;
388}
389
390int32_t
391AudioMixerManagerMac::MinSpeakerVolume(uint32_t& minVolume) const
392{
393
394    if (_outputDeviceID == kAudioObjectUnknown)
395    {
396        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
397                     "  device ID has not been set");
398        return -1;
399    }
400
401    // volume range is 0.0 to 1.0
402    // we convert that to 0 - 255
403    minVolume = 0;
404
405    return 0;
406}
407
408int32_t
409AudioMixerManagerMac::SpeakerVolumeStepSize(uint16_t& stepSize) const
410{
411
412    if (_outputDeviceID == kAudioObjectUnknown)
413    {
414        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
415                     "  device ID has not been set");
416        return -1;
417    }
418
419    // volume range is 0.0 to 1.0
420    // we convert that to 0 - 255
421    stepSize = 1;
422
423    return 0;
424}
425
426int32_t AudioMixerManagerMac::SpeakerVolumeIsAvailable(bool& available)
427{
428    if (_outputDeviceID == kAudioObjectUnknown)
429    {
430        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
431                     "  device ID has not been set");
432        return -1;
433    }
434
435    OSStatus err = noErr;
436
437    // Does the capture device have a master volume control?
438    // If so, use it exclusively.
439    AudioObjectPropertyAddress propertyAddress = {
440            kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput,
441            0 };
442    Boolean isSettable = false;
443    err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
444                                        &isSettable);
445    if (err == noErr && isSettable)
446    {
447        available = true;
448        return 0;
449    }
450
451    // Otherwise try to set each channel.
452    for (UInt32 i = 1; i <= _noOutputChannels; i++)
453    {
454        propertyAddress.mElement = i;
455        isSettable = false;
456        err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
457                                            &isSettable);
458        if (err != noErr || !isSettable)
459        {
460            available = false;
461            WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
462                         " Volume cannot be set for output channel %d, err=%d",
463                         i, err);
464            return -1;
465        }
466    }
467
468    available = true;
469    return 0;
470}
471
472int32_t AudioMixerManagerMac::SpeakerMuteIsAvailable(bool& available)
473{
474    if (_outputDeviceID == kAudioObjectUnknown)
475    {
476        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
477                     "  device ID has not been set");
478        return -1;
479    }
480
481    OSStatus err = noErr;
482
483    // Does the capture device have a master mute control?
484    // If so, use it exclusively.
485    AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyMute,
486            kAudioDevicePropertyScopeOutput, 0 };
487    Boolean isSettable = false;
488    err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
489                                        &isSettable);
490    if (err == noErr && isSettable)
491    {
492        available = true;
493        return 0;
494    }
495
496    // Otherwise try to set each channel.
497    for (UInt32 i = 1; i <= _noOutputChannels; i++)
498    {
499        propertyAddress.mElement = i;
500        isSettable = false;
501        err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
502                                            &isSettable);
503        if (err != noErr || !isSettable)
504        {
505            available = false;
506            WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
507                         " Mute cannot be set for output channel %d, err=%d",
508                         i, err);
509            return -1;
510        }
511    }
512
513    available = true;
514    return 0;
515}
516
517int32_t AudioMixerManagerMac::SetSpeakerMute(bool enable)
518{
519    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
520                 "AudioMixerManagerMac::SetSpeakerMute(enable=%u)", enable);
521
522    CriticalSectionScoped lock(&_critSect);
523
524    if (_outputDeviceID == kAudioObjectUnknown)
525    {
526        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
527                     "  device ID has not been set");
528        return -1;
529    }
530
531    OSStatus err = noErr;
532    UInt32 size = 0;
533    UInt32 mute = enable ? 1 : 0;
534    bool success = false;
535
536    // Does the render device have a master mute control?
537    // If so, use it exclusively.
538    AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyMute,
539            kAudioDevicePropertyScopeOutput, 0 };
540    Boolean isSettable = false;
541    err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
542                                        &isSettable);
543    if (err == noErr && isSettable)
544    {
545        size = sizeof(mute);
546        WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_outputDeviceID,
547                &propertyAddress, 0, NULL, size, &mute));
548
549        return 0;
550    }
551
552    // Otherwise try to set each channel.
553    for (UInt32 i = 1; i <= _noOutputChannels; i++)
554    {
555        propertyAddress.mElement = i;
556        isSettable = false;
557        err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
558                                            &isSettable);
559        if (err == noErr && isSettable)
560        {
561            size = sizeof(mute);
562            WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_outputDeviceID,
563                    &propertyAddress, 0, NULL, size, &mute));
564        }
565        success = true;
566    }
567
568    if (!success)
569    {
570        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
571                     " Unable to set mute on any input channel");
572        return -1;
573    }
574
575    return 0;
576}
577
578int32_t AudioMixerManagerMac::SpeakerMute(bool& enabled) const
579{
580
581    if (_outputDeviceID == kAudioObjectUnknown)
582    {
583        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
584                     "  device ID has not been set");
585        return -1;
586    }
587
588    OSStatus err = noErr;
589    UInt32 size = 0;
590    unsigned int channels = 0;
591    UInt32 channelMuted = 0;
592    UInt32 muted = 0;
593
594    // Does the device have a master volume control?
595    // If so, use it exclusively.
596    AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyMute,
597            kAudioDevicePropertyScopeOutput, 0 };
598    Boolean hasProperty = AudioObjectHasProperty(_outputDeviceID,
599                                                 &propertyAddress);
600    if (hasProperty)
601    {
602        size = sizeof(muted);
603        WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID,
604                &propertyAddress, 0, NULL, &size, &muted));
605
606        // 1 means muted
607        enabled = static_cast<bool> (muted);
608    } else
609    {
610        // Otherwise check if all channels are muted.
611        for (UInt32 i = 1; i <= _noOutputChannels; i++)
612        {
613            muted = 0;
614            propertyAddress.mElement = i;
615            hasProperty = AudioObjectHasProperty(_outputDeviceID,
616                                                 &propertyAddress);
617            if (hasProperty)
618            {
619                size = sizeof(channelMuted);
620                WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID,
621                        &propertyAddress, 0, NULL, &size, &channelMuted));
622
623                muted = (muted && channelMuted);
624                channels++;
625            }
626        }
627
628        if (channels == 0)
629        {
630            WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
631                         " Unable to get mute for any channel");
632            return -1;
633        }
634
635        assert(channels > 0);
636        // 1 means muted
637        enabled = static_cast<bool> (muted);
638    }
639
640    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
641                 "     AudioMixerManagerMac::SpeakerMute() => enabled=%d, enabled");
642
643    return 0;
644}
645
646int32_t AudioMixerManagerMac::StereoPlayoutIsAvailable(bool& available)
647{
648    if (_outputDeviceID == kAudioObjectUnknown)
649    {
650        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
651                     "  device ID has not been set");
652        return -1;
653    }
654
655    available = (_noOutputChannels == 2);
656    return 0;
657}
658
659int32_t AudioMixerManagerMac::StereoRecordingIsAvailable(bool& available)
660{
661    if (_inputDeviceID == kAudioObjectUnknown)
662    {
663        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
664                     "  device ID has not been set");
665        return -1;
666    }
667
668    available = (_noInputChannels == 2);
669    return 0;
670}
671
672int32_t AudioMixerManagerMac::MicrophoneMuteIsAvailable(bool& available)
673{
674    if (_inputDeviceID == kAudioObjectUnknown)
675    {
676        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
677                     "  device ID has not been set");
678        return -1;
679    }
680
681    OSStatus err = noErr;
682
683    // Does the capture device have a master mute control?
684    // If so, use it exclusively.
685    AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyMute,
686            kAudioDevicePropertyScopeInput, 0 };
687    Boolean isSettable = false;
688    err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
689                                        &isSettable);
690    if (err == noErr && isSettable)
691    {
692        available = true;
693        return 0;
694    }
695
696    // Otherwise try to set each channel.
697    for (UInt32 i = 1; i <= _noInputChannels; i++)
698    {
699        propertyAddress.mElement = i;
700        isSettable = false;
701        err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
702                                            &isSettable);
703        if (err != noErr || !isSettable)
704        {
705            available = false;
706            WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
707                         " Mute cannot be set for output channel %d, err=%d",
708                         i, err);
709            return -1;
710        }
711    }
712
713    available = true;
714    return 0;
715}
716
717int32_t AudioMixerManagerMac::SetMicrophoneMute(bool enable)
718{
719    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
720                 "AudioMixerManagerMac::SetMicrophoneMute(enable=%u)", enable);
721
722    CriticalSectionScoped lock(&_critSect);
723
724    if (_inputDeviceID == kAudioObjectUnknown)
725    {
726        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
727                     "  device ID has not been set");
728        return -1;
729    }
730
731    OSStatus err = noErr;
732    UInt32 size = 0;
733    UInt32 mute = enable ? 1 : 0;
734    bool success = false;
735
736    // Does the capture device have a master mute control?
737    // If so, use it exclusively.
738    AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyMute,
739            kAudioDevicePropertyScopeInput, 0 };
740    Boolean isSettable = false;
741    err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
742                                        &isSettable);
743    if (err == noErr && isSettable)
744    {
745        size = sizeof(mute);
746        WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_inputDeviceID,
747                &propertyAddress, 0, NULL, size, &mute));
748
749        return 0;
750    }
751
752    // Otherwise try to set each channel.
753    for (UInt32 i = 1; i <= _noInputChannels; i++)
754    {
755        propertyAddress.mElement = i;
756        isSettable = false;
757        err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
758                                            &isSettable);
759        if (err == noErr && isSettable)
760        {
761            size = sizeof(mute);
762            WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_inputDeviceID,
763                    &propertyAddress, 0, NULL, size, &mute));
764        }
765        success = true;
766    }
767
768    if (!success)
769    {
770        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
771                     " Unable to set mute on any input channel");
772        return -1;
773    }
774
775    return 0;
776}
777
778int32_t AudioMixerManagerMac::MicrophoneMute(bool& enabled) const
779{
780
781    if (_inputDeviceID == kAudioObjectUnknown)
782    {
783        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
784                     "  device ID has not been set");
785        return -1;
786    }
787
788    OSStatus err = noErr;
789    UInt32 size = 0;
790    unsigned int channels = 0;
791    UInt32 channelMuted = 0;
792    UInt32 muted = 0;
793
794    // Does the device have a master volume control?
795    // If so, use it exclusively.
796    AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyMute,
797            kAudioDevicePropertyScopeInput, 0 };
798    Boolean hasProperty = AudioObjectHasProperty(_inputDeviceID,
799                                                 &propertyAddress);
800    if (hasProperty)
801    {
802        size = sizeof(muted);
803        WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID,
804                &propertyAddress, 0, NULL, &size, &muted));
805
806        // 1 means muted
807        enabled = static_cast<bool> (muted);
808    } else
809    {
810        // Otherwise check if all channels are muted.
811        for (UInt32 i = 1; i <= _noInputChannels; i++)
812        {
813            muted = 0;
814            propertyAddress.mElement = i;
815            hasProperty = AudioObjectHasProperty(_inputDeviceID,
816                                                 &propertyAddress);
817            if (hasProperty)
818            {
819                size = sizeof(channelMuted);
820                WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID,
821                        &propertyAddress, 0, NULL, &size, &channelMuted));
822
823                muted = (muted && channelMuted);
824                channels++;
825            }
826        }
827
828        if (channels == 0)
829        {
830            WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
831                         " Unable to get mute for any channel");
832            return -1;
833        }
834
835        assert(channels > 0);
836        // 1 means muted
837        enabled = static_cast<bool> (muted);
838    }
839
840    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
841                 "     AudioMixerManagerMac::MicrophoneMute() => enabled=%d",
842                 enabled);
843
844    return 0;
845}
846
847int32_t AudioMixerManagerMac::MicrophoneBoostIsAvailable(bool& available)
848{
849    if (_inputDeviceID == kAudioObjectUnknown)
850    {
851        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
852                     "  device ID has not been set");
853        return -1;
854    }
855
856    available = false; // No AudioObjectPropertySelector value for Mic Boost
857
858    return 0;
859}
860
861int32_t AudioMixerManagerMac::SetMicrophoneBoost(bool enable)
862{
863    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
864                 "AudioMixerManagerMac::SetMicrophoneBoost(enable=%u)", enable);
865
866    CriticalSectionScoped lock(&_critSect);
867
868    if (_inputDeviceID == kAudioObjectUnknown)
869    {
870        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
871                     "  device ID has not been set");
872        return -1;
873    }
874
875    // Ensure that the selected microphone has a valid boost control.
876    bool available(false);
877    MicrophoneBoostIsAvailable(available);
878    if (!available)
879    {
880        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
881                     "  it is not possible to enable microphone boost");
882        return -1;
883    }
884
885    // It is assumed that the call above fails!
886    return 0;
887}
888
889int32_t AudioMixerManagerMac::MicrophoneBoost(bool& enabled) const
890{
891
892    if (_inputDeviceID == kAudioObjectUnknown)
893    {
894        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
895                     "  device ID has not been set");
896        return -1;
897    }
898
899    // Microphone boost cannot be enabled on this platform!
900    enabled = false;
901
902    return 0;
903}
904
905int32_t AudioMixerManagerMac::MicrophoneVolumeIsAvailable(bool& available)
906{
907    if (_inputDeviceID == kAudioObjectUnknown)
908    {
909        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
910                     "  device ID has not been set");
911        return -1;
912    }
913
914    OSStatus err = noErr;
915
916    // Does the capture device have a master volume control?
917    // If so, use it exclusively.
918    AudioObjectPropertyAddress
919        propertyAddress = { kAudioDevicePropertyVolumeScalar,
920                kAudioDevicePropertyScopeInput, 0 };
921    Boolean isSettable = false;
922    err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
923                                        &isSettable);
924    if (err == noErr && isSettable)
925    {
926        available = true;
927        return 0;
928    }
929
930    // Otherwise try to set each channel.
931    for (UInt32 i = 1; i <= _noInputChannels; i++)
932    {
933        propertyAddress.mElement = i;
934        isSettable = false;
935        err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
936                                            &isSettable);
937        if (err != noErr || !isSettable)
938        {
939            available = false;
940            WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
941                         " Volume cannot be set for input channel %d, err=%d",
942                         i, err);
943            return -1;
944        }
945    }
946
947    available = true;
948    return 0;
949}
950
951int32_t AudioMixerManagerMac::SetMicrophoneVolume(uint32_t volume)
952{
953    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
954                 "AudioMixerManagerMac::SetMicrophoneVolume(volume=%u)", volume);
955
956    CriticalSectionScoped lock(&_critSect);
957
958    if (_inputDeviceID == kAudioObjectUnknown)
959    {
960        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
961                     "  device ID has not been set");
962        return -1;
963    }
964
965    OSStatus err = noErr;
966    UInt32 size = 0;
967    bool success = false;
968
969    // volume range is 0.0 - 1.0, convert from 0 - 255
970    const Float32 vol = (Float32)(volume / 255.0);
971
972    assert(vol <= 1.0 && vol >= 0.0);
973
974    // Does the capture device have a master volume control?
975    // If so, use it exclusively.
976    AudioObjectPropertyAddress
977        propertyAddress = { kAudioDevicePropertyVolumeScalar,
978                kAudioDevicePropertyScopeInput, 0 };
979    Boolean isSettable = false;
980    err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
981                                        &isSettable);
982    if (err == noErr && isSettable)
983    {
984        size = sizeof(vol);
985        WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_inputDeviceID,
986                &propertyAddress, 0, NULL, size, &vol));
987
988        return 0;
989    }
990
991    // Otherwise try to set each channel.
992    for (UInt32 i = 1; i <= _noInputChannels; i++)
993    {
994        propertyAddress.mElement = i;
995        isSettable = false;
996        err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
997                                            &isSettable);
998        if (err == noErr && isSettable)
999        {
1000            size = sizeof(vol);
1001            WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_inputDeviceID,
1002                    &propertyAddress, 0, NULL, size, &vol));
1003        }
1004        success = true;
1005    }
1006
1007    if (!success)
1008    {
1009        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1010                     " Unable to set a level on any input channel");
1011        return -1;
1012    }
1013
1014    return 0;
1015}
1016
1017int32_t
1018AudioMixerManagerMac::MicrophoneVolume(uint32_t& volume) const
1019{
1020
1021    if (_inputDeviceID == kAudioObjectUnknown)
1022    {
1023        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1024                     "  device ID has not been set");
1025        return -1;
1026    }
1027
1028    OSStatus err = noErr;
1029    UInt32 size = 0;
1030    unsigned int channels = 0;
1031    Float32 channelVol = 0;
1032    Float32 volFloat32 = 0;
1033
1034    // Does the device have a master volume control?
1035    // If so, use it exclusively.
1036    AudioObjectPropertyAddress
1037        propertyAddress = { kAudioDevicePropertyVolumeScalar,
1038                kAudioDevicePropertyScopeInput, 0 };
1039    Boolean hasProperty = AudioObjectHasProperty(_inputDeviceID,
1040                                                 &propertyAddress);
1041    if (hasProperty)
1042    {
1043        size = sizeof(volFloat32);
1044        WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID,
1045                &propertyAddress, 0, NULL, &size, &volFloat32));
1046
1047        // vol 0.0 to 1.0 -> convert to 0 - 255
1048        volume = static_cast<uint32_t> (volFloat32 * 255 + 0.5);
1049    } else
1050    {
1051        // Otherwise get the average volume across channels.
1052        volFloat32 = 0;
1053        for (UInt32 i = 1; i <= _noInputChannels; i++)
1054        {
1055            channelVol = 0;
1056            propertyAddress.mElement = i;
1057            hasProperty = AudioObjectHasProperty(_inputDeviceID,
1058                                                 &propertyAddress);
1059            if (hasProperty)
1060            {
1061                size = sizeof(channelVol);
1062                WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID,
1063                        &propertyAddress, 0, NULL, &size, &channelVol));
1064
1065                volFloat32 += channelVol;
1066                channels++;
1067            }
1068        }
1069
1070        if (channels == 0)
1071        {
1072            WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1073                         " Unable to get a level on any channel");
1074            return -1;
1075        }
1076
1077        assert(channels > 0);
1078        // vol 0.0 to 1.0 -> convert to 0 - 255
1079        volume = static_cast<uint32_t>
1080            (255 * volFloat32 / channels + 0.5);
1081    }
1082
1083    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1084                 "     AudioMixerManagerMac::MicrophoneVolume() => vol=%u",
1085                 volume);
1086
1087    return 0;
1088}
1089
1090int32_t
1091AudioMixerManagerMac::MaxMicrophoneVolume(uint32_t& maxVolume) const
1092{
1093
1094    if (_inputDeviceID == kAudioObjectUnknown)
1095    {
1096        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1097                     "  device ID has not been set");
1098        return -1;
1099    }
1100
1101    // volume range is 0.0 to 1.0
1102    // we convert that to 0 - 255
1103    maxVolume = 255;
1104
1105    return 0;
1106}
1107
1108int32_t
1109AudioMixerManagerMac::MinMicrophoneVolume(uint32_t& minVolume) const
1110{
1111
1112    if (_inputDeviceID == kAudioObjectUnknown)
1113    {
1114        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1115                     "  device ID has not been set");
1116        return -1;
1117    }
1118
1119    // volume range is 0.0 to 1.0
1120    // we convert that to 0 - 10
1121    minVolume = 0;
1122
1123    return 0;
1124}
1125
1126int32_t
1127AudioMixerManagerMac::MicrophoneVolumeStepSize(uint16_t& stepSize) const
1128{
1129
1130    if (_inputDeviceID == kAudioObjectUnknown)
1131    {
1132        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1133                     "  device ID has not been set");
1134        return -1;
1135    }
1136
1137    // volume range is 0.0 to 1.0
1138    // we convert that to 0 - 10
1139    stepSize = 1;
1140
1141    return 0;
1142}
1143
1144// ============================================================================
1145//                                 Private Methods
1146// ============================================================================
1147
1148// CoreAudio errors are best interpreted as four character strings.
1149void AudioMixerManagerMac::logCAMsg(const TraceLevel level,
1150                                    const TraceModule module,
1151                                    const int32_t id, const char *msg,
1152                                    const char *err)
1153{
1154    assert(msg != NULL);
1155    assert(err != NULL);
1156
1157#ifdef WEBRTC_ARCH_BIG_ENDIAN
1158    WEBRTC_TRACE(level, module, id, "%s: %.4s", msg, err);
1159#else
1160    // We need to flip the characters in this case.
1161    WEBRTC_TRACE(level, module, id, "%s: %.1s%.1s%.1s%.1s", msg, err + 3, err
1162        + 2, err + 1, err);
1163#endif
1164}
1165
1166}  // namespace webrtc
1167// EOF
1168