2025-11-28 00:35:46 +09:00

229 lines
6.1 KiB
C++

// 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 "stdafx.h"
#include "osd.h"
#include "endpointMonitor.h"
CVolumeMonitor::CVolumeMonitor()
: m_bRegisteredForEndpointNotifications(FALSE),
m_bRegisteredForVolumeNotifications(FALSE),
m_cRef(1)
{
}
CVolumeMonitor::~CVolumeMonitor()
{
}
// ----------------------------------------------------------------------
// Call when the app is done with this object before calling release.
// This detaches from the endpoint and releases all audio service references.
//
// ----------------------------------------------------------------------
void CVolumeMonitor::Dispose()
{
DetachFromEndpoint();
if (m_bRegisteredForEndpointNotifications)
{
m_spEnumerator->UnregisterEndpointNotificationCallback(this);
m_bRegisteredForEndpointNotifications = FALSE;
}
}
// ----------------------------------------------------------------------
// Initialize this object. Call after constructor.
//
// ----------------------------------------------------------------------
HRESULT CVolumeMonitor::Initialize()
{
HRESULT hr;
// create enumerator
hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator));
if (SUCCEEDED(hr))
{
hr = m_spEnumerator->RegisterEndpointNotificationCallback(this);
if (SUCCEEDED(hr))
{
hr = AttachToDefaultEndpoint();
}
}
return hr;
}
// ----------------------------------------------------------------------
// Called from the UI thread when the volume is changed (see OSD.cpp
// WM_VOLUMECHANGE handler)
//
// ----------------------------------------------------------------------
HRESULT CVolumeMonitor::GetLevelInfo(VOLUME_INFO* pInfo)
{
HRESULT hr = E_FAIL;
m_csEndpoint.Enter();
if (m_spVolumeControl != NULL)
{
hr = m_spVolumeControl->GetMute(&pInfo->bMuted);
if (SUCCEEDED(hr))
{
hr = m_spVolumeControl->GetVolumeStepInfo(&pInfo->nStep, &pInfo->cSteps);
}
}
m_csEndpoint.Leave();
return hr;
}
// ----------------------------------------------------------------------
// Start monitoring the current default device
//
// ----------------------------------------------------------------------
HRESULT CVolumeMonitor::AttachToDefaultEndpoint()
{
m_csEndpoint.Enter();
// get the default music & movies playback device
HRESULT hr = m_spEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &m_spAudioEndpoint);
if (SUCCEEDED(hr))
{
// get the volume control for it
hr = m_spAudioEndpoint->Activate(__uuidof(m_spVolumeControl), CLSCTX_INPROC_SERVER, NULL, (void**)&m_spVolumeControl);
if (SUCCEEDED(hr))
{
// register for callbacks
hr = m_spVolumeControl->RegisterControlChangeNotify(this);
m_bRegisteredForVolumeNotifications = SUCCEEDED(hr);
}
}
m_csEndpoint.Leave();
return hr;
}
// ----------------------------------------------------------------------
// Stop monitoring the device and release all associated references
//
// ----------------------------------------------------------------------
void CVolumeMonitor::DetachFromEndpoint()
{
m_csEndpoint.Enter();
if (m_spVolumeControl != NULL)
{
// be sure to unregister...
if (m_bRegisteredForVolumeNotifications)
{
m_spVolumeControl->UnregisterControlChangeNotify(this);
m_bRegisteredForVolumeNotifications = FALSE;
}
m_spVolumeControl.Release();
}
if (m_spAudioEndpoint != NULL)
{
m_spAudioEndpoint.Release();
}
m_csEndpoint.Leave();
}
// ----------------------------------------------------------------------
// Call this from the UI thread when the default device changes
//
// ----------------------------------------------------------------------
void CVolumeMonitor::ChangeEndpoint()
{
DetachFromEndpoint();
AttachToDefaultEndpoint();
}
// ----------------------------------------------------------------------
// Implementation of IMMNotificationClient::OnDefaultDeviceChanged
//
// When the user changes the default output device we want to stop monitoring the
// former default and start monitoring the new default
//
// ----------------------------------------------------------------------
HRESULT CVolumeMonitor::OnDefaultDeviceChanged
(
EDataFlow flow,
ERole /*role*/,
LPCWSTR /*pwstrDefaultDeviceId*/
)
{
if (flow == eRender)
{
if (g_hwndOSD != NULL)
PostMessage(g_hwndOSD, WM_ENDPOINTCHANGE, 0, 0);
}
// return value of this callback is ignored
return S_OK;
}
// ----------------------------------------------------------------------
// Implementation of IAudioEndpointVolumeCallback::OnNotify
//
// This is called by the audio core when anyone in any process changes the volume or
// mute state for the endpoint we are monitoring
//
// ----------------------------------------------------------------------
HRESULT CVolumeMonitor::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA /*pNotify*/)
{
if (g_hwndOSD != NULL)
PostMessage(g_hwndOSD, WM_VOLUMECHANGE, 0, 0);
return S_OK;
}
// IUnknown methods
HRESULT CVolumeMonitor::QueryInterface(REFIID iid, void** ppUnk)
{
if ((iid == __uuidof(IUnknown)) ||
(iid == __uuidof(IMMNotificationClient)))
{
*ppUnk = static_cast<IMMNotificationClient*>(this);
}
else if (iid == __uuidof(IAudioEndpointVolumeCallback))
{
*ppUnk = static_cast<IAudioEndpointVolumeCallback*>(this);
}
else
{
*ppUnk = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG CVolumeMonitor::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
ULONG CVolumeMonitor::Release()
{
long lRef = InterlockedDecrement(&m_cRef);
if (lRef == 0)
{
delete this;
}
return lRef;
}