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/win/audio_mixer_manager_win.h"
12#include "webrtc/system_wrappers/include/trace.h"
13
14#include <assert.h>      // assert()
15#include <strsafe.h>    // StringCchCopy(), StringCchCat(), StringCchPrintf()
16
17#ifdef _WIN32
18// removes warning: "reinterpret_cast: conversion from 'UINT' to 'HMIXEROBJ'
19//                of greater size"
20#pragma warning(disable:4312)
21#endif
22
23// Avoids the need of Windows 7 SDK
24#ifndef WAVE_MAPPED_kDefaultCommunicationDevice
25#define  WAVE_MAPPED_kDefaultCommunicationDevice   0x0010
26#endif
27
28namespace webrtc {
29
30// ============================================================================
31//                             CONSTRUCTION/DESTRUCTION
32// ============================================================================
33
34AudioMixerManager::AudioMixerManager(const int32_t id) :
35    _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
36    _id(id),
37    _inputMixerHandle(NULL),
38    _outputMixerHandle(NULL)
39{
40    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s constructed", __FUNCTION__);
41    ClearSpeakerState();
42    ClearMicrophoneState();
43}
44
45AudioMixerManager::~AudioMixerManager()
46{
47    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destructed", __FUNCTION__);
48
49    Close();
50
51    delete &_critSect;
52}
53
54// ============================================================================
55//                                 PUBLIC METHODS
56// ============================================================================
57
58// ----------------------------------------------------------------------------
59//  Close
60// ----------------------------------------------------------------------------
61
62int32_t AudioMixerManager::Close()
63{
64    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
65
66    CriticalSectionScoped lock(&_critSect);
67
68    if (_outputMixerHandle != NULL)
69    {
70        mixerClose(_outputMixerHandle);
71        _outputMixerHandle = NULL;
72    }
73    if (_inputMixerHandle != NULL)
74    {
75        mixerClose(_inputMixerHandle);
76        _inputMixerHandle = NULL;
77    }
78    return 0;
79
80}
81
82// ----------------------------------------------------------------------------
83//  CloseSpeaker
84// ----------------------------------------------------------------------------
85
86int32_t AudioMixerManager::CloseSpeaker()
87{
88    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
89
90    CriticalSectionScoped lock(&_critSect);
91
92    if (_outputMixerHandle == NULL)
93    {
94        return -1;
95    }
96
97    ClearSpeakerState(_outputMixerID);
98
99    mixerClose(_outputMixerHandle);
100    _outputMixerHandle = NULL;
101
102    return 0;
103}
104
105// ----------------------------------------------------------------------------
106//  CloseMicrophone
107// ----------------------------------------------------------------------------
108
109int32_t AudioMixerManager::CloseMicrophone()
110{
111    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
112
113    CriticalSectionScoped lock(&_critSect);
114
115    if (_inputMixerHandle == NULL)
116    {
117        return -1;
118    }
119
120    ClearMicrophoneState(_inputMixerID);
121
122    mixerClose(_inputMixerHandle);
123    _inputMixerHandle = NULL;
124
125    return 0;
126}
127
128// ----------------------------------------------------------------------------
129//  EnumerateAll
130// ----------------------------------------------------------------------------
131
132int32_t AudioMixerManager::EnumerateAll()
133{
134    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
135
136    UINT nDevices = mixerGetNumDevs();
137    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#mixer devices: %u", nDevices);
138
139    MIXERCAPS    caps;
140    MIXERLINE    destLine;
141    MIXERLINE    sourceLine;
142    MIXERCONTROL controlArray[MAX_NUMBER_OF_LINE_CONTROLS];
143
144    UINT mixId(0);
145    UINT destId(0);
146    UINT sourceId(0);
147
148    for (mixId = 0; mixId < nDevices; mixId++)
149    {
150        if (!GetCapabilities(mixId, caps, true))
151            continue;
152
153        for (destId = 0; destId < caps.cDestinations; destId++)
154        {
155            GetDestinationLineInfo(mixId, destId, destLine, true);
156            GetAllLineControls(mixId, destLine, controlArray, true);
157
158            for (sourceId = 0; sourceId < destLine.cConnections; sourceId++)
159            {
160                GetSourceLineInfo(mixId, destId, sourceId, sourceLine, true);
161                GetAllLineControls(mixId, sourceLine, controlArray, true);
162            }
163        }
164    }
165
166    return 0;
167}
168
169// ----------------------------------------------------------------------------
170//  EnumerateSpeakers
171// ----------------------------------------------------------------------------
172
173int32_t AudioMixerManager::EnumerateSpeakers()
174{
175    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
176
177    UINT nDevices = mixerGetNumDevs();
178    if (nDevices > MAX_NUMBER_MIXER_DEVICES)
179    {
180        assert(false);
181        return -1;
182    }
183    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#mixer devices: %u", nDevices);
184
185    MIXERCAPS    caps;
186    MIXERLINE    destLine;
187    MIXERCONTROL controlArray[MAX_NUMBER_OF_LINE_CONTROLS];
188
189    UINT mixId(0);
190    UINT destId(0);
191
192    ClearSpeakerState();
193
194    // scan all avaliable mixer devices
195    for (mixId = 0; mixId < nDevices; mixId++)
196    {
197        // get capabilities for the specified mixer ID
198        if (!GetCapabilities(mixId, caps))
199            continue;
200
201        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[mixerID=%d] %s: ", mixId, WideToUTF8(caps.szPname));
202        // scan all avaliable destinations for this mixer
203        for (destId = 0; destId < caps.cDestinations; destId++)
204        {
205            GetDestinationLineInfo(mixId, destId, destLine);
206            if ((destLine.cControls == 0)                         ||    // no controls or
207                (destLine.cConnections == 0)                      ||    // no source lines or
208                (destLine.fdwLine & MIXERLINE_LINEF_DISCONNECTED) ||    // disconnected or
209                !(destLine.fdwLine & MIXERLINE_LINEF_ACTIVE))           // inactive
210            {
211                // don't store this line ID since it will not be possible to control
212                continue;
213            }
214            if ((destLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS) ||
215                (destLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_HEADPHONES))
216            {
217                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found valid speaker/headphone (name: %s, ID: %u)", WideToUTF8(destLine.szName), destLine.dwLineID);
218                _speakerState[mixId].dwLineID = destLine.dwLineID;
219                _speakerState[mixId].speakerIsValid = true;
220                // retrieve all controls for the speaker component
221                GetAllLineControls(mixId, destLine, controlArray);
222                for (UINT c = 0; c < destLine.cControls; c++)
223                {
224                    if (controlArray[c].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
225                    {
226                        _speakerState[mixId].dwVolumeControlID = controlArray[c].dwControlID;
227                        _speakerState[mixId].volumeControlIsValid = true;
228                        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found volume control (name: %s, ID: %u)", WideToUTF8(controlArray[c].szName), controlArray[c].dwControlID);
229                    }
230                    else if (controlArray[c].dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
231                    {
232                        _speakerState[mixId].dwMuteControlID = controlArray[c].dwControlID;
233                        _speakerState[mixId].muteControlIsValid = true;
234                        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found mute control (name: %s, ID: %u)", WideToUTF8(controlArray[c].szName), controlArray[c].dwControlID);
235                    }
236                }
237                break;
238            }
239        }
240        if (!SpeakerIsValid(mixId))
241        {
242            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "unable to find a valid speaker destination line", mixId);
243        }
244    }
245
246    if (ValidSpeakers() == 0)
247    {
248        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "failed to locate any valid speaker line");
249        return -1;
250    }
251
252    return 0;
253}
254
255// ----------------------------------------------------------------------------
256//  EnumerateMicrophones
257// ----------------------------------------------------------------------------
258
259int32_t AudioMixerManager::EnumerateMicrophones()
260{
261    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
262
263    UINT nDevices = mixerGetNumDevs();
264    if (nDevices > MAX_NUMBER_MIXER_DEVICES)
265    {
266        assert(false);
267        return -1;
268    }
269    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#mixer devices: %u", nDevices);
270
271    MIXERCAPS    caps;
272    MIXERLINE    destLine;
273    MIXERLINE    sourceLine;
274    MIXERCONTROL controlArray[MAX_NUMBER_OF_LINE_CONTROLS];
275
276    UINT mixId(0);
277    UINT destId(0);
278
279    ClearMicrophoneState();
280
281    // scan all avaliable mixer devices
282    for (mixId = 0; mixId < nDevices; mixId++)
283    {
284        // get capabilities for the specified mixer ID
285        if (!GetCapabilities(mixId, caps))
286            continue;
287
288        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[mixerID=%d] %s: ", mixId, WideToUTF8(caps.szPname));
289        // scan all avaliable destinations for this mixer
290        for (destId = 0; destId < caps.cDestinations; destId++)
291        {
292            GetDestinationLineInfo(mixId, destId, destLine);
293
294            if ((destLine.cConnections == 0)                      ||    // no source lines or
295                (destLine.fdwLine & MIXERLINE_LINEF_DISCONNECTED) ||    // disconnected or
296               !(destLine.fdwLine & MIXERLINE_LINEF_ACTIVE))            // inactive
297            {
298                // Don't store this line ID since there are no sources connected to this destination.
299                // Compare with the speaker side where we also exclude lines with no controls.
300                continue;
301            }
302
303            if (destLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN)
304            {
305                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found valid Wave In destination (name: %s, ID: %u)", WideToUTF8(destLine.szName), destLine.dwLineID);
306                _microphoneState[mixId].dwLineID = destLine.dwLineID;
307                _microphoneState[mixId].microphoneIsValid = true;
308
309                // retrieve all controls for the identified wave-in destination
310                if (!GetAllLineControls(mixId, destLine, controlArray))
311                {
312                    // This destination has no controls. We must try to control
313                    // one of its sources instead.
314                    // This is a rare state but has been found for some
315                    // Logitech USB headsets.
316
317                    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
318                    "this destination has no controls => must control source");
319                    for (DWORD sourceId = 0; sourceId < destLine.cConnections; sourceId++)
320                    {
321                        GetSourceLineInfo(mixId, destId, sourceId, sourceLine, false);
322                        if (sourceLine.dwComponentType ==
323                            MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)
324                        {
325                            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
326                            "found microphone source ( name: %s, ID: %u)",
327                            WideToUTF8(sourceLine.szName), sourceId);
328                            GetAllLineControls(mixId, sourceLine, controlArray, false);
329                            // scan the controls for this source and search for volume,
330                            // mute and on/off (<=> boost) controls
331                            for (UINT sc = 0; sc < sourceLine.cControls; sc++)
332                            {
333                                if (controlArray[sc].dwControlType ==
334                                    MIXERCONTROL_CONTROLTYPE_VOLUME)
335                                {
336                                    // store this volume control
337                                    _microphoneState[mixId].dwVolumeControlID =
338                                    controlArray[sc].dwControlID;
339                                    _microphoneState[mixId].volumeControlIsValid = true;
340                                    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
341                                    "found volume control (name: %s, ID: %u)",
342                                    WideToUTF8(controlArray[sc].szName),
343                                    controlArray[sc].dwControlID);
344                                }
345                                else if (controlArray[sc].dwControlType ==
346                                         MIXERCONTROL_CONTROLTYPE_MUTE)
347                                {
348                                    // store this mute control
349                                    _microphoneState[mixId].dwMuteControlID =
350                                    controlArray[sc].dwControlID;
351                                    _microphoneState[mixId].muteControlIsValid = true;
352                                    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
353                                    "found mute control (name: %s, ID: %u)",
354                                    WideToUTF8(controlArray[sc].szName),
355                                    controlArray[sc].dwControlID);
356                                }
357                                else if (controlArray[sc].dwControlType ==
358                                         MIXERCONTROL_CONTROLTYPE_ONOFF ||
359                                         controlArray[sc].dwControlType ==
360                                         MIXERCONTROL_CONTROLTYPE_LOUDNESS)
361                                {
362                                    // store this on/off control (most likely a Boost control)
363                                    _microphoneState[mixId].dwOnOffControlID =
364                                    controlArray[sc].dwControlID;
365                                    _microphoneState[mixId].onOffControlIsValid = true;
366                                    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
367                                    "found on/off control (name: %s, ID: %u)",
368                                    WideToUTF8(controlArray[sc].szName),
369                                    controlArray[sc].dwControlID);
370                                 }
371                             }
372                         }
373                    }
374
375                    break;
376                }
377
378                // It seems like there are three different configurations we can find in this state:
379                //
380                // (1) The Wave-in destination contains one MUX control only
381                // (2) The Wave-in destination contains one or more controls where one is a volume control
382                // (3) On Vista and Win 7, it seems like case 2 above is extended.
383                //     It is common that a Wave-in destination has two master controls (volume and mute),
384                //     AND a microphone source as well with its own volume and mute controls with unique
385                //     identifiers. Initial tests have shown that it is sufficient to modify the master
386                //     controls only. The source controls will "follow" the master settings, hence the
387                //     source controls seem to be redundant.
388                //
389                // For case 1, we should locate the selected source and its controls. The MUX setting will
390                // give us the selected source. NOTE - the selecion might not be a microphone.
391                //
392                // For case 2, the volume control works as a master level control and we should use that one.
393                //
394                // For case 3, we use the master controls only and assume that the source control will "follow".
395                //
396                // Examples of case 1: - SigmaTel Audio (built-in)
397                //                     - add more..
398                //
399                // Examples of case 2: - Plantronics USB Headset
400                //                      - Eutectics IPP 200 USB phone
401                //                      - add more...
402                //
403                // Examples of case 3: - Realtek High Definition on Vista (TL)
404                //                     - add more...
405
406                if ((destLine.cControls == 1) &&
407                    (controlArray[0].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX))
408                {
409                    // Case 1: MUX control detected  => locate the selected source and its volume control
410                    //         Note that, the selecion might not be a microphone. A warning is given for
411                    //         this case only, i.e., it is OK to control a selected Line In source as long
412                    //         as it is connected to the wave-in destination.
413
414                    UINT selection(0);
415                    const DWORD nItemsInMux(controlArray[0].cMultipleItems);
416
417                    // decide which source line that is selected in the mux
418                    if (GetSelectedMuxSource(mixId, controlArray[0].dwControlID, nItemsInMux, selection))
419                    {
420                        // selection now contains the index of the selected source =>
421                        // read the line information for this source
422                        // if conditions listed below
423                        // condition 1: invalid source
424                        // condition 2: no controls
425                        // condition 3: disconnected
426                        // condition 4: inactive
427                        if (!GetSourceLineInfo(mixId, destId, selection, sourceLine)  ||
428                           (sourceLine.cControls == 0)                                ||
429                           (sourceLine.fdwLine & MIXERLINE_LINEF_DISCONNECTED)        ||
430                          !(sourceLine.fdwLine & MIXERLINE_LINEF_ACTIVE))
431                        {
432                            continue;
433                        }
434
435                        if (sourceLine.dwComponentType != MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)
436                        {
437                            // add more details about the selected source (not a microphone)
438                            TraceComponentType(sourceLine.dwComponentType);
439                            // send a warning just to inform about the fact that a non-microphone source will be controlled
440                            WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "the selected (to be controlled) source is not a microphone type");
441                        }
442
443                        // retrieve all controls for the selected source
444                        GetAllLineControls(mixId, sourceLine, controlArray);
445                        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "MUX selection is %u [0,%u]", selection, nItemsInMux-1);
446
447                        // scan the controls for this source and search for volume, mute and on/off (<=> boost) controls
448                        for (UINT sc = 0; sc < sourceLine.cControls; sc++)
449                        {
450                            if (controlArray[sc].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
451                            {
452                                // store this volume control
453                                _microphoneState[mixId].dwVolumeControlID = controlArray[sc].dwControlID;
454                                _microphoneState[mixId].volumeControlIsValid = true;
455                                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found volume control (name: %s, ID: %u)", WideToUTF8(controlArray[sc].szName), controlArray[sc].dwControlID);
456                            }
457                            else if (controlArray[sc].dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
458                            {
459                                // store this mute control
460                                _microphoneState[mixId].dwMuteControlID = controlArray[sc].dwControlID;
461                                _microphoneState[mixId].muteControlIsValid = true;
462                                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found mute control (name: %s, ID: %u)", WideToUTF8(controlArray[sc].szName), controlArray[sc].dwControlID);
463                            }
464                            else if (controlArray[sc].dwControlType == MIXERCONTROL_CONTROLTYPE_ONOFF ||
465                                     controlArray[sc].dwControlType == MIXERCONTROL_CONTROLTYPE_LOUDNESS)
466                            {
467                                // store this on/off control (most likely a Boost control)
468                                _microphoneState[mixId].dwOnOffControlID = controlArray[sc].dwControlID;
469                                _microphoneState[mixId].onOffControlIsValid = true;
470                                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found on/off control (name: %s, ID: %u)", WideToUTF8(controlArray[sc].szName), controlArray[sc].dwControlID);
471                            }
472                        }
473                    }
474                    else
475                    {
476                        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to detect which source to control");
477                    }
478
479                }
480                else if (destLine.cConnections == 1)
481                {
482                    // Case 2 or Case 3:
483
484                    GetSourceLineInfo(mixId, destId, 0, sourceLine);
485                    if ((sourceLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) &&
486                        (sourceLine.cControls > 0))
487                    {
488                        // Case 3: same as Case 2 below but we have also detected a Microphone source
489                        //         with its own controls. So far, I have not been able to find any device
490                        //         where it is required to modify these controls. Until I have found such
491                        //         a device, this case will be handled as a Case 2 (see below).
492
493                        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "microphone source controls will not be controlled");
494                    }
495                    else if ((sourceLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) &&
496                             (sourceLine.cControls == 0))
497                    {
498                        // default state on non Vista/Win 7 machines
499                        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "microphone source has no controls => use master controls instead");
500                    }
501                    else
502                    {
503                        // add more details about the selected source (not a microphone)
504                        TraceComponentType(sourceLine.dwComponentType);
505                        // send a warning just to inform about the fact that a non-microphone source will be controlled
506                        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "the connected (to be controlled) source is not a microphone type");
507                    }
508
509                    // Case 2 : one source only and no MUX control detected =>
510                    //          locate the master volume control (and mute + boost controls if possible)
511
512                    // scan the controls for this wave-in destination and search for volume, mute and on/off (<=> boost) controls
513                    for (UINT dc = 0; dc < destLine.cControls; dc++)
514                    {
515                        if (controlArray[dc].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
516                        {
517                            // store this volume control
518                            _microphoneState[mixId].dwVolumeControlID = controlArray[dc].dwControlID;
519                            _microphoneState[mixId].volumeControlIsValid = true;
520                            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found volume control (name: %s, ID: %u)", WideToUTF8(controlArray[dc].szName), controlArray[dc].dwControlID);
521                        }
522                        else if (controlArray[dc].dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
523                        {
524                            // store this mute control
525                            _microphoneState[mixId].dwMuteControlID = controlArray[dc].dwControlID;
526                            _microphoneState[mixId].muteControlIsValid = true;
527                            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found mute control (name: %s, ID: %u)", WideToUTF8(controlArray[dc].szName), controlArray[dc].dwControlID);
528                        }
529                        else if (controlArray[dc].dwControlType == MIXERCONTROL_CONTROLTYPE_ONOFF ||
530                                 controlArray[dc].dwControlType == MIXERCONTROL_CONTROLTYPE_LOUDNESS ||
531                                 controlArray[dc].dwControlType == MIXERCONTROL_CONTROLTYPE_BOOLEAN)
532                        {
533                            // store this on/off control
534                            _microphoneState[mixId].dwOnOffControlID = controlArray[dc].dwControlID;
535                            _microphoneState[mixId].onOffControlIsValid = true;
536                            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found on/off control (name: %s, ID: %u)", WideToUTF8(controlArray[dc].szName), controlArray[dc].dwControlID);
537                        }
538                    }
539                }
540                else
541                {
542                    // We are in a state where more than one source is connected to the wave-in destination.
543                    // I am bailing out here for now until I understand this case better.
544                    WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to locate valid microphone controls for this mixer");
545                }
546                break;
547            }
548        }  // for (destId = 0; destId < caps.cDestinations; destId++)
549
550        if (!MicrophoneIsValid(mixId))
551        {
552            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "unable to find a valid microphone destination line", mixId);
553        }
554    }  // for (mixId = 0; mixId < nDevices; mixId++)
555
556    if (ValidMicrophones() == 0)
557    {
558        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "failed to locate any valid microphone line");
559        return -1;
560    }
561
562    return 0;
563}
564
565// ----------------------------------------------------------------------------
566//  OpenSpeaker I(II)
567//
568//  Verifies that the mixer contains a valid speaker destination line.
569//  Avoids opening the mixer if valid control has not been found.
570// ----------------------------------------------------------------------------
571
572int32_t AudioMixerManager::OpenSpeaker(AudioDeviceModule::WindowsDeviceType device)
573{
574    if (device == AudioDeviceModule::kDefaultDevice)
575    {
576        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::OpenSpeaker(kDefaultDevice)");
577    }
578    else if (device == AudioDeviceModule::kDefaultCommunicationDevice)
579    {
580        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::OpenSpeaker(kDefaultCommunicationDevice)");
581    }
582
583    CriticalSectionScoped lock(&_critSect);
584
585    // Close any existing output mixer handle
586    //
587    if (_outputMixerHandle != NULL)
588    {
589        mixerClose(_outputMixerHandle);
590        _outputMixerHandle = NULL;
591    }
592
593    MMRESULT     res = MMSYSERR_NOERROR;
594    WAVEFORMATEX waveFormat;
595    HWAVEOUT     hWaveOut(NULL);
596
597    waveFormat.wFormatTag      = WAVE_FORMAT_PCM ;
598    waveFormat.nChannels       = 2;
599    waveFormat.nSamplesPerSec  = 48000;
600    waveFormat.wBitsPerSample  = 16;
601    waveFormat.nBlockAlign     = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
602    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
603    waveFormat.cbSize          = 0;
604
605    // We need a waveform-audio output handle for the currently selected output device.
606    // This handle will then give us the corresponding mixer identifier. Once the mixer
607    // ID is known, it is possible to open the output mixer.
608    //
609    if (device == AudioDeviceModule::kDefaultCommunicationDevice)
610    {
611        // check if it is possible to open the default communication device (supported on Windows 7)
612        res = waveOutOpen(NULL, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL |
613            WAVE_MAPPED_kDefaultCommunicationDevice | WAVE_FORMAT_QUERY);
614        if (MMSYSERR_NOERROR == res)
615        {
616            // if so, open the default communication device for real
617            res = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_MAPPED_kDefaultCommunicationDevice);
618            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default communication device");
619        }
620        else
621        {
622            // use default device since default communication device was not avaliable
623            res = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL);
624            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
625                "unable to open default communication device => using default instead");
626        }
627    }
628    else if (device == AudioDeviceModule::kDefaultDevice)
629    {
630        // open default device since it has been requested
631        res = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL);
632        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default output device");
633    }
634
635    if (MMSYSERR_NOERROR != res)
636    {
637        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutOpen() failed (err=%d)", res);
638        TraceWaveOutError(res);
639    }
640
641    UINT   mixerId(0);
642    HMIXER hMixer(NULL);
643
644    // Retrieve the device identifier for a mixer device associated with the
645    // aquired waveform-audio output handle.
646    //
647    res = mixerGetID((HMIXEROBJ)hWaveOut, &mixerId, MIXER_OBJECTF_HWAVEOUT);
648    if (MMSYSERR_NOERROR != res)
649    {
650        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetID(MIXER_OBJECTF_HWAVEOUT) failed (err=%d)", res);
651        // identification failed => use default mixer identifier (=0)
652        mixerId = 0;
653    }
654    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "specified output device <=> mixer ID %u", mixerId);
655
656    // The waveform-audio output handle is no longer needed.
657    //
658    waveOutClose(hWaveOut);
659
660    // Verify that the mixer contains a valid speaker destination line.
661    // Avoid opening the mixer if valid control has not been found.
662    //
663    if (!SpeakerIsValid(mixerId))
664    {
665        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to control the speaker volume for this mixer device");
666        return -1;
667    }
668
669    // Open the specified mixer device and ensure that the device will not
670    // be removed until the application closes the handle.
671    //
672    res = mixerOpen(&hMixer, mixerId, 0, 0, MIXER_OBJECTF_MIXER);
673    if (MMSYSERR_NOERROR != res)
674    {
675        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerOpen() failed (err=%d)", res);
676    }
677
678    // Store the output mixer handle and active mixer identifier
679    //
680    _outputMixerHandle = hMixer;
681    _outputMixerID = mixerId;
682
683    if (_outputMixerHandle != NULL)
684    {
685        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "the output mixer device is now open (0x%x)", _outputMixerHandle);
686    }
687
688    return 0;
689}
690
691// ----------------------------------------------------------------------------
692//  OpenSpeaker II(II)
693//
694//  Verifies that the mixer contains a valid speaker destination line.
695//  Avoids opening the mixer if valid control has not been found.
696// ----------------------------------------------------------------------------
697
698int32_t AudioMixerManager::OpenSpeaker(uint16_t index)
699{
700    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::OpenSpeaker(index=%d)", index);
701
702    CriticalSectionScoped lock(&_critSect);
703
704    // Close any existing output mixer handle
705    //
706    if (_outputMixerHandle != NULL)
707    {
708        mixerClose(_outputMixerHandle);
709        _outputMixerHandle = NULL;
710    }
711
712    MMRESULT     res;
713    WAVEFORMATEX waveFormat;
714    HWAVEOUT     hWaveOut(NULL);
715
716    const UINT   deviceID(index);  // use index parameter as device identifier
717
718    waveFormat.wFormatTag      = WAVE_FORMAT_PCM ;
719    waveFormat.nChannels       = 2;
720    waveFormat.nSamplesPerSec  = 48000;
721    waveFormat.wBitsPerSample  = 16;
722    waveFormat.nBlockAlign     = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
723    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
724    waveFormat.cbSize          = 0;
725
726    // We need a waveform-audio output handle for the currently selected output device.
727    // This handle will then give us the corresponding mixer identifier. Once the mixer
728    // ID is known, it is possible to open the output mixer.
729    //
730    res = waveOutOpen(&hWaveOut, deviceID, &waveFormat, 0, 0, CALLBACK_NULL);
731    if (MMSYSERR_NOERROR != res)
732    {
733        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutOpen(deviceID=%u) failed (err=%d)", index, res);
734        TraceWaveOutError(res);
735    }
736
737    UINT   mixerId(0);
738    HMIXER hMixer(NULL);
739
740    // Retrieve the device identifier for a mixer device associated with the
741    // aquired waveform-audio output handle.
742    //
743    res = mixerGetID((HMIXEROBJ)hWaveOut, &mixerId, MIXER_OBJECTF_HWAVEOUT);
744    if (MMSYSERR_NOERROR != res)
745    {
746        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetID(MIXER_OBJECTF_HWAVEOUT) failed (err=%d)", res);
747        // identification failed => use default mixer identifier (=0)
748        mixerId = 0;
749    }
750    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "specified output device <=> mixer ID %u", mixerId);
751
752    // The waveform-audio output handle is no longer needed.
753    //
754    waveOutClose(hWaveOut);
755
756    // Verify that the mixer contains a valid speaker destination line.
757    // Avoid opening the mixer if valid control has not been found.
758    //
759    if (!SpeakerIsValid(mixerId))
760    {
761        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to control the speaker volume for this mixer device");
762        return -1;
763    }
764
765    // Open the specified mixer device and ensure that the device will not
766    // be removed until the application closes the handle.
767    //
768    res = mixerOpen(&hMixer, mixerId, 0, 0, MIXER_OBJECTF_MIXER);
769    if (MMSYSERR_NOERROR != res)
770    {
771        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerOpen() failed (err=%d)", res);
772    }
773
774    // Store the output mixer handle and active mixer identifier
775    //
776    _outputMixerHandle = hMixer;
777    _outputMixerID = mixerId;
778
779    if (_outputMixerHandle != NULL)
780    {
781        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "the output mixer device is now open (0x%x)", _outputMixerHandle);
782    }
783
784    return 0;
785}
786
787// ----------------------------------------------------------------------------
788//  OpenMicrophone I(II)
789//
790//  Verifies that the mixer contains a valid wave-in destination line.
791//  Avoids opening the mixer if valid control has not been found.
792// ----------------------------------------------------------------------------
793
794int32_t AudioMixerManager::OpenMicrophone(AudioDeviceModule::WindowsDeviceType device)
795{
796    if (device == AudioDeviceModule::kDefaultDevice)
797    {
798        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::OpenMicrophone(kDefaultDevice)");
799    }
800    else if (device == AudioDeviceModule::kDefaultCommunicationDevice)
801    {
802        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::OpenMicrophone(kDefaultCommunicationDevice)");
803    }
804
805    CriticalSectionScoped lock(&_critSect);
806
807    // Close any existing output mixer handle
808    //
809    if (_inputMixerHandle != NULL)
810    {
811        mixerClose(_inputMixerHandle);
812        _inputMixerHandle = NULL;
813    }
814
815    MMRESULT     res = MMSYSERR_NOERROR;
816    WAVEFORMATEX waveFormat;
817    HWAVEIN         hWaveIn(NULL);
818
819    waveFormat.wFormatTag      = WAVE_FORMAT_PCM ;
820    waveFormat.nChannels       = 1;
821    waveFormat.nSamplesPerSec  = 48000;
822    waveFormat.wBitsPerSample  = 16;
823    waveFormat.nBlockAlign     = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
824    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
825    waveFormat.cbSize          = 0 ;
826
827    // We need a waveform-audio input handle for the currently selected input device.
828    // This handle will then give us the corresponding mixer identifier. Once the mixer
829    // ID is known, it is possible to open the input mixer.
830    //
831    if (device == AudioDeviceModule::kDefaultCommunicationDevice)
832    {
833        // check if it is possible to open the default communication device (supported on Windows 7)
834        res = waveInOpen(NULL, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL |
835            WAVE_MAPPED_kDefaultCommunicationDevice | WAVE_FORMAT_QUERY);
836        if (MMSYSERR_NOERROR == res)
837        {
838            // if so, open the default communication device for real
839            res = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_MAPPED_kDefaultCommunicationDevice);
840            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default communication device");
841        }
842        else
843        {
844            // use default device since default communication device was not avaliable
845            res = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL);
846            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
847                "unable to open default communication device => using default instead");
848        }
849    }
850    else if (device == AudioDeviceModule::kDefaultDevice)
851    {
852        // open default device since it has been requested
853        res = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL);
854        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default input device");
855    }
856
857    if (MMSYSERR_NOERROR != res)
858    {
859        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInOpen() failed (err=%d)", res);
860        TraceWaveInError(res);
861    }
862
863    UINT   mixerId(0);
864    HMIXER hMixer(NULL);
865
866    // Retrieve the device identifier for a mixer device associated with the
867    // aquired waveform-audio input handle.
868    //
869    res = mixerGetID((HMIXEROBJ)hWaveIn, &mixerId, MIXER_OBJECTF_HWAVEIN);
870    if (MMSYSERR_NOERROR != res)
871    {
872        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetID(MIXER_OBJECTF_HWAVEIN) failed (err=%d)", res);
873        // identification failed => use default mixer identifier (=0)
874        mixerId = 0;
875    }
876    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "specified input device <=> mixer ID %u", mixerId);
877
878    // The waveform-audio input handle is no longer needed.
879    //
880    waveInClose(hWaveIn);
881
882    // Verify that the mixer contains a valid wave-in destination line and a volume control.
883    // Avoid opening the mixer if valid control has not been found.
884    //
885    if (!MicrophoneIsValid(mixerId))
886    {
887        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to control the microphone volume for this mixer device");
888        return -1;
889    }
890
891    // Open the specified mixer device and ensure that the device will not
892    // be removed until the application closes the handle.
893    //
894    res = mixerOpen(&hMixer, mixerId, 0, 0, MIXER_OBJECTF_MIXER);
895    if (MMSYSERR_NOERROR != res)
896    {
897        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerOpen() failed (err=%d)", res);
898    }
899
900    // Store the input mixer handle and active mixer identifier
901    //
902    _inputMixerHandle = hMixer;
903    _inputMixerID = mixerId;
904
905    if (_inputMixerHandle != NULL)
906    {
907        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "the input mixer device is now open (0x%x)", _inputMixerHandle);
908    }
909
910    return 0;
911}
912
913// ----------------------------------------------------------------------------
914//  OpenMicrophone II(II)
915//
916//  Verifies that the mixer contains a valid wave-in destination line.
917//  Avoids opening the mixer if valid control has not been found.
918// ----------------------------------------------------------------------------
919
920int32_t AudioMixerManager::OpenMicrophone(uint16_t index)
921{
922    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::OpenMicrophone(index=%d)", index);
923
924    CriticalSectionScoped lock(&_critSect);
925
926    // Close any existing input mixer handle
927    //
928    if (_inputMixerHandle != NULL)
929    {
930        mixerClose(_inputMixerHandle);
931        _inputMixerHandle = NULL;
932    }
933
934    MMRESULT     res;
935    WAVEFORMATEX waveFormat;
936    HWAVEIN         hWaveIn(NULL);
937
938    const UINT   deviceID(index);  // use index parameter as device identifier
939
940    waveFormat.wFormatTag      = WAVE_FORMAT_PCM ;
941    waveFormat.nChannels       = 1;
942    waveFormat.nSamplesPerSec  = 48000;
943    waveFormat.wBitsPerSample  = 16;
944    waveFormat.nBlockAlign     = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
945    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
946    waveFormat.cbSize          = 0;
947
948    // We need a waveform-audio input handle for the currently selected input device.
949    // This handle will then give us the corresponding mixer identifier. Once the mixer
950    // ID is known, it is possible to open the input mixer.
951    //
952    res = waveInOpen(&hWaveIn, deviceID, &waveFormat, 0, 0, CALLBACK_NULL);
953    if (MMSYSERR_NOERROR != res)
954    {
955        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInOpen(deviceID=%u) failed (err=%d)", index, res);
956        TraceWaveInError(res);
957    }
958
959    UINT   mixerId(0);
960    HMIXER hMixer(NULL);
961
962    // Retrieve the device identifier for a mixer device associated with the
963    // aquired waveform-audio input handle.
964    //
965    res = mixerGetID((HMIXEROBJ)hWaveIn, &mixerId, MIXER_OBJECTF_HWAVEIN);
966    if (MMSYSERR_NOERROR != res)
967    {
968        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetID(MIXER_OBJECTF_HWAVEIN) failed (err=%d)", res);
969        // identification failed => use default mixer identifier (=0)
970        mixerId = 0;
971    }
972    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "specified input device <=> mixer ID %u", mixerId);
973
974    // The waveform-audio input handle is no longer needed.
975    //
976    waveInClose(hWaveIn);
977
978    // Verify that the mixer contains a valid wave-in destination line.
979    // Avoid opening the mixer if valid control has not been found.
980    //
981    if (!MicrophoneIsValid(mixerId))
982    {
983        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to control the microphone volume for this mixer device");
984        return -1;
985    }
986
987    // Open the specified mixer device and ensure that the device will not
988    // be removed until the application closes the handle.
989    //
990    res = mixerOpen(&hMixer, mixerId, 0, 0, MIXER_OBJECTF_MIXER);
991    if (MMSYSERR_NOERROR != res)
992    {
993        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerOpen() failed (err=%d)", res);
994    }
995
996    // Store the input mixer handle and active mixer identifier
997    //
998    _inputMixerHandle = hMixer;
999    _inputMixerID = mixerId;
1000
1001    if (_inputMixerHandle != NULL)
1002    {
1003        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "the input mixer device is now open (0x%x)", _inputMixerHandle);
1004    }
1005
1006    return 0;
1007}
1008
1009// ----------------------------------------------------------------------------
1010// SpeakerIsInitialized
1011// ----------------------------------------------------------------------------
1012
1013bool AudioMixerManager::SpeakerIsInitialized() const
1014{
1015    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__);
1016
1017    return (_outputMixerHandle != NULL);
1018}
1019
1020// ----------------------------------------------------------------------------
1021// MicrophoneIsInitialized
1022// ----------------------------------------------------------------------------
1023
1024bool AudioMixerManager::MicrophoneIsInitialized() const
1025{
1026    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__);
1027
1028    return (_inputMixerHandle != NULL);
1029}
1030
1031// ----------------------------------------------------------------------------
1032// SetSpeakerVolume
1033// ----------------------------------------------------------------------------
1034
1035int32_t AudioMixerManager::SetSpeakerVolume(uint32_t volume)
1036{
1037    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::SetSpeakerVolume(volume=%u)", volume);
1038
1039    CriticalSectionScoped lock(&_critSect);
1040
1041    if (_outputMixerHandle == NULL)
1042    {
1043        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists");
1044        return -1;
1045    }
1046
1047    const UINT mixerID(_outputMixerID);
1048    const DWORD dwControlID(_speakerState[_outputMixerID].dwVolumeControlID);
1049    DWORD dwValue(volume);
1050
1051    // Set one unsigned control value for a specified volume-control identifier
1052    //
1053    if (!SetUnsignedControlValue(mixerID, dwControlID, dwValue))
1054    {
1055        return -1;
1056    }
1057
1058    return (0);
1059}
1060
1061// ----------------------------------------------------------------------------
1062//  SpeakerVolume
1063//
1064//  Note that (MIXERCONTROL_CONTROLTYPE_VOLUME & MIXERCONTROL_CT_UNITS_MASK)
1065//  always equals MIXERCONTROL_CT_UNITS_UNSIGNED;
1066// ----------------------------------------------------------------------------
1067
1068int32_t AudioMixerManager::SpeakerVolume(uint32_t& volume) const
1069{
1070
1071    if (_outputMixerHandle == NULL)
1072    {
1073        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists");
1074        return -1;
1075    }
1076
1077    const UINT mixerID(_outputMixerID);
1078    const DWORD dwControlID(_speakerState[_outputMixerID].dwVolumeControlID);
1079    DWORD dwValue(0);
1080
1081    // Retrieve one unsigned control value for a specified volume-control identifier
1082    //
1083    if (!GetUnsignedControlValue(mixerID, dwControlID, dwValue))
1084    {
1085        return -1;
1086    }
1087
1088    volume = dwValue;
1089
1090    return 0;
1091}
1092
1093// ----------------------------------------------------------------------------
1094//  MaxSpeakerVolume
1095//
1096//  Note that (MIXERCONTROL_CONTROLTYPE_VOLUME & MIXERCONTROL_CT_UNITS_MASK)
1097//  always equals MIXERCONTROL_CT_UNITS_UNSIGNED
1098// ----------------------------------------------------------------------------
1099
1100int32_t AudioMixerManager::MaxSpeakerVolume(uint32_t& maxVolume) const
1101{
1102
1103    if (_outputMixerHandle == NULL)
1104    {
1105        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists");
1106        return -1;
1107    }
1108
1109    const UINT mixerID(_outputMixerID);
1110    const DWORD dwControlID(_speakerState[_outputMixerID].dwVolumeControlID);
1111    MIXERCONTROL mixerControl;
1112
1113    // Retrieve one control line for a specified volume-control identifier
1114    //
1115    if (!GetLineControl(mixerID, dwControlID, mixerControl))
1116    {
1117        return -1;
1118    }
1119
1120    maxVolume = mixerControl.Bounds.dwMaximum;
1121
1122    return 0;
1123}
1124
1125// ----------------------------------------------------------------------------
1126// MinSpeakerVolume
1127// ----------------------------------------------------------------------------
1128
1129int32_t AudioMixerManager::MinSpeakerVolume(uint32_t& minVolume) const
1130{
1131
1132    if (_outputMixerHandle == NULL)
1133    {
1134        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists");
1135        return -1;
1136    }
1137
1138    const UINT mixerID(_outputMixerID);
1139    const DWORD dwControlID(_speakerState[_outputMixerID].dwVolumeControlID);
1140    MIXERCONTROL mixerControl;
1141
1142    // Retrieve one control line for a specified volume-control identifier
1143    //
1144    if (!GetLineControl(mixerID, dwControlID, mixerControl))
1145    {
1146        return -1;
1147    }
1148
1149    minVolume = mixerControl.Bounds.dwMinimum;
1150
1151    return 0;
1152}
1153
1154// ----------------------------------------------------------------------------
1155// SpeakerVolumeStepSize
1156// ----------------------------------------------------------------------------
1157
1158int32_t AudioMixerManager::SpeakerVolumeStepSize(uint16_t& stepSize) const
1159{
1160
1161    if (_outputMixerHandle == NULL)
1162    {
1163        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists");
1164        return -1;
1165    }
1166
1167    const UINT mixerID(_outputMixerID);
1168    MIXERCONTROL mixerControl;
1169
1170    // Retrieve one control line for a specified volume-control identifier
1171    //
1172    if (!GetLineControl(mixerID, _speakerState[mixerID].dwVolumeControlID, mixerControl))
1173    {
1174        return -1;
1175    }
1176
1177    stepSize = static_cast<uint16_t> (mixerControl.Metrics.cSteps);
1178
1179    return 0;
1180}
1181
1182// ----------------------------------------------------------------------------
1183// SpeakerVolumeIsAvailable
1184// ----------------------------------------------------------------------------
1185
1186int32_t AudioMixerManager::SpeakerVolumeIsAvailable(bool& available)
1187{
1188    if (_outputMixerHandle == NULL)
1189    {
1190        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists");
1191        return -1;
1192    }
1193
1194    available = _speakerState[_outputMixerID].volumeControlIsValid;
1195
1196    return 0;
1197}
1198
1199// ----------------------------------------------------------------------------
1200// SpeakerMuteIsAvailable
1201// ----------------------------------------------------------------------------
1202
1203int32_t AudioMixerManager::SpeakerMuteIsAvailable(bool& available)
1204{
1205    if (_outputMixerHandle == NULL)
1206    {
1207        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists");
1208        return -1;
1209    }
1210
1211    available = _speakerState[_outputMixerID].muteControlIsValid;
1212
1213    return 0;
1214}
1215
1216// ----------------------------------------------------------------------------
1217//  SetSpeakerMute
1218//
1219//  This mute function works a master mute for the output speaker.
1220// ----------------------------------------------------------------------------
1221
1222int32_t AudioMixerManager::SetSpeakerMute(bool enable)
1223{
1224    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::SetSpeakerMute(enable=%u)", enable);
1225
1226    CriticalSectionScoped lock(&_critSect);
1227
1228    if (_outputMixerHandle == NULL)
1229    {
1230        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists");
1231        return -1;
1232    }
1233
1234    // Ensure that the selected speaker destination has a valid mute control.
1235    // If so, its identifier was stored during the enumeration phase which must
1236    // have taken place since the output mixer handle exists.
1237    //
1238    if (!_speakerState[_outputMixerID].muteControlIsValid)
1239    {
1240        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to mute this speaker line");
1241        return -1;
1242    }
1243
1244    const DWORD dwControlID(_speakerState[_outputMixerID].dwMuteControlID);
1245
1246    // Set one boolean control value for the specified mute-control
1247    //
1248    if (!SetBooleanControlValue(_outputMixerID, dwControlID, enable))
1249    {
1250        return -1;
1251    }
1252
1253    return (0);
1254}
1255
1256// ----------------------------------------------------------------------------
1257//  SpeakerMute
1258// ----------------------------------------------------------------------------
1259
1260int32_t AudioMixerManager::SpeakerMute(bool& enabled) const
1261{
1262
1263    if (_outputMixerHandle == NULL)
1264    {
1265        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists");
1266        return -1;
1267    }
1268
1269    // Ensure that the selected speaker destination has a valid mute control.
1270    // If so, its identifier was stored during the enumeration phase which must
1271    // have taken place since the output mixer handle exists.
1272    //
1273    if (!_speakerState[_outputMixerID].muteControlIsValid)
1274    {
1275        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to mute this speaker line");
1276        return -1;
1277    }
1278
1279    const DWORD dwControlID(_speakerState[_outputMixerID].dwMuteControlID);
1280    bool value(false);
1281
1282    // Retrieve one boolean control value for a specified mute-control identifier
1283    //
1284    if (!GetBooleanControlValue(_outputMixerID, dwControlID, value))
1285    {
1286        return -1;
1287    }
1288
1289    enabled = value;
1290
1291    return 0;
1292}
1293
1294// ----------------------------------------------------------------------------
1295//  MicrophoneMuteIsAvailable
1296// ----------------------------------------------------------------------------
1297
1298int32_t AudioMixerManager::MicrophoneMuteIsAvailable(bool& available)
1299{
1300    if (_inputMixerHandle == NULL)
1301    {
1302        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists");
1303        return -1;
1304    }
1305
1306    available = _microphoneState[_inputMixerID].muteControlIsValid;
1307
1308    return 0;
1309}
1310
1311// ----------------------------------------------------------------------------
1312// SetMicrophoneMute
1313//
1314//  This mute function works a master mute for the input microphone.
1315// ----------------------------------------------------------------------------
1316
1317int32_t AudioMixerManager::SetMicrophoneMute(bool enable)
1318{
1319    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::SetMicrophoneMute(enable=%u)", enable);
1320
1321    CriticalSectionScoped lock(&_critSect);
1322
1323    if (_inputMixerHandle == NULL)
1324    {
1325        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists");
1326        return -1;
1327    }
1328
1329    // Ensure that the selected wave-in destinationhas a valid mute control.
1330    // If so, its identifier was stored during the enumeration phase which must
1331    // have taken place since the input mixer handle exists.
1332    //
1333    if (!_microphoneState[_inputMixerID].muteControlIsValid)
1334    {
1335        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to mute this microphone line");
1336        return -1;
1337    }
1338
1339    const DWORD dwControlID(_microphoneState[_inputMixerID].dwMuteControlID);
1340
1341    // Set one boolean control value for the specified mute-control
1342    //
1343    if (!SetBooleanControlValue(_inputMixerID, dwControlID, enable))
1344    {
1345        return -1;
1346    }
1347
1348    return (0);
1349}
1350
1351// ----------------------------------------------------------------------------
1352//  MicrophoneMute
1353// ----------------------------------------------------------------------------
1354
1355int32_t AudioMixerManager::MicrophoneMute(bool& enabled) const
1356{
1357
1358    if (_inputMixerHandle == NULL)
1359    {
1360        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists");
1361        return -1;
1362    }
1363
1364    // Ensure that the selected wave-in destinationhas a valid mute control.
1365    // If so, its identifier was stored during the enumeration phase which must
1366    // have taken place since the input mixer handle exists.
1367    //
1368    if (!_microphoneState[_inputMixerID].muteControlIsValid)
1369    {
1370        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to mute this microphone line");
1371        return -1;
1372    }
1373
1374    const DWORD dwControlID(_microphoneState[_inputMixerID].dwMuteControlID);
1375    bool value(false);
1376
1377    // Retrieve one boolean control value for a specified mute-control identifier
1378    //
1379    if (!GetBooleanControlValue(_inputMixerID, dwControlID, value))
1380    {
1381        return -1;
1382    }
1383
1384    enabled = value;
1385
1386    return 0;
1387}
1388
1389// ----------------------------------------------------------------------------
1390//  MicrophoneBoostIsAvailable
1391// ----------------------------------------------------------------------------
1392
1393int32_t AudioMixerManager::MicrophoneBoostIsAvailable(bool& available)
1394{
1395    if (_inputMixerHandle == NULL)
1396    {
1397        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists");
1398        return -1;
1399    }
1400
1401    available = _microphoneState[_inputMixerID].onOffControlIsValid;
1402
1403    return 0;
1404}
1405
1406// ----------------------------------------------------------------------------
1407//  SetMicrophoneBoost
1408// ----------------------------------------------------------------------------
1409
1410int32_t AudioMixerManager::SetMicrophoneBoost(bool enable)
1411{
1412    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::SetMicrophoneBoost(enable=%u)", enable);
1413
1414    CriticalSectionScoped lock(&_critSect);
1415
1416    if (_inputMixerHandle == NULL)
1417    {
1418        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists");
1419        return -1;
1420    }
1421
1422    // Ensure that the selected wave-in destination has a valid boost (on/off) control.
1423    // If so, its identifier was stored during the enumeration phase which must
1424    // have taken place since the input mixer handle exists.
1425    //
1426    if (!_microphoneState[_inputMixerID].onOffControlIsValid)
1427    {
1428        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no boost control exists for this wave-in line");
1429        return -1;
1430    }
1431
1432    const DWORD dwControlID(_microphoneState[_inputMixerID].dwOnOffControlID);
1433
1434    // Set one boolean control value for the specified boost (on/off) control
1435    //
1436    if (!SetBooleanControlValue(_inputMixerID, dwControlID, enable))
1437    {
1438        return -1;
1439    }
1440
1441    return (0);
1442}
1443
1444// ----------------------------------------------------------------------------
1445//  MicrophoneBoost
1446// ----------------------------------------------------------------------------
1447
1448int32_t AudioMixerManager::MicrophoneBoost(bool& enabled) const
1449{
1450
1451    if (_inputMixerHandle == NULL)
1452    {
1453        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists");
1454        return -1;
1455    }
1456
1457    // Ensure that the selected wave-in destination has a valid boost (on/off) control.
1458    // If so, its identifier was stored during the enumeration phase which must
1459    // have taken place since the input mixer handle exists.
1460    //
1461    if (!_microphoneState[_inputMixerID].onOffControlIsValid)
1462    {
1463        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no boost control exists for this wave-in line");
1464        return -1;
1465    }
1466
1467    const DWORD dwControlID(_microphoneState[_inputMixerID].dwOnOffControlID);
1468    bool value(false);
1469
1470    // Retrieve one boolean control value for a specified boost-control identifier
1471    //
1472    if (!GetBooleanControlValue(_inputMixerID, dwControlID, value))
1473    {
1474        return -1;
1475    }
1476
1477    enabled = value;
1478
1479    return 0;
1480}
1481
1482// ----------------------------------------------------------------------------
1483//  MicrophoneVolumeIsAvailable
1484// ----------------------------------------------------------------------------
1485
1486int32_t AudioMixerManager::MicrophoneVolumeIsAvailable(bool& available)
1487{
1488    if (_inputMixerHandle == NULL)
1489    {
1490        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists");
1491        return -1;
1492    }
1493
1494    available = _microphoneState[_inputMixerID].volumeControlIsValid;
1495
1496    return 0;
1497}
1498
1499// ----------------------------------------------------------------------------
1500//  SetMicrophoneVolume
1501// ----------------------------------------------------------------------------
1502
1503int32_t AudioMixerManager::SetMicrophoneVolume(uint32_t volume)
1504{
1505    CriticalSectionScoped lock(&_critSect);
1506
1507    if (_inputMixerHandle == NULL)
1508    {
1509        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists");
1510        return -1;
1511    }
1512
1513    const UINT mixerID(_inputMixerID);
1514    const DWORD dwControlID(_microphoneState[_inputMixerID].dwVolumeControlID);
1515    DWORD dwValue(volume);
1516
1517    // Set one unsigned control value for a specified volume-control identifier
1518    //
1519    if (!SetUnsignedControlValue(mixerID, dwControlID, dwValue))
1520    {
1521        return -1;
1522    }
1523
1524    return (0);
1525}
1526
1527// ----------------------------------------------------------------------------
1528//  MicrophoneVolume
1529// ----------------------------------------------------------------------------
1530
1531int32_t AudioMixerManager::MicrophoneVolume(uint32_t& volume) const
1532{
1533    CriticalSectionScoped lock(&_critSect);
1534
1535    if (_inputMixerHandle == NULL)
1536    {
1537        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists");
1538        return -1;
1539    }
1540
1541    const UINT mixerID(_inputMixerID);
1542    const DWORD dwControlID(_microphoneState[_inputMixerID].dwVolumeControlID);
1543    DWORD dwValue(0);
1544
1545    // Retrieve one unsigned control value for a specified volume-control identifier
1546    //
1547    if (!GetUnsignedControlValue(mixerID, dwControlID, dwValue))
1548    {
1549        return -1;
1550    }
1551
1552    volume = dwValue;
1553
1554    return 0;
1555}
1556
1557// ----------------------------------------------------------------------------
1558//  MaxMicrophoneVolume
1559// ----------------------------------------------------------------------------
1560
1561int32_t AudioMixerManager::MaxMicrophoneVolume(uint32_t& maxVolume) const
1562{
1563    WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "%s", __FUNCTION__);
1564
1565    if (_inputMixerHandle == NULL)
1566    {
1567        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists");
1568        return -1;
1569    }
1570
1571    const UINT mixerID(_inputMixerID);
1572    const DWORD dwControlID(_microphoneState[_inputMixerID].dwVolumeControlID);
1573    MIXERCONTROL mixerControl;
1574
1575    // Retrieve one control line for a specified volume-control identifier
1576    //
1577    if (!GetLineControl(mixerID, dwControlID, mixerControl))
1578    {
1579        return -1;
1580    }
1581
1582    maxVolume = mixerControl.Bounds.dwMaximum;
1583
1584    return 0;
1585}
1586
1587// ----------------------------------------------------------------------------
1588// MinMicrophoneVolume
1589// ----------------------------------------------------------------------------
1590
1591int32_t AudioMixerManager::MinMicrophoneVolume(uint32_t& minVolume) const
1592{
1593
1594    if (_inputMixerHandle == NULL)
1595    {
1596        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists");
1597        return -1;
1598    }
1599
1600    const UINT mixerID(_inputMixerID);
1601    const DWORD dwControlID(_microphoneState[_inputMixerID].dwVolumeControlID);
1602    MIXERCONTROL mixerControl;
1603
1604    // Retrieve one control line for a specified volume-control identifier
1605    //
1606    if (!GetLineControl(mixerID, dwControlID, mixerControl))
1607    {
1608        return -1;
1609    }
1610
1611    minVolume = mixerControl.Bounds.dwMinimum;
1612
1613    return 0;
1614}
1615
1616// ----------------------------------------------------------------------------
1617//  MicrophoneVolumeStepSize
1618// ----------------------------------------------------------------------------
1619
1620int32_t AudioMixerManager::MicrophoneVolumeStepSize(uint16_t& stepSize) const
1621{
1622
1623    if (_inputMixerHandle == NULL)
1624    {
1625        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists");
1626        return -1;
1627    }
1628
1629    const UINT mixerID(_inputMixerID);
1630    const DWORD dwControlID(_microphoneState[_inputMixerID].dwVolumeControlID);
1631    MIXERCONTROL mixerControl;
1632
1633    // Retrieve one control line for a specified volume-control identifier
1634    //
1635    if (!GetLineControl(mixerID, dwControlID, mixerControl))
1636    {
1637        return -1;
1638    }
1639
1640    stepSize = static_cast<uint16_t> (mixerControl.Metrics.cSteps);
1641
1642    return 0;
1643}
1644
1645// ============================================================================
1646//                              PRIVATE METHODS
1647// ============================================================================
1648
1649// ----------------------------------------------------------------------------
1650//  Devices
1651//
1652//  A given audio card has one Mixer device associated with it. All of the
1653//  various components on that card are controlled through that card's one
1654//  Mixer device.
1655// ----------------------------------------------------------------------------
1656
1657UINT AudioMixerManager::Devices() const
1658{
1659    UINT nDevs = mixerGetNumDevs();
1660    return nDevs;
1661}
1662
1663// ----------------------------------------------------------------------------
1664//  DestinationLines
1665//
1666//  # destination lines given mixer ID.
1667// ----------------------------------------------------------------------------
1668
1669UINT AudioMixerManager::DestinationLines(UINT mixId) const
1670{
1671    MIXERCAPS caps;
1672    if (!GetCapabilities(mixId, caps))
1673    {
1674        return 0;
1675    }
1676    return (caps.cDestinations);
1677}
1678// ----------------------------------------------------------------------------
1679//  DestinationLines
1680//
1681//  # source lines given mixer ID and destination ID.
1682// ----------------------------------------------------------------------------
1683
1684UINT AudioMixerManager::SourceLines(UINT mixId, DWORD destId) const
1685{
1686    MIXERLINE dline;
1687    if (!GetDestinationLineInfo(mixId, destId, dline))
1688    {
1689        return 0;
1690    }
1691    return (dline.cConnections);
1692}
1693
1694// ----------------------------------------------------------------------------
1695//  GetCapabilities
1696//
1697//  Queries a specified mixer device to determine its capabilities.
1698// ----------------------------------------------------------------------------
1699
1700bool AudioMixerManager::GetCapabilities(UINT mixId, MIXERCAPS& caps, bool trace) const
1701{
1702    MMRESULT res;
1703    MIXERCAPS mcaps;
1704
1705    res = mixerGetDevCaps(mixId, &mcaps, sizeof(MIXERCAPS));
1706    if (res != MMSYSERR_NOERROR)
1707    {
1708        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetDevCaps() failed (err=%d)", res);
1709        return false;
1710    }
1711
1712    memcpy(&caps, &mcaps, sizeof(MIXERCAPS));
1713
1714    if (trace)
1715    {
1716        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "===============================================================");
1717        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Mixer ID %u:", mixId);
1718        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "manufacturer ID      : %u", caps.wMid);
1719        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product ID           : %u", caps.wPid);
1720        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "version of driver    : %u", caps.vDriverVersion);
1721        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product name         : %s", WideToUTF8(caps.szPname));
1722        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "misc. support bits   : %u", caps.fdwSupport);
1723        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "count of destinations: %u (+)", caps.cDestinations);
1724        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "===============================================================");
1725    }
1726
1727    if (caps.cDestinations == 0)
1728    {
1729        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "invalid number of mixer destinations");
1730        return false;
1731    }
1732
1733    return true;
1734}
1735
1736// ----------------------------------------------------------------------------
1737//  GetDestinationLineInfo
1738// ----------------------------------------------------------------------------
1739
1740bool AudioMixerManager::GetDestinationLineInfo(UINT mixId, DWORD destId, MIXERLINE& line, bool trace) const
1741{
1742    MMRESULT  res;
1743    MIXERLINE mline;
1744
1745    mline.cbStruct = sizeof(MIXERLINE);
1746    mline.dwDestination = destId;   // max destination index is cDestinations-1
1747    mline.dwSource = 0;             // not set for MIXER_GETLINEINFOF_DESTINATION
1748
1749    // Retrieve information about the specified destination line of a mixer device.
1750    // Note that we use the mixer ID here and not a handle to an opened mixer.
1751    // It is not required to open the mixer for enumeration purposes only.
1752    //
1753    res = mixerGetLineInfo(reinterpret_cast<HMIXEROBJ>(mixId), &mline, MIXER_OBJECTF_MIXER | MIXER_GETLINEINFOF_DESTINATION);
1754    if (res != MMSYSERR_NOERROR)
1755    {
1756        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetLineInfo(MIXER_GETLINEINFOF_DESTINATION) failed (err=%d)", res);
1757        return false;
1758    }
1759
1760    memcpy(&line, &mline, sizeof(MIXERLINE));
1761
1762    if (trace)
1763    {
1764        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "> Destination Line ID %u:", destId);
1765        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
1766        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "destination line index : %u", mline.dwDestination);
1767        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "dwLineID               : %lu (unique)", mline.dwLineID);
1768        TraceStatusAndSupportFlags(mline.fdwLine);
1769        TraceComponentType(mline.dwComponentType);
1770        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "count of channels      : %u", mline.cChannels);
1771        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "# audio source lines   : %u (+)", mline.cConnections);    // valid only for destinations
1772        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "# controls             : %u (*)", mline.cControls);       // can be zero
1773        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "short name             : %s", WideToUTF8(mline.szShortName));
1774        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "full name              : %s", WideToUTF8(mline.szName));
1775        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
1776        TraceTargetType(mline.Target.dwType);
1777        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "target device ID       : %lu", mline.Target.dwDeviceID);
1778        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "manufacturer ID        : %u", mline.Target.wMid);
1779        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product ID             : %u", mline.Target.wPid);
1780        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "driver version         : %u", mline.Target.vDriverVersion);
1781        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product name           : %s", WideToUTF8(mline.Target.szPname));
1782        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "---------------------------------------------------------------");
1783    }
1784
1785    return true;
1786}
1787
1788// ----------------------------------------------------------------------------
1789//  GetSourceLineInfo
1790// ----------------------------------------------------------------------------
1791
1792bool AudioMixerManager::GetSourceLineInfo(UINT mixId, DWORD destId, DWORD srcId, MIXERLINE& line, bool trace) const
1793{
1794    MMRESULT  res;
1795    MIXERLINE mline;
1796
1797    mline.cbStruct = sizeof(MIXERLINE);
1798    mline.dwDestination = destId;   // we want the source info for this destination
1799    mline.dwSource = srcId;         // source index (enumerate over these)
1800
1801    // Retrieve information about the specified source line of a mixer device.
1802    // Note that we use the mixer ID here and not a handle to an opened mixer.
1803    // It is not required to open the mixer for enumeration purposes only.
1804    //
1805    res = mixerGetLineInfo(reinterpret_cast<HMIXEROBJ>(mixId), &mline, MIXER_OBJECTF_MIXER | MIXER_GETLINEINFOF_SOURCE);
1806    if (res != MMSYSERR_NOERROR)
1807    {
1808        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetLineInfo(MIXER_GETLINEINFOF_SOURCE) failed (err=%d)", res);
1809        return false;
1810    }
1811
1812    memcpy(&line, &mline, sizeof(MIXERLINE));
1813
1814    if (trace)
1815    {
1816        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " >> Source Line ID %u:", srcId);
1817        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "destination line index : %u", mline.dwDestination);
1818        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "dwSource               : %u", mline.dwSource);
1819        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "dwLineID               : %lu (unique)", mline.dwLineID);
1820        TraceStatusAndSupportFlags(mline.fdwLine);
1821        TraceComponentType(mline.dwComponentType);
1822        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "# controls             : %u (*)", mline.cControls);
1823        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "full name              : %s", WideToUTF8(mline.szName));
1824        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
1825        TraceTargetType(mline.Target.dwType);
1826        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "---------------------------------------------------------------");
1827    }
1828
1829    return true;
1830}
1831
1832// ----------------------------------------------------------------------------
1833// GetAllLineControls
1834// ----------------------------------------------------------------------------
1835
1836bool AudioMixerManager::GetAllLineControls(UINT mixId, const MIXERLINE& line, MIXERCONTROL* controlArray, bool trace) const
1837{
1838    // Ensure that we don't try to aquire information if there are no controls for this line
1839    //
1840    if (line.cControls == 0)
1841        return false;
1842
1843    MMRESULT          res;
1844    MIXERLINECONTROLS mlineControls;            // contains information about the controls of an audio line
1845
1846    mlineControls.dwLineID  = line.dwLineID;    // unique audio line identifier
1847    mlineControls.cControls = line.cControls;   // number of controls associated with the line
1848    mlineControls.pamxctrl  = controlArray;     // points to the first MIXERCONTROL structure to be filled
1849    mlineControls.cbStruct  = sizeof(MIXERLINECONTROLS);
1850    mlineControls.cbmxctrl  = sizeof(MIXERCONTROL);
1851
1852    // Get information on ALL controls associated with the specified audio line
1853    //
1854    res = mixerGetLineControls(reinterpret_cast<HMIXEROBJ>(mixId), &mlineControls, MIXER_OBJECTF_MIXER | MIXER_GETLINECONTROLSF_ALL);
1855    if (res != MMSYSERR_NOERROR)
1856    {
1857        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetLineControls(MIXER_GETLINECONTROLSF_ALL) failed  (err=%d)", res);
1858        return false;
1859    }
1860
1861    if (trace)
1862    {
1863        for (UINT c = 0; c < line.cControls; c++)
1864        {
1865            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " >> Control ID %u:", c);
1866            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "dwControlID            : %u (unique)", controlArray[c].dwControlID);
1867            TraceControlType(controlArray[c].dwControlType);
1868            TraceControlStatusAndSupportFlags(controlArray[c].fdwControl);
1869            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "cMultipleItems         : %u", controlArray[c].cMultipleItems);
1870            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "short name             : %s", WideToUTF8(controlArray[c].szShortName));
1871            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "full name              : %s", WideToUTF8(controlArray[c].szName));
1872            if ((controlArray[c].dwControlType & MIXERCONTROL_CT_UNITS_MASK) == MIXERCONTROL_CT_UNITS_SIGNED)
1873            {
1874                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "min signed value       : %d", controlArray[c].Bounds.lMinimum);
1875                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "max signed value       : %d", controlArray[c].Bounds.lMaximum);
1876            }
1877            else if ((controlArray[c].dwControlType & MIXERCONTROL_CT_UNITS_MASK) == MIXERCONTROL_CT_UNITS_UNSIGNED ||
1878                     (controlArray[c].dwControlType & MIXERCONTROL_CT_UNITS_MASK) == MIXERCONTROL_CT_UNITS_BOOLEAN)
1879            {
1880                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "min unsigned value     : %u",  controlArray[c].Bounds.dwMinimum);
1881                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "max unsigned value     : %u", controlArray[c].Bounds.dwMaximum);
1882            }
1883            if (controlArray[c].dwControlType  != MIXERCONTROL_CONTROLTYPE_CUSTOM)
1884            {
1885                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "cSteps                 : %u",  controlArray[c].Metrics.cSteps);
1886            }
1887            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "...............................................................");
1888            GetControlDetails(mixId, controlArray[c], true);
1889            WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "...............................................................");
1890
1891        }
1892    }
1893
1894    return true;
1895}
1896
1897// ----------------------------------------------------------------------------
1898//  GetLineControls
1899// ----------------------------------------------------------------------------
1900
1901bool AudioMixerManager::GetLineControl(UINT mixId, DWORD dwControlID, MIXERCONTROL& control) const
1902{
1903    MMRESULT          res;
1904    MIXERLINECONTROLS mlineControl;
1905
1906    mlineControl.dwControlID = dwControlID;
1907    mlineControl.cControls   = 1;
1908    mlineControl.pamxctrl    = &control;
1909    mlineControl.cbStruct    = sizeof(MIXERLINECONTROLS);
1910    mlineControl.cbmxctrl    = sizeof(MIXERCONTROL);
1911
1912    // Get information on one controls associated with the specified conrol identifier
1913    //
1914    res = mixerGetLineControls(reinterpret_cast<HMIXEROBJ>(mixId), &mlineControl, MIXER_OBJECTF_MIXER | MIXER_GETLINECONTROLSF_ONEBYID);
1915    if (res != MMSYSERR_NOERROR)
1916    {
1917        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetLineControls(MIXER_GETLINECONTROLSF_ONEBYID) failed (err=%d)", res);
1918        return false;
1919    }
1920
1921    return true;
1922}
1923
1924// ----------------------------------------------------------------------------
1925//  GetControlDetails
1926// ----------------------------------------------------------------------------
1927
1928bool AudioMixerManager::GetControlDetails(UINT mixId, MIXERCONTROL& controlArray, bool trace) const
1929{
1930    assert(controlArray.cMultipleItems <= MAX_NUMBER_OF_MULTIPLE_ITEMS);
1931
1932    MMRESULT                     res;
1933    MIXERCONTROLDETAILS          controlDetails;
1934
1935    MIXERCONTROLDETAILS_UNSIGNED valueUnsigned[MAX_NUMBER_OF_MULTIPLE_ITEMS];
1936    MIXERCONTROLDETAILS_SIGNED   valueSigned[MAX_NUMBER_OF_MULTIPLE_ITEMS];
1937    MIXERCONTROLDETAILS_BOOLEAN  valueBoolean[MAX_NUMBER_OF_MULTIPLE_ITEMS];
1938
1939    enum ControlType
1940    {
1941        CT_UNITS_UNSIGNED,
1942        CT_UNITS_SIGNED,
1943        CT_UNITS_BOOLEAN
1944    };
1945
1946    ControlType ctype(CT_UNITS_UNSIGNED);
1947
1948    controlDetails.cbStruct       = sizeof(MIXERCONTROLDETAILS);
1949    controlDetails.dwControlID    = controlArray.dwControlID;       // control identifier
1950    controlDetails.cChannels      = 1;                              // we need to set values as if they were uniform
1951    controlDetails.cMultipleItems = controlArray.cMultipleItems;    // only nonzero for CONTROLF_MULTIPLE controls
1952                                                                    // can e.g. happen for CONTROLTYPE_MUX
1953    if (controlDetails.cMultipleItems > MAX_NUMBER_OF_MULTIPLE_ITEMS)
1954    {
1955        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "cMultipleItems > %d", MAX_NUMBER_OF_MULTIPLE_ITEMS);
1956        controlDetails.cMultipleItems = MAX_NUMBER_OF_MULTIPLE_ITEMS;
1957    }
1958
1959    if ((controlArray.dwControlType & MIXERCONTROL_CT_UNITS_MASK) == MIXERCONTROL_CT_UNITS_SIGNED)
1960    {
1961        ctype = CT_UNITS_SIGNED;
1962        controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_SIGNED);
1963        controlDetails.paDetails = &valueSigned[0];
1964    }
1965    else if ((controlArray.dwControlType & MIXERCONTROL_CT_UNITS_MASK) == MIXERCONTROL_CT_UNITS_UNSIGNED)
1966    {
1967        ctype = CT_UNITS_UNSIGNED;
1968        controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
1969        controlDetails.paDetails = &valueUnsigned[0];
1970    }
1971    else if ((controlArray.dwControlType & MIXERCONTROL_CT_UNITS_MASK) == MIXERCONTROL_CT_UNITS_BOOLEAN)
1972    {
1973        ctype = CT_UNITS_BOOLEAN;
1974        controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
1975        controlDetails.paDetails = &valueBoolean[0];
1976    }
1977
1978    // Retrieve a control's value
1979    //
1980    res = mixerGetControlDetails(reinterpret_cast<HMIXEROBJ>(mixId), &controlDetails, MIXER_OBJECTF_MIXER | MIXER_GETCONTROLDETAILSF_VALUE);
1981    if (res != MMSYSERR_NOERROR)
1982    {
1983        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE) failed (err=%d)", res);
1984        return false;
1985    }
1986
1987    if (trace)
1988    {
1989        UINT nItems(1);
1990        nItems = (controlDetails.cMultipleItems > 0 ? controlDetails.cMultipleItems : 1);
1991        for (UINT i = 0; i < nItems; i++)
1992        {
1993            if (ctype == CT_UNITS_SIGNED)
1994            {
1995                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "signed value           : %d", valueSigned[i].lValue);
1996            }
1997            else if (ctype == CT_UNITS_UNSIGNED)
1998            {
1999                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "unsigned value         : %u", valueUnsigned[i].dwValue);
2000            }
2001            else if (ctype == CT_UNITS_BOOLEAN)
2002            {
2003                WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "boolean value          : %u", valueBoolean[i].fValue);
2004            }
2005        }
2006    }
2007
2008    return true;
2009}
2010
2011// ----------------------------------------------------------------------------
2012//  GetUnsignedControlValue
2013// ----------------------------------------------------------------------------
2014
2015bool AudioMixerManager::GetUnsignedControlValue(UINT mixId, DWORD dwControlID, DWORD& dwValue) const
2016{
2017    MMRESULT                     res;
2018    MIXERCONTROLDETAILS          controlDetails;
2019    MIXERCONTROLDETAILS_UNSIGNED valueUnsigned;
2020
2021    controlDetails.dwControlID    = dwControlID;
2022    controlDetails.cbStruct       = sizeof(MIXERCONTROLDETAILS);
2023    controlDetails.cChannels      = 1;
2024    controlDetails.cMultipleItems = 0;
2025    controlDetails.cbDetails      = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
2026    controlDetails.paDetails      = &valueUnsigned;
2027
2028    // Retrieve the unsigned value
2029    //
2030    res = mixerGetControlDetails(reinterpret_cast<HMIXEROBJ>(mixId), &controlDetails, MIXER_OBJECTF_MIXER | MIXER_GETCONTROLDETAILSF_VALUE);
2031    if (res != MMSYSERR_NOERROR)
2032    {
2033        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE) failed (err=%d)", res);
2034        return false;
2035    }
2036
2037    // Deliver the retrieved value
2038    //
2039    dwValue = valueUnsigned.dwValue;
2040
2041    return true;
2042}
2043
2044// ----------------------------------------------------------------------------
2045//  SetUnsignedControlValue
2046// ----------------------------------------------------------------------------
2047
2048bool AudioMixerManager::SetUnsignedControlValue(UINT mixId, DWORD dwControlID, DWORD dwValue) const
2049{
2050    WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "AudioMixerManager::SetUnsignedControlValue(mixId=%u, dwControlID=%d, dwValue=%d)", mixId, dwControlID, dwValue);
2051
2052    MMRESULT                     res;
2053    MIXERCONTROLDETAILS          controlDetails;
2054    MIXERCONTROLDETAILS_UNSIGNED valueUnsigned;
2055
2056    controlDetails.dwControlID    = dwControlID;
2057    controlDetails.cbStruct       = sizeof(MIXERCONTROLDETAILS);
2058    controlDetails.cChannels      = 1;
2059    controlDetails.cMultipleItems = 0;
2060    controlDetails.cbDetails      = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
2061    controlDetails.paDetails      = &valueUnsigned;
2062
2063    valueUnsigned.dwValue         = dwValue;
2064
2065    // Set the unsigned value
2066    //
2067    res = mixerSetControlDetails(reinterpret_cast<HMIXEROBJ>(mixId), &controlDetails, MIXER_OBJECTF_MIXER | MIXER_GETCONTROLDETAILSF_VALUE);
2068    if (res != MMSYSERR_NOERROR)
2069    {
2070        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerSetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE) failed (err=%d)", res);
2071        return false;
2072    }
2073
2074    return true;
2075}
2076
2077// ----------------------------------------------------------------------------
2078//  SetBooleanControlValue
2079// ----------------------------------------------------------------------------
2080
2081bool AudioMixerManager::SetBooleanControlValue(UINT mixId, DWORD dwControlID, bool value) const
2082{
2083    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::SetBooleanControlValue(mixId=%u, dwControlID=%d, value=%d)", mixId, dwControlID, value);
2084
2085    MMRESULT                    res;
2086    MIXERCONTROLDETAILS         controlDetails;
2087    MIXERCONTROLDETAILS_BOOLEAN valueBoolean;
2088
2089    controlDetails.dwControlID    = dwControlID;
2090    controlDetails.cbStruct       = sizeof(MIXERCONTROLDETAILS);
2091    controlDetails.cChannels      = 1;
2092    controlDetails.cMultipleItems = 0;
2093    controlDetails.cbDetails      = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
2094    controlDetails.paDetails      = &valueBoolean;
2095
2096    if (value == true)
2097        valueBoolean.fValue = TRUE;
2098    else
2099        valueBoolean.fValue = FALSE;
2100
2101    // Set the boolean value
2102    //
2103    res = mixerSetControlDetails(reinterpret_cast<HMIXEROBJ>(mixId), &controlDetails, MIXER_OBJECTF_MIXER | MIXER_GETCONTROLDETAILSF_VALUE);
2104    if (res != MMSYSERR_NOERROR)
2105    {
2106        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerSetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE) failed (err=%d)", res);
2107        return false;
2108    }
2109
2110    return true;
2111}
2112
2113// ----------------------------------------------------------------------------
2114//  GetBooleanControlValue
2115// ----------------------------------------------------------------------------
2116
2117bool AudioMixerManager::GetBooleanControlValue(UINT mixId, DWORD dwControlID, bool& value) const
2118{
2119    MMRESULT                    res;
2120    MIXERCONTROLDETAILS         controlDetails;
2121    MIXERCONTROLDETAILS_BOOLEAN valueBoolean;
2122
2123    controlDetails.dwControlID    = dwControlID;
2124    controlDetails.cbStruct       = sizeof(MIXERCONTROLDETAILS);
2125    controlDetails.cChannels      = 1;
2126    controlDetails.cMultipleItems = 0;
2127    controlDetails.cbDetails      = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
2128    controlDetails.paDetails      = &valueBoolean;
2129
2130    // Retrieve the boolean value
2131    //
2132    res = mixerGetControlDetails(reinterpret_cast<HMIXEROBJ>(mixId), &controlDetails, MIXER_OBJECTF_MIXER | MIXER_GETCONTROLDETAILSF_VALUE);
2133    if (res != MMSYSERR_NOERROR)
2134    {
2135        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE) failed (err=%d)", res);
2136        return false;
2137    }
2138
2139    // Deliver the retrieved value
2140    //
2141    if (valueBoolean.fValue == 0)
2142        value = false;
2143    else
2144        value = true;
2145
2146    return true;
2147}
2148
2149// ----------------------------------------------------------------------------
2150//  GetSelectedMuxSource
2151// ----------------------------------------------------------------------------
2152
2153bool AudioMixerManager::GetSelectedMuxSource(UINT mixId, DWORD dwControlID, DWORD cMultipleItems, UINT& index) const
2154{
2155    assert(cMultipleItems <= MAX_NUMBER_OF_MULTIPLE_ITEMS);
2156
2157    MMRESULT                    res;
2158    MIXERCONTROLDETAILS         controlDetails;
2159    MIXERCONTROLDETAILS_BOOLEAN valueBoolean[MAX_NUMBER_OF_MULTIPLE_ITEMS];
2160    memset(&valueBoolean, 0, sizeof(valueBoolean));
2161
2162    controlDetails.dwControlID    = dwControlID;
2163    controlDetails.cbStruct       = sizeof(MIXERCONTROLDETAILS);
2164    controlDetails.cChannels      = 1;
2165    controlDetails.cMultipleItems = cMultipleItems;
2166    controlDetails.cbDetails      = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
2167    controlDetails.paDetails      = &valueBoolean;
2168
2169    // Retrieve the boolean values
2170    //
2171    res = mixerGetControlDetails(reinterpret_cast<HMIXEROBJ>(mixId), &controlDetails, MIXER_OBJECTF_MIXER | MIXER_GETCONTROLDETAILSF_VALUE);
2172    if (res != MMSYSERR_NOERROR)
2173    {
2174        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE) failed (err=%d)", res);
2175        return false;
2176    }
2177
2178    // Map the current MUX setting to an index corresponding to a source index.
2179    // e.g. with cMultipleItems = 3,
2180    //  valueBoolean[] = {1,0,0} => index = 2
2181    //  valueBoolean[] = {0,1,0} => index = 1
2182    //  valueBoolean[] = {0,0,1} => index = 0
2183    //
2184    // If there is no "1" in the array, we assume index should be 0.
2185    index = 0;
2186    for (DWORD i = 0; i < cMultipleItems; i++)
2187    {
2188        if (valueBoolean[i].fValue > 0)
2189        {
2190            index = (cMultipleItems - 1) - i;
2191            break;
2192        }
2193    }
2194
2195    return true;
2196}
2197
2198// ----------------------------------------------------------------------------
2199//  TraceStatusAndSupportFlags
2200// ----------------------------------------------------------------------------
2201
2202void AudioMixerManager::TraceStatusAndSupportFlags(DWORD fdwLine) const
2203{
2204    TCHAR buf[128];
2205
2206    StringCchPrintf(buf, 128, TEXT("status & support flags : 0x%x "), fdwLine);
2207
2208    switch (fdwLine)
2209    {
2210    case MIXERLINE_LINEF_ACTIVE:
2211        StringCchCat(buf, 128, TEXT("(ACTIVE DESTINATION)"));
2212        break;
2213    case MIXERLINE_LINEF_DISCONNECTED:
2214        StringCchCat(buf, 128, TEXT("(DISCONNECTED)"));
2215        break;
2216    case MIXERLINE_LINEF_SOURCE:
2217        StringCchCat(buf, 128, TEXT("(INACTIVE SOURCE)"));
2218        break;
2219    case MIXERLINE_LINEF_SOURCE | MIXERLINE_LINEF_ACTIVE:
2220        StringCchCat(buf, 128, TEXT("(ACTIVE SOURCE)"));
2221        break;
2222    default:
2223        StringCchCat(buf, 128, TEXT("(INVALID)"));
2224        break;
2225    }
2226
2227    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf));
2228}
2229
2230// ----------------------------------------------------------------------------
2231//  TraceComponentType
2232// ----------------------------------------------------------------------------
2233
2234void AudioMixerManager::TraceComponentType(DWORD dwComponentType) const
2235{
2236    TCHAR buf[128];
2237
2238    StringCchPrintf(buf, 128, TEXT("component type         : 0x%x "), dwComponentType);
2239
2240    switch (dwComponentType)
2241    {
2242    // Destination
2243    case MIXERLINE_COMPONENTTYPE_DST_UNDEFINED:
2244        StringCchCat(buf, 128, TEXT("(DST_UNDEFINED)"));
2245        break;
2246    case MIXERLINE_COMPONENTTYPE_DST_DIGITAL:
2247        StringCchCat(buf, 128, TEXT("(DST_DIGITAL)"));
2248        break;
2249    case MIXERLINE_COMPONENTTYPE_DST_LINE:
2250        StringCchCat(buf, 128, TEXT("(DST_LINE)"));
2251        break;
2252    case MIXERLINE_COMPONENTTYPE_DST_MONITOR:
2253        StringCchCat(buf, 128, TEXT("(DST_MONITOR)"));
2254        break;
2255    case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
2256        StringCchCat(buf, 128, TEXT("(DST_SPEAKERS)"));
2257        break;
2258    case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES:
2259        StringCchCat(buf, 128, TEXT("(DST_HEADPHONES)"));
2260        break;
2261    case MIXERLINE_COMPONENTTYPE_DST_TELEPHONE:
2262        StringCchCat(buf, 128, TEXT("(DST_TELEPHONE)"));
2263        break;
2264    case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
2265        StringCchCat(buf, 128, TEXT("(DST_WAVEIN)"));
2266        break;
2267    case MIXERLINE_COMPONENTTYPE_DST_VOICEIN:
2268        StringCchCat(buf, 128, TEXT("(DST_VOICEIN)"));
2269        break;
2270    // Source
2271    case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
2272        StringCchCat(buf, 128, TEXT("(SRC_UNDEFINED)"));
2273        break;
2274    case MIXERLINE_COMPONENTTYPE_SRC_DIGITAL:
2275        StringCchCat(buf, 128, TEXT("(SRC_DIGITAL)"));
2276        break;
2277    case MIXERLINE_COMPONENTTYPE_SRC_LINE:
2278        StringCchCat(buf, 128, TEXT("(SRC_LINE)"));
2279        break;
2280    case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
2281        StringCchCat(buf, 128, TEXT("(SRC_MICROPHONE)"));
2282        break;
2283    case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
2284        StringCchCat(buf, 128, TEXT("(SRC_SYNTHESIZER)"));
2285        break;
2286    case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
2287        StringCchCat(buf, 128, TEXT("(SRC_COMPACTDISC)"));
2288        break;
2289    case MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE:
2290        StringCchCat(buf, 128, TEXT("(SRC_TELEPHONE)"));
2291        break;
2292    case MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER:
2293        StringCchCat(buf, 128, TEXT("(SRC_PCSPEAKER)"));
2294        break;
2295    case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
2296        StringCchCat(buf, 128, TEXT("(SRC_WAVEOUT)"));
2297        break;
2298    case MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY:
2299        StringCchCat(buf, 128, TEXT("(SRC_AUXILIARY)"));
2300        break;
2301    case MIXERLINE_COMPONENTTYPE_SRC_ANALOG:
2302        StringCchCat(buf, 128, TEXT("(SRC_ANALOG)"));
2303        break;
2304    default:
2305        StringCchCat(buf, 128, TEXT("(INVALID)"));
2306        break;
2307    }
2308
2309    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf));
2310}
2311
2312// ----------------------------------------------------------------------------
2313//  TraceTargetType
2314// ----------------------------------------------------------------------------
2315
2316void AudioMixerManager::TraceTargetType(DWORD dwType) const
2317{
2318    TCHAR buf[128];
2319
2320    StringCchPrintf(buf, 128, TEXT("media device type      : 0x%x "), dwType);
2321
2322    switch (dwType)
2323    {
2324    case MIXERLINE_TARGETTYPE_UNDEFINED:
2325        StringCchCat(buf, 128, TEXT("(UNDEFINED)"));
2326        break;
2327    case MIXERLINE_TARGETTYPE_WAVEOUT:
2328        StringCchCat(buf, 128, TEXT("(WAVEOUT)"));
2329        break;
2330    case MIXERLINE_TARGETTYPE_WAVEIN:
2331        StringCchCat(buf, 128, TEXT("(WAVEIN)"));
2332        break;
2333    case MIXERLINE_TARGETTYPE_MIDIOUT:
2334        StringCchCat(buf, 128, TEXT("(MIDIOUT)"));
2335        break;
2336    case MIXERLINE_TARGETTYPE_MIDIIN:
2337        StringCchCat(buf, 128, TEXT("(MIDIIN)"));
2338        break;
2339    default:
2340        StringCchCat(buf, 128, TEXT("(INVALID)"));
2341        break;
2342    }
2343
2344    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf));
2345}
2346
2347// ----------------------------------------------------------------------------
2348//  TraceControlType
2349// ----------------------------------------------------------------------------
2350
2351void AudioMixerManager::TraceControlType(DWORD dwControlType) const
2352{
2353    TCHAR buf[128];
2354
2355    // Class type classification
2356    //
2357    StringCchPrintf(buf, 128, TEXT("class type             : 0x%x "), dwControlType);
2358
2359    switch (dwControlType & MIXERCONTROL_CT_CLASS_MASK)
2360    {
2361    case MIXERCONTROL_CT_CLASS_CUSTOM:
2362        StringCchCat(buf, 128, TEXT("(CT_CLASS_CUSTOM)"));
2363        break;
2364    case MIXERCONTROL_CT_CLASS_METER:
2365        StringCchCat(buf, 128, TEXT("(CT_CLASS_METER)"));
2366        break;
2367    case MIXERCONTROL_CT_CLASS_SWITCH:
2368        StringCchCat(buf, 128, TEXT("(CT_CLASS_SWITCH)"));
2369        break;
2370    case MIXERCONTROL_CT_CLASS_NUMBER:
2371        StringCchCat(buf, 128, TEXT("(CT_CLASS_NUMBER)"));
2372        break;
2373    case MIXERCONTROL_CT_CLASS_SLIDER:
2374        StringCchCat(buf, 128, TEXT("(CT_CLASS_SLIDER)"));
2375        break;
2376    case MIXERCONTROL_CT_CLASS_FADER:
2377        StringCchCat(buf, 128, TEXT("(CT_CLASS_FADER)"));
2378        break;
2379    case MIXERCONTROL_CT_CLASS_TIME:
2380        StringCchCat(buf, 128, TEXT("(CT_CLASS_TIME)"));
2381        break;
2382    case MIXERCONTROL_CT_CLASS_LIST:
2383        StringCchCat(buf, 128, TEXT("(CT_CLASS_LIST)"));
2384        break;
2385    default:
2386        StringCchCat(buf, 128, TEXT("(INVALID)"));
2387        break;
2388    }
2389
2390    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf));
2391
2392    // Control type (for each class)
2393    //
2394    StringCchPrintf(buf, 128, TEXT("control type           : 0x%x "), dwControlType);
2395
2396    switch (dwControlType)
2397    {
2398    case MIXERCONTROL_CONTROLTYPE_CUSTOM:
2399        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_CUSTOM)"));
2400        break;
2401    case MIXERCONTROL_CONTROLTYPE_BOOLEANMETER:
2402        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_BOOLEANMETER)"));
2403        break;
2404    case MIXERCONTROL_CONTROLTYPE_SIGNEDMETER:
2405        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_SIGNEDMETER)"));
2406        break;
2407    case MIXERCONTROL_CONTROLTYPE_PEAKMETER:
2408        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_PEAKMETER)"));
2409        break;
2410    case MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER:
2411        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_UNSIGNEDMETER)"));
2412        break;
2413    case MIXERCONTROL_CONTROLTYPE_BOOLEAN:
2414        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_BOOLEAN)"));
2415        break;
2416    case MIXERCONTROL_CONTROLTYPE_ONOFF:
2417        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_ONOFF)"));
2418        break;
2419    case MIXERCONTROL_CONTROLTYPE_MUTE:
2420        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MUTE)"));
2421        break;
2422    case MIXERCONTROL_CONTROLTYPE_MONO:
2423        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MONO)"));
2424        break;
2425    case MIXERCONTROL_CONTROLTYPE_LOUDNESS:
2426        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_LOUDNESS)"));
2427        break;
2428    case MIXERCONTROL_CONTROLTYPE_STEREOENH:
2429        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_STEREOENH)"));
2430        break;
2431    case MIXERCONTROL_CONTROLTYPE_BASS_BOOST:
2432        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_BASS_BOOST)"));
2433        break;
2434    case MIXERCONTROL_CONTROLTYPE_BUTTON:
2435        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_BUTTON)"));
2436        break;
2437    case MIXERCONTROL_CONTROLTYPE_DECIBELS:
2438        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_DECIBELS)"));
2439        break;
2440    case MIXERCONTROL_CONTROLTYPE_SIGNED:
2441        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_SIGNED)"));
2442        break;
2443    case MIXERCONTROL_CONTROLTYPE_UNSIGNED:
2444        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_UNSIGNED)"));
2445        break;
2446    case MIXERCONTROL_CONTROLTYPE_PERCENT:
2447        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_PERCENT)"));
2448        break;
2449    case MIXERCONTROL_CONTROLTYPE_SLIDER:
2450        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_SLIDER)"));
2451        break;
2452    case MIXERCONTROL_CONTROLTYPE_PAN:
2453        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_PAN)"));
2454        break;
2455    case MIXERCONTROL_CONTROLTYPE_QSOUNDPAN:
2456        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_QSOUNDPAN)"));
2457        break;
2458    case MIXERCONTROL_CONTROLTYPE_FADER:
2459        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_FADER)"));
2460        break;
2461    case MIXERCONTROL_CONTROLTYPE_VOLUME:
2462        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_VOLUME)"));
2463        break;
2464    case MIXERCONTROL_CONTROLTYPE_BASS:
2465        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_BASS)"));
2466        break;
2467    case MIXERCONTROL_CONTROLTYPE_TREBLE:
2468        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_TREBLE)"));
2469        break;
2470    case MIXERCONTROL_CONTROLTYPE_EQUALIZER:
2471        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_EQUALIZER)"));
2472        break;
2473    case MIXERCONTROL_CONTROLTYPE_SINGLESELECT:
2474        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_SINGLESELECT)"));
2475        break;
2476    case MIXERCONTROL_CONTROLTYPE_MUX:
2477        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MUX)"));
2478        break;
2479    case MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT:
2480        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MULTIPLESELECT)"));
2481        break;
2482    case MIXERCONTROL_CONTROLTYPE_MIXER:
2483        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MIXER)"));
2484        break;
2485    case MIXERCONTROL_CONTROLTYPE_MICROTIME:
2486        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MICROTIME)"));
2487        break;
2488    case MIXERCONTROL_CONTROLTYPE_MILLITIME:
2489        StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MILLITIME)"));
2490        break;
2491    default:
2492        StringCchCat(buf, 128, TEXT("(INVALID)"));
2493        break;
2494    }
2495
2496    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf));
2497}
2498
2499// ----------------------------------------------------------------------------
2500//  TraceControlStatusAndSupportFlags
2501//
2502//  fdwControl
2503//
2504//  Status and support flags for the audio line control. The following values
2505//  are defined:
2506//
2507//  MIXERCONTROL_CONTROLF_DISABLED
2508//
2509//  The control is disabled, perhaps due to other settings for the mixer hardware,
2510//  and cannot be used. An application can read current settings from a
2511//  disabled control, but it cannot apply settings.
2512//
2513//  MIXERCONTROL_CONTROLF_MULTIPLE
2514//
2515//  The control has two or more settings per channel. An equalizer, for example,
2516//  requires this flag because each frequency band can be set to a different value.
2517//  An equalizer that affects both channels of a stereo line in a uniform fashion
2518//  will also specify the MIXERCONTROL_CONTROLF_UNIFORM flag.
2519//
2520//  MIXERCONTROL_CONTROLF_UNIFORM
2521//
2522//  The control acts on all channels of a multichannel line in a uniform fashion.
2523//  For example, a control that mutes both channels of a stereo line would set
2524//  this flag. Most MIXERCONTROL_CONTROLTYPE_MUX and
2525//  MIXERCONTROL_CONTROLTYPE_MIXER controls also specify the
2526//  MIXERCONTROL_CONTROLF_UNIFORM flag.
2527// ----------------------------------------------------------------------------
2528
2529void AudioMixerManager::TraceControlStatusAndSupportFlags(DWORD fdwControl) const
2530{
2531    TCHAR buf[128];
2532
2533    StringCchPrintf(buf, 128, TEXT("control support flags  : 0x%x "), fdwControl);
2534
2535    if (fdwControl & MIXERCONTROL_CONTROLF_DISABLED)
2536    {
2537        // The control is disabled, perhaps due to other settings for the mixer hardware,
2538        // and cannot be used. An application can read current settings from a disabled
2539        // control, but it cannot apply settings.
2540        StringCchCat(buf, 128, TEXT("(CONTROLF_DISABLED)"));
2541    }
2542
2543    if (fdwControl & MIXERCONTROL_CONTROLF_MULTIPLE)
2544    {
2545        // The control has two or more settings per channel. An equalizer, for example,
2546        // requires this flag because each frequency band can be set to a different
2547        // value. An equalizer that affects both channels of a stereo line in a
2548        // uniform fashion will also specify the MIXERCONTROL_CONTROLF_UNIFORM flag.
2549        StringCchCat(buf, 128, TEXT("(CONTROLF_MULTIPLE)"));
2550    }
2551
2552    if (fdwControl & MIXERCONTROL_CONTROLF_UNIFORM)
2553    {
2554        // The control acts on all channels of a multichannel line in a uniform
2555        // fashion. For example, a control that mutes both channels of a stereo
2556        // line would set this flag. Most MIXERCONTROL_CONTROLTYPE_MUX and
2557        // MIXERCONTROL_CONTROLTYPE_MIXER controls also specify the
2558        // MIXERCONTROL_CONTROLF_UNIFORM flag.
2559        StringCchCat(buf, 128, TEXT("(CONTROLF_UNIFORM)"));
2560    }
2561
2562    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf));
2563}
2564
2565// ----------------------------------------------------------------------------
2566//  ClearSpeakerState I (II)
2567// ----------------------------------------------------------------------------
2568
2569void AudioMixerManager::ClearSpeakerState(UINT idx)
2570{
2571    _speakerState[idx].dwLineID = 0L;
2572    _speakerState[idx].dwVolumeControlID = 0L;
2573    _speakerState[idx].dwMuteControlID = 0L;
2574    _speakerState[idx].speakerIsValid = false;
2575    _speakerState[idx].muteControlIsValid = false;
2576    _speakerState[idx].volumeControlIsValid = false;
2577}
2578
2579// ----------------------------------------------------------------------------
2580//  ClearSpeakerState II (II)
2581// ----------------------------------------------------------------------------
2582
2583void AudioMixerManager::ClearSpeakerState()
2584{
2585    for (int i = 0; i < MAX_NUMBER_MIXER_DEVICES; i++)
2586    {
2587        ClearSpeakerState(i);
2588    }
2589}
2590
2591// ----------------------------------------------------------------------------
2592//  SpeakerIsValid
2593// ----------------------------------------------------------------------------
2594
2595bool AudioMixerManager::SpeakerIsValid(UINT idx) const
2596{
2597    return (_speakerState[idx].speakerIsValid);
2598}
2599
2600// ----------------------------------------------------------------------------
2601//  ValidSpeakers
2602//
2603//  Counts number of valid speaker destinations for all mixer devices.
2604// ----------------------------------------------------------------------------
2605
2606UINT AudioMixerManager::ValidSpeakers() const
2607{
2608    UINT nSpeakers(0);
2609    for (int i = 0; i < MAX_NUMBER_MIXER_DEVICES; i++)
2610    {
2611        if (SpeakerIsValid(i))
2612            nSpeakers++;
2613    }
2614    return nSpeakers;
2615}
2616
2617// ----------------------------------------------------------------------------
2618//  ClearMicrophoneState I (II)
2619// ----------------------------------------------------------------------------
2620
2621void AudioMixerManager::ClearMicrophoneState(UINT idx)
2622{
2623    _microphoneState[idx].dwLineID = 0L;
2624    _microphoneState[idx].dwVolumeControlID = 0L;
2625    _microphoneState[idx].dwMuteControlID = 0L;
2626    _microphoneState[idx].dwOnOffControlID = 0L;
2627    _microphoneState[idx].microphoneIsValid = false;
2628    _microphoneState[idx].muteControlIsValid = false;
2629    _microphoneState[idx].volumeControlIsValid = false;
2630    _microphoneState[idx].onOffControlIsValid = false;
2631}
2632
2633// ----------------------------------------------------------------------------
2634//  ClearMicrophoneState II (II)
2635// ----------------------------------------------------------------------------
2636
2637void AudioMixerManager::ClearMicrophoneState()
2638{
2639    for (int i = 0; i < MAX_NUMBER_MIXER_DEVICES; i++)
2640    {
2641        ClearMicrophoneState(i);
2642    }
2643}
2644
2645// ----------------------------------------------------------------------------
2646//  MicrophoneIsValid
2647// ----------------------------------------------------------------------------
2648
2649bool AudioMixerManager::MicrophoneIsValid(UINT idx) const
2650{
2651    return (_microphoneState[idx].microphoneIsValid);
2652
2653}
2654
2655// ----------------------------------------------------------------------------
2656//  ValidMicrophones
2657//
2658//  Counts number of valid speaker destinations for all mixer devices.
2659//  To be valid, a speaker destination line must exist.
2660// ----------------------------------------------------------------------------
2661
2662UINT AudioMixerManager::ValidMicrophones() const
2663{
2664    UINT nMicrophones(0);
2665    for (int i = 0; i < MAX_NUMBER_MIXER_DEVICES; i++)
2666    {
2667        if (MicrophoneIsValid(i))
2668            nMicrophones++;
2669    }
2670    return nMicrophones;
2671}
2672
2673// ----------------------------------------------------------------------------
2674//  TraceWaveInError
2675// ----------------------------------------------------------------------------
2676
2677void AudioMixerManager::TraceWaveInError(MMRESULT error) const
2678{
2679    TCHAR buf[MAXERRORLENGTH];
2680    TCHAR msg[MAXERRORLENGTH];
2681
2682    StringCchPrintf(buf, MAXERRORLENGTH, TEXT("Error details: "));
2683    waveInGetErrorText(error, msg, MAXERRORLENGTH);
2684    StringCchCat(buf, MAXERRORLENGTH, msg);
2685    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf));
2686}
2687
2688// ----------------------------------------------------------------------------
2689//  TraceWaveOutError
2690// ----------------------------------------------------------------------------
2691
2692void AudioMixerManager::TraceWaveOutError(MMRESULT error) const
2693{
2694    TCHAR buf[MAXERRORLENGTH];
2695    TCHAR msg[MAXERRORLENGTH];
2696
2697    StringCchPrintf(buf, MAXERRORLENGTH, TEXT("Error details: "));
2698    waveOutGetErrorText(error, msg, MAXERRORLENGTH);
2699    StringCchCat(buf, MAXERRORLENGTH, msg);
2700    WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf));
2701}
2702
2703// ----------------------------------------------------------------------------
2704//  WideToUTF8
2705// ----------------------------------------------------------------------------
2706
2707char* AudioMixerManager::WideToUTF8(const TCHAR* src) const {
2708#ifdef UNICODE
2709    const size_t kStrLen = sizeof(_str);
2710    memset(_str, 0, kStrLen);
2711    // Get required size (in bytes) to be able to complete the conversion.
2712    int required_size = WideCharToMultiByte(CP_UTF8, 0, src, -1, _str, 0, 0, 0);
2713    if (required_size <= kStrLen)
2714    {
2715        // Process the entire input string, including the terminating null char.
2716        if (WideCharToMultiByte(CP_UTF8, 0, src, -1, _str, kStrLen, 0, 0) == 0)
2717            memset(_str, 0, kStrLen);
2718    }
2719    return _str;
2720#else
2721    return const_cast<char*>(src);
2722#endif
2723}
2724
2725}  // namespace webrtc
2726