Commit | Line | Data |
---|---|---|
31c28665 EP |
1 | /* -*- tab-width: 8; c-basic-offset: 4 -*- */ |
2 | ||
491502b9 AJ |
3 | /* |
4 | * Sample MIXER Wine Driver for Linux | |
5 | * | |
31c28665 | 6 | * Copyright 1997 Marcus Meissner |
2f9eb3b0 | 7 | * 1999,2001 Eric Pouech |
0799c1a7 AJ |
8 | * |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, write to the Free Software | |
360a3f91 | 21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
491502b9 AJ |
22 | */ |
23 | ||
1876d185 EP |
24 | /* TODO: |
25 | * + implement notification mechanism when state of mixer's controls | |
26 | */ | |
27 | ||
2c92835f | 28 | #include "config.h" |
49b7fdcf | 29 | #include "wine/port.h" |
2c92835f | 30 | |
491502b9 | 31 | #include <stdlib.h> |
e37c6e18 | 32 | #include <stdarg.h> |
383da68c | 33 | #include <stdio.h> |
ee517e86 | 34 | #include <string.h> |
d016f819 PS |
35 | #ifdef HAVE_UNISTD_H |
36 | # include <unistd.h> | |
37 | #endif | |
491502b9 | 38 | #include <fcntl.h> |
31031aba | 39 | #include <errno.h> |
2f9eb3b0 | 40 | #include <assert.h> |
5e6943f4 PS |
41 | #ifdef HAVE_SYS_IOCTL_H |
42 | # include <sys/ioctl.h> | |
43 | #endif | |
44 | ||
297f3d89 DP |
45 | #define NONAMELESSUNION |
46 | #define NONAMELESSSTRUCT | |
2e1cafa2 | 47 | #include "windef.h" |
e37c6e18 | 48 | #include "winbase.h" |
b4e49557 | 49 | #include "winnls.h" |
37c0217d EP |
50 | #include "mmddk.h" |
51 | #include "oss.h" | |
b4e49557 | 52 | #include "wine/unicode.h" |
0799c1a7 | 53 | #include "wine/debug.h" |
491502b9 | 54 | |
5ae2835e | 55 | WINE_DEFAULT_DEBUG_CHANNEL(mixer); |
b4b9fae6 | 56 | |
31c28665 | 57 | #ifdef HAVE_OSS |
aa67ac90 | 58 | |
5ae2835e RR |
59 | #define MAX_MIXERDRV (6) |
60 | ||
31c28665 | 61 | #define WINE_MIXER_MANUF_ID 0xAA |
54e4ba04 | 62 | #define WINE_MIXER_PRODUCT_ID 0x55 |
31c28665 EP |
63 | #define WINE_MIXER_VERSION 0x0100 |
64 | #define WINE_MIXER_NAME "WINE OSS Mixer" | |
65 | ||
54e4ba04 EP |
66 | #define WINE_CHN_MASK(_x) (1L << (_x)) |
67 | #define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x)) | |
909b677e | 68 | /* Bass and Treble are no longer in the mask as Windows does not handle them */ |
2f9eb3b0 | 69 | #define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \ |
54e4ba04 EP |
70 | WINE_CHN_MASK(SOUND_MIXER_PCM) | \ |
71 | WINE_CHN_MASK(SOUND_MIXER_LINE) | \ | |
72 | WINE_CHN_MASK(SOUND_MIXER_MIC) | \ | |
2f9eb3b0 EP |
73 | WINE_CHN_MASK(SOUND_MIXER_CD) ) |
74 | ||
75 | #define WINE_MIXER_MASK_RECORD (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \ | |
76 | WINE_CHN_MASK(SOUND_MIXER_LINE) | \ | |
77 | WINE_CHN_MASK(SOUND_MIXER_MIC) | \ | |
78 | WINE_CHN_MASK(SOUND_MIXER_IMIX) ) | |
54e4ba04 | 79 | |
aa67ac90 EP |
80 | /* FIXME: the two following string arrays should be moved to a resource file in a string table */ |
81 | /* if it's done, better use a struct to hold labels, name, and muted channel volume cache */ | |
b0517348 DT |
82 | static const char * const MIX_Labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; |
83 | static const char * const MIX_Names [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; | |
2f9eb3b0 EP |
84 | |
85 | struct mixerCtrl | |
86 | { | |
87 | DWORD dwLineID; | |
b4e49557 | 88 | MIXERCONTROLW ctrl; |
2f9eb3b0 EP |
89 | }; |
90 | ||
9a624916 | 91 | struct mixer |
2f9eb3b0 | 92 | { |
b15504e1 RR |
93 | char* name; |
94 | char* dev_name; | |
2f9eb3b0 EP |
95 | int volume[SOUND_MIXER_NRDEVICES]; |
96 | int devMask; | |
97 | int stereoMask; | |
98 | int recMask; | |
99 | BOOL singleRecChannel; | |
100 | struct mixerCtrl* ctrl; | |
101 | int numCtrl; | |
102 | }; | |
103 | ||
104 | #define LINEID_DST 0xFFFF | |
105 | #define LINEID_SPEAKER 0x0000 | |
106 | #define LINEID_RECORD 0x0001 | |
107 | ||
108 | static int MIX_NumMixers; | |
5ae2835e | 109 | static struct mixer MIX_Mixers[MAX_MIXERDRV]; |
2f9eb3b0 | 110 | |
6f4be549 RR |
111 | /************************************************************************** |
112 | */ | |
113 | ||
114 | static const char * getMessage(UINT uMsg) | |
115 | { | |
116 | static char str[64]; | |
117 | #define MSG_TO_STR(x) case x: return #x; | |
118 | switch (uMsg) { | |
119 | MSG_TO_STR(DRVM_INIT); | |
120 | MSG_TO_STR(DRVM_EXIT); | |
121 | MSG_TO_STR(DRVM_ENABLE); | |
122 | MSG_TO_STR(DRVM_DISABLE); | |
123 | MSG_TO_STR(MXDM_GETDEVCAPS); | |
124 | MSG_TO_STR(MXDM_GETLINEINFO); | |
125 | MSG_TO_STR(MXDM_GETNUMDEVS); | |
126 | MSG_TO_STR(MXDM_OPEN); | |
127 | MSG_TO_STR(MXDM_CLOSE); | |
128 | MSG_TO_STR(MXDM_GETLINECONTROLS); | |
129 | MSG_TO_STR(MXDM_GETCONTROLDETAILS); | |
130 | MSG_TO_STR(MXDM_SETCONTROLDETAILS); | |
131 | } | |
132 | #undef MSG_TO_STR | |
133 | sprintf(str, "UNKNOWN(%08x)", uMsg); | |
134 | return str; | |
135 | } | |
136 | ||
137 | static const char * getIoctlCommand(int command) | |
138 | { | |
139 | static char str[64]; | |
140 | #define IOCTL_TO_STR(x) case x: return #x; | |
141 | switch (command) { | |
6f4be549 RR |
142 | IOCTL_TO_STR(SOUND_MIXER_VOLUME); |
143 | IOCTL_TO_STR(SOUND_MIXER_BASS); | |
144 | IOCTL_TO_STR(SOUND_MIXER_TREBLE); | |
145 | IOCTL_TO_STR(SOUND_MIXER_SYNTH); | |
146 | IOCTL_TO_STR(SOUND_MIXER_PCM); | |
147 | IOCTL_TO_STR(SOUND_MIXER_SPEAKER); | |
148 | IOCTL_TO_STR(SOUND_MIXER_LINE); | |
149 | IOCTL_TO_STR(SOUND_MIXER_MIC); | |
150 | IOCTL_TO_STR(SOUND_MIXER_CD); | |
151 | IOCTL_TO_STR(SOUND_MIXER_IMIX); | |
152 | IOCTL_TO_STR(SOUND_MIXER_ALTPCM); | |
153 | IOCTL_TO_STR(SOUND_MIXER_RECLEV); | |
154 | IOCTL_TO_STR(SOUND_MIXER_IGAIN); | |
155 | IOCTL_TO_STR(SOUND_MIXER_OGAIN); | |
156 | IOCTL_TO_STR(SOUND_MIXER_LINE1); | |
157 | IOCTL_TO_STR(SOUND_MIXER_LINE2); | |
158 | IOCTL_TO_STR(SOUND_MIXER_LINE3); | |
159 | IOCTL_TO_STR(SOUND_MIXER_DIGITAL1); | |
160 | IOCTL_TO_STR(SOUND_MIXER_DIGITAL2); | |
161 | IOCTL_TO_STR(SOUND_MIXER_DIGITAL3); | |
09208ca4 | 162 | #ifdef SOUND_MIXER_PHONEIN |
6f4be549 | 163 | IOCTL_TO_STR(SOUND_MIXER_PHONEIN); |
09208ca4 BT |
164 | #endif |
165 | #ifdef SOUND_MIXER_PHONEOUT | |
6f4be549 | 166 | IOCTL_TO_STR(SOUND_MIXER_PHONEOUT); |
09208ca4 | 167 | #endif |
6f4be549 RR |
168 | IOCTL_TO_STR(SOUND_MIXER_VIDEO); |
169 | IOCTL_TO_STR(SOUND_MIXER_RADIO); | |
09208ca4 | 170 | #ifdef SOUND_MIXER_MONITOR |
6f4be549 | 171 | IOCTL_TO_STR(SOUND_MIXER_MONITOR); |
09208ca4 | 172 | #endif |
6f4be549 RR |
173 | } |
174 | #undef IOCTL_TO_STR | |
175 | sprintf(str, "UNKNOWN(%08x)", command); | |
176 | return str; | |
177 | } | |
178 | ||
179 | static const char * getControlType(DWORD dwControlType) | |
180 | { | |
181 | static char str[64]; | |
897a235e | 182 | #define TYPE_TO_STR(x) case x: return #x |
6f4be549 RR |
183 | switch (dwControlType) { |
184 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM); | |
185 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER); | |
186 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER); | |
187 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER); | |
188 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER); | |
189 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN); | |
190 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF); | |
191 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE); | |
192 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO); | |
193 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS); | |
194 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH); | |
195 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST); | |
196 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON); | |
197 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS); | |
198 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED); | |
199 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED); | |
200 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT); | |
201 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER); | |
202 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN); | |
203 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN); | |
204 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER); | |
205 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME); | |
206 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS); | |
207 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE); | |
208 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER); | |
209 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT); | |
210 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX); | |
211 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT); | |
212 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER); | |
213 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME); | |
214 | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME); | |
215 | } | |
216 | #undef TYPE_TO_STR | |
10ee330a | 217 | sprintf(str, "UNKNOWN(%08x)", dwControlType); |
6f4be549 RR |
218 | return str; |
219 | } | |
220 | ||
b15504e1 RR |
221 | static const char * getComponentType(DWORD dwComponentType) |
222 | { | |
223 | static char str[64]; | |
224 | #define TYPE_TO_STR(x) case x: return #x; | |
225 | switch (dwComponentType) { | |
226 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED); | |
227 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL); | |
228 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE); | |
229 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR); | |
230 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS); | |
231 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES); | |
232 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE); | |
233 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN); | |
234 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN); | |
235 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED); | |
236 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL); | |
237 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE); | |
238 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE); | |
239 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER); | |
240 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC); | |
241 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE); | |
242 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER); | |
243 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT); | |
244 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY); | |
245 | TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG); | |
246 | } | |
247 | #undef TYPE_TO_STR | |
10ee330a | 248 | sprintf(str, "UNKNOWN(%08x)", dwComponentType); |
b15504e1 RR |
249 | return str; |
250 | } | |
251 | ||
252 | static const char * getTargetType(DWORD dwType) | |
253 | { | |
254 | static char str[64]; | |
255 | #define TYPE_TO_STR(x) case x: return #x; | |
256 | switch (dwType) { | |
257 | TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED); | |
258 | TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT); | |
259 | TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN); | |
260 | TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT); | |
261 | TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN); | |
262 | TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX); | |
263 | } | |
264 | #undef TYPE_TO_STR | |
10ee330a | 265 | sprintf(str, "UNKNOWN(%08x)", dwType); |
b15504e1 RR |
266 | return str; |
267 | } | |
268 | ||
b4e49557 AJ |
269 | static const WCHAR sz_short_volume [] = {'V','o','l',0}; |
270 | static const WCHAR sz_long_volume [] = {'V','o','l','u','m','e',0}; | |
271 | static const WCHAR sz_shrtlng_mute [] = {'M','u','t','e',0}; | |
272 | static const WCHAR sz_shrtlng_mixer[] = {'M','i','x','e','r',0}; | |
273 | ||
2f9eb3b0 EP |
274 | /************************************************************************** |
275 | * MIX_FillLineControls [internal] | |
276 | */ | |
6f4be549 | 277 | static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, |
b15504e1 | 278 | DWORD dwControlType) |
2f9eb3b0 EP |
279 | { |
280 | struct mixerCtrl* mc = &mix->ctrl[c]; | |
281 | int j; | |
282 | ||
10ee330a | 283 | TRACE("(%p, %d, %08x, %s)\n", mix, c, lineID, |
b15504e1 | 284 | getControlType(dwControlType)); |
5ae2835e | 285 | |
2f9eb3b0 | 286 | mc->dwLineID = lineID; |
b4e49557 | 287 | mc->ctrl.cbStruct = sizeof(MIXERCONTROLW); |
2f9eb3b0 | 288 | mc->ctrl.dwControlID = c + 1; |
b15504e1 | 289 | mc->ctrl.dwControlType = dwControlType; |
9a624916 | 290 | |
b15504e1 | 291 | switch (dwControlType) |
2f9eb3b0 EP |
292 | { |
293 | case MIXERCONTROL_CONTROLTYPE_VOLUME: | |
294 | mc->ctrl.fdwControl = 0; | |
295 | mc->ctrl.cMultipleItems = 0; | |
b4e49557 AJ |
296 | strcpyW(mc->ctrl.szShortName, sz_short_volume); |
297 | strcpyW(mc->ctrl.szName, sz_long_volume); | |
2f9eb3b0 | 298 | memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds)); |
9a624916 | 299 | /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct, |
2f9eb3b0 EP |
300 | * [0, 100] is the range supported by OSS |
301 | * whatever the min and max values are they must match | |
302 | * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range | |
303 | */ | |
304 | mc->ctrl.Bounds.s1.dwMinimum = 0; | |
305 | mc->ctrl.Bounds.s1.dwMaximum = 65535; | |
306 | memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics)); | |
6f4be549 | 307 | mc->ctrl.Metrics.cSteps = 656; |
2f9eb3b0 EP |
308 | break; |
309 | case MIXERCONTROL_CONTROLTYPE_MUTE: | |
310 | case MIXERCONTROL_CONTROLTYPE_ONOFF: | |
311 | mc->ctrl.fdwControl = 0; | |
312 | mc->ctrl.cMultipleItems = 0; | |
b4e49557 AJ |
313 | strcpyW(mc->ctrl.szShortName, sz_shrtlng_mute); |
314 | strcpyW(mc->ctrl.szName, sz_shrtlng_mute); | |
2f9eb3b0 EP |
315 | memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds)); |
316 | mc->ctrl.Bounds.s1.dwMinimum = 0; | |
317 | mc->ctrl.Bounds.s1.dwMaximum = 1; | |
318 | memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics)); | |
319 | break; | |
320 | case MIXERCONTROL_CONTROLTYPE_MUX: | |
321 | case MIXERCONTROL_CONTROLTYPE_MIXER: | |
322 | mc->ctrl.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE; | |
323 | mc->ctrl.cMultipleItems = 0; | |
324 | for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) | |
325 | if (WINE_CHN_SUPPORTS(mix->recMask, j)) | |
326 | mc->ctrl.cMultipleItems++; | |
b4e49557 AJ |
327 | strcpyW(mc->ctrl.szShortName, sz_shrtlng_mixer); |
328 | strcpyW(mc->ctrl.szName, sz_shrtlng_mixer); | |
2f9eb3b0 | 329 | memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds)); |
d7d1c384 | 330 | mc->ctrl.Bounds.s1.dwMaximum = mc->ctrl.cMultipleItems - 1; |
2f9eb3b0 | 331 | memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics)); |
d7d1c384 | 332 | mc->ctrl.Metrics.cSteps = mc->ctrl.cMultipleItems; |
2f9eb3b0 | 333 | break; |
9a624916 | 334 | |
2f9eb3b0 | 335 | default: |
10ee330a | 336 | FIXME("Internal error: unknown type: %08x\n", dwControlType); |
2f9eb3b0 | 337 | } |
10ee330a | 338 | TRACE("ctrl[%2d]: typ=%08x lin=%08x\n", c + 1, dwControlType, lineID); |
2f9eb3b0 EP |
339 | } |
340 | ||
341 | /****************************************************************** | |
342 | * MIX_GetMixer | |
343 | * | |
344 | * | |
345 | */ | |
346 | static struct mixer* MIX_Get(WORD wDevID) | |
347 | { | |
5ae2835e RR |
348 | TRACE("(%04x)\n", wDevID); |
349 | ||
b15504e1 | 350 | if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].dev_name == NULL) |
5ae2835e RR |
351 | return NULL; |
352 | ||
2f9eb3b0 EP |
353 | return &MIX_Mixers[wDevID]; |
354 | } | |
355 | ||
356 | /************************************************************************** | |
357 | * MIX_Open [internal] | |
358 | */ | |
359 | static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags) | |
360 | { | |
361 | int mixer, i, j; | |
362 | unsigned caps; | |
363 | struct mixer* mix; | |
364 | DWORD ret = MMSYSERR_NOERROR; | |
365 | ||
10ee330a | 366 | TRACE("(%04X, %p, %u);\n", wDevID, lpMod, flags); |
9a624916 | 367 | |
6f4be549 RR |
368 | /* as we partly init the mixer with MIX_Open, we can allow null open decs |
369 | * EPP if (lpMod == NULL) return MMSYSERR_INVALPARAM; | |
370 | * anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer | |
371 | * device before sending messages to it... it seems to be linked to all | |
b15504e1 | 372 | * the equivalent of mixer identification |
2f9eb3b0 EP |
373 | * (with a reference to a wave, midi.. handle |
374 | */ | |
b6aeb56d RR |
375 | if ((mix = MIX_Get(wDevID)) == NULL) { |
376 | WARN("bad device ID: %04X\n", wDevID); | |
5ae2835e RR |
377 | return MMSYSERR_BADDEVICEID; |
378 | } | |
2f9eb3b0 | 379 | |
b15504e1 | 380 | if ((mixer = open(mix->dev_name, O_RDWR)) < 0) |
2f9eb3b0 | 381 | { |
6f4be549 | 382 | ERR("open(%s, O_RDWR) failed (%s)\n", |
b15504e1 | 383 | mix->dev_name, strerror(errno)); |
6f4be549 | 384 | |
9a624916 VB |
385 | if (errno == ENODEV || errno == ENXIO) |
386 | { | |
2f9eb3b0 | 387 | /* no driver present */ |
5ae2835e | 388 | WARN("no driver\n"); |
2f9eb3b0 EP |
389 | return MMSYSERR_NODRIVER; |
390 | } | |
391 | return MMSYSERR_ERROR; | |
392 | } | |
9a624916 VB |
393 | |
394 | if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1) | |
2f9eb3b0 | 395 | { |
5ae2835e | 396 | ERR("ioctl(%s, SOUND_MIXER_DEVMASK) failed (%s)\n", |
b15504e1 | 397 | mix->dev_name, strerror(errno)); |
2f9eb3b0 EP |
398 | ret = MMSYSERR_ERROR; |
399 | goto error; | |
400 | } | |
5ae2835e | 401 | |
2f9eb3b0 EP |
402 | mix->devMask &= WINE_MIXER_MASK_SPEAKER; |
403 | if (mix->devMask == 0) | |
404 | { | |
5ae2835e | 405 | WARN("no driver\n"); |
2f9eb3b0 EP |
406 | ret = MMSYSERR_NODRIVER; |
407 | goto error; | |
408 | } | |
409 | ||
9a624916 | 410 | if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1) |
2f9eb3b0 | 411 | { |
5ae2835e | 412 | ERR("ioctl(%s, SOUND_MIXER_STEREODEVS) failed (%s)\n", |
b15504e1 | 413 | mix->dev_name, strerror(errno)); |
2f9eb3b0 EP |
414 | ret = MMSYSERR_ERROR; |
415 | goto error; | |
416 | } | |
417 | mix->stereoMask &= WINE_MIXER_MASK_SPEAKER; | |
418 | ||
9a624916 | 419 | if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1) |
2f9eb3b0 | 420 | { |
5ae2835e | 421 | ERR("ioctl(%s, SOUND_MIXER_RECMASK) failed (%s)\n", |
b15504e1 | 422 | mix->dev_name, strerror(errno)); |
2f9eb3b0 EP |
423 | ret = MMSYSERR_ERROR; |
424 | goto error; | |
425 | } | |
426 | mix->recMask &= WINE_MIXER_MASK_RECORD; | |
427 | /* FIXME: we may need to support both rec lev & igain */ | |
428 | if (!WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_RECLEV)) | |
429 | { | |
430 | WARN("The sound card doesn't support rec level\n"); | |
431 | if (WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_IGAIN)) | |
432 | WARN("but it does support IGain, please report\n"); | |
433 | } | |
9a624916 | 434 | if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1) |
2f9eb3b0 | 435 | { |
5ae2835e | 436 | ERR("ioctl(%s, SOUND_MIXER_READ_CAPS) failed (%s)\n", |
b15504e1 | 437 | mix->dev_name, strerror(errno)); |
2f9eb3b0 EP |
438 | ret = MMSYSERR_ERROR; |
439 | goto error; | |
440 | } | |
441 | mix->singleRecChannel = caps & SOUND_CAP_EXCL_INPUT; | |
9a624916 VB |
442 | TRACE("dev=%04x rec=%04x stereo=%04x %s\n", |
443 | mix->devMask, mix->recMask, mix->stereoMask, | |
2f9eb3b0 | 444 | mix->singleRecChannel ? "single" : "multiple"); |
9a624916 | 445 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) |
2f9eb3b0 EP |
446 | { |
447 | mix->volume[i] = -1; | |
448 | } | |
449 | mix->numCtrl = 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */ | |
450 | /* FIXME: do we always have RECLEV on all cards ??? */ | |
451 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) | |
452 | { | |
453 | if (WINE_CHN_SUPPORTS(mix->devMask, i)) | |
454 | mix->numCtrl += 2; /* volume & mute */ | |
455 | if (WINE_CHN_SUPPORTS(mix->recMask, i)) | |
456 | mix->numCtrl += 2; /* volume & onoff */ | |
9a624916 | 457 | |
2f9eb3b0 | 458 | } |
6f4be549 RR |
459 | if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, |
460 | sizeof(mix->ctrl[0]) * mix->numCtrl))) | |
2f9eb3b0 EP |
461 | { |
462 | ret = MMSYSERR_NOMEM; | |
463 | goto error; | |
464 | } | |
9a624916 | 465 | |
2f9eb3b0 | 466 | j = 0; |
6f4be549 RR |
467 | MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), |
468 | MIXERCONTROL_CONTROLTYPE_VOLUME); | |
469 | MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), | |
470 | MIXERCONTROL_CONTROLTYPE_MUTE); | |
9a624916 | 471 | MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST), |
2f9eb3b0 | 472 | mix->singleRecChannel ? |
6f4be549 RR |
473 | MIXERCONTROL_CONTROLTYPE_MUX : |
474 | MIXERCONTROL_CONTROLTYPE_MIXER); | |
475 | MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST), | |
476 | MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/); | |
2f9eb3b0 EP |
477 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) |
478 | { | |
479 | if (WINE_CHN_SUPPORTS(mix->devMask, i)) | |
480 | { | |
481 | MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i), | |
482 | MIXERCONTROL_CONTROLTYPE_VOLUME); | |
483 | MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i), | |
484 | MIXERCONTROL_CONTROLTYPE_MUTE); | |
485 | } | |
486 | } | |
487 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) | |
488 | { | |
489 | if (WINE_CHN_SUPPORTS(mix->recMask, i)) | |
490 | { | |
491 | MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i), | |
492 | MIXERCONTROL_CONTROLTYPE_VOLUME); | |
493 | MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i), | |
494 | MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/); | |
495 | } | |
496 | } | |
497 | assert(j == mix->numCtrl); | |
498 | error: | |
499 | close(mixer); | |
500 | return ret; | |
501 | } | |
aa67ac90 | 502 | |
491502b9 | 503 | /************************************************************************** |
54e4ba04 | 504 | * MIX_GetVal [internal] |
491502b9 | 505 | */ |
2f9eb3b0 | 506 | static BOOL MIX_GetVal(struct mixer* mix, int chn, int* val) |
491502b9 | 507 | { |
54e4ba04 EP |
508 | int mixer; |
509 | BOOL ret = FALSE; | |
491502b9 | 510 | |
6f4be549 | 511 | TRACE("(%p, %s, %p\n", mix, getIoctlCommand(chn), val); |
5ae2835e | 512 | |
b15504e1 | 513 | if ((mixer = open(mix->dev_name, O_RDWR)) < 0) { |
54e4ba04 EP |
514 | /* FIXME: ENXIO => no mixer installed */ |
515 | WARN("mixer device not available !\n"); | |
6f4be549 RR |
516 | } else { |
517 | if (ioctl(mixer, MIXER_READ(chn), val) >= 0) { | |
518 | TRACE("Reading %04x for %s\n", *val, getIoctlCommand(chn)); | |
54e4ba04 | 519 | ret = TRUE; |
6f4be549 RR |
520 | } else { |
521 | ERR("ioctl(%s, MIXER_READ(%s)) failed (%s)\n", | |
b15504e1 | 522 | mix->dev_name, getIoctlCommand(chn), strerror(errno)); |
6f4be549 | 523 | } |
54e4ba04 EP |
524 | close(mixer); |
525 | } | |
526 | return ret; | |
527 | } | |
31c28665 | 528 | |
54e4ba04 EP |
529 | /************************************************************************** |
530 | * MIX_SetVal [internal] | |
531 | */ | |
2f9eb3b0 | 532 | static BOOL MIX_SetVal(struct mixer* mix, int chn, int val) |
54e4ba04 EP |
533 | { |
534 | int mixer; | |
535 | BOOL ret = FALSE; | |
31c28665 | 536 | |
6f4be549 | 537 | TRACE("(%p, %s, %x)\n", mix, getIoctlCommand(chn), val); |
31c28665 | 538 | |
b15504e1 | 539 | if ((mixer = open(mix->dev_name, O_RDWR)) < 0) { |
54e4ba04 EP |
540 | /* FIXME: ENXIO => no mixer installed */ |
541 | WARN("mixer device not available !\n"); | |
6f4be549 RR |
542 | } else { |
543 | if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0) { | |
544 | TRACE("Set %s to %04x\n", getIoctlCommand(chn), val); | |
54e4ba04 | 545 | ret = TRUE; |
6f4be549 RR |
546 | } else { |
547 | ERR("ioctl(%s, MIXER_WRITE(%s)) failed (%s)\n", | |
b15504e1 | 548 | mix->dev_name, getIoctlCommand(chn), strerror(errno)); |
6f4be549 | 549 | } |
54e4ba04 EP |
550 | close(mixer); |
551 | } | |
552 | return ret; | |
553 | } | |
491502b9 | 554 | |
2f9eb3b0 EP |
555 | /****************************************************************** |
556 | * MIX_GetRecSrc | |
557 | * | |
558 | * | |
559 | */ | |
560 | static BOOL MIX_GetRecSrc(struct mixer* mix, unsigned* mask) | |
561 | { | |
562 | int mixer; | |
563 | BOOL ret = FALSE; | |
564 | ||
5ae2835e RR |
565 | TRACE("(%p, %p)\n", mix, mask); |
566 | ||
b15504e1 | 567 | if ((mixer = open(mix->dev_name, O_RDWR)) >= 0) { |
6f4be549 RR |
568 | if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) { |
569 | ret = TRUE; | |
570 | } else { | |
571 | ERR("ioctl(%s, SOUND_MIXER_READ_RECSRC) failed (%s)\n", | |
b15504e1 | 572 | mix->dev_name, strerror(errno)); |
6f4be549 | 573 | } |
2f9eb3b0 EP |
574 | close(mixer); |
575 | } | |
576 | return ret; | |
577 | } | |
578 | ||
579 | /****************************************************************** | |
580 | * MIX_SetRecSrc | |
581 | * | |
582 | * | |
583 | */ | |
584 | static BOOL MIX_SetRecSrc(struct mixer* mix, unsigned mask) | |
585 | { | |
586 | int mixer; | |
587 | BOOL ret = FALSE; | |
588 | ||
5ae2835e RR |
589 | TRACE("(%p, %08x)\n", mix, mask); |
590 | ||
b15504e1 | 591 | if ((mixer = open(mix->dev_name, O_RDWR)) >= 0) { |
6f4be549 | 592 | if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) >= 0) { |
2f9eb3b0 | 593 | ret = TRUE; |
6f4be549 RR |
594 | } else { |
595 | ERR("ioctl(%s, SOUND_MIXER_WRITE_RECSRC) failed (%s)\n", | |
b15504e1 | 596 | mix->dev_name, strerror(errno)); |
6f4be549 | 597 | } |
2f9eb3b0 EP |
598 | close(mixer); |
599 | } | |
600 | return ret; | |
601 | } | |
602 | ||
54e4ba04 EP |
603 | /************************************************************************** |
604 | * MIX_GetDevCaps [internal] | |
605 | */ | |
b4e49557 | 606 | static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD dwSize) |
54e4ba04 | 607 | { |
2f9eb3b0 | 608 | struct mixer* mix; |
b4e49557 AJ |
609 | MIXERCAPSW capsW; |
610 | const char* name; | |
2f9eb3b0 | 611 | |
10ee330a | 612 | TRACE("(%04X, %p, %u);\n", wDevID, lpCaps, dwSize); |
9a624916 | 613 | |
5ae2835e RR |
614 | if (lpCaps == NULL) { |
615 | WARN("invalid parameter: lpCaps == NULL\n"); | |
616 | return MMSYSERR_INVALPARAM; | |
617 | } | |
618 | ||
b6aeb56d RR |
619 | if ((mix = MIX_Get(wDevID)) == NULL) { |
620 | WARN("bad device ID: %04X\n", wDevID); | |
5ae2835e RR |
621 | return MMSYSERR_BADDEVICEID; |
622 | } | |
aa67ac90 | 623 | |
b4e49557 AJ |
624 | capsW.wMid = WINE_MIXER_MANUF_ID; |
625 | capsW.wPid = WINE_MIXER_PRODUCT_ID; | |
626 | capsW.vDriverVersion = WINE_MIXER_VERSION; | |
627 | if (!(name = mix->name)) name = WINE_MIXER_NAME; | |
a9bf3813 | 628 | MultiByteToWideChar(CP_UNIXCP, 0, name, -1, capsW.szPname, sizeof(capsW.szPname) / sizeof(WCHAR)); |
b4e49557 AJ |
629 | capsW.cDestinations = 2; /* speakers & record */ |
630 | capsW.fdwSupport = 0; /* No bits defined yet */ | |
17a83723 | 631 | |
b4e49557 | 632 | memcpy(lpCaps, &capsW, min(dwSize, sizeof(capsW))); |
9a624916 | 633 | |
54e4ba04 | 634 | return MMSYSERR_NOERROR; |
491502b9 AJ |
635 | } |
636 | ||
54e4ba04 | 637 | /************************************************************************** |
2f9eb3b0 EP |
638 | * MIX_GetLineInfoDst [internal] |
639 | */ | |
b4e49557 | 640 | static DWORD MIX_GetLineInfoDst(struct mixer* mix, LPMIXERLINEW lpMl, |
6f4be549 | 641 | DWORD dst) |
9a624916 | 642 | { |
2f9eb3b0 EP |
643 | unsigned mask; |
644 | int j; | |
645 | ||
10ee330a | 646 | TRACE("(%p, %p, %08x)\n", mix, lpMl, dst); |
5ae2835e | 647 | |
2f9eb3b0 EP |
648 | lpMl->dwDestination = dst; |
649 | switch (dst) | |
650 | { | |
d7d1c384 | 651 | case LINEID_SPEAKER: |
2f9eb3b0 EP |
652 | lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS; |
653 | mask = mix->devMask; | |
654 | j = SOUND_MIXER_VOLUME; | |
d7d1c384 | 655 | lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT; |
2f9eb3b0 | 656 | break; |
d7d1c384 | 657 | case LINEID_RECORD: |
2f9eb3b0 EP |
658 | lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN; |
659 | mask = mix->recMask; | |
660 | j = SOUND_MIXER_RECLEV; | |
d7d1c384 | 661 | lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN; |
2f9eb3b0 EP |
662 | break; |
663 | default: | |
664 | FIXME("shouldn't happen\n"); | |
665 | return MMSYSERR_ERROR; | |
666 | } | |
667 | lpMl->dwSource = 0xFFFFFFFF; | |
a9bf3813 RS |
668 | MultiByteToWideChar(CP_UNIXCP, 0, MIX_Labels[j], -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR)); |
669 | MultiByteToWideChar(CP_UNIXCP, 0, MIX_Names[j], -1, lpMl->szName, sizeof(lpMl->szName) / sizeof(WCHAR)); | |
9a624916 | 670 | |
2f9eb3b0 EP |
671 | /* we have all connections found in the MIX_DevMask */ |
672 | lpMl->cConnections = 0; | |
673 | for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) | |
674 | if (WINE_CHN_SUPPORTS(mask, j)) | |
675 | lpMl->cConnections++; | |
676 | lpMl->cChannels = 1; | |
677 | if (WINE_CHN_SUPPORTS(mix->stereoMask, lpMl->dwLineID)) | |
678 | lpMl->cChannels++; | |
679 | lpMl->dwLineID = MAKELONG(dst, LINEID_DST); | |
680 | lpMl->cControls = 0; | |
681 | for (j = 0; j < mix->numCtrl; j++) | |
682 | if (mix->ctrl[j].dwLineID == lpMl->dwLineID) | |
683 | lpMl->cControls++; | |
684 | ||
685 | return MMSYSERR_NOERROR; | |
686 | } | |
687 | ||
688 | /************************************************************************** | |
689 | * MIX_GetLineInfoSrc [internal] | |
54e4ba04 | 690 | */ |
b4e49557 | 691 | static DWORD MIX_GetLineInfoSrc(struct mixer* mix, LPMIXERLINEW lpMl, |
6f4be549 | 692 | DWORD idx, DWORD dst) |
c11b167f | 693 | { |
2f9eb3b0 EP |
694 | int i, j; |
695 | unsigned mask = (dst) ? mix->recMask : mix->devMask; | |
696 | ||
10ee330a | 697 | TRACE("(%p, %p, %d, %08x)\n", mix, lpMl, idx, dst); |
5ae2835e | 698 | |
a9bf3813 RS |
699 | MultiByteToWideChar(CP_UNIXCP, 0, MIX_Labels[idx], -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR)); |
700 | MultiByteToWideChar(CP_UNIXCP, 0, MIX_Names[idx], -1, lpMl->szName, sizeof(lpMl->szName) / sizeof(WCHAR)); | |
2f9eb3b0 EP |
701 | lpMl->dwLineID = MAKELONG(dst, idx); |
702 | lpMl->dwDestination = dst; | |
54e4ba04 | 703 | lpMl->cConnections = 1; |
2f9eb3b0 EP |
704 | lpMl->cControls = 0; |
705 | for (i = 0; i < mix->numCtrl; i++) | |
706 | if (mix->ctrl[i].dwLineID == lpMl->dwLineID) | |
707 | lpMl->cControls++; | |
708 | ||
9a624916 | 709 | switch (idx) |
2f9eb3b0 | 710 | { |
54e4ba04 EP |
711 | case SOUND_MIXER_SYNTH: |
712 | lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER; | |
713 | lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE; | |
d7d1c384 | 714 | lpMl->Target.dwType = MIXERLINE_TARGETTYPE_MIDIOUT; |
54e4ba04 EP |
715 | break; |
716 | case SOUND_MIXER_CD: | |
717 | lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC; | |
718 | lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE; | |
d7d1c384 | 719 | lpMl->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED; |
54e4ba04 EP |
720 | break; |
721 | case SOUND_MIXER_LINE: | |
722 | lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE; | |
723 | lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE; | |
d7d1c384 | 724 | lpMl->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED; |
54e4ba04 EP |
725 | break; |
726 | case SOUND_MIXER_MIC: | |
727 | lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE; | |
728 | lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE; | |
d7d1c384 | 729 | lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN; |
54e4ba04 EP |
730 | break; |
731 | case SOUND_MIXER_PCM: | |
732 | lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT; | |
733 | lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE; | |
d7d1c384 | 734 | lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT; |
54e4ba04 | 735 | break; |
2f9eb3b0 EP |
736 | case SOUND_MIXER_IMIX: |
737 | lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED; | |
738 | lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE; | |
d7d1c384 | 739 | lpMl->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED; |
2f9eb3b0 | 740 | break; |
54e4ba04 | 741 | default: |
10ee330a | 742 | WARN("Index %d not handled.\n", idx); |
1bf5dea2 | 743 | return MIXERR_INVALLINE; |
54e4ba04 | 744 | } |
2f9eb3b0 EP |
745 | lpMl->cChannels = 1; |
746 | if (dst == 0 && WINE_CHN_SUPPORTS(mix->stereoMask, idx)) | |
747 | lpMl->cChannels++; | |
9a624916 | 748 | for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++) |
2f9eb3b0 EP |
749 | { |
750 | if (WINE_CHN_SUPPORTS(mask, j)) | |
751 | { | |
752 | if (j == idx) break; | |
753 | i++; | |
754 | } | |
9a624916 | 755 | } |
2f9eb3b0 | 756 | lpMl->dwSource = i; |
1bf5dea2 | 757 | return MMSYSERR_NOERROR; |
c11b167f | 758 | } |
491502b9 | 759 | |
2f9eb3b0 EP |
760 | /****************************************************************** |
761 | * MIX_CheckLine | |
762 | */ | |
763 | static BOOL MIX_CheckLine(DWORD lineID) | |
764 | { | |
10ee330a | 765 | TRACE("(%08x)\n",lineID); |
5ae2835e | 766 | |
9a624916 | 767 | return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) || |
b15504e1 | 768 | (HIWORD(lineID) == LINEID_DST && |
6f4be549 | 769 | LOWORD(lineID) < SOUND_MIXER_NRDEVICES)); |
2f9eb3b0 EP |
770 | } |
771 | ||
491502b9 | 772 | /************************************************************************** |
54e4ba04 | 773 | * MIX_GetLineInfo [internal] |
491502b9 | 774 | */ |
b4e49557 | 775 | static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD fdwInfo) |
491502b9 | 776 | { |
aa67ac90 | 777 | int i, j; |
54e4ba04 | 778 | DWORD ret = MMSYSERR_NOERROR; |
2f9eb3b0 EP |
779 | unsigned mask; |
780 | struct mixer* mix; | |
781 | ||
10ee330a | 782 | TRACE("(%04X, %p, %u);\n", wDevID, lpMl, fdwInfo); |
2f9eb3b0 | 783 | |
b15504e1 RR |
784 | if (lpMl == NULL) { |
785 | WARN("invalid parameter: lpMl = NULL\n"); | |
786 | return MMSYSERR_INVALPARAM; | |
787 | } | |
788 | ||
789 | if (lpMl->cbStruct != sizeof(*lpMl)) { | |
1470955c ML |
790 | WARN("invalid parameter: lpMl->cbStruct = %d\n", |
791 | lpMl->cbStruct); | |
54e4ba04 | 792 | return MMSYSERR_INVALPARAM; |
5ae2835e RR |
793 | } |
794 | ||
795 | if ((mix = MIX_Get(wDevID)) == NULL) { | |
b6aeb56d | 796 | WARN("bad device ID: %04X\n", wDevID); |
5ae2835e RR |
797 | return MMSYSERR_BADDEVICEID; |
798 | } | |
9a624916 | 799 | |
54e4ba04 EP |
800 | /* FIXME: set all the variables correctly... the lines below |
801 | * are very wrong... | |
802 | */ | |
803 | lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE; | |
54e4ba04 | 804 | lpMl->dwUser = 0; |
9a624916 VB |
805 | |
806 | switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) | |
2f9eb3b0 | 807 | { |
54e4ba04 | 808 | case MIXER_GETLINEINFOF_DESTINATION: |
10ee330a | 809 | TRACE("MIXER_GETLINEINFOF_DESTINATION (%08x)\n", lpMl->dwDestination); |
5ae2835e | 810 | if (lpMl->dwDestination >= 2) { |
10ee330a | 811 | WARN("invalid parameter: lpMl->dwDestination = %d >= 2\n", |
b15504e1 | 812 | lpMl->dwDestination); |
2f9eb3b0 | 813 | return MMSYSERR_INVALPARAM; |
5ae2835e | 814 | } |
6f4be549 RR |
815 | ret = MIX_GetLineInfoDst(mix, lpMl, lpMl->dwDestination); |
816 | if (ret != MMSYSERR_NOERROR) { | |
817 | WARN("error\n"); | |
2f9eb3b0 | 818 | return ret; |
6f4be549 | 819 | } |
54e4ba04 EP |
820 | break; |
821 | case MIXER_GETLINEINFOF_SOURCE: | |
10ee330a | 822 | TRACE("MIXER_GETLINEINFOF_SOURCE (%08x), dst=%08x\n", lpMl->dwSource, |
6f4be549 | 823 | lpMl->dwDestination); |
2f9eb3b0 EP |
824 | switch (lpMl->dwDestination) |
825 | { | |
d7d1c384 RR |
826 | case LINEID_SPEAKER: mask = mix->devMask; break; |
827 | case LINEID_RECORD: mask = mix->recMask; break; | |
6f4be549 | 828 | default: |
5ae2835e RR |
829 | WARN("invalid parameter\n"); |
830 | return MMSYSERR_INVALPARAM; | |
2f9eb3b0 | 831 | } |
54e4ba04 | 832 | i = lpMl->dwSource; |
9a624916 | 833 | for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) |
2f9eb3b0 EP |
834 | { |
835 | if (WINE_CHN_SUPPORTS(mask, j) && (i-- == 0)) | |
491502b9 AJ |
836 | break; |
837 | } | |
5ae2835e RR |
838 | if (j >= SOUND_MIXER_NRDEVICES) { |
839 | WARN("invalid line\n"); | |
54e4ba04 | 840 | return MIXERR_INVALLINE; |
5ae2835e | 841 | } |
6f4be549 RR |
842 | ret = MIX_GetLineInfoSrc(mix, lpMl, j, lpMl->dwDestination); |
843 | if (ret != MMSYSERR_NOERROR) { | |
844 | WARN("error\n"); | |
1bf5dea2 | 845 | return ret; |
6f4be549 | 846 | } |
54e4ba04 EP |
847 | break; |
848 | case MIXER_GETLINEINFOF_LINEID: | |
10ee330a | 849 | TRACE("MIXER_GETLINEINFOF_LINEID (%08x)\n", lpMl->dwLineID); |
2f9eb3b0 | 850 | |
5ae2835e RR |
851 | if (!MIX_CheckLine(lpMl->dwLineID)) { |
852 | WARN("invalid line\n"); | |
54e4ba04 | 853 | return MIXERR_INVALLINE; |
5ae2835e | 854 | } |
2f9eb3b0 EP |
855 | if (HIWORD(lpMl->dwLineID) == LINEID_DST) |
856 | ret = MIX_GetLineInfoDst(mix, lpMl, LOWORD(lpMl->dwLineID)); | |
857 | else | |
6f4be549 RR |
858 | ret = MIX_GetLineInfoSrc(mix, lpMl, HIWORD(lpMl->dwLineID), |
859 | LOWORD(lpMl->dwLineID)); | |
860 | if (ret != MMSYSERR_NOERROR) { | |
861 | WARN("error\n"); | |
1bf5dea2 | 862 | return ret; |
6f4be549 | 863 | } |
54e4ba04 EP |
864 | break; |
865 | case MIXER_GETLINEINFOF_COMPONENTTYPE: | |
b15504e1 RR |
866 | TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE (%s)\n", |
867 | getComponentType(lpMl->dwComponentType)); | |
9a624916 | 868 | switch (lpMl->dwComponentType) |
2f9eb3b0 | 869 | { |
d5749b45 | 870 | case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES: |
54e4ba04 | 871 | case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS: |
d5749b45 | 872 | ret = MIX_GetLineInfoDst(mix, lpMl, LINEID_SPEAKER); |
2f9eb3b0 | 873 | break; |
d5749b45 VB |
874 | case MIXERLINE_COMPONENTTYPE_DST_LINE: |
875 | case MIXERLINE_COMPONENTTYPE_DST_VOICEIN: | |
2f9eb3b0 | 876 | case MIXERLINE_COMPONENTTYPE_DST_WAVEIN: |
d5749b45 | 877 | ret = MIX_GetLineInfoDst(mix, lpMl, LINEID_RECORD); |
54e4ba04 EP |
878 | break; |
879 | case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER: | |
2f9eb3b0 | 880 | ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_SYNTH, 0); |
54e4ba04 EP |
881 | break; |
882 | case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC: | |
2f9eb3b0 | 883 | ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_CD, 0); |
54e4ba04 EP |
884 | break; |
885 | case MIXERLINE_COMPONENTTYPE_SRC_LINE: | |
2f9eb3b0 | 886 | ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_LINE, 0); |
54e4ba04 EP |
887 | break; |
888 | case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE: | |
2f9eb3b0 | 889 | ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_MIC, 1); |
54e4ba04 EP |
890 | break; |
891 | case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT: | |
2f9eb3b0 EP |
892 | ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_PCM, 0); |
893 | break; | |
894 | case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED: | |
895 | ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1); | |
54e4ba04 EP |
896 | break; |
897 | default: | |
b15504e1 RR |
898 | FIXME("Unhandled component type (%s)\n", |
899 | getComponentType(lpMl->dwComponentType)); | |
54e4ba04 EP |
900 | return MMSYSERR_INVALPARAM; |
901 | } | |
54e4ba04 EP |
902 | break; |
903 | case MIXER_GETLINEINFOF_TARGETTYPE: | |
6f4be549 | 904 | FIXME("MIXER_GETLINEINFOF_TARGETTYPE not implemented yet.\n"); |
b15504e1 RR |
905 | TRACE("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", |
906 | getTargetType(lpMl->Target.dwType)); | |
907 | switch (lpMl->Target.dwType) { | |
908 | case MIXERLINE_TARGETTYPE_UNDEFINED: | |
909 | case MIXERLINE_TARGETTYPE_WAVEOUT: | |
910 | case MIXERLINE_TARGETTYPE_WAVEIN: | |
911 | case MIXERLINE_TARGETTYPE_MIDIOUT: | |
912 | case MIXERLINE_TARGETTYPE_MIDIIN: | |
913 | case MIXERLINE_TARGETTYPE_AUX: | |
914 | default: | |
915 | FIXME("Unhandled target type (%s)\n", | |
916 | getTargetType(lpMl->Target.dwType)); | |
917 | return MMSYSERR_INVALPARAM; | |
918 | } | |
54e4ba04 EP |
919 | break; |
920 | default: | |
921 | WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK); | |
922 | break; | |
923 | } | |
9a624916 | 924 | |
d7d1c384 | 925 | if ((fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) != MIXER_GETLINEINFOF_TARGETTYPE) { |
b4e49557 | 926 | const char* name; |
d7d1c384 RR |
927 | lpMl->Target.dwDeviceID = 0xFFFFFFFF; |
928 | lpMl->Target.wMid = WINE_MIXER_MANUF_ID; | |
929 | lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID; | |
930 | lpMl->Target.vDriverVersion = WINE_MIXER_VERSION; | |
b4e49557 | 931 | if (!(name = mix->name)) name = WINE_MIXER_NAME; |
a9bf3813 | 932 | MultiByteToWideChar(CP_UNIXCP, 0, name, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR)); |
d7d1c384 | 933 | } |
9a624916 | 934 | |
54e4ba04 | 935 | return ret; |
491502b9 AJ |
936 | } |
937 | ||
2f9eb3b0 EP |
938 | /****************************************************************** |
939 | * MIX_CheckControl | |
940 | * | |
aa67ac90 | 941 | */ |
2f9eb3b0 | 942 | static BOOL MIX_CheckControl(struct mixer* mix, DWORD ctrlID) |
aa67ac90 | 943 | { |
10ee330a | 944 | TRACE("(%p, %08x)\n", mix, ctrlID); |
6f4be549 | 945 | |
2f9eb3b0 | 946 | return (ctrlID >= 1 && ctrlID <= mix->numCtrl); |
491502b9 AJ |
947 | } |
948 | ||
54e4ba04 EP |
949 | /************************************************************************** |
950 | * MIX_GetLineControls [internal] | |
951 | */ | |
b4e49557 | 952 | static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc, |
5ae2835e | 953 | DWORD flags) |
31c28665 | 954 | { |
aa67ac90 | 955 | DWORD dwRet = MMSYSERR_NOERROR; |
2f9eb3b0 | 956 | struct mixer* mix; |
31c28665 | 957 | |
10ee330a | 958 | TRACE("(%04X, %p, %u);\n", wDevID, lpMlc, flags); |
9a624916 | 959 | |
5ae2835e RR |
960 | if (lpMlc == NULL) { |
961 | WARN("invalid parameter: lpMlc == NULL\n"); | |
962 | return MMSYSERR_INVALPARAM; | |
963 | } | |
964 | ||
b15504e1 | 965 | if (lpMlc->cbStruct < sizeof(*lpMlc)) { |
1470955c ML |
966 | WARN("invalid parameter: lpMlc->cbStruct = %d\n", |
967 | lpMlc->cbStruct); | |
b15504e1 RR |
968 | return MMSYSERR_INVALPARAM; |
969 | } | |
970 | ||
b4e49557 | 971 | if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) { |
1470955c ML |
972 | WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", |
973 | lpMlc->cbmxctrl); | |
54e4ba04 | 974 | return MMSYSERR_INVALPARAM; |
5ae2835e RR |
975 | } |
976 | ||
977 | if ((mix = MIX_Get(wDevID)) == NULL) { | |
b6aeb56d | 978 | WARN("bad device ID: %04X\n", wDevID); |
5ae2835e RR |
979 | return MMSYSERR_BADDEVICEID; |
980 | } | |
31c28665 | 981 | |
9a624916 | 982 | switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK) |
2f9eb3b0 | 983 | { |
54e4ba04 | 984 | case MIXER_GETLINECONTROLSF_ALL: |
2f9eb3b0 EP |
985 | { |
986 | int i, j; | |
987 | ||
10ee330a | 988 | TRACE("line=%08x MIXER_GETLINECONTROLSF_ALL (%d)\n", |
6f4be549 | 989 | lpMlc->dwLineID, lpMlc->cControls); |
9a624916 | 990 | |
2f9eb3b0 EP |
991 | for (i = j = 0; i < mix->numCtrl; i++) |
992 | { | |
993 | if (mix->ctrl[i].dwLineID == lpMlc->dwLineID) | |
994 | j++; | |
995 | } | |
5ae2835e RR |
996 | |
997 | if (!j || lpMlc->cControls != j) { | |
998 | WARN("invalid parameter\n"); | |
999 | dwRet = MMSYSERR_INVALPARAM; | |
1000 | } else if (!MIX_CheckLine(lpMlc->dwLineID)) { | |
1001 | WARN("invalid line\n"); | |
1002 | dwRet = MIXERR_INVALLINE; | |
1003 | } else { | |
2f9eb3b0 EP |
1004 | for (i = j = 0; i < mix->numCtrl; i++) |
1005 | { | |
1006 | if (mix->ctrl[i].dwLineID == lpMlc->dwLineID) | |
1007 | { | |
10ee330a | 1008 | TRACE("[%d] => [%2d]: typ=%08x\n", j, i + 1, |
5ae2835e | 1009 | mix->ctrl[i].ctrl.dwControlType); |
2f9eb3b0 | 1010 | lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl; |
1876d185 | 1011 | } |
2f9eb3b0 EP |
1012 | } |
1013 | } | |
aa67ac90 | 1014 | } |
54e4ba04 EP |
1015 | break; |
1016 | case MIXER_GETLINECONTROLSF_ONEBYID: | |
10ee330a | 1017 | TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYID (%x)\n", |
6f4be549 | 1018 | lpMlc->dwLineID, lpMlc->u.dwControlID); |
2f9eb3b0 EP |
1019 | |
1020 | if (!MIX_CheckControl(mix, lpMlc->u.dwControlID) || | |
5ae2835e RR |
1021 | mix->ctrl[lpMlc->u.dwControlID - 1].dwLineID != lpMlc->dwLineID) { |
1022 | WARN("invalid parameter\n"); | |
aa67ac90 | 1023 | dwRet = MMSYSERR_INVALPARAM; |
5ae2835e | 1024 | } else |
2f9eb3b0 | 1025 | lpMlc->pamxctrl[0] = mix->ctrl[lpMlc->u.dwControlID - 1].ctrl; |
54e4ba04 EP |
1026 | break; |
1027 | case MIXER_GETLINECONTROLSF_ONEBYTYPE: | |
10ee330a | 1028 | TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", |
6f4be549 | 1029 | lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType)); |
5ae2835e RR |
1030 | if (!MIX_CheckLine(lpMlc->dwLineID)) { |
1031 | WARN("invalid line\n"); | |
1032 | dwRet = MIXERR_INVALLINE; | |
1033 | } else { | |
1034 | int i; | |
1035 | DWORD ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK; | |
1036 | for (i = 0; i < mix->numCtrl; i++) { | |
2f9eb3b0 | 1037 | if (mix->ctrl[i].dwLineID == lpMlc->dwLineID && |
5ae2835e RR |
1038 | ct == (mix->ctrl[i].ctrl.dwControlType & |
1039 | MIXERCONTROL_CT_CLASS_MASK)) { | |
2f9eb3b0 EP |
1040 | lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl; |
1041 | break; | |
1042 | } | |
1043 | } | |
5ae2835e RR |
1044 | |
1045 | if (i == mix->numCtrl) { | |
6f4be549 | 1046 | WARN("invalid parameter: control not found\n"); |
5ae2835e RR |
1047 | dwRet = MMSYSERR_INVALPARAM; |
1048 | } | |
aa67ac90 | 1049 | } |
54e4ba04 EP |
1050 | break; |
1051 | default: | |
b343fa80 | 1052 | ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK); |
aa67ac90 | 1053 | dwRet = MMSYSERR_INVALPARAM; |
54e4ba04 | 1054 | } |
aa67ac90 EP |
1055 | |
1056 | return dwRet; | |
31c28665 | 1057 | } |
54e4ba04 EP |
1058 | |
1059 | /************************************************************************** | |
1060 | * MIX_GetControlDetails [internal] | |
1061 | */ | |
6f4be549 RR |
1062 | static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, |
1063 | DWORD fdwDetails) | |
54e4ba04 | 1064 | { |
2f9eb3b0 | 1065 | DWORD ret = MMSYSERR_NOTSUPPORTED; |
aff83e69 | 1066 | DWORD chnl; |
2f9eb3b0 | 1067 | struct mixer* mix; |
9a624916 | 1068 | |
10ee330a | 1069 | TRACE("(%04X, %p, %u);\n", wDevID, lpmcd, fdwDetails); |
9a624916 | 1070 | |
5ae2835e RR |
1071 | if (lpmcd == NULL) { |
1072 | WARN("invalid parameter: lpmcd == NULL\n"); | |
1073 | return MMSYSERR_INVALPARAM; | |
1074 | } | |
1075 | ||
1076 | if ((mix = MIX_Get(wDevID)) == NULL) { | |
b6aeb56d | 1077 | WARN("bad device ID: %04X\n", wDevID); |
5ae2835e RR |
1078 | return MMSYSERR_BADDEVICEID; |
1079 | } | |
54e4ba04 | 1080 | |
9a624916 | 1081 | switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) |
2f9eb3b0 | 1082 | { |
54e4ba04 | 1083 | case MIXER_GETCONTROLDETAILSF_VALUE: |
10ee330a | 1084 | TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%08x)\n", lpmcd->dwControlID); |
2f9eb3b0 EP |
1085 | if (MIX_CheckControl(mix, lpmcd->dwControlID)) |
1086 | { | |
aff83e69 | 1087 | DWORD c = lpmcd->dwControlID - 1; |
2f9eb3b0 | 1088 | chnl = HIWORD(mix->ctrl[c].dwLineID); |
9a624916 | 1089 | if (chnl == LINEID_DST) |
6f4be549 RR |
1090 | chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : |
1091 | SOUND_MIXER_VOLUME; | |
9a624916 | 1092 | switch (mix->ctrl[c].ctrl.dwControlType) |
2f9eb3b0 | 1093 | { |
aa67ac90 EP |
1094 | case MIXERCONTROL_CONTROLTYPE_VOLUME: |
1095 | { | |
1096 | LPMIXERCONTROLDETAILS_UNSIGNED mcdu; | |
1097 | int val; | |
1098 | ||
b15504e1 | 1099 | if (lpmcd->cbDetails != |
6f4be549 | 1100 | sizeof(MIXERCONTROLDETAILS_UNSIGNED)) { |
1470955c ML |
1101 | WARN("invalid parameter: cbDetails = %d\n", |
1102 | lpmcd->cbDetails); | |
6f4be549 RR |
1103 | return MMSYSERR_INVALPARAM; |
1104 | } | |
1105 | ||
10ee330a | 1106 | TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n", |
6f4be549 RR |
1107 | getControlType(mix->ctrl[c].ctrl.dwControlType), |
1108 | lpmcd->cChannels); | |
1109 | ||
c0a9281c | 1110 | mcdu = lpmcd->paDetails; |
6f4be549 | 1111 | |
aa67ac90 | 1112 | /* return value is 00RL (4 bytes)... */ |
6f4be549 RR |
1113 | if ((val = mix->volume[chnl]) == -1 && |
1114 | !MIX_GetVal(mix, chnl, &val)) { | |
1115 | WARN("invalid parameter\n"); | |
aa67ac90 | 1116 | return MMSYSERR_INVALPARAM; |
6f4be549 | 1117 | } |
9a624916 VB |
1118 | |
1119 | switch (lpmcd->cChannels) | |
2f9eb3b0 | 1120 | { |
aa67ac90 EP |
1121 | case 1: |
1122 | /* mono... so R = L */ | |
79b1c4db | 1123 | mcdu->dwValue = ((LOBYTE(LOWORD(val)) * 65536.0) / 100.0) + 0.5; |
10ee330a | 1124 | TRACE("Reading RL = %d\n", mcdu->dwValue); |
aa67ac90 EP |
1125 | break; |
1126 | case 2: | |
1127 | /* stereo, left is paDetails[0] */ | |
79b1c4db | 1128 | mcdu->dwValue = ((LOBYTE(LOWORD(val)) * 65536.0) / 100.0) + 0.5; |
10ee330a | 1129 | TRACE("Reading L = %d\n", mcdu->dwValue); |
6f4be549 | 1130 | mcdu++; |
79b1c4db | 1131 | mcdu->dwValue = ((HIBYTE(LOWORD(val)) * 65536.0) / 100.0) + 0.5; |
10ee330a | 1132 | TRACE("Reading R = %d\n", mcdu->dwValue); |
aa67ac90 EP |
1133 | break; |
1134 | default: | |
10ee330a | 1135 | WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels); |
aa67ac90 EP |
1136 | return MMSYSERR_INVALPARAM; |
1137 | } | |
10ee330a | 1138 | TRACE("=> %08x\n", mcdu->dwValue); |
aa67ac90 | 1139 | } |
54e4ba04 | 1140 | break; |
aa67ac90 | 1141 | case MIXERCONTROL_CONTROLTYPE_MUTE: |
2f9eb3b0 | 1142 | case MIXERCONTROL_CONTROLTYPE_ONOFF: |
aa67ac90 EP |
1143 | { |
1144 | LPMIXERCONTROLDETAILS_BOOLEAN mcdb; | |
9a624916 | 1145 | |
b15504e1 | 1146 | if (lpmcd->cbDetails != |
6f4be549 | 1147 | sizeof(MIXERCONTROLDETAILS_BOOLEAN)) { |
1470955c ML |
1148 | WARN("invalid parameter: cbDetails = %d\n", |
1149 | lpmcd->cbDetails); | |
6f4be549 RR |
1150 | return MMSYSERR_INVALPARAM; |
1151 | } | |
1152 | ||
10ee330a | 1153 | TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", |
6f4be549 RR |
1154 | getControlType(mix->ctrl[c].ctrl.dwControlType), |
1155 | lpmcd->cChannels); | |
1156 | ||
aa67ac90 | 1157 | /* we mute both channels at the same time */ |
c0a9281c | 1158 | mcdb = lpmcd->paDetails; |
2f9eb3b0 EP |
1159 | mcdb->fValue = (mix->volume[chnl] != -1); |
1160 | TRACE("=> %s\n", mcdb->fValue ? "on" : "off"); | |
aa67ac90 | 1161 | } |
54e4ba04 | 1162 | break; |
2f9eb3b0 EP |
1163 | case MIXERCONTROL_CONTROLTYPE_MIXER: |
1164 | case MIXERCONTROL_CONTROLTYPE_MUX: | |
1165 | { | |
1166 | unsigned mask; | |
1167 | ||
b15504e1 | 1168 | if (lpmcd->cbDetails != |
6f4be549 | 1169 | sizeof(MIXERCONTROLDETAILS_BOOLEAN)) { |
1470955c ML |
1170 | WARN("invalid parameter: cbDetails = %d\n", |
1171 | lpmcd->cbDetails); | |
6f4be549 RR |
1172 | return MMSYSERR_INVALPARAM; |
1173 | } | |
1174 | ||
10ee330a | 1175 | TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", |
6f4be549 RR |
1176 | getControlType(mix->ctrl[c].ctrl.dwControlType), |
1177 | lpmcd->cChannels); | |
1178 | ||
2f9eb3b0 EP |
1179 | if (!MIX_GetRecSrc(mix, &mask)) |
1180 | { | |
1181 | /* FIXME: ENXIO => no mixer installed */ | |
1182 | WARN("mixer device not available !\n"); | |
1183 | ret = MMSYSERR_ERROR; | |
1184 | } | |
9a624916 | 1185 | else |
2f9eb3b0 EP |
1186 | { |
1187 | LPMIXERCONTROLDETAILS_BOOLEAN mcdb; | |
1188 | int i, j; | |
1189 | ||
1190 | /* we mute both channels at the same time */ | |
c0a9281c | 1191 | mcdb = lpmcd->paDetails; |
2f9eb3b0 EP |
1192 | |
1193 | for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++) | |
1194 | { | |
1195 | if (WINE_CHN_SUPPORTS(mix->recMask, j)) | |
1196 | { | |
9a624916 | 1197 | if (i >= lpmcd->u.cMultipleItems) |
2f9eb3b0 EP |
1198 | return MMSYSERR_INVALPARAM; |
1199 | mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j); | |
1200 | } | |
1201 | } | |
1202 | } | |
1203 | } | |
1204 | break; | |
1205 | default: | |
6f4be549 RR |
1206 | WARN("%s Unsupported\n", |
1207 | getControlType(mix->ctrl[c].ctrl.dwControlType)); | |
54e4ba04 | 1208 | } |
aa67ac90 | 1209 | ret = MMSYSERR_NOERROR; |
2f9eb3b0 | 1210 | } |
9a624916 | 1211 | else |
2f9eb3b0 | 1212 | { |
6f4be549 | 1213 | WARN("invalid parameter\n"); |
aa67ac90 | 1214 | ret = MMSYSERR_INVALPARAM; |
54e4ba04 | 1215 | } |
54e4ba04 EP |
1216 | break; |
1217 | case MIXER_GETCONTROLDETAILSF_LISTTEXT: | |
10ee330a | 1218 | TRACE("MIXER_GETCONTROLDETAILSF_LISTTEXT (%08x)\n", |
6f4be549 | 1219 | lpmcd->dwControlID); |
2f9eb3b0 EP |
1220 | |
1221 | ret = MMSYSERR_INVALPARAM; | |
1222 | if (MIX_CheckControl(mix, lpmcd->dwControlID)) | |
1223 | { | |
aff83e69 | 1224 | DWORD c = lpmcd->dwControlID - 1; |
2f9eb3b0 EP |
1225 | |
1226 | if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX || | |
1227 | mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER) | |
1228 | { | |
b4e49557 | 1229 | LPMIXERCONTROLDETAILS_LISTTEXTW mcdlt; |
2f9eb3b0 EP |
1230 | int i, j; |
1231 | ||
c0a9281c | 1232 | mcdlt = lpmcd->paDetails; |
2f9eb3b0 EP |
1233 | for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++) |
1234 | { | |
1235 | if (WINE_CHN_SUPPORTS(mix->recMask, j)) | |
1236 | { | |
1237 | mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j); | |
1238 | mcdlt[i].dwParam2 = 0; | |
a9bf3813 | 1239 | MultiByteToWideChar(CP_UNIXCP, 0, MIX_Names[j], -1, |
b4e49557 | 1240 | mcdlt[i].szName, sizeof(mcdlt[i]) / sizeof(WCHAR)); |
2f9eb3b0 EP |
1241 | i++; |
1242 | } | |
1243 | } | |
1244 | if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n"); | |
1245 | ret = MMSYSERR_NOERROR; | |
1246 | } | |
1247 | } | |
54e4ba04 EP |
1248 | break; |
1249 | default: | |
6f4be549 RR |
1250 | WARN("Unknown flag (%08lx)\n", |
1251 | fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK); | |
54e4ba04 EP |
1252 | } |
1253 | return ret; | |
1254 | } | |
1255 | ||
1256 | /************************************************************************** | |
1257 | * MIX_SetControlDetails [internal] | |
1258 | */ | |
6f4be549 RR |
1259 | static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, |
1260 | DWORD fdwDetails) | |
54e4ba04 | 1261 | { |
2f9eb3b0 EP |
1262 | DWORD ret = MMSYSERR_NOTSUPPORTED; |
1263 | DWORD c, chnl; | |
1264 | int val; | |
1265 | struct mixer* mix; | |
9a624916 | 1266 | |
10ee330a | 1267 | TRACE("(%04X, %p, %u);\n", wDevID, lpmcd, fdwDetails); |
9a624916 | 1268 | |
5ae2835e RR |
1269 | if (lpmcd == NULL) { |
1270 | TRACE("invalid parameter: lpmcd == NULL\n"); | |
1271 | return MMSYSERR_INVALPARAM; | |
1272 | } | |
1273 | ||
1274 | if ((mix = MIX_Get(wDevID)) == NULL) { | |
b6aeb56d | 1275 | WARN("bad device ID: %04X\n", wDevID); |
5ae2835e RR |
1276 | return MMSYSERR_BADDEVICEID; |
1277 | } | |
9a624916 VB |
1278 | |
1279 | switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) | |
2f9eb3b0 | 1280 | { |
54e4ba04 | 1281 | case MIXER_GETCONTROLDETAILSF_VALUE: |
10ee330a | 1282 | TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%08x)\n", lpmcd->dwControlID); |
2f9eb3b0 EP |
1283 | if (MIX_CheckControl(mix, lpmcd->dwControlID)) |
1284 | { | |
1285 | c = lpmcd->dwControlID - 1; | |
6f4be549 | 1286 | |
10ee330a | 1287 | TRACE("dwLineID=%08x\n",mix->ctrl[c].dwLineID); |
6f4be549 | 1288 | |
2f9eb3b0 | 1289 | chnl = HIWORD(mix->ctrl[c].dwLineID); |
9a624916 | 1290 | if (chnl == LINEID_DST) |
6f4be549 RR |
1291 | chnl = LOWORD(mix->ctrl[c].dwLineID) ? |
1292 | SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME; | |
2f9eb3b0 | 1293 | |
9a624916 | 1294 | switch (mix->ctrl[c].ctrl.dwControlType) |
2f9eb3b0 | 1295 | { |
aa67ac90 EP |
1296 | case MIXERCONTROL_CONTROLTYPE_VOLUME: |
1297 | { | |
1298 | LPMIXERCONTROLDETAILS_UNSIGNED mcdu; | |
9a624916 | 1299 | |
b15504e1 | 1300 | if (lpmcd->cbDetails != |
6f4be549 | 1301 | sizeof(MIXERCONTROLDETAILS_UNSIGNED)) { |
1470955c ML |
1302 | WARN("invalid parameter: cbDetails = %d\n", |
1303 | lpmcd->cbDetails); | |
6f4be549 RR |
1304 | return MMSYSERR_INVALPARAM; |
1305 | } | |
1306 | ||
10ee330a | 1307 | TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n", |
6f4be549 RR |
1308 | getControlType(mix->ctrl[c].ctrl.dwControlType), |
1309 | lpmcd->cChannels); | |
1310 | ||
c0a9281c | 1311 | mcdu = lpmcd->paDetails; |
aa67ac90 | 1312 | /* val should contain 00RL */ |
9a624916 | 1313 | switch (lpmcd->cChannels) |
2f9eb3b0 | 1314 | { |
aa67ac90 EP |
1315 | case 1: |
1316 | /* mono... so R = L */ | |
10ee330a | 1317 | TRACE("Setting RL to %d\n", mcdu->dwValue); |
aa67ac90 EP |
1318 | val = 0x101 * ((mcdu->dwValue * 100) >> 16); |
1319 | break; | |
1320 | case 2: | |
1321 | /* stereo, left is paDetails[0] */ | |
10ee330a | 1322 | TRACE("Setting L to %d\n", mcdu->dwValue); |
79b1c4db | 1323 | val = ((mcdu->dwValue * 100.0) / 65536.0) + 0.5; |
6f4be549 | 1324 | mcdu++; |
10ee330a | 1325 | TRACE("Setting R to %d\n", mcdu->dwValue); |
79b1c4db | 1326 | val += (int)(((mcdu->dwValue * 100) / 65536.0) + 0.5) << 8; |
aa67ac90 EP |
1327 | break; |
1328 | default: | |
10ee330a | 1329 | WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels); |
aa67ac90 EP |
1330 | return MMSYSERR_INVALPARAM; |
1331 | } | |
9a624916 VB |
1332 | |
1333 | if (mix->volume[chnl] == -1) | |
2f9eb3b0 | 1334 | { |
6f4be549 RR |
1335 | if (!MIX_SetVal(mix, chnl, val)) { |
1336 | WARN("invalid parameter\n"); | |
aa67ac90 | 1337 | return MMSYSERR_INVALPARAM; |
6f4be549 | 1338 | } |
2f9eb3b0 | 1339 | } |
9a624916 | 1340 | else |
2f9eb3b0 EP |
1341 | { |
1342 | mix->volume[chnl] = val; | |
aa67ac90 EP |
1343 | } |
1344 | } | |
1345 | ret = MMSYSERR_NOERROR; | |
54e4ba04 | 1346 | break; |
aa67ac90 | 1347 | case MIXERCONTROL_CONTROLTYPE_MUTE: |
2f9eb3b0 | 1348 | case MIXERCONTROL_CONTROLTYPE_ONOFF: |
aa67ac90 EP |
1349 | { |
1350 | LPMIXERCONTROLDETAILS_BOOLEAN mcdb; | |
9a624916 | 1351 | |
b15504e1 | 1352 | if (lpmcd->cbDetails != |
6f4be549 | 1353 | sizeof(MIXERCONTROLDETAILS_BOOLEAN)) { |
1470955c ML |
1354 | WARN("invalid parameter: cbDetails = %d\n", |
1355 | lpmcd->cbDetails); | |
6f4be549 RR |
1356 | return MMSYSERR_INVALPARAM; |
1357 | } | |
1358 | ||
10ee330a | 1359 | TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", |
6f4be549 RR |
1360 | getControlType(mix->ctrl[c].ctrl.dwControlType), |
1361 | lpmcd->cChannels); | |
1362 | ||
c0a9281c | 1363 | mcdb = lpmcd->paDetails; |
9a624916 | 1364 | if (mcdb->fValue) |
2f9eb3b0 | 1365 | { |
6f4be549 RR |
1366 | /* save the volume and then set it to 0 */ |
1367 | if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) || | |
1368 | !MIX_SetVal(mix, chnl, 0)) { | |
1369 | WARN("invalid parameter\n"); | |
aa67ac90 | 1370 | return MMSYSERR_INVALPARAM; |
6f4be549 | 1371 | } |
9a624916 VB |
1372 | } |
1373 | else | |
2f9eb3b0 | 1374 | { |
9a624916 | 1375 | if (mix->volume[chnl] == -1) |
2f9eb3b0 EP |
1376 | { |
1377 | ret = MMSYSERR_NOERROR; | |
1378 | break; | |
276495e3 | 1379 | } |
6f4be549 RR |
1380 | if (!MIX_SetVal(mix, chnl, mix->volume[chnl])) { |
1381 | WARN("invalid parameter\n"); | |
aa67ac90 | 1382 | return MMSYSERR_INVALPARAM; |
6f4be549 | 1383 | } |
2f9eb3b0 | 1384 | mix->volume[chnl] = -1; |
aa67ac90 EP |
1385 | } |
1386 | } | |
1387 | ret = MMSYSERR_NOERROR; | |
54e4ba04 | 1388 | break; |
2f9eb3b0 EP |
1389 | case MIXERCONTROL_CONTROLTYPE_MIXER: |
1390 | case MIXERCONTROL_CONTROLTYPE_MUX: | |
1391 | { | |
1392 | LPMIXERCONTROLDETAILS_BOOLEAN mcdb; | |
1393 | unsigned mask; | |
1394 | int i, j; | |
1395 | ||
b15504e1 | 1396 | if (lpmcd->cbDetails != |
6f4be549 | 1397 | sizeof(MIXERCONTROLDETAILS_BOOLEAN)) { |
1470955c ML |
1398 | WARN("invalid parameter: cbDetails = %d\n", |
1399 | lpmcd->cbDetails); | |
6f4be549 RR |
1400 | return MMSYSERR_INVALPARAM; |
1401 | } | |
1402 | ||
10ee330a | 1403 | TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", |
6f4be549 RR |
1404 | getControlType(mix->ctrl[c].ctrl.dwControlType), |
1405 | lpmcd->cChannels); | |
1406 | ||
2f9eb3b0 | 1407 | /* we mute both channels at the same time */ |
c0a9281c | 1408 | mcdb = lpmcd->paDetails; |
2f9eb3b0 EP |
1409 | mask = 0; |
1410 | for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++) | |
1411 | { | |
6f4be549 RR |
1412 | if (WINE_CHN_SUPPORTS(mix->recMask, j) && |
1413 | mcdb[i++].fValue) | |
2f9eb3b0 EP |
1414 | { |
1415 | /* a mux can only select one line at a time... */ | |
9a624916 | 1416 | if (mix->singleRecChannel && mask != 0) |
2f9eb3b0 EP |
1417 | { |
1418 | FIXME("!!!\n"); | |
1419 | return MMSYSERR_INVALPARAM; | |
1420 | } | |
1421 | mask |= WINE_CHN_MASK(j); | |
1422 | } | |
1423 | } | |
6f4be549 RR |
1424 | if (i != lpmcd->u.cMultipleItems) |
1425 | FIXME("bad count\n"); | |
2f9eb3b0 | 1426 | TRACE("writing %04x as rec src\n", mask); |
9a624916 | 1427 | if (!MIX_SetRecSrc(mix, mask)) |
2f9eb3b0 EP |
1428 | ERR("Can't write new mixer settings\n"); |
1429 | else | |
1430 | ret = MMSYSERR_NOERROR; | |
1431 | } | |
1432 | break; | |
54e4ba04 | 1433 | } |
54e4ba04 | 1434 | } |
54e4ba04 | 1435 | break; |
54e4ba04 | 1436 | default: |
6f4be549 RR |
1437 | WARN("Unknown SetControlDetails flag (%08lx)\n", |
1438 | fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK); | |
54e4ba04 | 1439 | } |
2f9eb3b0 | 1440 | return ret; |
aa67ac90 EP |
1441 | } |
1442 | ||
1443 | /************************************************************************** | |
1444 | * MIX_Init [internal] | |
1445 | */ | |
c7a59d11 | 1446 | LRESULT OSS_MixerInit(void) |
aa67ac90 | 1447 | { |
b15504e1 | 1448 | int i, mixer; |
c1e3bbff | 1449 | |
5ae2835e RR |
1450 | TRACE("()\n"); |
1451 | ||
b15504e1 RR |
1452 | MIX_NumMixers = 0; |
1453 | ||
1454 | for (i = 0; i < MAX_MIXERDRV; i++) { | |
1455 | char name[32]; | |
1456 | ||
1457 | if (i == 0) | |
1458 | sprintf(name, "/dev/mixer"); | |
1459 | else | |
1460 | sprintf(name, "/dev/mixer%d", i); | |
1461 | ||
1462 | if ((mixer = open(name, O_RDWR)) >= 0) { | |
1463 | #ifdef SOUND_MIXER_INFO | |
1464 | mixer_info info; | |
1465 | if (ioctl(mixer, SOUND_MIXER_INFO, &info) >= 0) { | |
b6aeb56d RR |
1466 | MIX_Mixers[MIX_NumMixers].name = HeapAlloc(GetProcessHeap(),0,strlen(info.name) + 1); |
1467 | strcpy(MIX_Mixers[MIX_NumMixers].name, info.name); | |
b15504e1 RR |
1468 | } else { |
1469 | /* FreeBSD up to at least 5.2 provides this ioctl, but does not | |
1470 | * implement it properly, and there are probably similar issues | |
1471 | * on other platforms, so we warn but try to go ahead. | |
1472 | */ | |
1473 | WARN("%s: cannot read SOUND_MIXER_INFO!\n", name); | |
1474 | } | |
1475 | #endif | |
1476 | close(mixer); | |
1477 | ||
b6aeb56d RR |
1478 | MIX_Mixers[MIX_NumMixers].dev_name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1); |
1479 | strcpy(MIX_Mixers[MIX_NumMixers].dev_name, name); | |
b15504e1 | 1480 | MIX_NumMixers++; |
b6aeb56d | 1481 | MIX_Open(MIX_NumMixers - 1, NULL, 0); /* FIXME */ |
b15504e1 RR |
1482 | } else { |
1483 | WARN("couldn't open %s\n", name); | |
1484 | } | |
aa67ac90 | 1485 | } |
b15504e1 RR |
1486 | |
1487 | if (MIX_NumMixers == 0) { | |
1488 | WARN("no driver\n"); | |
1489 | return MMSYSERR_NODRIVER; | |
1490 | } | |
1491 | ||
1492 | return MMSYSERR_NOERROR; | |
1493 | } | |
1494 | ||
1495 | /************************************************************************** | |
1496 | * MIX_Exit [internal] | |
1497 | */ | |
c7a59d11 | 1498 | LRESULT OSS_MixerExit(void) |
b15504e1 RR |
1499 | { |
1500 | int i; | |
1501 | ||
1502 | TRACE("()\n"); | |
1503 | ||
1504 | for (i = 0; i < MIX_NumMixers; i++) { | |
15a308a9 MS |
1505 | HeapFree(GetProcessHeap(),0,MIX_Mixers[i].name); |
1506 | HeapFree(GetProcessHeap(),0,MIX_Mixers[i].dev_name); | |
b15504e1 RR |
1507 | } |
1508 | ||
aa67ac90 | 1509 | return MMSYSERR_NOERROR; |
c1e3bbff | 1510 | } |
aa67ac90 | 1511 | |
2f9eb3b0 EP |
1512 | /************************************************************************** |
1513 | * MIX_GetNumDevs [internal] | |
1514 | */ | |
1515 | static DWORD MIX_GetNumDevs(void) | |
1516 | { | |
5ae2835e RR |
1517 | TRACE("()\n"); |
1518 | ||
2f9eb3b0 EP |
1519 | return MIX_NumMixers; |
1520 | } | |
1521 | ||
54e4ba04 | 1522 | #endif /* HAVE_OSS */ |
31c28665 | 1523 | |
491502b9 | 1524 | /************************************************************************** |
b9f0fce6 | 1525 | * mxdMessage (WINEOSS.3) |
491502b9 | 1526 | */ |
c4b52339 ML |
1527 | DWORD WINAPI OSS_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser, |
1528 | DWORD_PTR dwParam1, DWORD_PTR dwParam2) | |
491502b9 | 1529 | { |
80427737 | 1530 | #ifdef HAVE_OSS |
c4b52339 | 1531 | TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg), |
6f4be549 | 1532 | dwUser, dwParam1, dwParam2); |
9a624916 | 1533 | |
9a624916 | 1534 | switch (wMsg) |
2f9eb3b0 | 1535 | { |
22d333d8 EP |
1536 | case DRVM_INIT: |
1537 | case DRVM_EXIT: | |
1538 | case DRVM_ENABLE: | |
1539 | case DRVM_DISABLE: | |
1540 | /* FIXME: Pretend this is supported */ | |
1541 | return 0; | |
9a624916 | 1542 | case MXDM_GETDEVCAPS: |
c4b52339 | 1543 | return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2); |
54e4ba04 | 1544 | case MXDM_GETLINEINFO: |
b4e49557 | 1545 | return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2); |
54e4ba04 | 1546 | case MXDM_GETNUMDEVS: |
2f9eb3b0 | 1547 | return MIX_GetNumDevs(); |
54e4ba04 | 1548 | case MXDM_OPEN: |
2f9eb3b0 EP |
1549 | return MMSYSERR_NOERROR; |
1550 | /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */ | |
54e4ba04 EP |
1551 | case MXDM_CLOSE: |
1552 | return MMSYSERR_NOERROR; | |
1553 | case MXDM_GETLINECONTROLS: | |
b4e49557 | 1554 | return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2); |
54e4ba04 EP |
1555 | case MXDM_GETCONTROLDETAILS: |
1556 | return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2); | |
1557 | case MXDM_SETCONTROLDETAILS: | |
1558 | return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2); | |
1559 | default: | |
1560 | WARN("unknown message %d!\n", wMsg); | |
2f9eb3b0 | 1561 | return MMSYSERR_NOTSUPPORTED; |
54e4ba04 | 1562 | } |
54e4ba04 | 1563 | #else |
ba3054b6 | 1564 | TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, |
80427737 HL |
1565 | dwUser, dwParam1, dwParam2); |
1566 | ||
54e4ba04 | 1567 | return MMSYSERR_NOTENABLED; |
31c28665 | 1568 | #endif |
491502b9 | 1569 | } |