Added SHDoDragDrop() stub.
[wine/multimedia.git] / multimedia / mixer.c
blob686adad2440a8d097d48d654d6d3abc260ce186a
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
4 * Sample MIXER Wine Driver for Linux
6 * Copyright 1997 Marcus Meissner
7 * 1999 Eric Pouech
8 */
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <sys/ioctl.h>
15 #include "windef.h"
16 #include "user.h"
17 #include "driver.h"
18 #include "multimedia.h"
19 #include "debugtools.h"
21 DEFAULT_DEBUG_CHANNEL(mmaux)
23 #ifdef HAVE_OSS
24 #define MIXER_DEV "/dev/mixer"
26 #define WINE_MIXER_MANUF_ID 0xAA
27 #define WINE_MIXER_PRODUCT_ID 0x55
28 #define WINE_MIXER_VERSION 0x0100
29 #define WINE_MIXER_NAME "WINE OSS Mixer"
31 #define WINE_CHN_MASK(_x) (1L << (_x))
32 #define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))
33 #define WINE_MIXER_MASK (WINE_CHN_MASK(SOUND_MIXER_VOLUME) | \
34 WINE_CHN_MASK(SOUND_MIXER_BASS) | \
35 WINE_CHN_MASK(SOUND_MIXER_TREBLE) | \
36 WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
37 WINE_CHN_MASK(SOUND_MIXER_PCM) | \
38 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
39 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
40 WINE_CHN_MASK(SOUND_MIXER_CD))
42 /**************************************************************************
43 * MIX_GetVal [internal]
45 static BOOL MIX_GetVal(int chn, int* val)
47 int mixer;
48 BOOL ret = FALSE;
50 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
51 /* FIXME: ENXIO => no mixer installed */
52 WARN("mixer device not available !\n");
53 } else {
54 if (ioctl(mixer, MIXER_READ(chn), val) >= 0) {
55 TRACE("Reading %x on %d\n", *val, chn);
56 ret = TRUE;
58 close(mixer);
60 return ret;
63 /**************************************************************************
64 * MIX_SetVal [internal]
66 static BOOL MIX_SetVal(int chn, int* val)
68 int mixer;
69 BOOL ret = FALSE;
71 TRACE("Writing %x on %d\n", *val, chn);
73 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
74 /* FIXME: ENXIO => no mixer installed */
75 WARN("mixer device not available !\n");
76 } else {
77 if (ioctl(mixer, MIXER_WRITE(chn), val) >= 0) {
78 ret = TRUE;
80 close(mixer);
82 return ret;
85 /**************************************************************************
86 * MIX_GetDevCaps [internal]
88 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSA lpCaps, DWORD dwSize)
90 int mixer, mask;
92 TRACE("(%04X, %p, %lu);\n", wDevID, lpCaps, dwSize);
94 if (wDevID != 0) return MMSYSERR_BADDEVICEID;
95 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
97 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
98 /* FIXME: ENXIO => no mixer installed */
99 WARN("mixer device not available !\n");
100 return MMSYSERR_NOTENABLED;
102 lpCaps->wMid = WINE_MIXER_MANUF_ID;
103 lpCaps->wPid = WINE_MIXER_PRODUCT_ID;
104 lpCaps->vDriverVersion = WINE_MIXER_VERSION;
105 strcpy(lpCaps->szPname, WINE_MIXER_NAME);
106 if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mask) == -1) {
107 close(mixer);
108 perror("ioctl mixer SOUND_MIXER_DEVMASK");
109 return MMSYSERR_NOTENABLED;
112 /* FIXME: can the Linux Mixer differ between multiple mixer targets ? */
113 lpCaps->cDestinations = 1;
114 lpCaps->fdwSupport = 0; /* No bits defined yet */
116 close(mixer);
117 return MMSYSERR_NOERROR;
120 static char *sdlabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
121 static char *sdnames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
123 /**************************************************************************
124 * MIX_GetLineInfoFromIndex [internal]
126 static void MIX_GetLineInfoFromIndex(LPMIXERLINEA lpMl, int devmask, DWORD idx)
128 strcpy(lpMl->szShortName, sdlabels[idx]);
129 strcpy(lpMl->szName, sdnames[idx]);
130 lpMl->dwLineID = idx;
131 lpMl->dwDestination = 0; /* index for speakers */
132 lpMl->cConnections = 1;
133 lpMl->cControls = 1;
134 switch (idx) {
135 case SOUND_MIXER_SYNTH:
136 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
137 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
138 break;
139 case SOUND_MIXER_CD:
140 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
141 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
142 break;
143 case SOUND_MIXER_LINE:
144 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
145 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
146 break;
147 case SOUND_MIXER_MIC:
148 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
149 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
150 break;
151 case SOUND_MIXER_PCM:
152 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
153 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
154 break;
155 default:
156 ERR("Index %ld not handled.\n", idx);
157 break;
161 /**************************************************************************
162 * MIX_GetLineInfo [internal]
164 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo)
166 int mixer, i, j;
167 int devmask, stereomask;
168 BOOL isDst = FALSE;
169 DWORD ret = MMSYSERR_NOERROR;
171 TRACE("(%04X, %p, %lu);\n", wDevID, lpMl, fdwInfo);
172 if (lpMl == NULL || lpMl->cbStruct != sizeof(*lpMl))
173 return MMSYSERR_INVALPARAM;
174 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
175 return MMSYSERR_NOTENABLED;
177 if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
178 close(mixer);
179 perror("ioctl mixer SOUND_MIXER_DEVMASK");
180 return MMSYSERR_NOTENABLED;
182 devmask &= WINE_MIXER_MASK;
183 if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &stereomask) == -1) {
184 close(mixer);
185 perror("ioctl mixer SOUND_MIXER_STEREODEVS");
186 return MMSYSERR_NOTENABLED;
188 stereomask &= WINE_MIXER_MASK;
190 #if 0
191 int recsrc, recmask;
193 if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &recsrc) == -1) {
194 close(mixer);
195 perror("ioctl mixer SOUND_MIXER_RECSRC");
196 return MMSYSERR_NOTENABLED;
199 if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &recmask) == -1) {
200 close(mixer);
201 perror("ioctl mixer SOUND_MIXER_RECMASK");
202 return MMSYSERR_NOTENABLED;
204 #endif
206 /* FIXME: set all the variables correctly... the lines below
207 * are very wrong...
209 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
210 lpMl->cChannels = 1;
211 lpMl->dwUser = 0;
212 lpMl->cControls = 1;
214 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
215 case MIXER_GETLINEINFOF_DESTINATION:
216 TRACE("DESTINATION (%08lx)\n", lpMl->dwDestination);
217 /* FIXME: Linux doesn't seem to support multiple outputs?
218 * So we have only one output type: Speaker.
220 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
221 lpMl->dwSource = 0xFFFFFFFF;
222 lpMl->dwLineID = SOUND_MIXER_VOLUME;
223 strncpy(lpMl->szShortName, sdlabels[SOUND_MIXER_VOLUME], MIXER_SHORT_NAME_CHARS);
224 strncpy(lpMl->szName, sdnames[SOUND_MIXER_VOLUME], MIXER_LONG_NAME_CHARS);
226 /* we have all connections found in the devmask */
227 lpMl->cConnections = 0;
228 for (j = 1; j < SOUND_MIXER_NRDEVICES; j++)
229 if (WINE_CHN_SUPPORTS(devmask, j))
230 lpMl->cConnections++;
231 if (stereomask & WINE_CHN_MASK(SOUND_MIXER_VOLUME))
232 lpMl->cChannels++;
233 break;
234 case MIXER_GETLINEINFOF_SOURCE:
235 TRACE("SOURCE (%08lx)\n", lpMl->dwSource);
236 i = lpMl->dwSource;
237 for (j = 1; j < SOUND_MIXER_NRDEVICES; j++) {
238 if (WINE_CHN_SUPPORTS(devmask, j) && (i-- == 0))
239 break;
241 if (j >= SOUND_MIXER_NRDEVICES)
242 return MIXERR_INVALLINE;
243 if (WINE_CHN_SUPPORTS(stereomask, j))
244 lpMl->cChannels++;
245 MIX_GetLineInfoFromIndex(lpMl, devmask, j);
246 break;
247 case MIXER_GETLINEINFOF_LINEID:
248 TRACE("LINEID (%08lx)\n", lpMl->dwLineID);
249 if (lpMl->dwLineID >= SOUND_MIXER_NRDEVICES)
250 return MIXERR_INVALLINE;
251 if (WINE_CHN_SUPPORTS(stereomask, lpMl->dwLineID))
252 lpMl->cChannels++;
253 MIX_GetLineInfoFromIndex(lpMl, devmask, lpMl->dwLineID);
254 break;
255 case MIXER_GETLINEINFOF_COMPONENTTYPE:
256 TRACE("COMPONENT TYPE (%08lx)\n", lpMl->dwComponentType);
258 switch (lpMl->dwComponentType) {
259 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
260 i = SOUND_MIXER_VOLUME;
261 lpMl->dwDestination = 0;
262 lpMl->dwSource = 0xFFFFFFFF;
263 isDst = TRUE;
264 break;
265 case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
266 i = SOUND_MIXER_SYNTH;
267 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
268 break;
269 case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
270 i = SOUND_MIXER_CD;
271 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
272 break;
273 case MIXERLINE_COMPONENTTYPE_SRC_LINE:
274 i = SOUND_MIXER_LINE;
275 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
276 break;
277 case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
278 i = SOUND_MIXER_MIC;
279 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
280 break;
281 case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
282 i = SOUND_MIXER_PCM;
283 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
284 break;
285 default:
286 FIXME("Unhandled component type (%08lx)\n", lpMl->dwComponentType);
287 return MMSYSERR_INVALPARAM;
290 if (WINE_CHN_SUPPORTS(devmask, i)) {
291 strcpy(lpMl->szShortName, sdlabels[i]);
292 strcpy(lpMl->szName, sdnames[i]);
293 lpMl->dwLineID = i;
295 if (WINE_CHN_SUPPORTS(stereomask, i))
296 lpMl->cChannels++;
297 lpMl->cConnections = 0;
298 if (isDst) {
299 for (j = 1; j < SOUND_MIXER_NRDEVICES; j++) {
300 if (WINE_CHN_SUPPORTS(devmask, j)) {
301 lpMl->cConnections++;
305 break;
306 case MIXER_GETLINEINFOF_TARGETTYPE:
307 FIXME("_TARGETTYPE not implemented yet.\n");
308 break;
309 default:
310 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
311 break;
314 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_AUX;
315 lpMl->Target.dwDeviceID = 0xFFFFFFFF;
316 lpMl->Target.wMid = WINE_MIXER_MANUF_ID;
317 lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID;
318 lpMl->Target.vDriverVersion = WINE_MIXER_VERSION;
319 strcpy(lpMl->Target.szPname, WINE_MIXER_NAME);
321 close(mixer);
322 return ret;
325 /**************************************************************************
326 * MIX_GetLineInfo [internal]
328 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
330 TRACE("(%04X, %p, %lu);\n", wDevID, lpMod, flags);
331 if (lpMod == NULL) return MMSYSERR_INVALPARAM;
332 /* hmm. We don't keep the mixer device open. So just pretend it works */
333 return MMSYSERR_NOERROR;
336 /**************************************************************************
337 * MIX_GetLineControls [internal]
339 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, DWORD flags)
341 LPMIXERCONTROLA mc;
343 TRACE("(%04X, %p, %lu): stub!\n", wDevID, lpMlc, flags);
345 if (lpMlc == NULL) return MMSYSERR_INVALPARAM;
346 if (lpMlc->cbStruct < sizeof(*lpMlc) ||
347 lpMlc->cbmxctrl < sizeof(MIXERCONTROLA))
348 return MMSYSERR_INVALPARAM;
350 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK) {
351 case MIXER_GETLINECONTROLSF_ALL:
352 TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc->dwLineID, lpMlc->cControls);
353 if (lpMlc->cControls != 1)
354 return MMSYSERR_INVALPARAM;
355 break;
356 case MIXER_GETLINECONTROLSF_ONEBYID:
357 TRACE("line=%08lx GLCF_ONEBYID (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
358 if (lpMlc->u.dwControlID != 0)
359 return MMSYSERR_INVALPARAM;
360 break;
361 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
362 TRACE("line=%08lx GLCF_ONEBYTYPE (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlType);
363 if ((lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK) != MIXERCONTROL_CT_CLASS_FADER)
364 return MMSYSERR_INVALPARAM;
365 break;
366 default:
367 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
368 return MMSYSERR_INVALPARAM;
370 TRACE("Returning volume control\n");
371 /* currently, OSS only provides 1 control per line
372 * so one by id == one by type == all
374 mc = lpMlc->pamxctrl;
375 mc->cbStruct = sizeof(MIXERCONTROLA);
376 /* since we always have a single control per line, we'll use for controlID the lineID */
377 mc->dwControlID = lpMlc->dwLineID;
378 mc->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
379 mc->fdwControl = 0;
380 mc->cMultipleItems = 0;
381 strncpy(mc->szShortName, "Vol", MIXER_SHORT_NAME_CHARS);
382 strncpy(mc->szName, "Volume", MIXER_LONG_NAME_CHARS);
383 memset(&mc->Bounds, 0, sizeof(mc->Bounds));
384 /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
385 * [0, 100] is the range supported by OSS
386 * FIXME: sounds like MIXERCONTROL_CONTROLTYPE_VOLUME is always between 0 and 65536...
387 * look at conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
389 mc->Bounds.dw.dwMinimum = 0;
390 mc->Bounds.dw.dwMaximum = 100;
391 memset(&mc->Metrics, 0, sizeof(mc->Metrics));
392 mc->Metrics.cSteps = 0;
393 return MMSYSERR_NOERROR;
396 /**************************************************************************
397 * MIX_GetControlDetails [internal]
399 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
401 DWORD ret = MMSYSERR_NOTSUPPORTED;
403 TRACE("(%04X, %p, %lu)\n", wDevID, lpmcd, fdwDetails);
405 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
407 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
408 case MIXER_GETCONTROLDETAILSF_VALUE:
409 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
411 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
412 int val;
414 /* ControlID == LineID == OSS mixer channel */
415 /* return value is 00RL (4 bytes)... */
416 if (!MIX_GetVal(lpmcd->dwControlID, &val))
417 return MMSYSERR_INVALPARAM;
419 switch (lpmcd->cChannels) {
420 case 1:
421 /* mono... so R = L */
422 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
423 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
424 break;
425 case 2:
426 /* stereo, left is paDetails[0] */
427 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
428 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
429 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
430 mcdu->dwValue = (HIBYTE(LOWORD(val)) * 65536L) / 100;
431 break;
432 default:
433 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
434 return MMSYSERR_INVALPARAM;
436 TRACE("=> %08lx\n", mcdu->dwValue);
438 ret = MMSYSERR_NOERROR;
439 break;
440 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
441 FIXME("NIY\n");
442 break;
443 default:
444 WARN("Unknown flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
446 return ret;
449 /**************************************************************************
450 * MIX_SetControlDetails [internal]
452 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
454 DWORD ret = MMSYSERR_NOTSUPPORTED;
456 TRACE("(%04X, %p, %lu)\n", wDevID, lpmcd, fdwDetails);
458 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
460 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
461 case MIXER_GETCONTROLDETAILSF_VALUE:
462 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
464 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
465 int val;
467 /* val should contain 00RL */
468 switch (lpmcd->cChannels) {
469 case 1:
470 /* mono... so R = L */
471 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
472 TRACE("Setting RL to %08ld\n", mcdu->dwValue);
473 val = 0x101 * ((mcdu->dwValue * 100) >> 16);
474 break;
475 case 2:
476 /* stereo, left is paDetails[0] */
477 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
478 TRACE("Setting L to %08ld\n", mcdu->dwValue);
479 val = ((mcdu->dwValue * 100) >> 16);
480 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
481 TRACE("Setting R to %08ld\n", mcdu->dwValue);
482 val += ((mcdu->dwValue * 100) >> 16) << 8;
483 break;
484 default:
485 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
486 return MMSYSERR_INVALPARAM;
489 /* ControlID == LineID == OSS mixer channel */
490 if (!MIX_SetVal(lpmcd->dwControlID, &val))
491 return MMSYSERR_INVALPARAM;
493 ret = MMSYSERR_NOERROR;
494 break;
495 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
496 FIXME("NIY\n");
497 break;
498 default:
499 WARN("Unknown GetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
501 return MMSYSERR_NOTSUPPORTED;
504 #endif /* HAVE_OSS */
506 /**************************************************************************
507 * mixMessage [sample driver]
509 DWORD WINAPI mixMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
510 DWORD dwParam1, DWORD dwParam2)
512 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
513 wDevID, wMsg, dwUser, dwParam1, dwParam2);
515 #ifdef HAVE_OSS
516 switch(wMsg) {
517 case MXDM_GETDEVCAPS:
518 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSA)dwParam1, dwParam2);
519 case MXDM_GETLINEINFO:
520 return MIX_GetLineInfo(wDevID, (LPMIXERLINEA)dwParam1, dwParam2);
521 case MXDM_GETNUMDEVS:
522 TRACE("return 1;\n");
523 return 1;
524 case MXDM_OPEN:
525 return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
526 case MXDM_CLOSE:
527 return MMSYSERR_NOERROR;
528 case MXDM_GETLINECONTROLS:
529 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSA)dwParam1, dwParam2);
530 case MXDM_GETCONTROLDETAILS:
531 return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
532 case MXDM_SETCONTROLDETAILS:
533 return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
534 default:
535 WARN("unknown message %d!\n", wMsg);
537 return MMSYSERR_NOTSUPPORTED;
538 #else
539 return MMSYSERR_NOTENABLED;
540 #endif