388 lines
9.3 KiB
C++
388 lines
9.3 KiB
C++
//------------------------------------------------------------------------------
|
|
//
|
|
// Manages the audio session.
|
|
//
|
|
// The CAudioSessionVolume class performs two functions:
|
|
//
|
|
// - Enables the application to set the volume on the audio session that
|
|
// MFPlay uses for audio playback.
|
|
//
|
|
// - Notifies the application when the session volume is changed externally
|
|
// (eg through SndVol application)
|
|
//
|
|
// This class uses two WASAPI interfaces:
|
|
//
|
|
// - IAudioSessionControl
|
|
// - ISimpleAudioVolume
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "MFPlayer.h"
|
|
#include "AudioSessionVolume.h"
|
|
|
|
// {2715279F-4139-4ba0-9CB1-B351F1B58A4A}
|
|
static const GUID AudioSessionVolumeCtx =
|
|
{ 0x2715279f, 0x4139, 0x4ba0, { 0x9c, 0xb1, 0xb3, 0x51, 0xf1, 0xb5, 0x8a, 0x4a } };
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Constructor
|
|
//-------------------------------------------------------------------
|
|
|
|
CAudioSessionVolume::CAudioSessionVolume(
|
|
UINT uNotificationMessage,
|
|
HWND hwndNotification
|
|
)
|
|
: m_cRef(1),
|
|
m_uNotificationMessage(uNotificationMessage),
|
|
m_hwndNotification(hwndNotification),
|
|
m_bNotificationsEnabled(FALSE),
|
|
m_pAudioSession(NULL),
|
|
m_pSimpleAudioVolume(NULL)
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Destructor
|
|
//-------------------------------------------------------------------
|
|
|
|
CAudioSessionVolume::~CAudioSessionVolume()
|
|
{
|
|
EnableNotifications(FALSE);
|
|
|
|
SafeRelease(&m_pAudioSession);
|
|
SafeRelease(&m_pSimpleAudioVolume);
|
|
};
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// CreateInstance
|
|
//
|
|
// Creates an instance of the CAudioSessionVolume object.
|
|
//-------------------------------------------------------------------
|
|
|
|
/* static */
|
|
HRESULT CAudioSessionVolume::CreateInstance(
|
|
UINT uNotificationMessage,
|
|
HWND hwndNotification,
|
|
CAudioSessionVolume **ppAudioSessionVolume
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CAudioSessionVolume *pAudioSessionVolume = NULL;
|
|
|
|
pAudioSessionVolume = new (std::nothrow) CAudioSessionVolume(
|
|
uNotificationMessage, hwndNotification);
|
|
|
|
if (pAudioSessionVolume == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
hr = pAudioSessionVolume->Initialize();
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
*ppAudioSessionVolume = pAudioSessionVolume;
|
|
(*ppAudioSessionVolume)->AddRef();
|
|
|
|
done:
|
|
SafeRelease(&pAudioSessionVolume);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Initialize
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
/* IUnknown methods */
|
|
|
|
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 c = InterlockedDecrement( &m_cRef );
|
|
if (c == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// EnableNotifications
|
|
//
|
|
// Enables or disables notifications from the audio session. For
|
|
// example, if the user mutes the audio through the system volume-
|
|
// control program (Sndvol), the application will be notified.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// GetVolume
|
|
//
|
|
// Gets the session volume level.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CAudioSessionVolume::GetVolume(float *pflVolume)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if ( m_pSimpleAudioVolume == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hr = m_pSimpleAudioVolume->GetMasterVolume(pflVolume);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// SetVolume
|
|
//
|
|
// Sets the session volume level.
|
|
//
|
|
// flVolume: Ranges from 0 (silent) to 1 (full volume)
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CAudioSessionVolume::SetVolume(float flVolume)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_pSimpleAudioVolume == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hr = m_pSimpleAudioVolume->SetMasterVolume(
|
|
flVolume,
|
|
&AudioSessionVolumeCtx // Event context.
|
|
);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// GetMute
|
|
//
|
|
// Gets the muting state of the session.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CAudioSessionVolume::GetMute(BOOL *pbMute)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_pSimpleAudioVolume == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hr = m_pSimpleAudioVolume->GetMute(pbMute);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// SetMute
|
|
//
|
|
// Mutes or unmutes the session audio.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CAudioSessionVolume::SetMute(BOOL bMute)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_pSimpleAudioVolume == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hr = m_pSimpleAudioVolume->SetMute(
|
|
bMute,
|
|
&AudioSessionVolumeCtx // Event context.
|
|
);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// SetDisplayName
|
|
//
|
|
// Sets the display name for the session audio.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CAudioSessionVolume::SetDisplayName(const WCHAR *wszName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_pAudioSession == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hr = m_pAudioSession->SetDisplayName(wszName, NULL);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// OnSimpleVolumeChanged
|
|
//
|
|
// Callback 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;
|
|
}
|
|
|