次の方法で共有


オーディオ セッションの管理

[このページに関連付けられている機能である MFPlay はレガシ機能です。 これは MediaPlayer および IMFMediaEngine に置き換えられました。 これらの機能は、Windows 10 および Windows 11 用に最適化されています。 Microsoftでは、新しいコードで可能な限り DirectShow ではなく MediaPlayerIMFMediaEngine を使用することを強くお勧めします。 Microsoft は、レガシ API を使用する既存コードを、新しい API を使用するように可能であれば書き直すことを提案しています。]

このトピックでは、MFPlay を使用してオーディオ/ビデオを再生するときにオーディオ ボリュームを制御する方法について説明します。

MFPlay には、再生中にオーディオ ボリュームを制御する次のメソッドが用意されています。

Method 説明
IMFPMediaPlayer::SetBalance 左右のチャンネル間のバランスを設定します。
IMFPMediaPlayer::SetMute オーディオをミュートまたはミュート解除します。
IMFPMediaPlayer::SetVolume ボリューム レベルを設定します。

 

これらのメソッドの動作を理解するには、MFPlay で使用される低レベルのオーディオ機能を実装する Windows Audio Session API (WASAPI) の用語をいくつか知っておく必要があります。

WASAPI では、すべてのオーディオ ストリームは、関連するオーディオ ストリームのグループである 1 つのオーディオ セッションに属します。 通常、アプリケーションは 1 つのオーディオ セッションを維持しますが、複数のセッションを作成することもできます。 システム ボリューム制御プログラム (Sndvol) には、各オーディオ セッションのボリューム コントロールが表示されます。 ユーザーは Sndvol を使用して、アプリケーションの外部からオーディオ セッションの音量を調整できます。 次の図は、このプロセスを示しています。

オーディオ ストリームがボリューム コントロールを通過してスピーカーに到達する様子を示す図。アプリケーションと sndvol はボリューム コントロールを指しています。

MFPlay では、メディア項目に 1 つ以上のアクティブなオーディオ ストリームを含めることができます (通常は 1 つだけ)。 内部的には、MFPlay はSストリーミング オーディオ レンダラー (SAR) を使用してオーディオ ストリームをレンダリングします。 特に構成しない限り、SAR はアプリケーションの既定のオーディオ セッションに参加します。

MFPlay オーディオ メソッドは、現在のメディア項目に属するストリームのみを制御します。 同じオーディオ セッションに属する他のストリームのボリュームには影響しません。 WASAPI に関しては、MFPlay メソッドはマスター ボリューム レベルではなく、チャネルごとにボリューム レベルを制御します。 次の図は、このプロセスを示しています。

前の図と似ていますが、2 番目のストリームはメディアアイテムから始まり、アプリケーションは 2 番目のストリームとボリュームコントロールを指します。

MFPlay のこの機能の影響を理解することが重要です。 まず、アプリケーションは、他のオーディオ ストリームに影響を与えずに再生ボリュームを調整できます。 MFPlay の場合、この機能を使用して、MFPlay オブジェクトのインスタンスを 2 つ作成し、音量を個別に調整することで、オーディオのクロスフェードを実装できます。

MFPlay メソッドを使用してボリュームまたはミュート状態を変更した場合、変更は Sndvol に表示されません。 たとえば、SetMute を呼び出してオーディオをミュートできますが、Sndvol はセッションをミュートとして表示しません。 逆に、SndVol を使用してセッション ボリュームを調整する場合、IMFPMediaPlayer::GetVolume または IMFPMediaPlayer::GetMute によって返される値に変更は反映されません。

MFPlay プレーヤー オブジェクトの各インスタンスについて、有効ボリューム レベルは fPlayerVolume × fSessionVolume と等しくなります。ここで、fPlayerVolumeGetVolume によって返される値であり、fSessionVolume はセッションのマスター ボリュームです。

単純な再生シナリオでは、MFPlay メソッドを使用するのではなく、WASAPI を使用してセッション全体のオーディオ ボリュームを制御することをお勧めします。

コード例

WASAPI の基本的なタスクを処理する C++ クラスを次に示します。

  • セッションのボリュームとミュート状態を制御する。
  • ボリュームまたはミュート状態が変化するたびに通知を受け取ります。

クラス宣言

CAudioSessionVolume クラス宣言は、オーディオ セッション イベントのコールバック インターフェイスである IAudioSessionEvents インターフェイスを実装します。

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 オブジェクトは、オーディオ セッション イベントを受信すると、プライベート ウィンドウ メッセージをアプリケーションに送信します。 ウィンドウ ハンドルとウィンドウ メッセージは、静的 CAudioSessionVolume::CreateInstance メソッドのパラメーターとして指定されます。

WASAPI インターフェイス ポインターの取得

CAudioSessionVolume では、次の 2 つの主要な WASAPI インターフェイスが使用されます。

これらのインターフェイスを取得するには、SAR によって使用されるオーディオ エンドポイントを列挙する必要があります。 オーディオ エンドポイントは、オーディオ データをキャプチャまたは使用するハードウェア デバイスです。 オーディオ再生の場合、エンドポイントは単なるスピーカーまたはその他のオーディオ出力です。 既定では、SAR は eConsole デバイス ロールの既定のエンドポイントを使用します。 デバイス ロールは、エンドポイントに割り当てられたロールです。 デバイス ロールは、ERole 列挙体によって指定されます。これは、 Core Audio API に記載されています。

次のコードは、エンドポイントを列挙し、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;
}

ボリュームの制御

オーディオ ボリュームを制御する CAudioSessionVolume メソッドは、同等の ISimpleAudioVolume メソッドを呼び出します。 たとえば、次のコードに示すように、CAudioSessionVolume::SetVolumeISimpleAudioVolume::SetMasterVolume を呼び出します。

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

完全な CAudioSessionVolume コード

次に、CAudioSessionVolume クラスのメソッドの完全なリストを示します。

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

要件

MFPlay には Windows 7 が必要です。

オーディオとビデオの再生に MFPlay を使用する