Bagikan melalui


Mengelola Sesi Audio

[Fitur yang terkait dengan halaman ini, MFPlay, adalah fitur warisan. Ini telah digantikan oleh MediaPlayer dan IMFMediaEngine. Fitur-fitur tersebut telah dioptimalkan untuk Windows 10 dan Windows 11. Microsoft sangat menyarankan agar kode baru menggunakan MediaPlayer dan IMFMediaEngine alih-alih DirectShow, jika memungkinkan. Microsoft menyarankan agar kode yang ada yang menggunakan API warisan ditulis ulang untuk menggunakan API baru jika memungkinkan.]

Topik ini menjelaskan cara mengontrol volume audio saat menggunakan MFPlay untuk pemutaran audio/video.

MFPlay menyediakan metode berikut untuk mengontrol volume audio selama pemutaran.

Metode Deskripsi
IMFPMediaPlayer::SetBalance Mengatur keseimbangan antara saluran kiri dan kanan.
IMFPMediaPlayer::SetMute Mematikan suara atau menyalakan suara audio.
IMFPMediaPlayer::SetVolume Mengatur tingkat volume.

 

Untuk memahami perilaku metode ini, Anda harus mengetahui beberapa terminologi dari WINDOWS Audio Session API (WASAPI), yang mengimplementasikan fungsionalitas audio tingkat rendah yang digunakan oleh MFPlay.

Di WASAPI, setiap aliran audio milik tepat satu sesi audio, yang merupakan sekelompok aliran audio terkait. Biasanya, aplikasi mempertahankan satu sesi audio, meskipun aplikasi dapat membuat lebih dari satu sesi. Program kontrol volume sistem (Sndvol) menampilkan kontrol volume untuk setiap sesi audio. Melalui Sndvol, pengguna dapat menyesuaikan volume sesi audio dari luar aplikasi. Gambar berikut mengilustrasikan proses ini.

diagram memperlihatkan aliran audio melewati kontrol volume dalam perjalanan ke speaker; aplikasi dan sndvol menunjuk ke kontrol volume

Di MFPlay, item media dapat memiliki satu atau beberapa aliran audio aktif (biasanya hanya satu). Secara internal, MFPlay menggunakan Streaming Audio Renderer (SAR) untuk merender aliran audio. Kecuali Anda mengonfigurasinya sebaliknya, SAR bergabung dengan sesi audio default aplikasi.

Metode audio MFPlay hanya mengontrol aliran yang termasuk dalam item media saat ini. Mereka tidak memengaruhi volume untuk aliran lain yang termasuk dalam sesi audio yang sama. Dalam hal WASAPI, metode MFPlay mengontrol tingkat volume per saluran , bukan tingkat volume master. Gambar berikut mengilustrasikan proses ini.

diagram yang mirip dengan yang sebelumnya, tetapi aliran kedua dimulai pada item media, dan aplikasi menunjuk ke aliran kedua dan ke kontrol volume

Penting untuk memahami beberapa implikasi dari fitur MFPlay ini. Pertama, aplikasi dapat menyesuaikan volume pemutaran tanpa memengaruhi aliran audio lainnya. Anda dapat menggunakan fitur ini jika MFPlay untuk mengimplementasikan cross-fading audio, dengan membuat dua instans objek MFPlay dan menyesuaikan volume secara terpisah.

Jika Anda menggunakan metode MFPlay untuk mengubah volume atau status bisukan, perubahan tidak muncul di Sndvol. Misalnya, Anda dapat memanggil SetMute untuk mematikan suara audio, tetapi Sndvol tidak akan menampilkan sesi sebagai dibisukan. Sebaliknya, jika SndVol digunakan untuk menyesuaikan volume sesi, perubahan tidak tercermin dalam nilai yang dikembalikan oleh IMFPMediaPlayer::GetVolume atau IMFPMediaPlayer::GetMute.

Untuk setiap instans objek pemutar MFPlay, tingkat volume efektif sama dengan fPlayerVolume × fSessionVolume, di mana fPlayerVolume adalah nilai yang dikembalikan oleh GetVolume, dan fSessionVolume adalah volume master untuk sesi tersebut.

Untuk skenario pemutaran sederhana, mungkin lebih baik menggunakan WASAPI untuk mengontrol volume audio untuk seluruh sesi, daripada menggunakan metode MFPlay.

Contoh Kode

Berikut ini adalah kelas C++ yang menangani tugas dasar di WASAPI:

  • Mengontrol volume dan membisukan status untuk sesi.
  • Mendapatkan pemberitahuan setiap kali volume atau status bisukan berubah.

