VirtualBox

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

Last change on this file since 90778 was 88853, checked in by vboxsync, 3 years ago

Audio: Reworking PDMIHOSTAUDIOPORT::pfnNotifyDevicesChanged a little, from now on it will only trigger re-enumeration (darwin still needs some adjusting). Use a timer to sligtly delay the enumeration and move it out of the re-init stream code, so that we don't redo it too frequently and that we do it even when there are active streams around. Made DrvHostAudioWasApi call pfnNotifyDevicesChanged. Reduced the DSound notification client code a little while adding pfnNotifyDeviceChanged calls to it. bugref:9890

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

© 2023 Oracle
ContactPrivacy policyTerms of Use