Partager via


Gérer la session audio

[La fonctionnalité associée à cette page, MFPlay, est une fonctionnalité héritée. Elle a été remplacée par MediaPlayer et IMFMediaEngine. Ces fonctionnalités ont été optimisées pour Windows 10 et Windows 11. Lorsque cela est possible, Microsoft recommande vivement l'utilisation par le nouveau code de MediaPlayer et IMFMediaEngine au lieu de DirectShow. Microsoft recommande, si possible, la réécriture du code existant qui utilise les API héritées pour être à jour avec les nouvelles API.]

Cette rubrique explique comment contrôler le volume audio lors de l’utilisation de MFPlay pour la lecture audio/vidéo.

MFPlay fournit les méthodes de contrôle du volume audio pendant la lecture suivantes.

Méthode Description
IMFPMediaPlayer::SetBalance Règle les paramètres de balance entre les canaux gauche et droit.
IMFPMediaPlayer::SetMute Désactive ou active le son.
IMFPMediaPlayer::SetVolume Règle les paramètres du volume.

 

Pour comprendre le comportement de ces méthodes, vous devez connaître la terminologie de l’API Windows Audio Session (WASAPI), qui implémente les fonctionnalités audio de bas niveau utilisées par MFPlay.

Dans WASAPI, chaque flux audio appartient précisément à une session audio, qui est un groupe de flux audio connexes. En règle générale, une application gère une session audio unique, bien que les applications puissent créer plusieurs sessions. Le programme de contrôle de volume système (Sndvol) affiche un contrôle de volume pour chaque session audio. Via Sndvol, un utilisateur peut ajuster le volume d’une session audio en dehors de l’application. Le schéma suivant illustre ce processus.

diagramme montrant les flux audio passant par le contrôle de volume sur le chemin vers les haut-parleurs ; point d’application et de volume vers le contrôle de volume

Dans MFPlay, un élément multimédia peut avoir un ou plusieurs flux audio actifs (généralement un seul). En interne, MFPlay utilise le convertisseur audio de streaming (SAR) pour restituer les flux audio. Sauf si vous le configurez autrement, le SAR rejoint la session audio par défaut de l’application.

Les méthodes audio MFPlay contrôlent uniquement les flux appartenant à l’élément multimédia actuel. Ils n’affectent pas le volume pour d’autres flux appartenant à la même session audio. En ce qui concerne WASAPI, les méthodes MFPlay contrôlent les niveaux de volume par canal, et non le niveau de volume maître. Le schéma suivant illustre ce processus.

diagramme similaire au précédent, à la différence que le deuxième flux commence à l’élément multimédia, et l’application pointe vers le deuxième flux et le contrôle de volume

Il est important de comprendre certaines implications de cette fonctionnalité de MFPlay. Tout d’abord, une application peut ajuster le volume de lecture sans affecter d’autres flux audio. Vous pouvez utiliser cette fonctionnalité de MFPlay pour implémenter le mode fondu audio, en créant deux instances de l’objet MFPlay et en ajustant le volume séparément.

Si vous utilisez des méthodes MFPlay pour modifier l’état du volume ou du son, les modifications n’apparaissent pas dans Sndvol. Par exemple, vous pouvez appeler SetMute pour désactiver le son, mais Sndvol n’affichera pas la session en son désactivé. À l’inverse, si SndVol est utilisé pour ajuster le volume de session, les modifications ne seront pas répercutées dans les valeurs renvoyées par IMFPMediaPlayer::GetVolume ou IMFPMediaPlayer::GetMute.

Pour chaque instance de l’objet lecteur MFPlay, le niveau de volume effectif est égal à fPlayerVolume × fSessionVolume, où fPlayerVolume est la valeur renvoyée par GetVolume, et fSessionVolume est le volume maître pour la session.

Pour les scénarios de lecture simples, il est conseillé d’utiliser l’API WASAPI pour contrôler le volume audio de l’ensemble de la session, plutôt que d’utiliser les méthodes MFPlay.

Exemple de code

Voici une classe C++ qui gère les tâches de base dans WASAPI :

  • Contrôle du volume et de l’état du son pour la session.
  • Réception de notifications à chaque changement d’état du volume ou du son.

Déclaration de classe

La CAudioSessionVolume déclaration de classe implémente l’interface IAudioSessionEvents, qui est l’interface de rappel pour les événements de session 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;
};

Lorsque l’objet CAudioSessionVolume reçoit un événement de session audio, il publie un message de fenêtre privée à l’application. Le handle de fenêtre et le message de fenêtre sont attribués en tant que paramètres à la méthode CAudioSessionVolume::CreateInstance statique.

Obtention des pointeurs d’interface WASAPI

CAudioSessionVolume utilise deux interfaces WASAPI principales :

Pour obtenir ces interfaces, vous devez énumérer le point de terminaison audio utilisé par le SAR. Un point de terminaison audio est un périphérique matériel qui capture ou consomme des données audio. Pour la lecture audio, un point de terminaison est simplement une enceinte ou une autre sortie audio. Par défaut, le SAR utilise le point de terminaison par défaut pour le rôle de périphérique eConsole. Un rôle de périphérique est un rôle attribué pour un point de terminaison. Les rôles de périphérique sont spécifiés par l’énumération ERole, qui est documentée dans les API Core Audio.

Le code suivant montre comment énumérer le point de terminaison et obtenir les interfaces 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;
}

Contrôle du volume

Les CAudioSessionVolume méthodes qui contrôlent le volume appellent les méthodes ISimpleAudioVolume équivalentes. Par exemple, CAudioSessionVolume::SetVolume appelle ISimpleAudioVolume::SetMasterVolume, comme illustré dans le code suivant.

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

Code CAudioSessionVolume complet

Voici la liste complète des méthodes de la CAudioSessionVolume classe.

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;
}

Spécifications

MFPlay nécessite Windows 7.

Utilisation de MFPlay pour la lecture audio/vidéo