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