Share via


舊版音訊應用程式的音訊事件

傳統音訊 API,例如 DirectSound、DirectShow 和 waveOutXxx 函式可讓應用程式取得和設定音訊數據流的音量層級。 應用程式可以使用這些 API 中的音量控制功能,在其應用程式視窗中顯示音量滑桿。

在 Windows Vista 中,系統音量控制程式 Sndvol 可讓使用者控制個別應用程式的音訊音量層級。 應用程式所顯示的磁碟區滑桿應該連結到 Sndvol 中對應的磁碟區滑桿。 如果使用者透過應用程式視窗中的磁碟區滑桿調整應用程式磁碟區,則 Sndvol 中的對應磁碟區滑桿會立即移動,以指出新的磁碟區層級。 相反地,如果使用者透過 Sndvol 調整應用程式磁碟區,則應用程式視窗中的磁碟區滑桿應該會移動,以指出新的磁碟區層級。

在 Windows Vista 中,Sndvol 會立即反映應用程式透過呼叫 IDirectSoundBuffer::SetVolume 方法或 waveOutSetVolume 函式所做的磁碟區變更。 不過,當使用者透過 Sndvol 變更應用程式音量時,DirectSound 或 waveOutXxx 函式等舊版音訊 API 不提供通知應用程式的方法。 如果應用程式顯示磁碟區滑桿,但未收到磁碟區變更的通知,滑桿將無法反映使用者在 Sndvol 中所做的變更。 若要實作適當的行為,應用程式設計工具必須以某種方式補償舊版音訊 API 缺少通知。

其中一個解決方案可能是讓應用程式設定定時器,以定期提醒它檢查磁碟區層級,以查看它是否已變更。

更優雅的解決方案是讓應用程式使用核心音訊 API 的事件通知功能。 特別是,應用程式可以註冊 IAudioSessionEvents 介面,以在音量變更或其他音訊事件類型發生時接收回呼。 當磁碟區變更時,磁碟區變更回呼例程可以立即更新應用程式的磁碟區滑桿以反映變更。

下列程式代碼範例示範應用程式如何註冊以接收音量變更和其他音訊事件的通知:

//-----------------------------------------------------------
// Register the application to receive notifications when the
// volume level changes on the default process-specific audio
// session (with session GUID value GUID_NULL) on the audio
// endpoint device with the specified data-flow direction
// (eRender or eCapture) and device role.
//-----------------------------------------------------------
#define EXIT_ON_ERROR(hr)  \
              if (FAILED(hr)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

class AudioVolumeEvents
{
    HRESULT _hrStatus;
    IAudioSessionManager *_pManager;
    IAudioSessionControl *_pControl;
    IAudioSessionEvents *_pAudioEvents;
public:
    AudioVolumeEvents(EDataFlow, ERole, IAudioSessionEvents*);
    ~AudioVolumeEvents();
    HRESULT GetStatus() { return _hrStatus; };
};

// Constructor
AudioVolumeEvents::AudioVolumeEvents(EDataFlow flow, ERole role,
                                     IAudioSessionEvents *pAudioEvents)
{
    IMMDeviceEnumerator *pEnumerator = NULL;
    IMMDevice *pDevice = NULL;

    _hrStatus = S_OK;
    _pManager = NULL;
    _pControl = NULL;
    _pAudioEvents = pAudioEvents;

    if (_pAudioEvents == NULL)
    {
        _hrStatus = E_POINTER;
        return;
    }

    _pAudioEvents->AddRef();

    // Get the enumerator for the audio endpoint devices
    // on this system.
    _hrStatus = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                 NULL, CLSCTX_INPROC_SERVER,
                                 __uuidof(IMMDeviceEnumerator),
                                 (void**)&pEnumerator);
    EXIT_ON_ERROR(_hrStatus)

    // Get the audio endpoint device with the specified data-flow
    // direction (eRender or eCapture) and device role.
    _hrStatus = pEnumerator->GetDefaultAudioEndpoint(flow, role,
                                                     &pDevice);
    EXIT_ON_ERROR(_hrStatus)

    // Get the session manager for the endpoint device.
    _hrStatus = pDevice->Activate(__uuidof(IAudioSessionManager),
                                  CLSCTX_INPROC_SERVER, NULL,
                                  (void**)&_pManager);
    EXIT_ON_ERROR(_hrStatus)

    // Get the control interface for the process-specific audio
    // session with session GUID = GUID_NULL. This is the session
    // that an audio stream for a DirectSound, DirectShow, waveOut,
    // or PlaySound application stream belongs to by default.
    _hrStatus = _pManager->GetAudioSessionControl(NULL, 0, &_pControl);
    EXIT_ON_ERROR(_hrStatus)

    _hrStatus = _pControl->RegisterAudioSessionNotification(_pAudioEvents);
    EXIT_ON_ERROR(_hrStatus)

Exit:
    SAFE_RELEASE(pEnumerator)
    SAFE_RELEASE(pDevice)
}

// Destructor
AudioVolumeEvents::~AudioVolumeEvents()
{
    if (_pControl != NULL)
    {
        _pControl->UnregisterAudioSessionNotification(_pAudioEvents);
    }
    SAFE_RELEASE(_pManager)
    SAFE_RELEASE(_pControl)
    SAFE_RELEASE(_pAudioEvents)
};