Deklarasi Kelas

CAudioSessionVolume Deklarasi kelas mengimplementasikan antarmuka IAudioSessionEvents, yang merupakan antarmuka panggilan balik untuk peristiwa sesi audio.

class CAudioSessionVolume : public IAudioSessionEvents
{
public:
    // Static method to create an instance of the object.
    static HRESULT CreateInstance(
        UINT uNotificationMessage,
        HWND hwndNotification,
        CAudioSessionVolume **ppAudioSessionVolume
    );

    // IUnknown methods.
    STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();

    // IAudioSessionEvents methods.

    STDMETHODIMP OnSimpleVolumeChanged(
        float NewVolume,
        BOOL NewMute,
        LPCGUID EventContext
        );

    // The remaining audio session events do not require any action.
    STDMETHODIMP OnDisplayNameChanged(LPCWSTR,LPCGUID)
    {
        return S_OK;
    }

    STDMETHODIMP OnIconPathChanged(LPCWSTR,LPCGUID)
    {
        return S_OK;
    }

    STDMETHODIMP OnChannelVolumeChanged(DWORD,float[],DWORD,LPCGUID)
    {
        return S_OK;
    }

    STDMETHODIMP OnGroupingParamChanged(LPCGUID,LPCGUID)
    {
        return S_OK;
    }

    STDMETHODIMP OnStateChanged(AudioSessionState)
    {
        return S_OK;
    }

    STDMETHODIMP OnSessionDisconnected(AudioSessionDisconnectReason)
    {
        return S_OK;
    }

    // Other methods
    HRESULT EnableNotifications(BOOL bEnable);
    HRESULT GetVolume(float *pflVolume);
    HRESULT SetVolume(float flVolume);
    HRESULT GetMute(BOOL *pbMute);
    HRESULT SetMute(BOOL bMute);
    HRESULT SetDisplayName(const WCHAR *wszName);

protected:
    CAudioSessionVolume(UINT uNotificationMessage, HWND hwndNotification);
    ~CAudioSessionVolume();

    HRESULT Initialize();

protected:
    LONG m_cRef;                        // Reference count.
    UINT m_uNotificationMessage;        // Window message to send when an audio event occurs.
    HWND m_hwndNotification;            // Window to receives messages.
    BOOL m_bNotificationsEnabled;       // Are audio notifications enabled?

    IAudioSessionControl    *m_pAudioSession;
    ISimpleAudioVolume      *m_pSimpleAudioVolume;
};

CAudioSessionVolume Saat objek menerima peristiwa sesi audio, objek memposting pesan jendela privat ke aplikasi. Handel jendela dan pesan jendela diberikan sebagai parameter ke metode statis CAudioSessionVolume::CreateInstance .

Mendapatkan Penunjuk Antarmuka WASAPI

CAudioSessionVolume menggunakan dua antarmuka WASAPI utama:

Untuk mendapatkan antarmuka ini, Anda harus menghitung titik akhir audio yang digunakan oleh SAR. Titik akhir audio adalah perangkat keras yang mengambil atau menggunakan data audio. Untuk pemutaran audio, titik akhir hanyalah speaker atau output audio lainnya. Secara default, SAR menggunakan titik akhir default untuk peran perangkat eConsole . Peran perangkat adalah peran yang ditetapkan untuk titik akhir. Peran perangkat ditentukan oleh enumerasi ERole , yang didokumenkan dalam API Core Audio.

Kode berikut menunjukkan cara menghitung titik akhir dan mendapatkan antarmuka WASAPI.

HRESULT CAudioSessionVolume::Initialize()
{
    HRESULT hr = S_OK;

    IMMDeviceEnumerator *pDeviceEnumerator = NULL;
    IMMDevice *pDevice = NULL;
    IAudioSessionManager *pAudioSessionManager = NULL;

    // Get the enumerator for the audio endpoint devices.
    hr = CoCreateInstance(
        __uuidof(MMDeviceEnumerator),
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&pDeviceEnumerator)
        );

    if (FAILED(hr))
    {
        goto done;
    }

    // Get the default audio endpoint that the SAR will use.
    hr = pDeviceEnumerator->GetDefaultAudioEndpoint(
        eRender,
        eConsole,   // The SAR uses 'eConsole' by default.
        &pDevice
        );

    if (FAILED(hr))
    {
        goto done;
    }

    // Get the session manager for this device.
    hr = pDevice->Activate(
        __uuidof(IAudioSessionManager),
        CLSCTX_INPROC_SERVER,
        NULL,
        (void**) &pAudioSessionManager
        );

    if (FAILED(hr))
    {
        goto done;
    }

    // Get the audio session.
    hr = pAudioSessionManager->GetAudioSessionControl(
        &GUID_NULL,     // Get the default audio session.
        FALSE,          // The session is not cross-process.
        &m_pAudioSession
        );


    if (FAILED(hr))
    {
        goto done;
    }

    hr = pAudioSessionManager->GetSimpleAudioVolume(
        &GUID_NULL, 0, &m_pSimpleAudioVolume
        );

