Make out-of-source-tree builds work with Solaris make.
[wine/dcerpc.git] / dlls / winmm / wineoss / mixer.c
blob8113c28ebbbcb945fddca601404e335c0e6b9b22
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
8 */
10 #include "config.h"
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <assert.h>
19 #include <sys/ioctl.h>
20 #include "windef.h"
21 #include "mmddk.h"
22 #include "oss.h"
23 #include "debugtools.h"
25 DEFAULT_DEBUG_CHANNEL(mmaux);
27 #ifdef HAVE_OSS
29 #define WINE_MIXER_MANUF_ID 0xAA
30 #define WINE_MIXER_PRODUCT_ID 0x55
31 #define WINE_MIXER_VERSION 0x0100
32 #define WINE_MIXER_NAME "WINE OSS Mixer"
34 #define WINE_CHN_MASK(_x) (1L << (_x))
35 #define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))
36 /* Bass and Treble are no longer in the mask as Windows does not handle them */
37 #define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
38 WINE_CHN_MASK(SOUND_MIXER_PCM) | \
39 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
40 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
41 WINE_CHN_MASK(SOUND_MIXER_CD) )
43 #define WINE_MIXER_MASK_RECORD (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
44 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
45 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
46 WINE_CHN_MASK(SOUND_MIXER_IMIX) )
48 /* FIXME: the two following string arrays should be moved to a resource file in a string table */
49 /* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
50 static char* MIX_Labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
51 static char* MIX_Names [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
53 struct mixerCtrl
55 DWORD dwLineID;
56 MIXERCONTROLA ctrl;
59 struct mixer
61 const char* name;
62 int volume[SOUND_MIXER_NRDEVICES];
63 int devMask;
64 int stereoMask;
65 int recMask;
66 BOOL singleRecChannel;
67 struct mixerCtrl* ctrl;
68 int numCtrl;
71 #define LINEID_DST 0xFFFF
72 #define LINEID_SPEAKER 0x0000
73 #define LINEID_RECORD 0x0001
75 static int MIX_NumMixers;
76 static struct mixer MIX_Mixers[1];
78 /**************************************************************************
79 * MIX_FillLineControls [internal]
81 static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, DWORD dwType)
83 struct mixerCtrl* mc = &mix->ctrl[c];
84 int j;
86 mc->dwLineID = lineID;
87 mc->ctrl.cbStruct = sizeof(MIXERCONTROLA);
88 mc->ctrl.dwControlID = c + 1;
89 mc->ctrl.dwControlType = dwType;
91 switch (dwType)
93 case MIXERCONTROL_CONTROLTYPE_VOLUME:
94 mc->ctrl.fdwControl = 0;
95 mc->ctrl.cMultipleItems = 0;
96 lstrcpynA(mc->ctrl.szShortName, "Vol", MIXER_SHORT_NAME_CHARS);
97 lstrcpynA(mc->ctrl.szName, "Volume", MIXER_LONG_NAME_CHARS);
98 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
99 /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
100 * [0, 100] is the range supported by OSS
101 * whatever the min and max values are they must match
102 * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
104 mc->ctrl.Bounds.s1.dwMinimum = 0;
105 mc->ctrl.Bounds.s1.dwMaximum = 65535;
106 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
107 break;
108 case MIXERCONTROL_CONTROLTYPE_MUTE:
109 case MIXERCONTROL_CONTROLTYPE_ONOFF:
110 mc->ctrl.fdwControl = 0;
111 mc->ctrl.cMultipleItems = 0;
112 lstrcpynA(mc->ctrl.szShortName, "Mute", MIXER_SHORT_NAME_CHARS);
113 lstrcpynA(mc->ctrl.szName, "Mute", MIXER_LONG_NAME_CHARS);
114 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
115 mc->ctrl.Bounds.s1.dwMinimum = 0;
116 mc->ctrl.Bounds.s1.dwMaximum = 1;
117 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
118 break;
119 case MIXERCONTROL_CONTROLTYPE_MUX:
120 case MIXERCONTROL_CONTROLTYPE_MIXER:
121 mc->ctrl.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
122 mc->ctrl.cMultipleItems = 0;
123 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
124 if (WINE_CHN_SUPPORTS(mix->recMask, j))
125 mc->ctrl.cMultipleItems++;
126 lstrcpynA(mc->ctrl.szShortName, "Mixer", MIXER_SHORT_NAME_CHARS);
127 lstrcpynA(mc->ctrl.szName, "Mixer", MIXER_LONG_NAME_CHARS);
128 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
129 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
130 break;
132 default:
133 FIXME("Internal error: unknown type: %08lx\n", dwType);
135 TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c + 1, dwType, lineID);
138 /******************************************************************
139 * MIX_GetMixer
143 static struct mixer* MIX_Get(WORD wDevID)
145 if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].name == NULL) return NULL;
146 return &MIX_Mixers[wDevID];
149 /**************************************************************************
150 * MIX_Open [internal]
152 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
154 int mixer, i, j;
155 unsigned caps;
156 struct mixer* mix;
157 DWORD ret = MMSYSERR_NOERROR;
159 TRACE("(%04X, %p, %lu);\n", wDevID, lpMod, flags);
161 /* as we partly init the mixer with MIX_Open, we can allow null open decs */
162 /* EPP if (lpMod == NULL) return MMSYSERR_INVALPARAM; */
163 /* anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer device before sending
164 * messages to it... it seems to be linked to all the equivalent of mixer identification
165 * (with a reference to a wave, midi.. handle
167 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
169 if ((mixer = open(mix->name, O_RDWR)) < 0)
171 if (errno == ENODEV || errno == ENXIO)
173 /* no driver present */
174 return MMSYSERR_NODRIVER;
176 return MMSYSERR_ERROR;
179 if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1)
181 perror("ioctl mixer SOUND_MIXER_DEVMASK");
182 ret = MMSYSERR_ERROR;
183 goto error;
185 mix->devMask &= WINE_MIXER_MASK_SPEAKER;
186 if (mix->devMask == 0)
188 ret = MMSYSERR_NODRIVER;
189 goto error;
192 if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1)
194 perror("ioctl mixer SOUND_MIXER_STEREODEVS");
195 ret = MMSYSERR_ERROR;
196 goto error;
198 mix->stereoMask &= WINE_MIXER_MASK_SPEAKER;
200 if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1)
202 perror("ioctl mixer SOUND_MIXER_RECMASK");
203 ret = MMSYSERR_ERROR;
204 goto error;
206 mix->recMask &= WINE_MIXER_MASK_RECORD;
207 /* FIXME: we may need to support both rec lev & igain */
208 if (!WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_RECLEV))
210 WARN("The sound card doesn't support rec level\n");
211 if (WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_IGAIN))
212 WARN("but it does support IGain, please report\n");
214 if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1)
216 perror("ioctl mixer SOUND_MIXER_READ_CAPS");
217 ret = MMSYSERR_ERROR;
218 goto error;
220 mix->singleRecChannel = caps & SOUND_CAP_EXCL_INPUT;
221 TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
222 mix->devMask, mix->recMask, mix->stereoMask,
223 mix->singleRecChannel ? "single" : "multiple");
224 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
226 mix->volume[i] = -1;
228 mix->numCtrl = 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
229 /* FIXME: do we always have RECLEV on all cards ??? */
230 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
232 if (WINE_CHN_SUPPORTS(mix->devMask, i))
233 mix->numCtrl += 2; /* volume & mute */
234 if (WINE_CHN_SUPPORTS(mix->recMask, i))
235 mix->numCtrl += 2; /* volume & onoff */
238 if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), 0, sizeof(mix->ctrl[0]) * mix->numCtrl)))
240 ret = MMSYSERR_NOMEM;
241 goto error;
244 j = 0;
245 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_VOLUME);
246 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE);
247 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST),
248 mix->singleRecChannel ?
249 MIXERCONTROL_CONTROLTYPE_MUX : MIXERCONTROL_CONTROLTYPE_MIXER);
250 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
251 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
253 if (WINE_CHN_SUPPORTS(mix->devMask, i))
255 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
256 MIXERCONTROL_CONTROLTYPE_VOLUME);
257 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
258 MIXERCONTROL_CONTROLTYPE_MUTE);
261 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
263 if (WINE_CHN_SUPPORTS(mix->recMask, i))
265 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
266 MIXERCONTROL_CONTROLTYPE_VOLUME);
267 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
268 MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
271 assert(j == mix->numCtrl);
272 error:
273 close(mixer);
274 return ret;
277 /**************************************************************************
278 * MIX_GetVal [internal]
280 static BOOL MIX_GetVal(struct mixer* mix, int chn, int* val)
282 int mixer;
283 BOOL ret = FALSE;
285 if ((mixer = open(mix->name, O_RDWR)) < 0)
287 /* FIXME: ENXIO => no mixer installed */
288 WARN("mixer device not available !\n");
290 else
292 if (ioctl(mixer, MIXER_READ(chn), val) >= 0)
294 TRACE("Reading volume %x on %d\n", *val, chn);
295 ret = TRUE;
297 close(mixer);
299 return ret;
302 /**************************************************************************
303 * MIX_SetVal [internal]
305 static BOOL MIX_SetVal(struct mixer* mix, int chn, int val)
307 int mixer;
308 BOOL ret = FALSE;
310 TRACE("Writing volume %x on %d\n", val, chn);
312 if ((mixer = open(mix->name, O_RDWR)) < 0)
314 /* FIXME: ENXIO => no mixer installed */
315 WARN("mixer device not available !\n");
317 else
319 if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0)
321 ret = TRUE;
323 close(mixer);
325 return ret;
328 /******************************************************************
329 * MIX_GetRecSrc
333 static BOOL MIX_GetRecSrc(struct mixer* mix, unsigned* mask)
335 int mixer;
336 BOOL ret = FALSE;
338 if ((mixer = open(mix->name, O_RDWR)) >= 0)
340 if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) ret = TRUE;
341 close(mixer);
343 return ret;
346 /******************************************************************
347 * MIX_SetRecSrc
351 static BOOL MIX_SetRecSrc(struct mixer* mix, unsigned mask)
353 int mixer;
354 BOOL ret = FALSE;
356 if ((mixer = open(mix->name, O_RDWR)) >= 0)
358 if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) < 0)
360 ERR("Can't write new mixer settings\n");
362 else
363 ret = TRUE;
364 close(mixer);
366 return ret;
369 /**************************************************************************
370 * MIX_GetDevCaps [internal]
372 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSA lpCaps, DWORD dwSize)
374 struct mixer* mix;
376 TRACE("(%04X, %p, %lu);\n", wDevID, lpCaps, dwSize);
378 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
379 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
381 lpCaps->wMid = WINE_MIXER_MANUF_ID;
382 lpCaps->wPid = WINE_MIXER_PRODUCT_ID;
383 lpCaps->vDriverVersion = WINE_MIXER_VERSION;
384 strcpy(lpCaps->szPname, WINE_MIXER_NAME);
386 lpCaps->cDestinations = 2; /* speakers & record */
387 lpCaps->fdwSupport = 0; /* No bits defined yet */
389 return MMSYSERR_NOERROR;
392 /**************************************************************************
393 * MIX_GetLineInfoDst [internal]
395 static DWORD MIX_GetLineInfoDst(struct mixer* mix, LPMIXERLINEA lpMl, DWORD dst)
397 unsigned mask;
398 int j;
400 lpMl->dwDestination = dst;
401 switch (dst)
403 case 0:
404 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
405 mask = mix->devMask;
406 j = SOUND_MIXER_VOLUME;
407 break;
408 case 1:
409 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
410 mask = mix->recMask;
411 j = SOUND_MIXER_RECLEV;
412 break;
413 default:
414 FIXME("shouldn't happen\n");
415 return MMSYSERR_ERROR;
417 lpMl->dwSource = 0xFFFFFFFF;
418 lstrcpynA(lpMl->szShortName, MIX_Labels[j], MIXER_SHORT_NAME_CHARS);
419 lstrcpynA(lpMl->szName, MIX_Names[j], MIXER_LONG_NAME_CHARS);
421 /* we have all connections found in the MIX_DevMask */
422 lpMl->cConnections = 0;
423 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
424 if (WINE_CHN_SUPPORTS(mask, j))
425 lpMl->cConnections++;
426 lpMl->cChannels = 1;
427 if (WINE_CHN_SUPPORTS(mix->stereoMask, lpMl->dwLineID))
428 lpMl->cChannels++;
429 lpMl->dwLineID = MAKELONG(dst, LINEID_DST);
430 lpMl->cControls = 0;
431 for (j = 0; j < mix->numCtrl; j++)
432 if (mix->ctrl[j].dwLineID == lpMl->dwLineID)
433 lpMl->cControls++;
435 return MMSYSERR_NOERROR;
438 /**************************************************************************
439 * MIX_GetLineInfoSrc [internal]
441 static DWORD MIX_GetLineInfoSrc(struct mixer* mix, LPMIXERLINEA lpMl, DWORD idx, DWORD dst)
443 int i, j;
444 unsigned mask = (dst) ? mix->recMask : mix->devMask;
446 strcpy(lpMl->szShortName, MIX_Labels[idx]);
447 strcpy(lpMl->szName, MIX_Names[idx]);
448 lpMl->dwLineID = MAKELONG(dst, idx);
449 lpMl->dwDestination = dst;
450 lpMl->cConnections = 1;
451 lpMl->cControls = 0;
452 for (i = 0; i < mix->numCtrl; i++)
453 if (mix->ctrl[i].dwLineID == lpMl->dwLineID)
454 lpMl->cControls++;
456 switch (idx)
458 case SOUND_MIXER_SYNTH:
459 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
460 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
461 break;
462 case SOUND_MIXER_CD:
463 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
464 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
465 break;
466 case SOUND_MIXER_LINE:
467 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
468 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
469 break;
470 case SOUND_MIXER_MIC:
471 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
472 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
473 break;
474 case SOUND_MIXER_PCM:
475 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
476 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
477 break;
478 case SOUND_MIXER_IMIX:
479 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
480 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
481 break;
482 default:
483 WARN("Index %ld not handled.\n", idx);
484 return MIXERR_INVALLINE;
486 lpMl->cChannels = 1;
487 if (dst == 0 && WINE_CHN_SUPPORTS(mix->stereoMask, idx))
488 lpMl->cChannels++;
489 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
491 if (WINE_CHN_SUPPORTS(mask, j))
493 if (j == idx) break;
494 i++;
497 lpMl->dwSource = i;
498 return MMSYSERR_NOERROR;
501 /******************************************************************
502 * MIX_CheckLine
504 static BOOL MIX_CheckLine(DWORD lineID)
506 return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) ||
507 (HIWORD(lineID) == LINEID_DST && LOWORD(lineID) < SOUND_MIXER_NRDEVICES));
510 /**************************************************************************
511 * MIX_GetLineInfo [internal]
513 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo)
515 int i, j;
516 DWORD ret = MMSYSERR_NOERROR;
517 unsigned mask;
518 struct mixer* mix;
520 TRACE("(%04X, %p, %lu);\n", wDevID, lpMl, fdwInfo);
522 if (lpMl == NULL || lpMl->cbStruct != sizeof(*lpMl))
523 return MMSYSERR_INVALPARAM;
524 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
526 /* FIXME: set all the variables correctly... the lines below
527 * are very wrong...
529 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
530 lpMl->dwUser = 0;
532 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
534 case MIXER_GETLINEINFOF_DESTINATION:
535 TRACE("DESTINATION (%08lx)\n", lpMl->dwDestination);
536 if (lpMl->dwDestination >= 2)
537 return MMSYSERR_INVALPARAM;
538 if ((ret = MIX_GetLineInfoDst(mix, lpMl, lpMl->dwDestination)) != MMSYSERR_NOERROR)
539 return ret;
540 break;
541 case MIXER_GETLINEINFOF_SOURCE:
542 TRACE("SOURCE (%08lx), dst=%08lx\n", lpMl->dwSource, lpMl->dwDestination);
543 switch (lpMl->dwDestination)
545 case 0: mask = mix->devMask; break;
546 case 1: mask = mix->recMask; break;
547 default: return MMSYSERR_INVALPARAM;
549 i = lpMl->dwSource;
550 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
552 if (WINE_CHN_SUPPORTS(mask, j) && (i-- == 0))
553 break;
555 if (j >= SOUND_MIXER_NRDEVICES)
556 return MIXERR_INVALLINE;
557 if ((ret = MIX_GetLineInfoSrc(mix, lpMl, j, lpMl->dwDestination)) != MMSYSERR_NOERROR)
558 return ret;
559 break;
560 case MIXER_GETLINEINFOF_LINEID:
561 TRACE("LINEID (%08lx)\n", lpMl->dwLineID);
563 if (!MIX_CheckLine(lpMl->dwLineID))
564 return MIXERR_INVALLINE;
565 if (HIWORD(lpMl->dwLineID) == LINEID_DST)
566 ret = MIX_GetLineInfoDst(mix, lpMl, LOWORD(lpMl->dwLineID));
567 else
568 ret = MIX_GetLineInfoSrc(mix, lpMl, HIWORD(lpMl->dwLineID), LOWORD(lpMl->dwLineID));
569 if (ret != MMSYSERR_NOERROR)
570 return ret;
571 break;
572 case MIXER_GETLINEINFOF_COMPONENTTYPE:
573 TRACE("COMPONENT TYPE (%08lx)\n", lpMl->dwComponentType);
574 switch (lpMl->dwComponentType)
576 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
577 ret = MIX_GetLineInfoDst(mix, lpMl, 0);
578 break;
579 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
580 ret = MIX_GetLineInfoDst(mix, lpMl, 1);
581 break;
582 case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
583 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_SYNTH, 0);
584 break;
585 case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
586 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_CD, 0);
587 break;
588 case MIXERLINE_COMPONENTTYPE_SRC_LINE:
589 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_LINE, 0);
590 break;
591 case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
592 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_MIC, 1);
593 break;
594 case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
595 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_PCM, 0);
596 break;
597 case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
598 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1);
599 break;
600 default:
601 FIXME("Unhandled component type (%08lx)\n", lpMl->dwComponentType);
602 return MMSYSERR_INVALPARAM;
604 break;
605 case MIXER_GETLINEINFOF_TARGETTYPE:
606 FIXME("_TARGETTYPE not implemented yet.\n");
607 break;
608 default:
609 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
610 break;
613 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_AUX; /* FIXME */
614 lpMl->Target.dwDeviceID = 0xFFFFFFFF;
615 lpMl->Target.wMid = WINE_MIXER_MANUF_ID;
616 lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID;
617 lpMl->Target.vDriverVersion = WINE_MIXER_VERSION;
618 strcpy(lpMl->Target.szPname, WINE_MIXER_NAME);
620 return ret;
623 /******************************************************************
624 * MIX_CheckControl
627 static BOOL MIX_CheckControl(struct mixer* mix, DWORD ctrlID)
629 return (ctrlID >= 1 && ctrlID <= mix->numCtrl);
632 /**************************************************************************
633 * MIX_GetLineControls [internal]
635 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, DWORD flags)
637 DWORD dwRet = MMSYSERR_NOERROR;
638 struct mixer* mix;
640 TRACE("(%04X, %p, %lu);\n", wDevID, lpMlc, flags);
642 if (lpMlc == NULL) return MMSYSERR_INVALPARAM;
643 if (lpMlc->cbStruct < sizeof(*lpMlc) ||
644 lpMlc->cbmxctrl < sizeof(MIXERCONTROLA))
645 return MMSYSERR_INVALPARAM;
646 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
648 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
650 case MIXER_GETLINECONTROLSF_ALL:
652 int i, j;
654 TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc->dwLineID, lpMlc->cControls);
656 for (i = j = 0; i < mix->numCtrl; i++)
658 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
659 j++;
661 if (!j || lpMlc->cControls != j) dwRet = MMSYSERR_INVALPARAM;
662 else if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
663 else
665 for (i = j = 0; i < mix->numCtrl; i++)
667 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
669 TRACE("[%d] => [%2d]: typ=%08lx\n", j, i + 1, mix->ctrl[i].ctrl.dwControlType);
670 lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl;
675 break;
676 case MIXER_GETLINECONTROLSF_ONEBYID:
677 TRACE("line=%08lx GLCF_ONEBYID (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
679 if (!MIX_CheckControl(mix, lpMlc->u.dwControlID) ||
680 mix->ctrl[lpMlc->u.dwControlID - 1].dwLineID != lpMlc->dwLineID)
681 dwRet = MMSYSERR_INVALPARAM;
682 else
683 lpMlc->pamxctrl[0] = mix->ctrl[lpMlc->u.dwControlID - 1].ctrl;
684 break;
685 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
686 TRACE("line=%08lx GLCF_ONEBYTYPE (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlType);
687 if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
688 else
690 int i;
691 DWORD ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
693 for (i = 0; i < mix->numCtrl; i++)
695 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID &&
696 ct == (mix->ctrl[i].ctrl.dwControlType & MIXERCONTROL_CT_CLASS_MASK))
698 lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl;
699 break;
702 if (i == mix->numCtrl) dwRet = MMSYSERR_INVALPARAM;
704 break;
705 default:
706 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
707 dwRet = MMSYSERR_INVALPARAM;
710 return dwRet;
713 /**************************************************************************
714 * MIX_GetControlDetails [internal]
716 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
718 DWORD ret = MMSYSERR_NOTSUPPORTED;
719 DWORD c, chnl;
720 struct mixer* mix;
722 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
724 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
725 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
727 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
729 case MIXER_GETCONTROLDETAILSF_VALUE:
730 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
731 if (MIX_CheckControl(mix, lpmcd->dwControlID))
733 c = lpmcd->dwControlID - 1;
734 chnl = HIWORD(mix->ctrl[c].dwLineID);
735 if (chnl == LINEID_DST)
736 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
737 switch (mix->ctrl[c].ctrl.dwControlType)
739 case MIXERCONTROL_CONTROLTYPE_VOLUME:
741 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
742 int val;
744 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
745 /* return value is 00RL (4 bytes)... */
746 if ((val = mix->volume[chnl]) == -1 && !MIX_GetVal(mix, chnl, &val))
747 return MMSYSERR_INVALPARAM;
749 switch (lpmcd->cChannels)
751 case 1:
752 /* mono... so R = L */
753 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
754 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
755 break;
756 case 2:
757 /* stereo, left is paDetails[0] */
758 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
759 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
760 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
761 mcdu->dwValue = (HIBYTE(LOWORD(val)) * 65536L) / 100;
762 break;
763 default:
764 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
765 return MMSYSERR_INVALPARAM;
767 TRACE("=> %08lx\n", mcdu->dwValue);
769 break;
770 case MIXERCONTROL_CONTROLTYPE_MUTE:
771 case MIXERCONTROL_CONTROLTYPE_ONOFF:
773 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
775 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
776 /* we mute both channels at the same time */
777 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
778 mcdb->fValue = (mix->volume[chnl] != -1);
779 TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
781 break;
782 case MIXERCONTROL_CONTROLTYPE_MIXER:
783 case MIXERCONTROL_CONTROLTYPE_MUX:
785 unsigned mask;
787 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
788 if (!MIX_GetRecSrc(mix, &mask))
790 /* FIXME: ENXIO => no mixer installed */
791 WARN("mixer device not available !\n");
792 ret = MMSYSERR_ERROR;
794 else
796 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
797 int i, j;
799 /* we mute both channels at the same time */
800 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
802 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
804 if (WINE_CHN_SUPPORTS(mix->recMask, j))
806 if (i >= lpmcd->u.cMultipleItems)
807 return MMSYSERR_INVALPARAM;
808 mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j);
813 break;
814 default:
815 WARN("Unsupported\n");
817 ret = MMSYSERR_NOERROR;
819 else
821 ret = MMSYSERR_INVALPARAM;
823 break;
824 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
825 TRACE("LIST TEXT (%08lx)\n", lpmcd->dwControlID);
827 ret = MMSYSERR_INVALPARAM;
828 if (MIX_CheckControl(mix, lpmcd->dwControlID))
830 int c = lpmcd->dwControlID - 1;
832 if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ||
833 mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
835 LPMIXERCONTROLDETAILS_LISTTEXTA mcdlt;
836 int i, j;
838 mcdlt = (LPMIXERCONTROLDETAILS_LISTTEXTA)lpmcd->paDetails;
839 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
841 if (WINE_CHN_SUPPORTS(mix->recMask, j))
843 mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j);
844 mcdlt[i].dwParam2 = 0;
845 strcpy(mcdlt[i].szName, MIX_Names[j]);
846 i++;
849 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
850 ret = MMSYSERR_NOERROR;
853 break;
854 default:
855 WARN("Unknown flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
857 return ret;
860 /**************************************************************************
861 * MIX_SetControlDetails [internal]
863 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
865 DWORD ret = MMSYSERR_NOTSUPPORTED;
866 DWORD c, chnl;
867 int val;
868 struct mixer* mix;
870 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
872 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
873 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
875 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
877 case MIXER_GETCONTROLDETAILSF_VALUE:
878 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
879 if (MIX_CheckControl(mix, lpmcd->dwControlID))
881 c = lpmcd->dwControlID - 1;
882 chnl = HIWORD(mix->ctrl[c].dwLineID);
883 if (chnl == LINEID_DST)
884 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
886 switch (mix->ctrl[c].ctrl.dwControlType)
888 case MIXERCONTROL_CONTROLTYPE_VOLUME:
890 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
892 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
893 /* val should contain 00RL */
894 switch (lpmcd->cChannels)
896 case 1:
897 /* mono... so R = L */
898 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
899 TRACE("Setting RL to %08ld\n", mcdu->dwValue);
900 val = 0x101 * ((mcdu->dwValue * 100) >> 16);
901 break;
902 case 2:
903 /* stereo, left is paDetails[0] */
904 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
905 TRACE("Setting L to %08ld\n", mcdu->dwValue);
906 val = ((mcdu->dwValue * 100) >> 16);
907 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
908 TRACE("Setting R to %08ld\n", mcdu->dwValue);
909 val += ((mcdu->dwValue * 100) >> 16) << 8;
910 break;
911 default:
912 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
913 return MMSYSERR_INVALPARAM;
916 if (mix->volume[chnl] == -1)
918 if (!MIX_SetVal(mix, chnl, val))
919 return MMSYSERR_INVALPARAM;
921 else
923 mix->volume[chnl] = val;
926 ret = MMSYSERR_NOERROR;
927 break;
928 case MIXERCONTROL_CONTROLTYPE_MUTE:
929 case MIXERCONTROL_CONTROLTYPE_ONOFF:
931 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
933 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
934 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
935 if (mcdb->fValue)
937 if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) || !MIX_SetVal(mix, chnl, 0))
938 return MMSYSERR_INVALPARAM;
940 else
942 if (mix->volume[chnl] == -1)
944 ret = MMSYSERR_NOERROR;
945 break;
947 if (!MIX_SetVal(mix, chnl, mix->volume[chnl]))
948 return MMSYSERR_INVALPARAM;
949 mix->volume[chnl] = -1;
952 ret = MMSYSERR_NOERROR;
953 break;
954 case MIXERCONTROL_CONTROLTYPE_MIXER:
955 case MIXERCONTROL_CONTROLTYPE_MUX:
957 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
958 unsigned mask;
959 int i, j;
961 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
962 /* we mute both channels at the same time */
963 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
964 mask = 0;
965 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
967 if (WINE_CHN_SUPPORTS(mix->recMask, j) && mcdb[i++].fValue)
969 /* a mux can only select one line at a time... */
970 if (mix->singleRecChannel && mask != 0)
972 FIXME("!!!\n");
973 return MMSYSERR_INVALPARAM;
975 mask |= WINE_CHN_MASK(j);
978 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
979 TRACE("writing %04x as rec src\n", mask);
980 if (!MIX_SetRecSrc(mix, mask))
981 ERR("Can't write new mixer settings\n");
982 else
983 ret = MMSYSERR_NOERROR;
985 break;
988 break;
989 default:
990 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
992 return ret;
995 /**************************************************************************
996 * MIX_Init [internal]
998 static DWORD MIX_Init(void)
1000 int mixer;
1002 #define MIXER_DEV "/dev/mixer"
1003 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
1005 if (errno == ENODEV || errno == ENXIO)
1007 /* no driver present */
1008 return MMSYSERR_NODRIVER;
1010 MIX_NumMixers = 0;
1011 return MMSYSERR_ERROR;
1013 close(mixer);
1014 MIX_NumMixers = 1;
1015 MIX_Mixers[0].name = MIXER_DEV;
1016 MIX_Open(0, NULL, 0); /* FIXME */
1017 #undef MIXER_DEV
1018 return MMSYSERR_NOERROR;
1021 /**************************************************************************
1022 * MIX_GetNumDevs [internal]
1024 static DWORD MIX_GetNumDevs(void)
1026 return MIX_NumMixers;
1029 #endif /* HAVE_OSS */
1031 /**************************************************************************
1032 * mixMessage (WINEOSS.3)
1034 DWORD WINAPI OSS_mixMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1035 DWORD dwParam1, DWORD dwParam2)
1037 /* TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); */
1039 #ifdef HAVE_OSS
1040 switch (wMsg)
1042 case DRVM_INIT:
1043 return MIX_Init();
1044 case DRVM_EXIT:
1045 case DRVM_ENABLE:
1046 case DRVM_DISABLE:
1047 /* FIXME: Pretend this is supported */
1048 return 0;
1049 case MXDM_GETDEVCAPS:
1050 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSA)dwParam1, dwParam2);
1051 case MXDM_GETLINEINFO:
1052 return MIX_GetLineInfo(wDevID, (LPMIXERLINEA)dwParam1, dwParam2);
1053 case MXDM_GETNUMDEVS:
1054 return MIX_GetNumDevs();
1055 case MXDM_OPEN:
1056 return MMSYSERR_NOERROR;
1057 /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1058 case MXDM_CLOSE:
1059 return MMSYSERR_NOERROR;
1060 case MXDM_GETLINECONTROLS:
1061 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSA)dwParam1, dwParam2);
1062 case MXDM_GETCONTROLDETAILS:
1063 return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1064 case MXDM_SETCONTROLDETAILS:
1065 return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1066 default:
1067 WARN("unknown message %d!\n", wMsg);
1068 return MMSYSERR_NOTSUPPORTED;
1070 #else
1071 return MMSYSERR_NOTENABLED;
1072 #endif