ReactOS 0.4.17-dev-769-g1500a35
mediabtns.cpp
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS Explorer
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: Backend class for media buttons
5 * COPYRIGHT: Copyright 2026 Vitaly Orekhov <vkvo2000@vivaldi.net>
6 */
7
8#include "precomp.h"
9#include <math.h>
10#include "mediabtns.h"
11
13{
14 HMODULE hWinMM = LoadLibraryW(L"winmm.dll");
15 bool bFailedGetProcAddress = false;
16
17 if (!hWinMM)
18 return;
19
20 bFailedGetProcAddress |= !WINMM_PROC(mixerOpen, pmxOpen);
21 bFailedGetProcAddress |= !WINMM_PROC(mixerClose, pmxClose);
22 bFailedGetProcAddress |= !WINMM_PROC(mixerSetControlDetails, pmxControlDetails);
23 bFailedGetProcAddress |= !WINMM_PROC(mixerGetControlDetailsW, pmxControlDetails);
24 bFailedGetProcAddress |= !WINMM_PROC(mixerGetID, pmxGetID);
25 bFailedGetProcAddress |= !WINMM_PROC(mixerGetLineInfoW, pmxLine);
26 bFailedGetProcAddress |= !WINMM_PROC(mixerGetLineControlsW, pmxLineControls);
27 bFailedGetProcAddress |= !WINMM_PROC(waveOutGetErrorTextW, pwoGetErrText);
28 bFailedGetProcAddress |= !WINMM_PROC(waveOutGetNumDevs, pwoGetNumDevs);
29 bFailedGetProcAddress |= !WINMM_PROC(waveOutMessage, pwoMessage);
30
31 if (bFailedGetProcAddress)
32 {
33 ERR("CMultimediaBackend::ctor: One or more entry points to WinMM functions not found\n");
34 FreeLibrary(hWinMM);
35 return;
36 }
37
38 m_hWinMM = hWinMM;
39};
40
42{
43 if (m_hMixer)
45
47
49}
50
51void
53{
54 if (m_hMixer)
56 m_hMixer = NULL;
57
61
62 if (waveOutGetNumDevs() == 0)
63 {
64 ERR("CMultimediaBackend::Initialize: No waveform-audio output devices detected\n");
65 return;
66 }
67
68 DWORD dwWaveOutDeviceID;
69 DWORD dw2 = 0;
70
72 (DWORD_PTR)&dwWaveOutDeviceID, (DWORD_PTR)&dw2);
73 if (mmres != MMSYSERR_NOERROR)
74 LogError(__func__, "waveOutMessage failed", mmres);
75
76 if (dwWaveOutDeviceID == (DWORD)-1)
77 {
78 TRACE("CMultimediaBackend::Initialize: No default output device was assigned, falling back to the first device\n");
79 dwWaveOutDeviceID = 0;
80 }
81
82 UINT mxID;
83 mmres = mixerGetID((HMIXEROBJ)UlongToHandle(dwWaveOutDeviceID), &mxID, MIXER_OBJECTF_WAVEOUT);
84 if (mmres != MMSYSERR_NOERROR)
85 {
86 LogError(__func__, "mixerGetID failed", mmres);
87
88 if (mmres == MMSYSERR_NODRIVER)
89 {
90 ERR(" Not providing fallback to the first mixer device.\n");
91 return;
92 }
93
94 mxID = 0;
95 }
96
97 mmres = mixerOpen(&m_hMixer, mxID, NULL, 0, MIXER_OBJECTF_MIXER);
98 if (mmres != MMSYSERR_NOERROR)
99 {
100 m_hMixer = NULL;
101 LogError(__func__, "mixerOpen failed", mmres);
102 return;
103 }
104
105 MIXERLINEW mxln;
106 mxln.cbStruct = sizeof(mxln);
108
110 if (mmres != MMSYSERR_NOERROR)
111 {
112 LogError(__func__, "mixerGetLineInfoW failed", mmres);
113 return;
114 }
115
117
118 MIXERLINECONTROLS mxlctrl;
119 MIXERCONTROLW mxc;
120 mxlctrl.cbStruct = sizeof(mxlctrl);
121 mxlctrl.dwLineID = mxln.dwLineID;
123 mxlctrl.cControls = 1;
124 mxlctrl.cbmxctrl = sizeof(mxc);
125 mxlctrl.pamxctrl = &mxc;
126
128 if (mmres != MMSYSERR_NOERROR)
129 {
130 LogError(__func__, "mixerGetLineControlsW failed retrieving Master Mute control", mmres);
131 return;
132 }
133
135
137
139 if (mmres != MMSYSERR_NOERROR)
140 {
141 LogError(__func__, "mixerGetLineControlsW failed retrieving Master Volume control", mmres);
142 return;
143 }
144
146 m_dwMasterRanges.dwMinimum = mxc.Bounds.dwMinimum;
147 m_dwMasterRanges.dwMaximum = mxc.Bounds.dwMaximum;
148
150
151 TRACE("CMultimediaBackend::Initialize: Master controls configured successfully:\n"
152 " Master Mute dwControlID: %lu; Master Volume dwControlID %lu\n"
153 " Master Volume channels: %lu; Master Volume value range: %lu-%lu\n",
156}
157
158bool
160{
161 if (!m_hWinMM)
162 {
163 WARN("CMultimediaBackend::Mute: WinMM functions unavailable; cannot (un)mute sound. Check the prior class constructor calls\n");
164 return false;
165 }
166
169 mxcd.cbStruct = sizeof(mxcd);
171 mxcd.cChannels = 1;
172 mxcd.cMultipleItems = 0;
173 mxcd.cbDetails = sizeof(mxcdMute);
174 mxcd.paDetails = &mxcdMute;
175
177 mxcdMute.fValue = !mxcdMute.fValue;
179
180 return true;
181}
182
183bool
185{
186 if (!m_hWinMM)
187 {
188 WARN("CMultimediaBackend: WinMM functions unavailable; cannot %s volume. Check the prior class constructor calls\n",
189 direction == APPCOMMAND_VOLUME_DOWN ? "decrease" : "increase");
190 return FALSE;
191 }
192
193 MMRESULT mmres;
194
196 mxcd.cbStruct = sizeof(mxcd);
197 mxcd.cMultipleItems = 0;
198
199 /* If sound is muted and we want to increase volume, we are unmuting it. */
200 if (direction == APPCOMMAND_VOLUME_UP)
201 {
203 mxcd.cChannels = 1;
205 mxcd.cbDetails = sizeof(mxcdMute);
206 mxcd.paDetails = &mxcdMute;
207 mxcdMute.fValue = false;
208
210 if (mmres != MMSYSERR_NOERROR)
211 LogError(__func__, "mixerSetControlDetails failed unmuting Master Volume", mmres);
212 }
213
215 return FALSE;
216
218
219 bool bEitherCeiling = [&]()
220 {
221 DWORD dwValue = m_dwMasterRanges.dwMinimum;
222
223 for (DWORD i = 0; i < m_dwMasterChannels; ++i)
224 {
225 if (mxcdVolume[i].dwValue > dwValue)
226 dwValue = mxcdVolume[i].dwValue;
227 }
228
229 return direction == APPCOMMAND_VOLUME_DOWN
230 ? dwValue == m_dwMasterRanges.dwMinimum
231 : dwValue == m_dwMasterRanges.dwMaximum;
232 }();
233
234 if (bEitherCeiling)
235 {
236 TRACE("CMultimediaBackend::AdjustVolume: Volume boundary hit, no action\n");
237 goto cleanup;
238 }
239
240 CalculateSteps(mxcdVolume);
241
242 for (DWORD i = 0; i < m_dwMasterChannels; ++i)
243 {
244 if (direction == APPCOMMAND_VOLUME_DOWN)
245 {
246 /* Protecting from underflow when decreasing volume */
247 if (mxcdVolume[i].dwValue < m_pdwChannelSteps[i])
248 mxcdVolume[i].dwValue = m_dwMasterRanges.dwMinimum;
249 else
250 mxcdVolume[i].dwValue -= m_pdwChannelSteps[i];
251 }
252 else if (direction == APPCOMMAND_VOLUME_UP)
253 {
254 /* Protecting from overflow when increasing volume */
255 if (m_dwMasterRanges.dwMaximum - mxcdVolume[i].dwValue < m_pdwChannelSteps[i])
256 mxcdVolume[i].dwValue = m_dwMasterRanges.dwMaximum;
257 else
258 mxcdVolume[i].dwValue += m_pdwChannelSteps[i];
259 }
260 }
261
263 if (mmres == MMSYSERR_NOERROR)
264 {
265 TRACE("CMultimediaBackend::AdjustVolume: Volume values were updated:\n");
266 for (DWORD i = 0; i < m_dwMasterChannels; ++i)
267 TRACE(" Channel %lu volume: %lu\n", i, mxcdVolume[i].dwValue);
268 }
269 else
270 {
271 LogError(__func__, "mixerSetControlDetails failed updating Master Volume control details", mmres);
272 }
273
274cleanup:
275 HeapFree(hProcessHeap, 0, mxcdVolume);
276 return TRUE;
277}
278
279void
281{
282 DWORD dwHighestVolume = m_dwMasterRanges.dwMinimum;
283 DWORD dwLoudestChIdx = 0;
284
285 /* Find the loudest channel of all available */
286 for (DWORD i = 0; i < m_dwMasterChannels; ++i)
287 {
288 if (pmxcdVolume[i].dwValue < dwHighestVolume)
289 continue;
290
291 dwLoudestChIdx = i;
292 dwHighestVolume = pmxcdVolume[dwLoudestChIdx].dwValue;
293 }
294
295 /* Calculate steps for uneven channels */
296 for (DWORD i = 0; i < m_dwMasterChannels; ++i)
297 {
298 /* As we rely on MIXERLINEW.cChannels, there may be a case of multiple channels having equal high volume. */
299 if (dwHighestVolume == m_dwMasterRanges.dwMinimum)
300 {
301 TRACE("CMultimediaBackend::CalculateSteps: All channels were at minimum volume, steps will be unchanged\n");
302 break;
303 }
304 else if (i == dwLoudestChIdx || (pmxcdVolume[i].dwValue == dwHighestVolume))
305 {
307 TRACE("CMultimediaBackend::CalculateSteps: Channel %lu is the loudest, volume step will be %lu\n", i, m_pdwChannelSteps[i]);
308 }
309 else
310 {
311 if (dwHighestVolume > 0)
312 {
313 float dwStepRatio = (float)pmxcdVolume[i].dwValue / dwHighestVolume;
314 m_pdwChannelSteps[i] = ceil(dwStepRatio * m_pdwChannelSteps[dwLoudestChIdx]);
315 }
316 else
317 {
318 m_pdwChannelSteps[i] = 0;
319 }
320 TRACE("CMultimediaBackend::CalculateSteps: Channel %lu volume step will be %lu\n", i, m_pdwChannelSteps[i]);
321 }
322 }
323}
324
325void
326CMultimediaBackend::LogError(_In_ const char* pszFunctionName, _In_ const char* pszMessage, _In_ MMRESULT mmres)
327{
328 static WCHAR pszWinMMErrText[MAXERRORLENGTH] = {0};
329 waveOutGetErrorTextW(mmres, pszWinMMErrText, _countof(pszWinMMErrText));
330 ERR("CMultimediaBackend::%s: %: %lu (%S)\n", pszFunctionName, pszMessage, mmres, pszWinMMErrText);
331}
332
335{
338 m_dwMasterChannels * sizeof(*mxcdVolume));
339
340 if (mxcdVolume == NULL)
341 {
342 ERR("CMultimediaBackend::GetCurrentVolume: HeapAlloc failed, requested %lu bytes\n", m_dwMasterChannels * sizeof(*mxcdVolume));
343 return MMSYSERR_NOMEM;
344 }
345
346 pmxcd->cChannels = m_dwMasterChannels;
347 pmxcd->dwControlID = m_dwMasterVolumeID;
348 pmxcd->cbDetails = sizeof(*mxcdVolume);
349 pmxcd->paDetails = mxcdVolume;
350
352}
#define WARN(fmt,...)
Definition: precomp.h:61
#define ERR(fmt,...)
Definition: precomp.h:57
#define UlongToHandle(ul)
Definition: basetsd.h:91
HMODULE m_hWinMM
Definition: mediabtns.h:54
bool AdjustVolume(_In_ UINT direction, _In_ HANDLE hProcessHeap)
Definition: mediabtns.cpp:184
pmxControlDetails mixerGetControlDetailsW
Definition: mediabtns.h:44
pmxLineControls mixerGetLineControlsW
Definition: mediabtns.h:47
pmxControlDetails mixerSetControlDetails
Definition: mediabtns.h:43
DWORD m_dwMasterChannels
Definition: mediabtns.h:58
pwoGetNumDevs waveOutGetNumDevs
Definition: mediabtns.h:51
DWORD m_dwMasterVolumeID
Definition: mediabtns.h:57
pwoMessage waveOutMessage
Definition: mediabtns.h:52
void LogError(_In_ const char *pszFunctionName, _In_ const char *pszMessage, _In_ MMRESULT mmres)
Definition: mediabtns.cpp:326
DWORD m_dwMasterMuteControlID
Definition: mediabtns.h:56
pmxLine mixerGetLineInfoW
Definition: mediabtns.h:46
PDWORD m_pdwChannelSteps
Definition: mediabtns.h:64
MMRESULT GetCurrentVolume(_Inout_ PMIXERCONTROLDETAILS pmxcd)
Definition: mediabtns.cpp:334
pmxGetID mixerGetID
Definition: mediabtns.h:45
struct CMultimediaBackend::@96 m_dwMasterRanges
pwoGetErrText waveOutGetErrorTextW
Definition: mediabtns.h:50
pmxOpen mixerOpen
Definition: mediabtns.h:48
void CalculateSteps(_Inout_ PMIXERCONTROLDETAILS_UNSIGNED pmxcdVolume)
Definition: mediabtns.cpp:280
pmxClose mixerClose
Definition: mediabtns.h:49
_In_ ULONG _In_ BOOLEAN LogError
Definition: classpnp.h:1311
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define HeapAlloc
Definition: compat.h:733
#define FreeLibrary(x)
Definition: compat.h:748
#define HeapFree(x, y, z)
Definition: compat.h:735
#define LoadLibraryW(x)
Definition: compat.h:747
static void cleanup(void)
Definition: main.c:1335
_ACRTIMP double __cdecl ceil(double)
Definition: ceil.c:18
#define L(x)
Definition: resources.c:13
HANDLE hProcessHeap
Definition: explorer.cpp:25
unsigned long DWORD
Definition: ntddk_ex.h:95
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
unsigned int UINT
Definition: sysinfo.c:13
MMRESULT(WINAPI * pmxGetID)(HMIXEROBJ hmxobj, UINT *puMxId, DWORD fdwId)
Definition: mediabtns.h:11
#define WINMM_PROC(func, type)
Definition: mediabtns.h:19
MMRESULT(WINAPI * pmxLine)(HMIXEROBJ hmxobj, LPMIXERLINEW pmxcd, DWORD fdwDetails)
Definition: mediabtns.h:12
MMRESULT(WINAPI * pmxLineControls)(HMIXEROBJ hmxobj, LPMIXERLINECONTROLSW pmxcd, DWORD fdwDetails)
Definition: mediabtns.h:13
MMRESULT(WINAPI * pwoMessage)(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dw1, DWORD_PTR dw2)
Definition: mediabtns.h:18
MMRESULT(WINAPI * pwoGetErrText)(MMRESULT mmrError, LPWSTR pszText, UINT cchText)
Definition: mediabtns.h:16
MMRESULT(WINAPI * pmxClose)(HMIXER hmx)
Definition: mediabtns.h:15
MMRESULT(WINAPI * pmxOpen)(LPHMIXER phmx, UINT uMxId, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)
Definition: mediabtns.h:14
MMRESULT(WINAPI * pwoGetNumDevs)(VOID)
Definition: mediabtns.h:17
#define VOLUME_STEPS_COUNT
Definition: mediabtns.h:22
MMRESULT(WINAPI * pmxControlDetails)(HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails)
Definition: mediabtns.h:10
#define DRVM_MAPPER_PREFERRED_GET
Definition: mmsys.h:39
#define MMSYSERR_NOMEM
Definition: mmsystem.h:103
#define MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
Definition: mmsystem.h:319
#define MIXER_OBJECTF_MIXER
Definition: mmsystem.h:300
struct tMIXERCONTROLDETAILS_UNSIGNED * PMIXERCONTROLDETAILS_UNSIGNED
UINT MMRESULT
Definition: mmsystem.h:964
#define MIXER_OBJECTF_WAVEOUT
Definition: mmsystem.h:302
#define MIXER_SETCONTROLDETAILSF_VALUE
Definition: mmsystem.h:415
#define MIXER_GETCONTROLDETAILSF_VALUE
Definition: mmsystem.h:412
#define MMSYSERR_NODRIVER
Definition: mmsystem.h:102
#define WAVE_MAPPER
Definition: mmsystem.h:187
#define MIXERCONTROL_CONTROLTYPE_VOLUME
Definition: mmsystem.h:398
#define MIXER_GETLINEINFOF_COMPONENTTYPE
Definition: mmsystem.h:347
#define MMSYSERR_NOERROR
Definition: mmsystem.h:96
#define MIXER_GETLINECONTROLSF_ONEBYTYPE
Definition: mmsystem.h:410
#define MIXER_OBJECTF_HMIXER
Definition: mmsystem.h:301
#define MAXERRORLENGTH
Definition: mmsystem.h:25
#define MIXERCONTROL_CONTROLTYPE_MUTE
Definition: mmsystem.h:384
static float(__cdecl *square_half_float)(float x
direction
Definition: netio.c:882
#define _Inout_
Definition: no_sal2.h:162
#define _In_
Definition: no_sal2.h:158
short WCHAR
Definition: pedump.c:58
DWORD * PDWORD
Definition: pedump.c:68
#define _countof(array)
Definition: sndvol32.h:70
#define TRACE(s)
Definition: solgame.cpp:4
union tagMIXERCONTROLW::@3447 Bounds
LPMIXERCONTROLA pamxctrl
Definition: mmsystem.h:1340
DWORD dwLineID
Definition: mmsystem.h:1260
DWORD cChannels
Definition: mmsystem.h:1264
DWORD cbStruct
Definition: mmsystem.h:1257
DWORD dwComponentType
Definition: mmsystem.h:1263
uint32_t DWORD_PTR
Definition: typedefs.h:65