done:
    SafeRelease(&pDeviceEnumerator);
    SafeRelease(&pDevice);
    SafeRelease(&pAudioSessionManager);
    return hr;
}

Mengontrol Volume

Metode CAudioSessionVolume yang mengontrol volume audio memanggil metode ISimpleAudioVolume yang setara. Misalnya, CAudioSessionVolume::SetVolume memanggil ISimpleAudioVolume::SetMasterVolume, seperti yang ditunjukkan dalam kode berikut.

HRESULT CAudioSessionVolume::SetVolume(float flVolume)
{
    if (m_pSimpleAudioVolume == NULL)
    {
        return E_FAIL;
    }
    else
    {
        return m_pSimpleAudioVolume->SetMasterVolume(
            flVolume,
            &AudioSessionVolumeCtx  // Event context.
            );
    }
}

Kode CAudioSessionVolume Lengkap

Berikut adalah daftar lengkap untuk metode CAudioSessionVolume kelas.

static const GUID AudioSessionVolumeCtx =
{ 0x2715279f, 0x4139, 0x4ba0, { 0x9c, 0xb1, 0xb3, 0x51, 0xf1, 0xb5, 0x8a, 0x4a } };


CAudioSessionVolume::CAudioSessionVolume(
    UINT uNotificationMessage,
    HWND hwndNotification
    )
    : m_cRef(1),
      m_uNotificationMessage(uNotificationMessage),
      m_hwndNotification(hwndNotification),
      m_bNotificationsEnabled(FALSE),
      m_pAudioSession(NULL),
      m_pSimpleAudioVolume(NULL)
{
}

CAudioSessionVolume::~CAudioSessionVolume()
{
    EnableNotifications(FALSE);

    SafeRelease(&m_pAudioSession);
    SafeRelease(&m_pSimpleAudioVolume);
};


//  Creates an instance of the CAudioSessionVolume object.

/* static */
HRESULT CAudioSessionVolume::CreateInstance(
    UINT uNotificationMessage,
    HWND hwndNotification,
    CAudioSessionVolume **ppAudioSessionVolume
    )
{

    CAudioSessionVolume *pAudioSessionVolume = new (std::nothrow)
        CAudioSessionVolume(uNotificationMessage, hwndNotification);

    if (pAudioSessionVolume == NULL)
    {
        return E_OUTOFMEMORY;
    }

    HRESULT hr = pAudioSessionVolume->Initialize();
    if (SUCCEEDED(hr))
    {
        *ppAudioSessionVolume = pAudioSessionVolume;
    }
    else
    {
        pAudioSessionVolume->Release();
    }

    return hr;
}


//  Initializes the CAudioSessionVolume object.

HRESULT CAudioSessionVolume::Initialize()
{
    HRESULT hr = S_OK;

    IMMDeviceEnumerator *pDeviceEnumerator = NULL;
    IMMDevice *pDevice = NULL;
    IAudioSessionManager *pAudioSessionManager = NULL;

    // Get the enumerator for the audio endpoint devices.
    hr = CoCreateInstance(
        __uuidof(MMDeviceEnumerator),
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&pDeviceEnumerator)
        );

    if (FAILED(hr))
    {
        goto done;
    }

    // Get the default audio endpoint that the SAR will use.
    hr = pDeviceEnumerator->GetDefaultAudioEndpoint(
        eRender,
        eConsole,   // The SAR uses 'eConsole' by default.
        &pDevice
        );

    if (FAILED(hr))
    {
        goto done;
    }

    // Get the session manager for this device.
    hr = pDevice->Activate(
        __uuidof(IAudioSessionManager),
        CLSCTX_INPROC_SERVER,
        NULL,
        (void**) &pAudioSessionManager
        );

    if (FAILED(hr))
    {
        goto done;
    }

    // Get the audio session.
    hr = pAudioSessionManager->GetAudioSessionControl(
        &GUID_NULL,     // Get the default audio session.
        FALSE,          // The session is not cross-process.
        &m_pAudioSession
        );


    if (FAILED(hr))
    {
        goto done;
    }

    hr = pAudioSessionManager->GetSimpleAudioVolume(
        &GUID_NULL, 0, &m_pSimpleAudioVolume
        );

done:
    SafeRelease(&pDeviceEnumerator);
    SafeRelease(&pDevice);
    SafeRelease(&pAudioSessionManager);
    return hr;
}

STDMETHODIMP CAudioSessionVolume::QueryInterface(REFIID riid, void **ppv)
{
    static const QITAB qit[] =
    {
        QITABENT(CAudioSessionVolume, IAudioSessionEvents),
        { 0 },
    };
    return QISearch(this, qit, riid, ppv);
}

STDMETHODIMP_(ULONG) CAudioSessionVolume::AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CAudioSessionVolume::Release()
{
    LONG cRef = InterlockedDecrement( &m_cRef );
    if (cRef == 0)
    {
        delete this;
    }
    return cRef;
}


// Enables or disables notifications from the audio session. For example, the
// application is notified if the user mutes the audio through the system
// volume-control program (Sndvol).

HRESULT CAudioSessionVolume::EnableNotifications(BOOL bEnable)
{
    HRESULT hr = S_OK;

    if (m_hwndNotification == NULL || m_pAudioSession == NULL)
    {
        return E_FAIL;
    }

    if (m_bNotificationsEnabled == bEnable)
    {
        // No change.
        return S_OK;
    }

    if (bEnable)
    {
        hr = m_pAudioSession->RegisterAudioSessionNotification(this);
    }
    else
    {
        hr = m_pAudioSession->UnregisterAudioSessionNotification(this);
    }

    if (SUCCEEDED(hr))
    {
        m_bNotificationsEnabled = bEnable;
    }

    return hr;
}


// Gets the session volume level.

HRESULT CAudioSessionVolume::GetVolume(float *pflVolume)
{
    if ( m_pSimpleAudioVolume == NULL)
    {
        return E_FAIL;
    }
    else
    {
        return m_pSimpleAudioVolume->GetMasterVolume(pflVolume);
    }
}

//  Sets the session volume level.
//
//  flVolume: Ranges from 0 (silent) to 1 (full volume)

HRESULT CAudioSessionVolume::SetVolume(float flVolume)
{
    if (m_pSimpleAudioVolume == NULL)
    {
        return E_FAIL;
    }
    else
    {
        return m_pSimpleAudioVolume->SetMasterVolume(
            flVolume,
            &AudioSessionVolumeCtx  // Event context.
            );
    }
}


//  Gets the muting state of the session.

HRESULT CAudioSessionVolume::GetMute(BOOL *pbMute)
{
    if (m_pSimpleAudioVolume == NULL)
    {
        return E_FAIL;
    }
    else
    {
        return m_pSimpleAudioVolume->GetMute(pbMute);
    }
}

//  Mutes or unmutes the session audio.

HRESULT CAudioSessionVolume::SetMute(BOOL bMute)
{
    if (m_pSimpleAudioVolume == NULL)
    {
        return E_FAIL;
    }
    else
    {
        return m_pSimpleAudioVolume->SetMute(
            bMute,
            &AudioSessionVolumeCtx  // Event context.
            );
    }
}

//  Sets the display name for the session audio.

HRESULT CAudioSessionVolume::SetDisplayName(const WCHAR *wszName)
{
    if (m_pAudioSession == NULL)
    {
        return E_FAIL;
    }
    else
    {
        return m_pAudioSession->SetDisplayName(wszName, NULL);
    }
}


//  Called when the session volume level or muting state changes.
//  (Implements IAudioSessionEvents::OnSimpleVolumeChanged.)

HRESULT CAudioSessionVolume::OnSimpleVolumeChanged(
    float NewVolume,
    BOOL NewMute,
    LPCGUID EventContext
    )
{
    // Check if we should post a message to the application.

    if ( m_bNotificationsEnabled &&
        (*EventContext != AudioSessionVolumeCtx) &&
        (m_hwndNotification != NULL)
        )
    {
        // Notifications are enabled, AND
        // We did not trigger the event ourselves, AND
        // We have a valid window handle.

        // Post the message.
        ::PostMessage(
            m_hwndNotification,
            m_uNotificationMessage,
            *((WPARAM*)(&NewVolume)),  // Coerce the float.
            (LPARAM)NewMute
            );
    }
    return S_OK;
}

Persyaratan

MFPlay memerlukan Windows 7.

Menggunakan MFPlay untuk Pemutaran Audio/Video