オーディオ セッションの管理
[このページに関連付けられている機能である MFPlay はレガシ機能です。 これは MediaPlayer および IMFMediaEngine に置き換えられました。 これらの機能は、Windows 10 および Windows 11 用に最適化されています。 Microsoftでは、新しいコードで可能な限り DirectShow ではなく MediaPlayer と IMFMediaEngine を使用することを強くお勧めします。 Microsoft は、レガシ API を使用する既存コードを、新しい API を使用するように可能であれば書き直すことを提案しています。]
このトピックでは、MFPlay を使用してオーディオ/ビデオを再生するときにオーディオ ボリュームを制御する方法について説明します。
MFPlay には、再生中にオーディオ ボリュームを制御する次のメソッドが用意されています。
Method | 説明 |
---|---|
IMFPMediaPlayer::SetBalance | 左右のチャンネル間のバランスを設定します。 |
IMFPMediaPlayer::SetMute | オーディオをミュートまたはミュート解除します。 |
IMFPMediaPlayer::SetVolume | ボリューム レベルを設定します。 |
これらのメソッドの動作を理解するには、MFPlay で使用される低レベルのオーディオ機能を実装する Windows Audio Session API (WASAPI) の用語をいくつか知っておく必要があります。
WASAPI では、すべてのオーディオ ストリームは、関連するオーディオ ストリームのグループである 1 つのオーディオ セッションに属します。 通常、アプリケーションは 1 つのオーディオ セッションを維持しますが、複数のセッションを作成することもできます。 システム ボリューム制御プログラム (Sndvol) には、各オーディオ セッションのボリューム コントロールが表示されます。 ユーザーは Sndvol を使用して、アプリケーションの外部からオーディオ セッションの音量を調整できます。 次の図は、このプロセスを示しています。
MFPlay では、メディア項目に 1 つ以上のアクティブなオーディオ ストリームを含めることができます (通常は 1 つだけ)。 内部的には、MFPlay はSストリーミング オーディオ レンダラー (SAR) を使用してオーディオ ストリームをレンダリングします。 特に構成しない限り、SAR はアプリケーションの既定のオーディオ セッションに参加します。
MFPlay オーディオ メソッドは、現在のメディア項目に属するストリームのみを制御します。 同じオーディオ セッションに属する他のストリームのボリュームには影響しません。 WASAPI に関しては、MFPlay メソッドはマスター ボリューム レベルではなく、チャネルごとにボリューム レベルを制御します。 次の図は、このプロセスを示しています。
MFPlay のこの機能の影響を理解することが重要です。 まず、アプリケーションは、他のオーディオ ストリームに影響を与えずに再生ボリュームを調整できます。 MFPlay の場合、この機能を使用して、MFPlay オブジェクトのインスタンスを 2 つ作成し、音量を個別に調整することで、オーディオのクロスフェードを実装できます。
MFPlay メソッドを使用してボリュームまたはミュート状態を変更した場合、変更は Sndvol に表示されません。 たとえば、SetMute を呼び出してオーディオをミュートできますが、Sndvol はセッションをミュートとして表示しません。 逆に、SndVol を使用してセッション ボリュームを調整する場合、IMFPMediaPlayer::GetVolume または IMFPMediaPlayer::GetMute によって返される値に変更は反映されません。
MFPlay プレーヤー オブジェクトの各インスタンスについて、有効ボリューム レベルは fPlayerVolume × fSessionVolume と等しくなります。ここで、fPlayerVolume は GetVolume によって返される値であり、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 インターフェイスが使用されます。
- IAudioSessionControl はオーディオ セッションを管理します。
- ISimpleAudioVolume は、セッションのボリューム レベルとミュート状態を制御します。
これらのインターフェイスを取得するには、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::SetVolume
ISimpleAudioVolume::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 が必要です。
関連トピック