windows 7 control playback volume of microphone

2019-07-10 18:49发布

问题:

I was trying to adjust playback volume level of microphone; but unfortunately I couldn't. I can control recording volume of microphone by using NAudio library but I want to control playback volume. Is there any way to control microphone playback volume on windows 7. Either C++ or C# solution is acceptable for me.

Here are some attempts to solve my problem:

http://www.computercabal.com/2010/11/mute-microphone-from-c-on-windows.html (This guy wrote a small library that can mute microphone recording volume).

NAudio - Cannot set line control mute value (This solution is able to mute only microphone recording volume).

This is another frustrating attempt to solve my problem by using NAudio, and this mutes only microphone recording level,too:

MMDeviceEnumerator DevEnum = new MMDeviceEnumerator();

MMDeviceCollection devices = DevEnum.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active);

MMDevice micDevice = null;

for (int i = 0; i < devices.Count; i++)
{
    MMDevice deviceAt = devices[i];
    Console.WriteLine(deviceAt.FriendlyName);
    if (deviceAt.FriendlyName.ToLower().StartsWith("mikrofon"))
    {
        micDevice = deviceAt;
    }
}

micDevice.AudioEndpointVolume.Mute = true;

回答1:

I had the same issue and I have found the answer by using the IDeviceTopology interface.

I used the WalkTreeBackwardsFromPart example that is floating around the web.

First you have to get the Parts for the Render device (Speaker).

HRESULT hr = CoInitialize(NULL);
if (FAILED(hr)) {
    printf("Failed CoInitializeEx: hr = 0x%08x\n", hr);
    return __LINE__;
}

// get default render endpoint
IMMDeviceEnumerator *pEnum = NULL;
hr = CoCreateInstance(
    __uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
    (void**)&pEnum
    );
if (FAILED(hr)) {
    printf("Couldn't get device enumerator: hr = 0x%08x\n", hr);
    CoUninitialize();
    return __LINE__;
}
IMMDevice *pDevice = NULL;
IMMDeviceCollection *pDevices = NULL;

hr = pEnum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pDevices);

UINT  count;
hr = pDevices->GetCount(&count);

// Each loop prints the name of an endpoint device.
for (ULONG i = 0; i < count; i++)
{

        //hr = pEnum->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
    hr = pDevices->Item(i, &pDevice);
    if (FAILED(hr)) {
        printf("Couldn't get default render device: hr = 0x%08x\n", hr);
        pEnum->Release();
        CoUninitialize();
        return __LINE__;
    }
    pEnum->Release();

    // get device topology object for that endpoint
    IDeviceTopology *pDT = NULL;
    hr = pDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&pDT);
    if (FAILED(hr)) {
        printf("Couldn't get device topology object: hr = 0x%08x\n", hr);
        pDevice->Release();
        CoUninitialize();
        return __LINE__;
    }
    pDevice->Release();

    // get the single connector for that endpoint
    IConnector *pConnEndpoint = NULL;
    hr = pDT->GetConnector(0, &pConnEndpoint);
    if (FAILED(hr)) {
        printf("Couldn't get the connector on the endpoint: hr = 0x%08x\n", hr);
        pDT->Release();
        CoUninitialize();
        return __LINE__;
    }
    pDT->Release();

    // get the connector on the device that is
    // connected to
    // the connector on the endpoint
    IConnector *pConnDevice = NULL;
    hr = pConnEndpoint->GetConnectedTo(&pConnDevice);
    if (FAILED(hr)) {
        printf("Couldn't get the connector on the device: hr = 0x%08x\n", hr);
        pConnEndpoint->Release();
        CoUninitialize();
        return __LINE__;
    }
    pConnEndpoint->Release();

    // QI on the device's connector for IPart
    IPart *pPart = NULL;
    hr = pConnDevice->QueryInterface(__uuidof(IPart), (void**)&pPart);
    if (FAILED(hr)) {
        printf("Couldn't get the part: hr = 0x%08x\n", hr);
        pConnDevice->Release();
        CoUninitialize();
        return __LINE__;
    }
    pConnDevice->Release();

    // all the real work is done in this function
    hr = WalkTreeBackwardsFromPart(pPart);
    if (FAILED(hr)) {
        printf("Couldn't walk the tree: hr = 0x%08x\n", hr);
        pPart->Release();
        CoUninitialize();
        return __LINE__;
    }
    pPart->Release();
}

The last part there where it call WalkTreeBackwardsFromPart is where the magic happens.

What my test code here does is just go through the Incoming Parts and finds the one labeled "Front Pink In" (this is the the jack for the mic) and when the Volume is being displayed it sets the volume for that part. This is not the greatest code but I was just trying to find the right part.

HRESULT WalkTreeBackwardsFromPart(IPart *pPart, int iTabLevel /* = 0 */) {
HRESULT hr = S_OK;

Tab(iTabLevel);
LPWSTR pwszPartName = NULL;
hr = pPart->GetName(&pwszPartName);
if (FAILED(hr)) {
    printf("Could not get part name: hr = 0x%08x", hr);
    return hr;
}
printf("Part name: %ws\n", *pwszPartName ? pwszPartName : L"(Unnamed)");
//CoTaskMemFree(pwszPartName);

// see if this is a volume node part
IAudioVolumeLevel *pVolume = NULL;
hr = pPart->Activate(CLSCTX_ALL, __uuidof(IAudioVolumeLevel), (void**)&pVolume);
if (E_NOINTERFACE == hr) {
    // not a volume node
} else if (FAILED(hr)) {
    printf("Unexpected failure trying to activate IAudioVolumeLevel: hr = 0x%08x\n", hr);
    return hr;
} else {
    // it's a volume node...
    hr = DisplayVolume(pVolume, iTabLevel, wcscmp(L"Front Pink In", pwszPartName) == 0);
    if (FAILED(hr)) {
        printf("DisplayVolume failed: hr = 0x%08x", hr);
        pVolume->Release();
        return hr;
    }

    if (wcscmp(L"Microphone Boost", pwszPartName) == 0)
    {
        SetVolume(pVolume);
    }

    pVolume->Release();
}

CoTaskMemFree(pwszPartName);

// see if this is a mute node part
IAudioMute *pMute = NULL;
hr = pPart->Activate(CLSCTX_ALL, __uuidof(IAudioMute), (void**)&pMute);
if (E_NOINTERFACE == hr) {
    // not a mute node
} else if (FAILED(hr)) {
    printf("Unexpected failure trying to activate IAudioMute: hr = 0x%08x\n", hr);
    return hr;
} else {
    // it's a mute node...
    hr = DisplayMute(pMute, iTabLevel);
    if (FAILED(hr)) {
        printf("DisplayMute failed: hr = 0x%08x", hr);
        pMute->Release();
        return hr;
    }

    pMute->Release();
}

// get the list of incoming parts
IPartsList *pIncomingParts = NULL;

//get the list of incoming parts    
//hr = pPart->EnumPartsOutgoing(&pIncomingParts);
hr = pPart->EnumPartsIncoming(&pIncomingParts);
if (E_NOTFOUND == hr) {
    // not an error... we've just reached the end of the path
    Tab(iTabLevel);
    printf("No incoming parts at this part\n");
    return S_OK;
}
else
{

    if (FAILED(hr)) {
        printf("Couldn't enum incoming parts: hr = 0x%08x\n", hr);
        return hr;
    }
    UINT nParts = 0;
    hr = pIncomingParts->GetCount(&nParts);
    if (FAILED(hr)) {
        printf("Couldn't get count of incoming parts: hr = 0x%08x\n", hr);
        pIncomingParts->Release();
        return hr;
    }

    // walk the tree on each incoming part recursively
    for (UINT n = 0; n < nParts; n++) {
        IPart *pIncomingPart = NULL;
        hr = pIncomingParts->GetPart(n, &pIncomingPart);
        if (FAILED(hr)) {
            printf("Couldn't get part #%u (0-based) of %u (1-basedSmile hr = 0x%08x\n", n, nParts, hr);
            pIncomingParts->Release();
            return hr;
        }

        hr = WalkTreeBackwardsFromPart(pIncomingPart, iTabLevel + 1);
        if (FAILED(hr)) {
            printf("Couldn't walk tree on part #%u (0-based) of %u (1-basedSmile hr = 0x%08x\n", n, nParts, hr);
            pIncomingPart->Release();
            pIncomingParts->Release();
            return hr;
        }
        pIncomingPart->Release();
    }

    pIncomingParts->Release();
}

return S_OK;
}

HRESULT SetVolume(IAudioVolumeLevel *pVolume)
{
HRESULT hr = S_OK;
UINT nChannels = 0;

hr = pVolume->SetLevel(0,20.0,NULL);
return hr;
}

HRESULT DisplayVolume(IAudioVolumeLevel *pVolume, int iTabLevel, bool setVolume) {
HRESULT hr = S_OK;
UINT nChannels = 0;

hr = pVolume->GetChannelCount(&nChannels);

if (FAILED(hr)) {
    printf("GetChannelCount failed: hr = %08x\n", hr);
    return hr;
}

for (UINT n = 0; n < nChannels; n++) {
    float fMinLevelDB, fMaxLevelDB, fStepping, fLevelDB;

    hr = pVolume->GetLevelRange(n, &fMinLevelDB, &fMaxLevelDB, &fStepping);
    if (FAILED(hr)) {
        printf("GetLevelRange failed: hr = 0x%08x\n", hr);
        return hr;
    }

    hr = pVolume->GetLevel(n, &fLevelDB);
    if (FAILED(hr)) {
        printf("GetLevel failed: hr = 0x%08x\n", hr);
        return hr;
    }

    float levetToSet = fMinLevelDB + ((fMaxLevelDB-fMinLevelDB) * 50 /100.0);

    if (setVolume && nChannels == 2)
    {
        if (n == 0)
        {
            hr = pVolume->SetLevel(n, fMinLevelDB,NULL);
        }
        else
        {
            hr = pVolume->SetLevel(n, fMaxLevelDB,NULL);
        }
    }

    Tab(iTabLevel);
    printf(
        "Channel %u volume is %.3f dB (range is %.3f dB to %.3f dB in increments of %.3f dB)\n",
        n, fLevelDB, fMinLevelDB, fMaxLevelDB, fStepping
        );
}

return S_OK;
}

HRESULT DisplayMute(IAudioMute *pMute, int iTabLevel) {
HRESULT hr = S_OK;
BOOL bMuted = FALSE;

hr = pMute->GetMute(&bMuted);

if (FAILED(hr)) {
    printf("GetMute failed: hr = 0x%08x\n", hr);
    return hr;
}

Tab(iTabLevel);
printf("Mute node: %s\n", bMuted ? "MUTED" : "NOT MUTED");

return S_OK;
}

void Tab(int iTabLevel) {
if (0 >= iTabLevel) { return; }
printf("\t");
Tab(iTabLevel - 1);
}

Anyways when I was setting this volume it was changing Playback Volume L and R and volume slider on the HD Audio Manager Mic Screen.