VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostAudioDSoundMMNotifClient.cpp

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 7.2 KB
Line 
1/* $Id: DrvHostAudioDSoundMMNotifClient.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * Host audio driver - DSound - Implementation of the IMMNotificationClient interface to detect audio endpoint changes.
4 */
5
6/*
7 * Copyright (C) 2017-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include "DrvHostAudioDSoundMMNotifClient.h"
29
30#include <iprt/win/windows.h>
31#include <mmdeviceapi.h>
32#include <iprt/win/endpointvolume.h>
33#include <iprt/errcore.h>
34
35#ifdef LOG_GROUP /** @todo r=bird: wtf? Put it before all other includes like you're supposed to. */
36# undef LOG_GROUP
37#endif
38#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
39#include <VBox/log.h>
40
41
42DrvHostAudioDSoundMMNotifClient::DrvHostAudioDSoundMMNotifClient(PPDMIHOSTAUDIOPORT pInterface, bool fDefaultIn, bool fDefaultOut)
43 : m_fDefaultIn(fDefaultIn)
44 , m_fDefaultOut(fDefaultOut)
45 , m_fRegisteredClient(false)
46 , m_cRef(1)
47 , m_pIAudioNotifyFromHost(pInterface)
48{
49}
50
51DrvHostAudioDSoundMMNotifClient::~DrvHostAudioDSoundMMNotifClient(void)
52{
53}
54
55/**
56 * Registers the mulitmedia notification client implementation.
57 */
58HRESULT DrvHostAudioDSoundMMNotifClient::Register(void)
59{
60 HRESULT hr = m_pEnum->RegisterEndpointNotificationCallback(this);
61 if (SUCCEEDED(hr))
62 m_fRegisteredClient = true;
63
64 return hr;
65}
66
67/**
68 * Unregisters the mulitmedia notification client implementation.
69 */
70void DrvHostAudioDSoundMMNotifClient::Unregister(void)
71{
72 if (m_fRegisteredClient)
73 {
74 m_pEnum->UnregisterEndpointNotificationCallback(this);
75
76 m_fRegisteredClient = false;
77 }
78}
79
80/**
81 * Initializes the mulitmedia notification client implementation.
82 *
83 * @return HRESULT
84 */
85HRESULT DrvHostAudioDSoundMMNotifClient::Initialize(void)
86{
87 HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), 0, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
88 (void **)&m_pEnum);
89
90 LogFunc(("Returning %Rhrc\n", hr));
91 return hr;
92}
93
94/**
95 * Handler implementation which is called when an audio device state
96 * has been changed.
97 *
98 * @return HRESULT
99 * @param pwstrDeviceId Device ID the state is announced for.
100 * @param dwNewState New state the device is now in.
101 */
102STDMETHODIMP DrvHostAudioDSoundMMNotifClient::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState)
103{
104 const char *pszState = "unknown";
105
106 switch (dwNewState)
107 {
108 case DEVICE_STATE_ACTIVE:
109 pszState = "active";
110 break;
111 case DEVICE_STATE_DISABLED:
112 pszState = "disabled";
113 break;
114 case DEVICE_STATE_NOTPRESENT:
115 pszState = "not present";
116 break;
117 case DEVICE_STATE_UNPLUGGED:
118 pszState = "unplugged";
119 break;
120 default:
121 break;
122 }
123
124 LogRel(("Audio: Device '%ls' has changed state to '%s'\n", pwstrDeviceId, pszState));
125
126 if (m_pIAudioNotifyFromHost)
127 m_pIAudioNotifyFromHost->pfnNotifyDevicesChanged(m_pIAudioNotifyFromHost);
128
129 return S_OK;
130}
131
132/**
133 * Handler implementation which is called when a new audio device has been added.
134 *
135 * @return HRESULT
136 * @param pwstrDeviceId Device ID which has been added.
137 */
138STDMETHODIMP DrvHostAudioDSoundMMNotifClient::OnDeviceAdded(LPCWSTR pwstrDeviceId)
139{
140 LogRel(("Audio: Device '%ls' has been added\n", pwstrDeviceId));
141 /* Note! It is hard to properly support non-default devices when the backend is DSound,
142 as DSound talks GUID where-as the pwszDeviceId string we get here is something
143 completely different. So, ignorining that edge case here. The WasApi backend
144 supports this, though. */
145 if (m_pIAudioNotifyFromHost)
146 m_pIAudioNotifyFromHost->pfnNotifyDevicesChanged(m_pIAudioNotifyFromHost);
147 return S_OK;
148}
149
150/**
151 * Handler implementation which is called when an audio device has been removed.
152 *
153 * @return HRESULT
154 * @param pwstrDeviceId Device ID which has been removed.
155 */
156STDMETHODIMP DrvHostAudioDSoundMMNotifClient::OnDeviceRemoved(LPCWSTR pwstrDeviceId)
157{
158 LogRel(("Audio: Device '%ls' has been removed\n", pwstrDeviceId));
159 if (m_pIAudioNotifyFromHost)
160 m_pIAudioNotifyFromHost->pfnNotifyDevicesChanged(m_pIAudioNotifyFromHost);
161 return S_OK;
162}
163
164/**
165 * Handler implementation which is called when the device audio device has been
166 * changed.
167 *
168 * @return HRESULT
169 * @param eFlow Flow direction of the new default device.
170 * @param eRole Role of the new default device.
171 * @param pwstrDefaultDeviceId ID of the new default device.
172 */
173STDMETHODIMP DrvHostAudioDSoundMMNotifClient::OnDefaultDeviceChanged(EDataFlow eFlow, ERole eRole, LPCWSTR pwstrDefaultDeviceId)
174{
175 /* When the user triggers a default device change, we'll typically get two or
176 three notifications. Just pick up the one for the multimedia role for now
177 (dunno if DSound default equals eMultimedia or eConsole, and whether it make
178 any actual difference). */
179 if (eRole == eMultimedia)
180 {
181 PDMAUDIODIR enmDir = PDMAUDIODIR_INVALID;
182 const char *pszRole = "unknown";
183 if (eFlow == eRender)
184 {
185 pszRole = "output";
186 if (m_fDefaultOut)
187 enmDir = PDMAUDIODIR_OUT;
188 }
189 else if (eFlow == eCapture)
190 {
191 pszRole = "input";
192 if (m_fDefaultIn)
193 enmDir = PDMAUDIODIR_IN;
194 }
195
196 LogRel(("Audio: Default %s device has been changed to '%ls'\n", pszRole, pwstrDefaultDeviceId));
197
198 if (m_pIAudioNotifyFromHost)
199 {
200 if (enmDir != PDMAUDIODIR_INVALID)
201 m_pIAudioNotifyFromHost->pfnNotifyDeviceChanged(m_pIAudioNotifyFromHost, enmDir, NULL);
202 m_pIAudioNotifyFromHost->pfnNotifyDevicesChanged(m_pIAudioNotifyFromHost);
203 }
204 }
205 return S_OK;
206}
207
208STDMETHODIMP DrvHostAudioDSoundMMNotifClient::QueryInterface(REFIID interfaceID, void **ppvInterface)
209{
210 const IID MY_IID_IMMNotificationClient = __uuidof(IMMNotificationClient);
211
212 if ( IsEqualIID(interfaceID, IID_IUnknown)
213 || IsEqualIID(interfaceID, MY_IID_IMMNotificationClient))
214 {
215 *ppvInterface = static_cast<IMMNotificationClient*>(this);
216 AddRef();
217 return S_OK;
218 }
219
220 *ppvInterface = NULL;
221 return E_NOINTERFACE;
222}
223
224STDMETHODIMP_(ULONG) DrvHostAudioDSoundMMNotifClient::AddRef(void)
225{
226 return InterlockedIncrement(&m_cRef);
227}
228
229STDMETHODIMP_(ULONG) DrvHostAudioDSoundMMNotifClient::Release(void)
230{
231 long lRef = InterlockedDecrement(&m_cRef);
232 if (lRef == 0)
233 delete this;
234
235 return lRef;
236}
237
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette