Fixed some issues found by winapi_check.
[wine/dcerpc.git] / dlls / winmm / wineoss / mixer.c
blob194c901ada704fab87f632b0e753118325c9e5db
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
4 * Sample MIXER Wine Driver for Linux
6 * Copyright 1997 Marcus Meissner
7 * 1999,2001 Eric Pouech
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.
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.
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
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <assert.h>
33 #ifdef HAVE_SYS_IOCTL_H
34 # include <sys/ioctl.h>
35 #endif
37 #include "windef.h"
38 #include "mmddk.h"
39 #include "oss.h"
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(mmaux);
44 #ifdef HAVE_OSS
46 #define WINE_MIXER_MANUF_ID 0xAA
47 #define WINE_MIXER_PRODUCT_ID 0x55
48 #define WINE_MIXER_VERSION 0x0100
49 #define WINE_MIXER_NAME "WINE OSS Mixer"
51 #define WINE_CHN_MASK(_x) (1L << (_x))
52 #define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))
53 /* Bass and Treble are no longer in the mask as Windows does not handle them */
54 #define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
55 WINE_CHN_MASK(SOUND_MIXER_PCM) | \
56 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
57 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
58 WINE_CHN_MASK(SOUND_MIXER_CD) )
60 #define WINE_MIXER_MASK_RECORD (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
61 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
62 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
63 WINE_CHN_MASK(SOUND_MIXER_IMIX) )
65 /* FIXME: the two following string arrays should be moved to a resource file in a string table */
66 /* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
67 static char* MIX_Labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
68 static char* MIX_Names [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
70 struct mixerCtrl
72 DWORD dwLineID;
73 MIXERCONTROLA ctrl;
76 struct mixer
78 const char* name;
79 int volume[SOUND_MIXER_NRDEVICES];
80 int devMask;
81 int stereoMask;
82 int recMask;
83 BOOL singleRecChannel;
84 struct mixerCtrl* ctrl;
85 int numCtrl;
88 #define LINEID_DST 0xFFFF
89 #define LINEID_SPEAKER 0x0000
90 #define LINEID_RECORD 0x0001
92 static int MIX_NumMixers;
93 static struct mixer MIX_Mixers[1];
95 /**************************************************************************
96 * MIX_FillLineControls [internal]
98 static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, DWORD dwType)
100 struct mixerCtrl* mc = &mix->ctrl[c];
101 int j;
103 mc->dwLineID = lineID;
104 mc->ctrl.cbStruct = sizeof(MIXERCONTROLA);
105 mc->ctrl.dwControlID = c + 1;
106 mc->ctrl.dwControlType = dwType;
108 switch (dwType)
110 case MIXERCONTROL_CONTROLTYPE_VOLUME:
111 mc->ctrl.fdwControl = 0;
112 mc->ctrl.cMultipleItems = 0;
113 lstrcpynA(mc->ctrl.szShortName, "Vol", MIXER_SHORT_NAME_CHARS);
114 lstrcpynA(mc->ctrl.szName, "Volume", MIXER_LONG_NAME_CHARS);
115 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
116 /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
117 * [0, 100] is the range supported by OSS
118 * whatever the min and max values are they must match
119 * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
121 mc->ctrl.Bounds.s1.dwMinimum = 0;
122 mc->ctrl.Bounds.s1.dwMaximum = 65535;
123 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
124 break;
125 case MIXERCONTROL_CONTROLTYPE_MUTE:
126 case MIXERCONTROL_CONTROLTYPE_ONOFF:
127 mc->ctrl.fdwControl = 0;
128 mc->ctrl.cMultipleItems = 0;
129 lstrcpynA(mc->ctrl.szShortName, "Mute", MIXER_SHORT_NAME_CHARS);
130 lstrcpynA(mc->ctrl.szName, "Mute", MIXER_LONG_NAME_CHARS);
131 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
132 mc->ctrl.Bounds.s1.dwMinimum = 0;
133 mc->ctrl.Bounds.s1.dwMaximum = 1;
134 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
135 break;
136 case MIXERCONTROL_CONTROLTYPE_MUX:
137 case MIXERCONTROL_CONTROLTYPE_MIXER:
138 mc->ctrl.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
139 mc->ctrl.cMultipleItems = 0;
140 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
141 if (WINE_CHN_SUPPORTS(mix->recMask, j))
142 mc->ctrl.cMultipleItems++;
143 lstrcpynA(mc->ctrl.szShortName, "Mixer", MIXER_SHORT_NAME_CHARS);
144 lstrcpynA(mc->ctrl.szName, "Mixer", MIXER_LONG_NAME_CHARS);
145 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
146 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
147 break;
149 default:
150 FIXME("Internal error: unknown type: %08lx\n", dwType);
152 TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c + 1, dwType, lineID);
155 /******************************************************************
156 * MIX_GetMixer
160 static struct mixer* MIX_Get(WORD wDevID)
162 if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].name == NULL) return NULL;
163 return &MIX_Mixers[wDevID];
166 /**************************************************************************
167 * MIX_Open [internal]
169 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
171 int mixer, i, j;
172 unsigned caps;
173 struct mixer* mix;
174 DWORD ret = MMSYSERR_NOERROR;
176 TRACE("(%04X, %p, %lu);\n", wDevID, lpMod, flags);
178 /* as we partly init the mixer with MIX_Open, we can allow null open decs */
179 /* EPP if (lpMod == NULL) return MMSYSERR_INVALPARAM; */
180 /* anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer device before sending
181 * messages to it... it seems to be linked to all the equivalent of mixer identification
182 * (with a reference to a wave, midi.. handle
184 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
186 if ((mixer = open(mix->name, O_RDWR)) < 0)
188 if (errno == ENODEV || errno == ENXIO)
190 /* no driver present */
191 return MMSYSERR_NODRIVER;
193 return MMSYSERR_ERROR;
196 if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1)
198 perror("ioctl mixer SOUND_MIXER_DEVMASK");
199 ret = MMSYSERR_ERROR;
200 goto error;
202 mix->devMask &= WINE_MIXER_MASK_SPEAKER;
203 if (mix->devMask == 0)
205 ret = MMSYSERR_NODRIVER;
206 goto error;
209 if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1)
211 perror("ioctl mixer SOUND_MIXER_STEREODEVS");
212 ret = MMSYSERR_ERROR;
213 goto error;
215 mix->stereoMask &= WINE_MIXER_MASK_SPEAKER;
217 if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1)
219 perror("ioctl mixer SOUND_MIXER_RECMASK");
220 ret = MMSYSERR_ERROR;
221 goto error;
223 mix->recMask &= WINE_MIXER_MASK_RECORD;
224 /* FIXME: we may need to support both rec lev & igain */
225 if (!WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_RECLEV))
227 WARN("The sound card doesn't support rec level\n");
228 if (WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_IGAIN))
229 WARN("but it does support IGain, please report\n");
231 if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1)
233 perror("ioctl mixer SOUND_MIXER_READ_CAPS");
234 ret = MMSYSERR_ERROR;
235 goto error;
237 mix->singleRecChannel = caps & SOUND_CAP_EXCL_INPUT;
238 TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
239 mix->devMask, mix->recMask, mix->stereoMask,
240 mix->singleRecChannel ? "single" : "multiple");
241 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
243 mix->volume[i] = -1;
245 mix->numCtrl = 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
246 /* FIXME: do we always have RECLEV on all cards ??? */
247 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
249 if (WINE_CHN_SUPPORTS(mix->devMask, i))
250 mix->numCtrl += 2; /* volume & mute */
251 if (WINE_CHN_SUPPORTS(mix->recMask, i))
252 mix->numCtrl += 2; /* volume & onoff */
255 if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), 0, sizeof(mix->ctrl[0]) * mix->numCtrl)))
257 ret = MMSYSERR_NOMEM;
258 goto error;
261 j = 0;
262 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_VOLUME);
263 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE);
264 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST),
265 mix->singleRecChannel ?
266 MIXERCONTROL_CONTROLTYPE_MUX : MIXERCONTROL_CONTROLTYPE_MIXER);
267 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
268 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
270 if (WINE_CHN_SUPPORTS(mix->devMask, i))
272 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
273 MIXERCONTROL_CONTROLTYPE_VOLUME);
274 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
275 MIXERCONTROL_CONTROLTYPE_MUTE);
278 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
280 if (WINE_CHN_SUPPORTS(mix->recMask, i))
282 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
283 MIXERCONTROL_CONTROLTYPE_VOLUME);
284 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
285 MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
288 assert(j == mix->numCtrl);
289 error:
290 close(mixer);
291 return ret;
294 /**************************************************************************
295 * MIX_GetVal [internal]
297 static BOOL MIX_GetVal(struct mixer* mix, int chn, int* val)
299 int mixer;
300 BOOL ret = FALSE;
302 if ((mixer = open(mix->name, O_RDWR)) < 0)
304 /* FIXME: ENXIO => no mixer installed */
305 WARN("mixer device not available !\n");
307 else
309 if (ioctl(mixer, MIXER_READ(chn), val) >= 0)
311 TRACE("Reading volume %x on %d\n", *val, chn);
312 ret = TRUE;
314 close(mixer);
316 return ret;
319 /**************************************************************************
320 * MIX_SetVal [internal]
322 static BOOL MIX_SetVal(struct mixer* mix, int chn, int val)
324 int mixer;
325 BOOL ret = FALSE;
327 TRACE("Writing volume %x on %d\n", val, chn);
329 if ((mixer = open(mix->name, O_RDWR)) < 0)
331 /* FIXME: ENXIO => no mixer installed */
332 WARN("mixer device not available !\n");
334 else
336 if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0)
338 ret = TRUE;
340 close(mixer);
342 return ret;
345 /******************************************************************
346 * MIX_GetRecSrc
350 static BOOL MIX_GetRecSrc(struct mixer* mix, unsigned* mask)
352 int mixer;
353 BOOL ret = FALSE;
355 if ((mixer = open(mix->name, O_RDWR)) >= 0)
357 if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) ret = TRUE;
358 close(mixer);
360 return ret;
363 /******************************************************************
364 * MIX_SetRecSrc
368 static BOOL MIX_SetRecSrc(struct mixer* mix, unsigned mask)
370 int mixer;
371 BOOL ret = FALSE;
373 if ((mixer = open(mix->name, O_RDWR)) >= 0)
375 if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) < 0)
377 ERR("Can't write new mixer settings\n");
379 else
380 ret = TRUE;
381 close(mixer);
383 return ret;
386 /**************************************************************************
387 * MIX_GetDevCaps [internal]
389 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSA lpCaps, DWORD dwSize)
391 struct mixer* mix;
393 TRACE("(%04X, %p, %lu);\n", wDevID, lpCaps, dwSize);
395 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
396 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
398 lpCaps->wMid = WINE_MIXER_MANUF_ID;
399 lpCaps->wPid = WINE_MIXER_PRODUCT_ID;
400 lpCaps->vDriverVersion = WINE_MIXER_VERSION;
401 strcpy(lpCaps->szPname, WINE_MIXER_NAME);
403 lpCaps->cDestinations = 2; /* speakers & record */
404 lpCaps->fdwSupport = 0; /* No bits defined yet */
406 return MMSYSERR_NOERROR;
409 /**************************************************************************
410 * MIX_GetLineInfoDst [internal]
412 static DWORD MIX_GetLineInfoDst(struct mixer* mix, LPMIXERLINEA lpMl, DWORD dst)
414 unsigned mask;
415 int j;
417 lpMl->dwDestination = dst;
418 switch (dst)
420 case 0:
421 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
422 mask = mix->devMask;
423 j = SOUND_MIXER_VOLUME;
424 break;
425 case 1:
426 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
427 mask = mix->recMask;
428 j = SOUND_MIXER_RECLEV;
429 break;
430 default:
431 FIXME("shouldn't happen\n");
432 return MMSYSERR_ERROR;
434 lpMl->dwSource = 0xFFFFFFFF;
435 lstrcpynA(lpMl->szShortName, MIX_Labels[j], MIXER_SHORT_NAME_CHARS);
436 lstrcpynA(lpMl->szName, MIX_Names[j], MIXER_LONG_NAME_CHARS);
438 /* we have all connections found in the MIX_DevMask */
439 lpMl->cConnections = 0;
440 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
441 if (WINE_CHN_SUPPORTS(mask, j))
442 lpMl->cConnections++;
443 lpMl->cChannels = 1;
444 if (WINE_CHN_SUPPORTS(mix->stereoMask, lpMl->dwLineID))
445 lpMl->cChannels++;
446 lpMl->dwLineID = MAKELONG(dst, LINEID_DST);
447 lpMl->cControls = 0;
448 for (j = 0; j < mix->numCtrl; j++)
449 if (mix->ctrl[j].dwLineID == lpMl->dwLineID)
450 lpMl->cControls++;
452 return MMSYSERR_NOERROR;
455 /**************************************************************************
456 * MIX_GetLineInfoSrc [internal]
458 static DWORD MIX_GetLineInfoSrc(struct mixer* mix, LPMIXERLINEA lpMl, DWORD idx, DWORD dst)
460 int i, j;
461 unsigned mask = (dst) ? mix->recMask : mix->devMask;
463 strcpy(lpMl->szShortName, MIX_Labels[idx]);
464 strcpy(lpMl->szName, MIX_Names[idx]);
465 lpMl->dwLineID = MAKELONG(dst, idx);
466 lpMl->dwDestination = dst;
467 lpMl->cConnections = 1;
468 lpMl->cControls = 0;
469 for (i = 0; i < mix->numCtrl; i++)
470 if (mix->ctrl[i].dwLineID == lpMl->dwLineID)
471 lpMl->cControls++;
473 switch (idx)
475 case SOUND_MIXER_SYNTH:
476 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
477 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
478 break;
479 case SOUND_MIXER_CD:
480 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
481 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
482 break;
483 case SOUND_MIXER_LINE:
484 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
485 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
486 break;
487 case SOUND_MIXER_MIC:
488 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
489 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
490 break;
491 case SOUND_MIXER_PCM:
492 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
493 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
494 break;
495 case SOUND_MIXER_IMIX:
496 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
497 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
498 break;
499 default:
500 WARN("Index %ld not handled.\n", idx);
501 return MIXERR_INVALLINE;
503 lpMl->cChannels = 1;
504 if (dst == 0 && WINE_CHN_SUPPORTS(mix->stereoMask, idx))
505 lpMl->cChannels++;
506 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
508 if (WINE_CHN_SUPPORTS(mask, j))
510 if (j == idx) break;
511 i++;
514 lpMl->dwSource = i;
515 return MMSYSERR_NOERROR;
518 /******************************************************************
519 * MIX_CheckLine
521 static BOOL MIX_CheckLine(DWORD lineID)
523 return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) ||
524 (HIWORD(lineID) == LINEID_DST && LOWORD(lineID) < SOUND_MIXER_NRDEVICES));
527 /**************************************************************************
528 * MIX_GetLineInfo [internal]
530 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo)
532 int i, j;
533 DWORD ret = MMSYSERR_NOERROR;
534 unsigned mask;
535 struct mixer* mix;
537 TRACE("(%04X, %p, %lu);\n", wDevID, lpMl, fdwInfo);
539 if (lpMl == NULL || lpMl->cbStruct != sizeof(*lpMl))
540 return MMSYSERR_INVALPARAM;
541 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
543 /* FIXME: set all the variables correctly... the lines below
544 * are very wrong...
546 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
547 lpMl->dwUser = 0;
549 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
551 case MIXER_GETLINEINFOF_DESTINATION:
552 TRACE("DESTINATION (%08lx)\n", lpMl->dwDestination);
553 if (lpMl->dwDestination >= 2)
554 return MMSYSERR_INVALPARAM;
555 if ((ret = MIX_GetLineInfoDst(mix, lpMl, lpMl->dwDestination)) != MMSYSERR_NOERROR)
556 return ret;
557 break;
558 case MIXER_GETLINEINFOF_SOURCE:
559 TRACE("SOURCE (%08lx), dst=%08lx\n", lpMl->dwSource, lpMl->dwDestination);
560 switch (lpMl->dwDestination)
562 case 0: mask = mix->devMask; break;
563 case 1: mask = mix->recMask; break;
564 default: return MMSYSERR_INVALPARAM;
566 i = lpMl->dwSource;
567 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
569 if (WINE_CHN_SUPPORTS(mask, j) && (i-- == 0))
570 break;
572 if (j >= SOUND_MIXER_NRDEVICES)
573 return MIXERR_INVALLINE;
574 if ((ret = MIX_GetLineInfoSrc(mix, lpMl, j, lpMl->dwDestination)) != MMSYSERR_NOERROR)
575 return ret;
576 break;
577 case MIXER_GETLINEINFOF_LINEID:
578 TRACE("LINEID (%08lx)\n", lpMl->dwLineID);
580 if (!MIX_CheckLine(lpMl->dwLineID))
581 return MIXERR_INVALLINE;
582 if (HIWORD(lpMl->dwLineID) == LINEID_DST)
583 ret = MIX_GetLineInfoDst(mix, lpMl, LOWORD(lpMl->dwLineID));
584 else
585 ret = MIX_GetLineInfoSrc(mix, lpMl, HIWORD(lpMl->dwLineID), LOWORD(lpMl->dwLineID));
586 if (ret != MMSYSERR_NOERROR)
587 return ret;
588 break;
589 case MIXER_GETLINEINFOF_COMPONENTTYPE:
590 TRACE("COMPONENT TYPE (%08lx)\n", lpMl->dwComponentType);
591 switch (lpMl->dwComponentType)
593 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
594 ret = MIX_GetLineInfoDst(mix, lpMl, 0);
595 break;
596 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
597 ret = MIX_GetLineInfoDst(mix, lpMl, 1);
598 break;
599 case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
600 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_SYNTH, 0);
601 break;
602 case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
603 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_CD, 0);
604 break;
605 case MIXERLINE_COMPONENTTYPE_SRC_LINE:
606 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_LINE, 0);
607 break;
608 case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
609 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_MIC, 1);
610 break;
611 case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
612 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_PCM, 0);
613 break;
614 case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
615 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1);
616 break;
617 default:
618 FIXME("Unhandled component type (%08lx)\n", lpMl->dwComponentType);
619 return MMSYSERR_INVALPARAM;
621 break;
622 case MIXER_GETLINEINFOF_TARGETTYPE:
623 FIXME("_TARGETTYPE not implemented yet.\n");
624 break;
625 default:
626 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
627 break;
630 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_AUX; /* FIXME */
631 lpMl->Target.dwDeviceID = 0xFFFFFFFF;
632 lpMl->Target.wMid = WINE_MIXER_MANUF_ID;
633 lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID;
634 lpMl->Target.vDriverVersion = WINE_MIXER_VERSION;
635 strcpy(lpMl->Target.szPname, WINE_MIXER_NAME);
637 return ret;
640 /******************************************************************
641 * MIX_CheckControl
644 static BOOL MIX_CheckControl(struct mixer* mix, DWORD ctrlID)
646 return (ctrlID >= 1 && ctrlID <= mix->numCtrl);
649 /**************************************************************************
650 * MIX_GetLineControls [internal]
652 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, DWORD flags)
654 DWORD dwRet = MMSYSERR_NOERROR;
655 struct mixer* mix;
657 TRACE("(%04X, %p, %lu);\n", wDevID, lpMlc, flags);
659 if (lpMlc == NULL) return MMSYSERR_INVALPARAM;
660 if (lpMlc->cbStruct < sizeof(*lpMlc) ||
661 lpMlc->cbmxctrl < sizeof(MIXERCONTROLA))
662 return MMSYSERR_INVALPARAM;
663 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
665 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
667 case MIXER_GETLINECONTROLSF_ALL:
669 int i, j;
671 TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc->dwLineID, lpMlc->cControls);
673 for (i = j = 0; i < mix->numCtrl; i++)
675 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
676 j++;
678 if (!j || lpMlc->cControls != j) dwRet = MMSYSERR_INVALPARAM;
679 else if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
680 else
682 for (i = j = 0; i < mix->numCtrl; i++)
684 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
686 TRACE("[%d] => [%2d]: typ=%08lx\n", j, i + 1, mix->ctrl[i].ctrl.dwControlType);
687 lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl;
692 break;
693 case MIXER_GETLINECONTROLSF_ONEBYID:
694 TRACE("line=%08lx GLCF_ONEBYID (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
696 if (!MIX_CheckControl(mix, lpMlc->u.dwControlID) ||
697 mix->ctrl[lpMlc->u.dwControlID - 1].dwLineID != lpMlc->dwLineID)
698 dwRet = MMSYSERR_INVALPARAM;
699 else
700 lpMlc->pamxctrl[0] = mix->ctrl[lpMlc->u.dwControlID - 1].ctrl;
701 break;
702 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
703 TRACE("line=%08lx GLCF_ONEBYTYPE (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlType);
704 if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
705 else
707 int i;
708 DWORD ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
710 for (i = 0; i < mix->numCtrl; i++)
712 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID &&
713 ct == (mix->ctrl[i].ctrl.dwControlType & MIXERCONTROL_CT_CLASS_MASK))
715 lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl;
716 break;
719 if (i == mix->numCtrl) dwRet = MMSYSERR_INVALPARAM;
721 break;
722 default:
723 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
724 dwRet = MMSYSERR_INVALPARAM;
727 return dwRet;
730 /**************************************************************************
731 * MIX_GetControlDetails [internal]
733 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
735 DWORD ret = MMSYSERR_NOTSUPPORTED;
736 DWORD c, chnl;
737 struct mixer* mix;
739 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
741 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
742 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
744 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
746 case MIXER_GETCONTROLDETAILSF_VALUE:
747 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
748 if (MIX_CheckControl(mix, lpmcd->dwControlID))
750 c = lpmcd->dwControlID - 1;
751 chnl = HIWORD(mix->ctrl[c].dwLineID);
752 if (chnl == LINEID_DST)
753 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
754 switch (mix->ctrl[c].ctrl.dwControlType)
756 case MIXERCONTROL_CONTROLTYPE_VOLUME:
758 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
759 int val;
761 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
762 /* return value is 00RL (4 bytes)... */
763 if ((val = mix->volume[chnl]) == -1 && !MIX_GetVal(mix, chnl, &val))
764 return MMSYSERR_INVALPARAM;
766 switch (lpmcd->cChannels)
768 case 1:
769 /* mono... so R = L */
770 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
771 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
772 break;
773 case 2:
774 /* stereo, left is paDetails[0] */
775 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
776 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
777 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
778 mcdu->dwValue = (HIBYTE(LOWORD(val)) * 65536L) / 100;
779 break;
780 default:
781 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
782 return MMSYSERR_INVALPARAM;
784 TRACE("=> %08lx\n", mcdu->dwValue);
786 break;
787 case MIXERCONTROL_CONTROLTYPE_MUTE:
788 case MIXERCONTROL_CONTROLTYPE_ONOFF:
790 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
792 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
793 /* we mute both channels at the same time */
794 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
795 mcdb->fValue = (mix->volume[chnl] != -1);
796 TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
798 break;
799 case MIXERCONTROL_CONTROLTYPE_MIXER:
800 case MIXERCONTROL_CONTROLTYPE_MUX:
802 unsigned mask;
804 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
805 if (!MIX_GetRecSrc(mix, &mask))
807 /* FIXME: ENXIO => no mixer installed */
808 WARN("mixer device not available !\n");
809 ret = MMSYSERR_ERROR;
811 else
813 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
814 int i, j;
816 /* we mute both channels at the same time */
817 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
819 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
821 if (WINE_CHN_SUPPORTS(mix->recMask, j))
823 if (i >= lpmcd->u.cMultipleItems)
824 return MMSYSERR_INVALPARAM;
825 mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j);
830 break;
831 default:
832 WARN("Unsupported\n");
834 ret = MMSYSERR_NOERROR;
836 else
838 ret = MMSYSERR_INVALPARAM;
840 break;
841 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
842 TRACE("LIST TEXT (%08lx)\n", lpmcd->dwControlID);
844 ret = MMSYSERR_INVALPARAM;
845 if (MIX_CheckControl(mix, lpmcd->dwControlID))
847 int c = lpmcd->dwControlID - 1;
849 if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ||
850 mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
852 LPMIXERCONTROLDETAILS_LISTTEXTA mcdlt;
853 int i, j;
855 mcdlt = (LPMIXERCONTROLDETAILS_LISTTEXTA)lpmcd->paDetails;
856 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
858 if (WINE_CHN_SUPPORTS(mix->recMask, j))
860 mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j);
861 mcdlt[i].dwParam2 = 0;
862 strcpy(mcdlt[i].szName, MIX_Names[j]);
863 i++;
866 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
867 ret = MMSYSERR_NOERROR;
870 break;
871 default:
872 WARN("Unknown flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
874 return ret;
877 /**************************************************************************
878 * MIX_SetControlDetails [internal]
880 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
882 DWORD ret = MMSYSERR_NOTSUPPORTED;
883 DWORD c, chnl;
884 int val;
885 struct mixer* mix;
887 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
889 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
890 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
892 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
894 case MIXER_GETCONTROLDETAILSF_VALUE:
895 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
896 if (MIX_CheckControl(mix, lpmcd->dwControlID))
898 c = lpmcd->dwControlID - 1;
899 chnl = HIWORD(mix->ctrl[c].dwLineID);
900 if (chnl == LINEID_DST)
901 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
903 switch (mix->ctrl[c].ctrl.dwControlType)
905 case MIXERCONTROL_CONTROLTYPE_VOLUME:
907 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
909 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
910 /* val should contain 00RL */
911 switch (lpmcd->cChannels)
913 case 1:
914 /* mono... so R = L */
915 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
916 TRACE("Setting RL to %08ld\n", mcdu->dwValue);
917 val = 0x101 * ((mcdu->dwValue * 100) >> 16);
918 break;
919 case 2:
920 /* stereo, left is paDetails[0] */
921 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
922 TRACE("Setting L to %08ld\n", mcdu->dwValue);
923 val = ((mcdu->dwValue * 100) >> 16);
924 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
925 TRACE("Setting R to %08ld\n", mcdu->dwValue);
926 val += ((mcdu->dwValue * 100) >> 16) << 8;
927 break;
928 default:
929 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
930 return MMSYSERR_INVALPARAM;
933 if (mix->volume[chnl] == -1)
935 if (!MIX_SetVal(mix, chnl, val))
936 return MMSYSERR_INVALPARAM;
938 else
940 mix->volume[chnl] = val;
943 ret = MMSYSERR_NOERROR;
944 break;
945 case MIXERCONTROL_CONTROLTYPE_MUTE:
946 case MIXERCONTROL_CONTROLTYPE_ONOFF:
948 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
950 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
951 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
952 if (mcdb->fValue)
954 if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) || !MIX_SetVal(mix, chnl, 0))
955 return MMSYSERR_INVALPARAM;
957 else
959 if (mix->volume[chnl] == -1)
961 ret = MMSYSERR_NOERROR;
962 break;
964 if (!MIX_SetVal(mix, chnl, mix->volume[chnl]))
965 return MMSYSERR_INVALPARAM;
966 mix->volume[chnl] = -1;
969 ret = MMSYSERR_NOERROR;
970 break;
971 case MIXERCONTROL_CONTROLTYPE_MIXER:
972 case MIXERCONTROL_CONTROLTYPE_MUX:
974 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
975 unsigned mask;
976 int i, j;
978 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
979 /* we mute both channels at the same time */
980 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
981 mask = 0;
982 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
984 if (WINE_CHN_SUPPORTS(mix->recMask, j) && mcdb[i++].fValue)
986 /* a mux can only select one line at a time... */
987 if (mix->singleRecChannel && mask != 0)
989 FIXME("!!!\n");
990 return MMSYSERR_INVALPARAM;
992 mask |= WINE_CHN_MASK(j);
995 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
996 TRACE("writing %04x as rec src\n", mask);
997 if (!MIX_SetRecSrc(mix, mask))
998 ERR("Can't write new mixer settings\n");
999 else
1000 ret = MMSYSERR_NOERROR;
1002 break;
1005 break;
1006 default:
1007 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
1009 return ret;
1012 /**************************************************************************
1013 * MIX_Init [internal]
1015 static DWORD MIX_Init(void)
1017 int mixer;
1019 #define MIXER_DEV "/dev/mixer"
1020 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
1022 if (errno == ENODEV || errno == ENXIO)
1024 /* no driver present */
1025 return MMSYSERR_NODRIVER;
1027 MIX_NumMixers = 0;
1028 return MMSYSERR_ERROR;
1030 close(mixer);
1031 MIX_NumMixers = 1;
1032 MIX_Mixers[0].name = MIXER_DEV;
1033 MIX_Open(0, NULL, 0); /* FIXME */
1034 #undef MIXER_DEV
1035 return MMSYSERR_NOERROR;
1038 /**************************************************************************
1039 * MIX_GetNumDevs [internal]
1041 static DWORD MIX_GetNumDevs(void)
1043 return MIX_NumMixers;
1046 #endif /* HAVE_OSS */
1048 /**************************************************************************
1049 * mixMessage (WINEOSS.3)
1051 DWORD WINAPI OSS_mixMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1052 DWORD dwParam1, DWORD dwParam2)
1054 /* TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); */
1056 #ifdef HAVE_OSS
1057 switch (wMsg)
1059 case DRVM_INIT:
1060 return MIX_Init();
1061 case DRVM_EXIT:
1062 case DRVM_ENABLE:
1063 case DRVM_DISABLE:
1064 /* FIXME: Pretend this is supported */
1065 return 0;
1066 case MXDM_GETDEVCAPS:
1067 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSA)dwParam1, dwParam2);
1068 case MXDM_GETLINEINFO:
1069 return MIX_GetLineInfo(wDevID, (LPMIXERLINEA)dwParam1, dwParam2);
1070 case MXDM_GETNUMDEVS:
1071 return MIX_GetNumDevs();
1072 case MXDM_OPEN:
1073 return MMSYSERR_NOERROR;
1074 /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1075 case MXDM_CLOSE:
1076 return MMSYSERR_NOERROR;
1077 case MXDM_GETLINECONTROLS:
1078 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSA)dwParam1, dwParam2);
1079 case MXDM_GETCONTROLDETAILS:
1080 return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1081 case MXDM_SETCONTROLDETAILS:
1082 return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1083 default:
1084 WARN("unknown message %d!\n", wMsg);
1085 return MMSYSERR_NOTSUPPORTED;
1087 #else
1088 return MMSYSERR_NOTENABLED;
1089 #endif