音频会话事件

管理共享模式音频流的应用程序可以注册,以便在会话事件发生时接收通知。 如前所述,每个流都属于一个音频会话。 会话事件由音频会话状态的变化所引发。

客户端应用程序可以注册接收以下类型的会话事件通知:

  • 会话子混合的主音量或静音状态发生了变化。
  • 会话子混合中一个或多个通道的音量水平发生了变化。
  • 会话已断开连接。
  • 会话的活动状态已变为活动、非活动或已过期。
  • 会话已分配了新的分组参数。
  • 会话的用户界面属性(图标或显示名称)已更改。

客户端通过 IAudioSessionEvents 接口实现中的方法来接收这些事件的通知。 与 WASAPI 中由 WASAPI 系统模块实现的其他接口不同,客户端会实现 IAudioSessionEvents。 当会话事件发生时,该接口中的方法会从 WASAPI 系统模块接收回调。

要开始接收通知,客户端需要调用 IAudioSessionControl::RegisterAudioSessionNotification 方法来注册其 IAudioSessionEvents 接口。 当客户端不再需要通知时,它会调用 IAudioSessionControl::UnregisterAudioSessionNotification 方法来删除注册。

以下代码示例展示了 IAudioSessionEvents 接口可能的实现:

//-----------------------------------------------------------
// Client implementation of IAudioSessionEvents interface.
// WASAPI calls these methods to notify the application when
// a parameter or property of the audio session changes.
//-----------------------------------------------------------
class CAudioSessionEvents : public IAudioSessionEvents
{
    LONG _cRef;

public:
    CAudioSessionEvents() :
        _cRef(1)
    {
    }

    ~CAudioSessionEvents()
    {
    }

    // IUnknown methods -- AddRef, Release, and QueryInterface

    ULONG STDMETHODCALLTYPE AddRef()
    {
        return InterlockedIncrement(&_cRef);
    }

    ULONG STDMETHODCALLTYPE Release()
    {
        ULONG ulRef = InterlockedDecrement(&_cRef);
        if (0 == ulRef)
        {
            delete this;
        }
        return ulRef;
    }

    HRESULT STDMETHODCALLTYPE QueryInterface(
                                REFIID  riid,
                                VOID  **ppvInterface)
    {
        if (IID_IUnknown == riid)
        {
            AddRef();
            *ppvInterface = (IUnknown*)this;
        }
        else if (__uuidof(IAudioSessionEvents) == riid)
        {
            AddRef();
            *ppvInterface = (IAudioSessionEvents*)this;
        }
        else
        {
            *ppvInterface = NULL;
            return E_NOINTERFACE;
        }
        return S_OK;
    }

    // Notification methods for audio session events

    HRESULT STDMETHODCALLTYPE OnDisplayNameChanged(
                                LPCWSTR NewDisplayName,
                                LPCGUID EventContext)
    {
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE OnIconPathChanged(
                                LPCWSTR NewIconPath,
                                LPCGUID EventContext)
    {
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE OnSimpleVolumeChanged(
                                float NewVolume,
                                BOOL NewMute,
                                LPCGUID EventContext)
    {
        if (NewMute)
        {
            printf("MUTE\n");
        }
        else
        {
            printf("Volume = %d percent\n",
                   (UINT32)(100*NewVolume + 0.5));
        }

        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE OnChannelVolumeChanged(
                                DWORD ChannelCount,
                                float NewChannelVolumeArray[],
                                DWORD ChangedChannel,
                                LPCGUID EventContext)
    {
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE OnGroupingParamChanged(
                                LPCGUID NewGroupingParam,
                                LPCGUID EventContext)
    {
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE OnStateChanged(
                                AudioSessionState NewState)
    {
        char *pszState = "?????";

        switch (NewState)
        {
        case AudioSessionStateActive:
            pszState = "active";
            break;
        case AudioSessionStateInactive:
            pszState = "inactive";
            break;
        }
        printf("New session state = %s\n", pszState);

        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE OnSessionDisconnected(
              AudioSessionDisconnectReason DisconnectReason)
    {
        char *pszReason = "?????";

        switch (DisconnectReason)
        {
        case DisconnectReasonDeviceRemoval:
            pszReason = "device removed";
            break;
        case DisconnectReasonServerShutdown:
            pszReason = "server shut down";
            break;
        case DisconnectReasonFormatChanged:
            pszReason = "format changed";
            break;
        case DisconnectReasonSessionLogoff:
            pszReason = "user logged off";
            break;
        case DisconnectReasonSessionDisconnected:
            pszReason = "session disconnected";
            break;
        case DisconnectReasonExclusiveModeOverride:
            pszReason = "exclusive-mode override";
            break;
        }
        printf("Audio session disconnected (reason: %s)\n",
               pszReason);

        return S_OK;
    }
};

前面代码示例中的 CAudioSessionEvents 类是 IAudioSessionEvents 接口的实现。 这种特定的实现可能是控制台应用程序的一部分,该程序会将会话事件的相关信息打印到命令提示符窗口中。 由于 IAudioSessionEvents 继承自 IUnknown,因此该类的定义包含对 IUnknown 方法 AddRefReleaseQueryInterface 的实现。 类定义中的其余公开方法是 IAudioSessionEvents 接口所专用的。

某些客户可能对监控所有类型的会话事件不感兴趣。 在前面的代码示例中,CAudioSessionEvents 类中的几种通知方法没有执行任何操作。 例如,OnChannelVolumeChanged 方法除了返回状态代码 S_OK 之外,没有执行任何其他操作。 此应用程序不会监控通道音量,因为它不会更改通道音量(通过调用 IChannelAudioVolume 接口中的方法),也不会与可能更改通道音量的其他应用程序共享会话。

CAudioSessionEvents 类中通知用户会话事件的方法只有三个:OnSimpleVolumeChangedOnStateChangedOnSessionDisconnected。 例如,如果用户运行系统音量控制程序 Sndvol,并使用 Sndvol 中的音量控制来更改应用程序的音量水平,OnSimpleVolumeChanged 会立即打印出新的音量。

有关注册和取消注册客户端 IAudioSessionEvents 接口的代码示例,请参阅传统音频应用程序的音频事件

音频会话