Release 20030408.
[wine/gsoc-2012-control.git] / dlls / winmm / wineoss / mixer.c
blob685e8a6f47321168a1fda5ce97fa792a0043b354
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 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <assert.h>
35 #ifdef HAVE_SYS_IOCTL_H
36 # include <sys/ioctl.h>
37 #endif
39 #define NONAMELESSUNION
40 #define NONAMELESSSTRUCT
41 #include "windef.h"
42 #include "mmddk.h"
43 #include "oss.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(mmaux);
48 #ifdef HAVE_OSS
50 #define WINE_MIXER_MANUF_ID 0xAA
51 #define WINE_MIXER_PRODUCT_ID 0x55
52 #define WINE_MIXER_VERSION 0x0100
53 #define WINE_MIXER_NAME "WINE OSS Mixer"
55 #define WINE_CHN_MASK(_x) (1L << (_x))
56 #define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))
57 /* Bass and Treble are no longer in the mask as Windows does not handle them */
58 #define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
59 WINE_CHN_MASK(SOUND_MIXER_PCM) | \
60 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
61 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
62 WINE_CHN_MASK(SOUND_MIXER_CD) )
64 #define WINE_MIXER_MASK_RECORD (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
65 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
66 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
67 WINE_CHN_MASK(SOUND_MIXER_IMIX) )
69 /* FIXME: the two following string arrays should be moved to a resource file in a string table */
70 /* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
71 static char* MIX_Labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
72 static char* MIX_Names [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
74 struct mixerCtrl
76 DWORD dwLineID;
77 MIXERCONTROLA ctrl;
80 struct mixer
82 const char* name;
83 int volume[SOUND_MIXER_NRDEVICES];
84 int devMask;
85 int stereoMask;
86 int recMask;
87 BOOL singleRecChannel;
88 struct mixerCtrl* ctrl;
89 int numCtrl;
92 #define LINEID_DST 0xFFFF
93 #define LINEID_SPEAKER 0x0000
94 #define LINEID_RECORD 0x0001
96 static int MIX_NumMixers;
97 static struct mixer MIX_Mixers[1];
99 /**************************************************************************
100 * MIX_FillLineControls [internal]
102 static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, DWORD dwType)
104 struct mixerCtrl* mc = &mix->ctrl[c];
105 int j;
107 mc->dwLineID = lineID;
108 mc->ctrl.cbStruct = sizeof(MIXERCONTROLA);
109 mc->ctrl.dwControlID = c + 1;
110 mc->ctrl.dwControlType = dwType;
112 switch (dwType)
114 case MIXERCONTROL_CONTROLTYPE_VOLUME:
115 mc->ctrl.fdwControl = 0;
116 mc->ctrl.cMultipleItems = 0;
117 lstrcpynA(mc->ctrl.szShortName, "Vol", MIXER_SHORT_NAME_CHARS);
118 lstrcpynA(mc->ctrl.szName, "Volume", MIXER_LONG_NAME_CHARS);
119 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
120 /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
121 * [0, 100] is the range supported by OSS
122 * whatever the min and max values are they must match
123 * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
125 mc->ctrl.Bounds.s1.dwMinimum = 0;
126 mc->ctrl.Bounds.s1.dwMaximum = 65535;
127 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
128 break;
129 case MIXERCONTROL_CONTROLTYPE_MUTE:
130 case MIXERCONTROL_CONTROLTYPE_ONOFF:
131 mc->ctrl.fdwControl = 0;
132 mc->ctrl.cMultipleItems = 0;
133 lstrcpynA(mc->ctrl.szShortName, "Mute", MIXER_SHORT_NAME_CHARS);
134 lstrcpynA(mc->ctrl.szName, "Mute", MIXER_LONG_NAME_CHARS);
135 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
136 mc->ctrl.Bounds.s1.dwMinimum = 0;
137 mc->ctrl.Bounds.s1.dwMaximum = 1;
138 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
139 break;
140 case MIXERCONTROL_CONTROLTYPE_MUX:
141 case MIXERCONTROL_CONTROLTYPE_MIXER:
142 mc->ctrl.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
143 mc->ctrl.cMultipleItems = 0;
144 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
145 if (WINE_CHN_SUPPORTS(mix->recMask, j))
146 mc->ctrl.cMultipleItems++;
147 lstrcpynA(mc->ctrl.szShortName, "Mixer", MIXER_SHORT_NAME_CHARS);
148 lstrcpynA(mc->ctrl.szName, "Mixer", MIXER_LONG_NAME_CHARS);
149 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
150 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
151 break;
153 default:
154 FIXME("Internal error: unknown type: %08lx\n", dwType);
156 TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c + 1, dwType, lineID);
159 /******************************************************************
160 * MIX_GetMixer
164 static struct mixer* MIX_Get(WORD wDevID)
166 if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].name == NULL) return NULL;
167 return &MIX_Mixers[wDevID];
170 /**************************************************************************
171 * MIX_Open [internal]
173 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
175 int mixer, i, j;
176 unsigned caps;
177 struct mixer* mix;
178 DWORD ret = MMSYSERR_NOERROR;
180 TRACE("(%04X, %p, %lu);\n", wDevID, lpMod, flags);
182 /* as we partly init the mixer with MIX_Open, we can allow null open decs */
183 /* EPP if (lpMod == NULL) return MMSYSERR_INVALPARAM; */
184 /* anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer device before sending
185 * messages to it... it seems to be linked to all the equivalent of mixer identification
186 * (with a reference to a wave, midi.. handle
188 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
190 if ((mixer = open(mix->name, O_RDWR)) < 0)
192 if (errno == ENODEV || errno == ENXIO)
194 /* no driver present */
195 return MMSYSERR_NODRIVER;
197 return MMSYSERR_ERROR;
200 if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1)
202 perror("ioctl mixer SOUND_MIXER_DEVMASK");
203 ret = MMSYSERR_ERROR;
204 goto error;
206 mix->devMask &= WINE_MIXER_MASK_SPEAKER;
207 if (mix->devMask == 0)
209 ret = MMSYSERR_NODRIVER;
210 goto error;
213 if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1)
215 perror("ioctl mixer SOUND_MIXER_STEREODEVS");
216 ret = MMSYSERR_ERROR;
217 goto error;
219 mix->stereoMask &= WINE_MIXER_MASK_SPEAKER;
221 if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1)
223 perror("ioctl mixer SOUND_MIXER_RECMASK");
224 ret = MMSYSERR_ERROR;
225 goto error;
227 mix->recMask &= WINE_MIXER_MASK_RECORD;
228 /* FIXME: we may need to support both rec lev & igain */
229 if (!WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_RECLEV))
231 WARN("The sound card doesn't support rec level\n");
232 if (WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_IGAIN))
233 WARN("but it does support IGain, please report\n");
235 if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1)
237 perror("ioctl mixer SOUND_MIXER_READ_CAPS");
238 ret = MMSYSERR_ERROR;
239 goto error;
241 mix->singleRecChannel = caps & SOUND_CAP_EXCL_INPUT;
242 TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
243 mix->devMask, mix->recMask, mix->stereoMask,
244 mix->singleRecChannel ? "single" : "multiple");
245 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
247 mix->volume[i] = -1;
249 mix->numCtrl = 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
250 /* FIXME: do we always have RECLEV on all cards ??? */
251 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
253 if (WINE_CHN_SUPPORTS(mix->devMask, i))
254 mix->numCtrl += 2; /* volume & mute */
255 if (WINE_CHN_SUPPORTS(mix->recMask, i))
256 mix->numCtrl += 2; /* volume & onoff */
259 if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), 0, sizeof(mix->ctrl[0]) * mix->numCtrl)))
261 ret = MMSYSERR_NOMEM;
262 goto error;
265 j = 0;
266 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_VOLUME);
267 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE);
268 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST),
269 mix->singleRecChannel ?
270 MIXERCONTROL_CONTROLTYPE_MUX : MIXERCONTROL_CONTROLTYPE_MIXER);
271 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
272 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
274 if (WINE_CHN_SUPPORTS(mix->devMask, i))
276 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
277 MIXERCONTROL_CONTROLTYPE_VOLUME);
278 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
279 MIXERCONTROL_CONTROLTYPE_MUTE);
282 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
284 if (WINE_CHN_SUPPORTS(mix->recMask, i))
286 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
287 MIXERCONTROL_CONTROLTYPE_VOLUME);
288 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
289 MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
292 assert(j == mix->numCtrl);
293 error:
294 close(mixer);
295 return ret;
298 /**************************************************************************
299 * MIX_GetVal [internal]
301 static BOOL MIX_GetVal(struct mixer* mix, int chn, int* val)
303 int mixer;
304 BOOL ret = FALSE;
306 if ((mixer = open(mix->name, O_RDWR)) < 0)
308 /* FIXME: ENXIO => no mixer installed */
309 WARN("mixer device not available !\n");
311 else
313 if (ioctl(mixer, MIXER_READ(chn), val) >= 0)
315 TRACE("Reading volume %x on %d\n", *val, chn);
316 ret = TRUE;
318 close(mixer);
320 return ret;
323 /**************************************************************************
324 * MIX_SetVal [internal]
326 static BOOL MIX_SetVal(struct mixer* mix, int chn, int val)
328 int mixer;
329 BOOL ret = FALSE;
331 TRACE("Writing volume %x on %d\n", val, chn);
333 if ((mixer = open(mix->name, O_RDWR)) < 0)
335 /* FIXME: ENXIO => no mixer installed */
336 WARN("mixer device not available !\n");
338 else
340 if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0)
342 ret = TRUE;
344 close(mixer);
346 return ret;
349 /******************************************************************
350 * MIX_GetRecSrc
354 static BOOL MIX_GetRecSrc(struct mixer* mix, unsigned* mask)
356 int mixer;
357 BOOL ret = FALSE;
359 if ((mixer = open(mix->name, O_RDWR)) >= 0)
361 if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) ret = TRUE;
362 close(mixer);
364 return ret;
367 /******************************************************************
368 * MIX_SetRecSrc
372 static BOOL MIX_SetRecSrc(struct mixer* mix, unsigned mask)
374 int mixer;
375 BOOL ret = FALSE;
377 if ((mixer = open(mix->name, O_RDWR)) >= 0)
379 if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) < 0)
381 ERR("Can't write new mixer settings\n");
383 else
384 ret = TRUE;
385 close(mixer);
387 return ret;
390 /**************************************************************************
391 * MIX_GetDevCaps [internal]
393 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSA lpCaps, DWORD dwSize)
395 struct mixer* mix;
397 TRACE("(%04X, %p, %lu);\n", wDevID, lpCaps, dwSize);
399 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
400 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
402 lpCaps->wMid = WINE_MIXER_MANUF_ID;
403 lpCaps->wPid = WINE_MIXER_PRODUCT_ID;
404 lpCaps->vDriverVersion = WINE_MIXER_VERSION;
405 strcpy(lpCaps->szPname, WINE_MIXER_NAME);
407 lpCaps->cDestinations = 2; /* speakers & record */
408 lpCaps->fdwSupport = 0; /* No bits defined yet */
410 return MMSYSERR_NOERROR;
413 /**************************************************************************
414 * MIX_GetLineInfoDst [internal]
416 static DWORD MIX_GetLineInfoDst(struct mixer* mix, LPMIXERLINEA lpMl, DWORD dst)
418 unsigned mask;
419 int j;
421 lpMl->dwDestination = dst;
422 switch (dst)
424 case 0:
425 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
426 mask = mix->devMask;
427 j = SOUND_MIXER_VOLUME;
428 break;
429 case 1:
430 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
431 mask = mix->recMask;
432 j = SOUND_MIXER_RECLEV;
433 break;
434 default:
435 FIXME("shouldn't happen\n");
436 return MMSYSERR_ERROR;
438 lpMl->dwSource = 0xFFFFFFFF;
439 lstrcpynA(lpMl->szShortName, MIX_Labels[j], MIXER_SHORT_NAME_CHARS);
440 lstrcpynA(lpMl->szName, MIX_Names[j], MIXER_LONG_NAME_CHARS);
442 /* we have all connections found in the MIX_DevMask */
443 lpMl->cConnections = 0;
444 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
445 if (WINE_CHN_SUPPORTS(mask, j))
446 lpMl->cConnections++;
447 lpMl->cChannels = 1;
448 if (WINE_CHN_SUPPORTS(mix->stereoMask, lpMl->dwLineID))
449 lpMl->cChannels++;
450 lpMl->dwLineID = MAKELONG(dst, LINEID_DST);
451 lpMl->cControls = 0;
452 for (j = 0; j < mix->numCtrl; j++)
453 if (mix->ctrl[j].dwLineID == lpMl->dwLineID)
454 lpMl->cControls++;
456 return MMSYSERR_NOERROR;
459 /**************************************************************************
460 * MIX_GetLineInfoSrc [internal]
462 static DWORD MIX_GetLineInfoSrc(struct mixer* mix, LPMIXERLINEA lpMl, DWORD idx, DWORD dst)
464 int i, j;
465 unsigned mask = (dst) ? mix->recMask : mix->devMask;
467 strcpy(lpMl->szShortName, MIX_Labels[idx]);
468 strcpy(lpMl->szName, MIX_Names[idx]);
469 lpMl->dwLineID = MAKELONG(dst, idx);
470 lpMl->dwDestination = dst;
471 lpMl->cConnections = 1;
472 lpMl->cControls = 0;
473 for (i = 0; i < mix->numCtrl; i++)
474 if (mix->ctrl[i].dwLineID == lpMl->dwLineID)
475 lpMl->cControls++;
477 switch (idx)
479 case SOUND_MIXER_SYNTH:
480 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
481 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
482 break;
483 case SOUND_MIXER_CD:
484 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
485 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
486 break;
487 case SOUND_MIXER_LINE:
488 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
489 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
490 break;
491 case SOUND_MIXER_MIC:
492 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
493 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
494 break;
495 case SOUND_MIXER_PCM:
496 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
497 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
498 break;
499 case SOUND_MIXER_IMIX:
500 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
501 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
502 break;
503 default:
504 WARN("Index %ld not handled.\n", idx);
505 return MIXERR_INVALLINE;
507 lpMl->cChannels = 1;
508 if (dst == 0 && WINE_CHN_SUPPORTS(mix->stereoMask, idx))
509 lpMl->cChannels++;
510 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
512 if (WINE_CHN_SUPPORTS(mask, j))
514 if (j == idx) break;
515 i++;
518 lpMl->dwSource = i;
519 return MMSYSERR_NOERROR;
522 /******************************************************************
523 * MIX_CheckLine
525 static BOOL MIX_CheckLine(DWORD lineID)
527 return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) ||
528 (HIWORD(lineID) == LINEID_DST && LOWORD(lineID) < SOUND_MIXER_NRDEVICES));
531 /**************************************************************************
532 * MIX_GetLineInfo [internal]
534 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo)
536 int i, j;
537 DWORD ret = MMSYSERR_NOERROR;
538 unsigned mask;
539 struct mixer* mix;
541 TRACE("(%04X, %p, %lu);\n", wDevID, lpMl, fdwInfo);
543 if (lpMl == NULL || lpMl->cbStruct != sizeof(*lpMl))
544 return MMSYSERR_INVALPARAM;
545 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
547 /* FIXME: set all the variables correctly... the lines below
548 * are very wrong...
550 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
551 lpMl->dwUser = 0;
553 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
555 case MIXER_GETLINEINFOF_DESTINATION:
556 TRACE("DESTINATION (%08lx)\n", lpMl->dwDestination);
557 if (lpMl->dwDestination >= 2)
558 return MMSYSERR_INVALPARAM;
559 if ((ret = MIX_GetLineInfoDst(mix, lpMl, lpMl->dwDestination)) != MMSYSERR_NOERROR)
560 return ret;
561 break;
562 case MIXER_GETLINEINFOF_SOURCE:
563 TRACE("SOURCE (%08lx), dst=%08lx\n", lpMl->dwSource, lpMl->dwDestination);
564 switch (lpMl->dwDestination)
566 case 0: mask = mix->devMask; break;
567 case 1: mask = mix->recMask; break;
568 default: return MMSYSERR_INVALPARAM;
570 i = lpMl->dwSource;
571 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
573 if (WINE_CHN_SUPPORTS(mask, j) && (i-- == 0))
574 break;
576 if (j >= SOUND_MIXER_NRDEVICES)
577 return MIXERR_INVALLINE;
578 if ((ret = MIX_GetLineInfoSrc(mix, lpMl, j, lpMl->dwDestination)) != MMSYSERR_NOERROR)
579 return ret;
580 break;
581 case MIXER_GETLINEINFOF_LINEID:
582 TRACE("LINEID (%08lx)\n", lpMl->dwLineID);
584 if (!MIX_CheckLine(lpMl->dwLineID))
585 return MIXERR_INVALLINE;
586 if (HIWORD(lpMl->dwLineID) == LINEID_DST)
587 ret = MIX_GetLineInfoDst(mix, lpMl, LOWORD(lpMl->dwLineID));
588 else
589 ret = MIX_GetLineInfoSrc(mix, lpMl, HIWORD(lpMl->dwLineID), LOWORD(lpMl->dwLineID));
590 if (ret != MMSYSERR_NOERROR)
591 return ret;
592 break;
593 case MIXER_GETLINEINFOF_COMPONENTTYPE:
594 TRACE("COMPONENT TYPE (%08lx)\n", lpMl->dwComponentType);
595 switch (lpMl->dwComponentType)
597 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
598 ret = MIX_GetLineInfoDst(mix, lpMl, 0);
599 break;
600 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
601 ret = MIX_GetLineInfoDst(mix, lpMl, 1);
602 break;
603 case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
604 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_SYNTH, 0);
605 break;
606 case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
607 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_CD, 0);
608 break;
609 case MIXERLINE_COMPONENTTYPE_SRC_LINE:
610 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_LINE, 0);
611 break;
612 case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
613 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_MIC, 1);
614 break;
615 case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
616 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_PCM, 0);
617 break;
618 case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
619 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1);
620 break;
621 default:
622 FIXME("Unhandled component type (%08lx)\n", lpMl->dwComponentType);
623 return MMSYSERR_INVALPARAM;
625 break;
626 case MIXER_GETLINEINFOF_TARGETTYPE:
627 FIXME("_TARGETTYPE not implemented yet.\n");
628 break;
629 default:
630 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
631 break;
634 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_AUX; /* FIXME */
635 lpMl->Target.dwDeviceID = 0xFFFFFFFF;
636 lpMl->Target.wMid = WINE_MIXER_MANUF_ID;
637 lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID;
638 lpMl->Target.vDriverVersion = WINE_MIXER_VERSION;
639 strcpy(lpMl->Target.szPname, WINE_MIXER_NAME);
641 return ret;
644 /******************************************************************
645 * MIX_CheckControl
648 static BOOL MIX_CheckControl(struct mixer* mix, DWORD ctrlID)
650 return (ctrlID >= 1 && ctrlID <= mix->numCtrl);
653 /**************************************************************************
654 * MIX_GetLineControls [internal]
656 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, DWORD flags)
658 DWORD dwRet = MMSYSERR_NOERROR;
659 struct mixer* mix;
661 TRACE("(%04X, %p, %lu);\n", wDevID, lpMlc, flags);
663 if (lpMlc == NULL) return MMSYSERR_INVALPARAM;
664 if (lpMlc->cbStruct < sizeof(*lpMlc) ||
665 lpMlc->cbmxctrl < sizeof(MIXERCONTROLA))
666 return MMSYSERR_INVALPARAM;
667 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
669 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
671 case MIXER_GETLINECONTROLSF_ALL:
673 int i, j;
675 TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc->dwLineID, lpMlc->cControls);
677 for (i = j = 0; i < mix->numCtrl; i++)
679 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
680 j++;
682 if (!j || lpMlc->cControls != j) dwRet = MMSYSERR_INVALPARAM;
683 else if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
684 else
686 for (i = j = 0; i < mix->numCtrl; i++)
688 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
690 TRACE("[%d] => [%2d]: typ=%08lx\n", j, i + 1, mix->ctrl[i].ctrl.dwControlType);
691 lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl;
696 break;
697 case MIXER_GETLINECONTROLSF_ONEBYID:
698 TRACE("line=%08lx GLCF_ONEBYID (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
700 if (!MIX_CheckControl(mix, lpMlc->u.dwControlID) ||
701 mix->ctrl[lpMlc->u.dwControlID - 1].dwLineID != lpMlc->dwLineID)
702 dwRet = MMSYSERR_INVALPARAM;
703 else
704 lpMlc->pamxctrl[0] = mix->ctrl[lpMlc->u.dwControlID - 1].ctrl;
705 break;
706 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
707 TRACE("line=%08lx GLCF_ONEBYTYPE (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlType);
708 if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
709 else
711 int i;
712 DWORD ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
714 for (i = 0; i < mix->numCtrl; i++)
716 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID &&
717 ct == (mix->ctrl[i].ctrl.dwControlType & MIXERCONTROL_CT_CLASS_MASK))
719 lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl;
720 break;
723 if (i == mix->numCtrl) dwRet = MMSYSERR_INVALPARAM;
725 break;
726 default:
727 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
728 dwRet = MMSYSERR_INVALPARAM;
731 return dwRet;
734 /**************************************************************************
735 * MIX_GetControlDetails [internal]
737 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
739 DWORD ret = MMSYSERR_NOTSUPPORTED;
740 DWORD c, chnl;
741 struct mixer* mix;
743 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
745 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
746 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
748 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
750 case MIXER_GETCONTROLDETAILSF_VALUE:
751 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
752 if (MIX_CheckControl(mix, lpmcd->dwControlID))
754 c = lpmcd->dwControlID - 1;
755 chnl = HIWORD(mix->ctrl[c].dwLineID);
756 if (chnl == LINEID_DST)
757 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
758 switch (mix->ctrl[c].ctrl.dwControlType)
760 case MIXERCONTROL_CONTROLTYPE_VOLUME:
762 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
763 int val;
765 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
766 /* return value is 00RL (4 bytes)... */
767 if ((val = mix->volume[chnl]) == -1 && !MIX_GetVal(mix, chnl, &val))
768 return MMSYSERR_INVALPARAM;
770 switch (lpmcd->cChannels)
772 case 1:
773 /* mono... so R = L */
774 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
775 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
776 break;
777 case 2:
778 /* stereo, left is paDetails[0] */
779 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
780 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
781 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
782 mcdu->dwValue = (HIBYTE(LOWORD(val)) * 65536L) / 100;
783 break;
784 default:
785 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
786 return MMSYSERR_INVALPARAM;
788 TRACE("=> %08lx\n", mcdu->dwValue);
790 break;
791 case MIXERCONTROL_CONTROLTYPE_MUTE:
792 case MIXERCONTROL_CONTROLTYPE_ONOFF:
794 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
796 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
797 /* we mute both channels at the same time */
798 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
799 mcdb->fValue = (mix->volume[chnl] != -1);
800 TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
802 break;
803 case MIXERCONTROL_CONTROLTYPE_MIXER:
804 case MIXERCONTROL_CONTROLTYPE_MUX:
806 unsigned mask;
808 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
809 if (!MIX_GetRecSrc(mix, &mask))
811 /* FIXME: ENXIO => no mixer installed */
812 WARN("mixer device not available !\n");
813 ret = MMSYSERR_ERROR;
815 else
817 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
818 int i, j;
820 /* we mute both channels at the same time */
821 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
823 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
825 if (WINE_CHN_SUPPORTS(mix->recMask, j))
827 if (i >= lpmcd->u.cMultipleItems)
828 return MMSYSERR_INVALPARAM;
829 mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j);
834 break;
835 default:
836 WARN("Unsupported\n");
838 ret = MMSYSERR_NOERROR;
840 else
842 ret = MMSYSERR_INVALPARAM;
844 break;
845 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
846 TRACE("LIST TEXT (%08lx)\n", lpmcd->dwControlID);
848 ret = MMSYSERR_INVALPARAM;
849 if (MIX_CheckControl(mix, lpmcd->dwControlID))
851 int c = lpmcd->dwControlID - 1;
853 if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ||
854 mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
856 LPMIXERCONTROLDETAILS_LISTTEXTA mcdlt;
857 int i, j;
859 mcdlt = (LPMIXERCONTROLDETAILS_LISTTEXTA)lpmcd->paDetails;
860 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
862 if (WINE_CHN_SUPPORTS(mix->recMask, j))
864 mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j);
865 mcdlt[i].dwParam2 = 0;
866 strcpy(mcdlt[i].szName, MIX_Names[j]);
867 i++;
870 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
871 ret = MMSYSERR_NOERROR;
874 break;
875 default:
876 WARN("Unknown flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
878 return ret;
881 /**************************************************************************
882 * MIX_SetControlDetails [internal]
884 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
886 DWORD ret = MMSYSERR_NOTSUPPORTED;
887 DWORD c, chnl;
888 int val;
889 struct mixer* mix;
891 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
893 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
894 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
896 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
898 case MIXER_GETCONTROLDETAILSF_VALUE:
899 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
900 if (MIX_CheckControl(mix, lpmcd->dwControlID))
902 c = lpmcd->dwControlID - 1;
903 chnl = HIWORD(mix->ctrl[c].dwLineID);
904 if (chnl == LINEID_DST)
905 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
907 switch (mix->ctrl[c].ctrl.dwControlType)
909 case MIXERCONTROL_CONTROLTYPE_VOLUME:
911 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
913 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
914 /* val should contain 00RL */
915 switch (lpmcd->cChannels)
917 case 1:
918 /* mono... so R = L */
919 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
920 TRACE("Setting RL to %08ld\n", mcdu->dwValue);
921 val = 0x101 * ((mcdu->dwValue * 100) >> 16);
922 break;
923 case 2:
924 /* stereo, left is paDetails[0] */
925 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
926 TRACE("Setting L to %08ld\n", mcdu->dwValue);
927 val = ((mcdu->dwValue * 100) >> 16);
928 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
929 TRACE("Setting R to %08ld\n", mcdu->dwValue);
930 val += ((mcdu->dwValue * 100) >> 16) << 8;
931 break;
932 default:
933 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
934 return MMSYSERR_INVALPARAM;
937 if (mix->volume[chnl] == -1)
939 if (!MIX_SetVal(mix, chnl, val))
940 return MMSYSERR_INVALPARAM;
942 else
944 mix->volume[chnl] = val;
947 ret = MMSYSERR_NOERROR;
948 break;
949 case MIXERCONTROL_CONTROLTYPE_MUTE:
950 case MIXERCONTROL_CONTROLTYPE_ONOFF:
952 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
954 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
955 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
956 if (mcdb->fValue)
958 if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) || !MIX_SetVal(mix, chnl, 0))
959 return MMSYSERR_INVALPARAM;
961 else
963 if (mix->volume[chnl] == -1)
965 ret = MMSYSERR_NOERROR;
966 break;
968 if (!MIX_SetVal(mix, chnl, mix->volume[chnl]))
969 return MMSYSERR_INVALPARAM;
970 mix->volume[chnl] = -1;
973 ret = MMSYSERR_NOERROR;
974 break;
975 case MIXERCONTROL_CONTROLTYPE_MIXER:
976 case MIXERCONTROL_CONTROLTYPE_MUX:
978 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
979 unsigned mask;
980 int i, j;
982 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
983 /* we mute both channels at the same time */
984 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
985 mask = 0;
986 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
988 if (WINE_CHN_SUPPORTS(mix->recMask, j) && mcdb[i++].fValue)
990 /* a mux can only select one line at a time... */
991 if (mix->singleRecChannel && mask != 0)
993 FIXME("!!!\n");
994 return MMSYSERR_INVALPARAM;
996 mask |= WINE_CHN_MASK(j);
999 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
1000 TRACE("writing %04x as rec src\n", mask);
1001 if (!MIX_SetRecSrc(mix, mask))
1002 ERR("Can't write new mixer settings\n");
1003 else
1004 ret = MMSYSERR_NOERROR;
1006 break;
1009 break;
1010 default:
1011 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
1013 return ret;
1016 /**************************************************************************
1017 * MIX_Init [internal]
1019 static DWORD MIX_Init(void)
1021 int mixer;
1023 #define MIXER_DEV "/dev/mixer"
1024 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
1026 if (errno == ENODEV || errno == ENXIO)
1028 /* no driver present */
1029 return MMSYSERR_NODRIVER;
1031 MIX_NumMixers = 0;
1032 return MMSYSERR_ERROR;
1034 close(mixer);
1035 MIX_NumMixers = 1;
1036 MIX_Mixers[0].name = MIXER_DEV;
1037 MIX_Open(0, NULL, 0); /* FIXME */
1038 #undef MIXER_DEV
1039 return MMSYSERR_NOERROR;
1042 /**************************************************************************
1043 * MIX_GetNumDevs [internal]
1045 static DWORD MIX_GetNumDevs(void)
1047 return MIX_NumMixers;
1050 #endif /* HAVE_OSS */
1052 /**************************************************************************
1053 * mxdMessage (WINEOSS.3)
1055 DWORD WINAPI OSS_mxdMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1056 DWORD dwParam1, DWORD dwParam2)
1058 /* TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); */
1060 #ifdef HAVE_OSS
1061 switch (wMsg)
1063 case DRVM_INIT:
1064 return MIX_Init();
1065 case DRVM_EXIT:
1066 case DRVM_ENABLE:
1067 case DRVM_DISABLE:
1068 /* FIXME: Pretend this is supported */
1069 return 0;
1070 case MXDM_GETDEVCAPS:
1071 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSA)dwParam1, dwParam2);
1072 case MXDM_GETLINEINFO:
1073 return MIX_GetLineInfo(wDevID, (LPMIXERLINEA)dwParam1, dwParam2);
1074 case MXDM_GETNUMDEVS:
1075 return MIX_GetNumDevs();
1076 case MXDM_OPEN:
1077 return MMSYSERR_NOERROR;
1078 /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1079 case MXDM_CLOSE:
1080 return MMSYSERR_NOERROR;
1081 case MXDM_GETLINECONTROLS:
1082 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSA)dwParam1, dwParam2);
1083 case MXDM_GETCONTROLDETAILS:
1084 return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1085 case MXDM_SETCONTROLDETAILS:
1086 return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1087 default:
1088 WARN("unknown message %d!\n", wMsg);
1089 return MMSYSERR_NOTSUPPORTED;
1091 #else
1092 return MMSYSERR_NOTENABLED;
1093 #endif