上述程式代碼範例會實作名為 AudioVolumeEvents 的類別。 在程式初始化期間,音訊應用程式會藉由建立 AudioVolumeEvents 物件來啟用音訊事件通知。 這個類別的建構函式接受三個輸入參數:

  • flow—音訊端點裝置數據流方向(EDataFlow 列舉值)。
  • role—端點裝置的目前 裝置角色ERole 列舉值)。
  • pAudioEvents- 物件的指標( IAudioSessionEvents 介面實例),其中包含應用程式所實作的一組回呼例程。

建構函式會將流程和角色值當做輸入參數 提供給IMMDeviceEnumerator::GetDefaultAudioEndpoint 方法。 方法會 建立 IMMDevice 物件,該物件會封裝具有指定數據流方向和裝置角色的音訊端點裝置。

應用程式會實作 pAudioEvents 所 指向的物件。 (上述程式代碼範例中未顯示 實作。如需實作 IAudioSessionEvents 介面的程式代碼範例,請參閱 音訊會話事件。這個介面中的每個方法都會接收特定音訊事件的通知。 如果應用程式對特定事件類型不感興趣,則該事件類型的方法應該不會執行任何動作,但傳回S_OK。

IAudioSessionEvents::OnSimpleVolumeChanged 方法會收到磁碟區變更的通知。 通常,此方法會更新應用程式的磁碟區滑桿。

在上述程式代碼範例中,AudioVolumeEvents 類別的建構函式會在會話 GUID 值所識別的進程特定 音訊會話 上註冊通知,GUID_NULL。 根據預設,DirectSound、DirectShow 和 waveOutXxx 函式等舊版音訊 API 會將其串流指派給此會話。 不過,DirectSound 或 DirectShow 應用程式可以選擇覆寫預設行為,並將其串流指派給跨進程會話,或指派給 GUID_NULL guiD 值以外的 GUID 值所識別的會話。 (目前未提供任何機制給 waveOutXxx 應用程式以類似的方式覆寫預設行為。如需具有此行為之 DirectShow 應用程式的程式代碼範例,請參閱 DirectShow 應用程式的裝置角色。 若要容納這類應用程式,您可以修改上述程式代碼範例中的建構函式,以接受兩個額外的輸入參數:會話 GUID 和旗標,以指出要監視的會話是跨進程或進程特定的會話。 將這些參數傳遞至建構函式中 IAudioSessionManager::GetAudioSessionControl 方法的呼叫

建構函式呼叫 IAudioSessionControl::RegisterAudioSessionNotification 方法來註冊通知之後,只要 IAudioSessionControlIAudioSessionManager 介面存在,應用程式才會繼續接收通知。 上述程式代碼範例中的 AudioVolumeEvents 物件會保留這些介面的參考,直到呼叫其解構函式為止。 此行為可確保應用程式會繼續接收 AudioVolumeEvents 物件的存留期通知。

DirectSound 或舊版 Windows 多媒體應用程式可能會允許使用者從易記名稱識別的可用裝置清單中明確選取裝置,而不是隱含選取音訊裝置。 若要支援此行為,必須修改上述程式代碼範例,以產生所選裝置的音訊事件通知。 需要兩個修改。 首先,變更建構函式定義以接受 端點標識符字串 做為輸入參數(取代程式代碼範例中的流程和角色參數)。 此字串會識別對應至所選 DirectSound 或舊版超聲波裝置的音訊端點裝置。 其次,將 IMMDeviceEnumerator::GetDefaultAudioEndpoint 方法的呼叫取代為 IMMDeviceEnumerator::GetDevice 方法的呼叫。 GetDevice 呼叫會採用端點標識符字串作為輸入參數,並建立字串所識別的端點裝置實例。

取得 DirectSound 裝置或舊版電壓裝置端點標識符字串的技術如下。

首先,在裝置列舉期間,DirectSound 會為每個列舉裝置提供端點標識符字串。 為了開始裝置列舉,應用程式會將回呼函式指標當做輸入參數傳遞至 DirectSoundCreateDirectSoundCaptureCreate 函式。 回呼函式的定義如下:

BOOL DSEnumCallback(
  LPGUID  lpGuid,
  LPCSTR  lpcstrDescription,
  LPCSTR  lpcstrModule,
  LPVOID  lpContext
);

在 Windows Vista 中 ,lpcstrModule 參數會指向端點標識符字串。 (在舊版的 Windows 中,包括 Windows Server 2003、Windows XP 和 Windows 2000,lpcstrModule 參數指向裝置的驅動程式模組名稱。lpcstrDescription 參數會指向包含裝置易記名稱的字串。 如需 DirectSound 裝置列舉的詳細資訊,請參閱 Windows SDK 檔。

其次,若要取得舊版超聲波裝置的端點標識符字串,請使用 waveOutMessage 或 waveInMessage 函式,將DRV_QUERYFUNCTIONINSTANCEID訊息傳送至超聲波設備驅動器。 如需顯示使用此訊息的程式代碼範例,請參閱 舊版 Windows 多媒體應用程式的裝置角色。

與舊版音訊 API 的互操作性