18#include <mmdeviceapi.h>
21#include <QtCore/qmap.h>
22#include <private/qcomobject_p.h>
23#include <private/qsystemerror_p.h>
29 ComPtr<IMMDeviceEnumerator> m_enumerator;
31 QMap<QString, DWORD> m_deviceState;
35 ComPtr<IMMDeviceEnumerator> enumerator,
36 QMap<QString, DWORD> &&deviceState)
37 : m_enumerator(enumerator),
38 m_windowsMediaDevices(windowsMediaDevices),
39 m_deviceState(deviceState)
44 if (role == ERole::eMultimedia)
53 if (
it == std::end(m_deviceState)) {
64 auto it = m_deviceState.find(
key);
65 if (
it != std::end(m_deviceState)) {
66 if (
it.value() == DEVICE_STATE_ACTIVE)
68 m_deviceState.remove(
key);
78 if ((
it.value() == DEVICE_STATE_ACTIVE) != (
newState == DEVICE_STATE_ACTIVE)) {
95 if (flow == EDataFlow::eCapture) {
97 }
else if (flow == EDataFlow::eRender) {
105 ComPtr<IMMEndpoint> endpoint;
108 if (SUCCEEDED(m_enumerator->GetDevice(deviceID,
device.GetAddressOf()))
109 && SUCCEEDED(
device->QueryInterface(__uuidof(IMMEndpoint), (
void**)endpoint.GetAddressOf()))
110 && SUCCEEDED(endpoint->GetDataFlow(&flow)))
124 auto hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
nullptr,
125 CLSCTX_INPROC_SERVER,__uuidof(IMMDeviceEnumerator),
126 (
void**)&m_deviceEnumerator);
129 qWarning(
"Failed to instantiate IMMDeviceEnumerator (%s)."
130 "Audio device change notification will be disabled",
131 qPrintable(QSystemError::windowsComString(hr)));
135 QMap<QString, DWORD> devState;
136 ComPtr<IMMDeviceCollection> devColl;
139 if (SUCCEEDED(m_deviceEnumerator->EnumAudioEndpoints(EDataFlow::eAll, DEVICE_STATEMASK_ALL, devColl.GetAddressOf()))
140 && SUCCEEDED(devColl->GetCount(&
count)))
145 QComTaskResource<WCHAR>
id;
147 if (SUCCEEDED(devColl->Item(
i,
device.GetAddressOf()))
149 && SUCCEEDED(
device->GetId(
id.address()))) {
156 m_notificationClient = makeComObject<CMMNotificationClient>(
this, m_deviceEnumerator, std::move(devState));
157 m_deviceEnumerator->RegisterEndpointNotificationCallback(m_notificationClient.Get());
162 if (m_deviceEnumerator) {
166 m_deviceEnumerator->UnregisterEndpointNotificationCallback(m_notificationClient.Get());
168 if (m_warmUpAudioClient) {
169 HRESULT hr = m_warmUpAudioClient->Stop();
171 qWarning() <<
"Failed to stop audio engine" << hr;
175 m_deviceEnumerator.Reset();
176 m_notificationClient.Reset();
177 m_warmUpAudioClient.Reset();
182 if (!m_deviceEnumerator)
187 const auto defaultAudioDeviceID = [
this, audioOut]{
188 const auto dataFlow = audioOut ? EDataFlow::eRender : EDataFlow::eCapture;
189 ComPtr<IMMDevice> dev;
190 QComTaskResource<WCHAR>
id;
193 if (SUCCEEDED(m_deviceEnumerator->GetDefaultAudioEndpoint(dataFlow, ERole::eMultimedia, dev.GetAddressOf()))) {
194 if (dev && SUCCEEDED(dev->GetId(
id.address()))) {
203 auto waveDevices = audioOut ? waveOutGetNumDevs() : waveInGetNumDevs();
205 for (
auto waveID = 0u; waveID < waveDevices; waveID++) {
206 auto wave = IntToPtr(waveID);
207 auto waveMessage = [wave, audioOut](UINT msg,
auto p0,
auto p1) {
213 if (waveMessage(DRV_QUERYFUNCTIONINSTANCEIDSIZE, &
len, 0) != MMSYSERR_NOERROR)
216 QVarLengthArray<WCHAR>
id(
len);
217 if (waveMessage(DRV_QUERYFUNCTIONINSTANCEID,
id.
data(),
len) != MMSYSERR_NOERROR)
221 ComPtr<IPropertyStore>
props;
222 if (FAILED(m_deviceEnumerator->GetDevice(
id.data(),
device.GetAddressOf()))
223 || FAILED(
device->OpenPropertyStore(STGM_READ,
props.GetAddressOf()))) {
228 PropVariantInit(&varName);
235 dev->isDefault = strID == defaultAudioDeviceID;
239 PropVariantClear(&varName);
271 static bool isDisableAudioPrepareSet =
false;
272 static const int disableAudioPrepare =
275 return !isDisableAudioPrepareSet || disableAudioPrepare == 0;
283 if (m_isAudioClientWarmedUp.exchange(
true))
286 ComPtr<IMMDeviceEnumerator> deviceEnumerator;
287 HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
nullptr, CLSCTX_ALL,
288 __uuidof(IMMDeviceEnumerator),
289 reinterpret_cast<void **
>(deviceEnumerator.GetAddressOf()));
291 qWarning() <<
"Failed to create device enumerator" << hr;
296 hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole,
device.GetAddressOf());
298 if (hr != E_NOTFOUND)
299 qWarning() <<
"Failed to retrieve default audio endpoint" << hr;
303 hr =
device->Activate(__uuidof(IAudioClient3), CLSCTX_ALL,
nullptr,
304 reinterpret_cast<void **
>(m_warmUpAudioClient.GetAddressOf()));
306 qWarning() <<
"Failed to activate audio engine" << hr;
310 QComTaskResource<WAVEFORMATEX> deviceFormat;
311 UINT32 currentPeriodInFrames = 0;
312 hr = m_warmUpAudioClient->GetCurrentSharedModeEnginePeriod(deviceFormat.address(),
313 ¤tPeriodInFrames);
315 qWarning() <<
"Failed to retrieve the current format and periodicity of the audio engine"
320 UINT32 defaultPeriodInFrames = 0;
321 UINT32 fundamentalPeriodInFrames = 0;
322 UINT32 minPeriodInFrames = 0;
323 UINT32 maxPeriodInFrames = 0;
324 hr = m_warmUpAudioClient->GetSharedModeEnginePeriod(deviceFormat.get(), &defaultPeriodInFrames,
325 &fundamentalPeriodInFrames,
326 &minPeriodInFrames, &maxPeriodInFrames);
328 qWarning() <<
"Failed to retrieve the range of periodicities supported by the audio engine"
333 hr = m_warmUpAudioClient->InitializeSharedAudioStream(
334 AUDCLNT_SHAREMODE_SHARED, minPeriodInFrames, deviceFormat.get(),
nullptr);
336 qWarning() <<
"Failed to initialize audio engine stream" << hr;
340 hr = m_warmUpAudioClient->Start();
342 qWarning() <<
"Failed to start audio engine" << hr;
IOBluetoothDevice * device
void emitAudioDevicesChanged(EDataFlow flow)
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR) override
void emitAudioDevicesChanged(LPCWSTR deviceID)
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR deviceID, DWORD newState) override
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR deviceID) override
CMMNotificationClient(QWindowsMediaDevices *windowsMediaDevices, ComPtr< IMMDeviceEnumerator > enumerator, QMap< QString, DWORD > &&deviceState)
~CMMNotificationClient() override=default
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) override
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR deviceID) override
The QAudioDevice class provides an information about audio devices and their functionality.
Mode
Describes the mode of this device.
const QAudioDevicePrivate * handle() const
QObject * parent() const
Returns a pointer to the parent object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
QSet< QString >::iterator it
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
Combined button and popup list for selecting options.
static QDBusError::ErrorType get(const char *name)
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLsizei const GLenum * props
#define qPrintable(string)
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
const PROPERTYKEY QMM_PKEY_Device_FriendlyName