传统 Windows 多媒体应用程序的设备角色

注意

MMDevice API 支持设备角色。 但是,Windows Vista 的用户界面不支持这一功能。 将来的 Windows 版本可能会对设备角色提供用户界面支持。 有关详细信息,请参阅 Windows Vista 中的设备角色

 

传统的 Windows 多媒体 waveOutXxxwaveInXxx 函数无法让应用程序选择用户已分配给特定设备角色音频终结点设备。 但是,在 Windows Vista 中,核心音频 API 可与 Windows 多媒体应用程序配合使用,以便根据设备角色选择设备。 例如,在 MMDevice API 的帮助下,waveOutXxx 应用程序可以识别分配给某个角色的音频终结点设备,识别相应的波形输出设备,并调用 waveOutOpen 函数以打开该设备的实例。 有关 waveOutXxxwaveInXxx 的详细信息,请参阅 Windows SDK 文档。

下面的代码示例显示了如何获取分配给特定设备角色的呈现终结点设备的波形设备 ID:

//-----------------------------------------------------------
// This function gets the waveOut ID of the audio endpoint
// device that is currently assigned to the specified device
// role. The caller can use the waveOut ID to open the
// waveOut device that corresponds to the endpoint device.
//-----------------------------------------------------------
#define EXIT_ON_ERROR(hres)  \
              if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

HRESULT GetWaveOutId(ERole role, int *pWaveOutId)
{
    HRESULT hr;
    IMMDeviceEnumerator *pEnumerator = NULL;
    IMMDevice *pDevice = NULL;
    WCHAR *pstrEndpointIdKey = NULL;
    WCHAR *pstrEndpointId = NULL;

    if (pWaveOutId == NULL)
    {
        return E_POINTER;
    }

    // Create an audio endpoint device enumerator.
    hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                          NULL, CLSCTX_INPROC_SERVER,
                          __uuidof(IMMDeviceEnumerator),
                          (void**)&pEnumerator);
    EXIT_ON_ERROR(hr)

    // Get the audio endpoint device that the user has
    // assigned to the specified device role.
    hr = pEnumerator->GetDefaultAudioEndpoint(eRender, role,
                                              &pDevice);
    EXIT_ON_ERROR(hr)

    // Get the endpoint ID string of the audio endpoint device.
    hr = pDevice->GetId(&pstrEndpointIdKey);
    EXIT_ON_ERROR(hr)

    // Get the size of the endpoint ID string.
    size_t  cbEndpointIdKey;

    hr = StringCbLength(pstrEndpointIdKey,
                        STRSAFE_MAX_CCH * sizeof(WCHAR),
                        &cbEndpointIdKey);
    EXIT_ON_ERROR(hr)

    // Include terminating null in string size.
    cbEndpointIdKey += sizeof(WCHAR);

    // Allocate a buffer for a second string of the same size.
    pstrEndpointId = (WCHAR*)CoTaskMemAlloc(cbEndpointIdKey);
    if (pstrEndpointId == NULL)
    {
        EXIT_ON_ERROR(hr = E_OUTOFMEMORY)
    }

    // Each for-loop iteration below compares the endpoint ID
    // string of the audio endpoint device to the endpoint ID
    // string of an enumerated waveOut device. If the strings
    // match, then we've found the waveOut device that is
    // assigned to the specified device role.
    int waveOutId;
    int cWaveOutDevices = waveOutGetNumDevs();

    for (waveOutId = 0; waveOutId < cWaveOutDevices; waveOutId++)
    {
        MMRESULT mmr;
        size_t cbEndpointId;

        // Get the size (including the terminating null) of
        // the endpoint ID string of the waveOut device.
        mmr = waveOutMessage((HWAVEOUT)IntToPtr(waveOutId),
                             DRV_QUERYFUNCTIONINSTANCEIDSIZE,
                             (DWORD_PTR)&cbEndpointId, NULL);
        if (mmr != MMSYSERR_NOERROR ||
            cbEndpointIdKey != cbEndpointId)  // do sizes match?
        {
            continue;  // not a matching device
        }

        // Get the endpoint ID string for this waveOut device.
        mmr = waveOutMessage((HWAVEOUT)IntToPtr(waveOutId),
                             DRV_QUERYFUNCTIONINSTANCEID,
                             (DWORD_PTR)pstrEndpointId,
                             cbEndpointId);
        if (mmr != MMSYSERR_NOERROR)
        {
            continue;
        }

        // Check whether the endpoint ID string of this waveOut
        // device matches that of the audio endpoint device.
        if (lstrcmpi(pstrEndpointId, pstrEndpointIdKey) == 0)
        {
            *pWaveOutId = waveOutId;  // found match
            hr = S_OK;
            break;
        }
    }

    if (waveOutId == cWaveOutDevices)
    {
        // We reached the end of the for-loop above without
        // finding a waveOut device with a matching endpoint
        // ID string. This behavior is quite unexpected.
        hr = E_UNEXPECTED;
    }

Exit:
    SAFE_RELEASE(pEnumerator);
    SAFE_RELEASE(pDevice);
    CoTaskMemFree(pstrEndpointIdKey);  // NULL pointer okay
    CoTaskMemFree(pstrEndpointId);
    return hr;
}

在前面的代码示例中,GetWaveOutId 函数接受设备角色(eConsole、eMultimedia 或 eCommunications)作为输入参数。 第二个参数是一个指针,函数通过它写入分配给指定角色的波形输出设备的波形设备 ID。 然后,应用程序可以使用此 ID 来调用 waveOutOpen 以便打开设备。

前面代码示例中的主循环包含两次对 waveOutMessage 函数的调用。 第一次调用将发送 DRV_QUERYFUNCTIONINSTANCEIDSIZE 消息,以获取终结点 ID 字符串(由 waveOutId 参数标识)的波形设备大小(以字节为单位)。 (终结点 ID 字符串用于标识作为波形设备抽象基础的音频终结点设备)。此调用报告的大小包括字符串末尾空字符的空间。 程序可以使用大小信息来分配足够大的缓冲区,以容纳整个终结点 ID 字符串。

waveOutMessage 的第二次调用将发送 DRV_QUERYFUNCTIONINSTANCEID 消息,以获取波形输出设备的设备 ID 字符串。 示例代码会将此字符串与具有指定设备角色的音频终结点设备的设备 ID 字符串进行比较。 如果字符串匹配,函数就会将波形设备 ID 写入参数 pWaveOutId 指向的位置。 调用方可以使用此 ID 打开具有指定设备角色的波形输出设备。

Windows Vista 支持 DRV_QUERYFUNCTIONINSTANCEIDSIZE 和 DRV_QUERYFUNCTIONINSTANCEID 消息。 包括 Windows Server 2003、Windows XP 和 Windows 2000 在内的 Windows 早期版本不支持它们。

前面代码示例中的函数用于获取呈现设备的波形设备 ID,但只用稍加修改,也可用于获取捕获设备的波形设备 ID。 然后,应用程序可以使用此 ID 来调用 waveInOpen 以便打开设备。 要修改前面的代码示例以获取分配给特定角色的音频捕获终结点设备的波形设备 ID,请执行以下操作:

  • 用相应的 waveInXxx 函数调用替换上例中的所有 waveOutXxx 函数调用。
  • 将句柄类型 HWAVEOUT 更改为 HWAVEIN。
  • ERole 枚举常量 eRender 替换为 eCapture。

在 Windows Vista 中,waveOutOpenwaveInOpen 函数始终将其创建的音频流分配给默认会话,即由会话 GUID 值 GUID_NULL 标识的特定于进程的会话。

与传统音频 API 的互操作性