Fixed warnings with gcc option "-Wwrite-strings".
[wine/wine64.git] / dlls / winmm / wineoss / mixer.c
blob321542af12695e03e8d8f32286cf5e8501dad1b6
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 <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <assert.h>
36 #ifdef HAVE_SYS_IOCTL_H
37 # include <sys/ioctl.h>
38 #endif
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
42 #include "windef.h"
43 #include "winbase.h"
44 #include "mmddk.h"
45 #include "oss.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(mmaux);
50 #ifdef HAVE_OSS
52 #define WINE_MIXER_MANUF_ID 0xAA
53 #define WINE_MIXER_PRODUCT_ID 0x55
54 #define WINE_MIXER_VERSION 0x0100
55 #define WINE_MIXER_NAME "WINE OSS Mixer"
57 #define WINE_CHN_MASK(_x) (1L << (_x))
58 #define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))
59 /* Bass and Treble are no longer in the mask as Windows does not handle them */
60 #define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
61 WINE_CHN_MASK(SOUND_MIXER_PCM) | \
62 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
63 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
64 WINE_CHN_MASK(SOUND_MIXER_CD) )
66 #define WINE_MIXER_MASK_RECORD (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
67 WINE_CHN_MASK(SOUND_MIXER_LINE) | \
68 WINE_CHN_MASK(SOUND_MIXER_MIC) | \
69 WINE_CHN_MASK(SOUND_MIXER_IMIX) )
71 /* FIXME: the two following string arrays should be moved to a resource file in a string table */
72 /* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
73 static const char* MIX_Labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
74 static const char* MIX_Names [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
76 struct mixerCtrl
78 DWORD dwLineID;
79 MIXERCONTROLA ctrl;
82 struct mixer
84 const char* name;
85 int volume[SOUND_MIXER_NRDEVICES];
86 int devMask;
87 int stereoMask;
88 int recMask;
89 BOOL singleRecChannel;
90 struct mixerCtrl* ctrl;
91 int numCtrl;
94 #define LINEID_DST 0xFFFF
95 #define LINEID_SPEAKER 0x0000
96 #define LINEID_RECORD 0x0001
98 static int MIX_NumMixers;
99 static struct mixer MIX_Mixers[1];
101 /**************************************************************************
102 * MIX_FillLineControls [internal]
104 static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, DWORD dwType)
106 struct mixerCtrl* mc = &mix->ctrl[c];
107 int j;
109 mc->dwLineID = lineID;
110 mc->ctrl.cbStruct = sizeof(MIXERCONTROLA);
111 mc->ctrl.dwControlID = c + 1;
112 mc->ctrl.dwControlType = dwType;
114 switch (dwType)
116 case MIXERCONTROL_CONTROLTYPE_VOLUME:
117 mc->ctrl.fdwControl = 0;
118 mc->ctrl.cMultipleItems = 0;
119 lstrcpynA(mc->ctrl.szShortName, "Vol", MIXER_SHORT_NAME_CHARS);
120 lstrcpynA(mc->ctrl.szName, "Volume", MIXER_LONG_NAME_CHARS);
121 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
122 /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
123 * [0, 100] is the range supported by OSS
124 * whatever the min and max values are they must match
125 * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
127 mc->ctrl.Bounds.s1.dwMinimum = 0;
128 mc->ctrl.Bounds.s1.dwMaximum = 65535;
129 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
130 break;
131 case MIXERCONTROL_CONTROLTYPE_MUTE:
132 case MIXERCONTROL_CONTROLTYPE_ONOFF:
133 mc->ctrl.fdwControl = 0;
134 mc->ctrl.cMultipleItems = 0;
135 lstrcpynA(mc->ctrl.szShortName, "Mute", MIXER_SHORT_NAME_CHARS);
136 lstrcpynA(mc->ctrl.szName, "Mute", MIXER_LONG_NAME_CHARS);
137 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
138 mc->ctrl.Bounds.s1.dwMinimum = 0;
139 mc->ctrl.Bounds.s1.dwMaximum = 1;
140 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
141 break;
142 case MIXERCONTROL_CONTROLTYPE_MUX:
143 case MIXERCONTROL_CONTROLTYPE_MIXER:
144 mc->ctrl.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
145 mc->ctrl.cMultipleItems = 0;
146 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
147 if (WINE_CHN_SUPPORTS(mix->recMask, j))
148 mc->ctrl.cMultipleItems++;
149 lstrcpynA(mc->ctrl.szShortName, "Mixer", MIXER_SHORT_NAME_CHARS);
150 lstrcpynA(mc->ctrl.szName, "Mixer", MIXER_LONG_NAME_CHARS);
151 memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
152 memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
153 break;
155 default:
156 FIXME("Internal error: unknown type: %08lx\n", dwType);
158 TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c + 1, dwType, lineID);
161 /******************************************************************
162 * MIX_GetMixer
166 static struct mixer* MIX_Get(WORD wDevID)
168 if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].name == NULL) return NULL;
169 return &MIX_Mixers[wDevID];
172 /**************************************************************************
173 * MIX_Open [internal]
175 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
177 int mixer, i, j;
178 unsigned caps;
179 struct mixer* mix;
180 DWORD ret = MMSYSERR_NOERROR;
182 TRACE("(%04X, %p, %lu);\n", wDevID, lpMod, flags);
184 /* as we partly init the mixer with MIX_Open, we can allow null open decs */
185 /* EPP if (lpMod == NULL) return MMSYSERR_INVALPARAM; */
186 /* anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer device before sending
187 * messages to it... it seems to be linked to all the equivalent of mixer identification
188 * (with a reference to a wave, midi.. handle
190 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
192 if ((mixer = open(mix->name, O_RDWR)) < 0)
194 if (errno == ENODEV || errno == ENXIO)
196 /* no driver present */
197 return MMSYSERR_NODRIVER;
199 return MMSYSERR_ERROR;
202 if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1)
204 perror("ioctl mixer SOUND_MIXER_DEVMASK");
205 ret = MMSYSERR_ERROR;
206 goto error;
208 mix->devMask &= WINE_MIXER_MASK_SPEAKER;
209 if (mix->devMask == 0)
211 ret = MMSYSERR_NODRIVER;
212 goto error;
215 if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1)
217 perror("ioctl mixer SOUND_MIXER_STEREODEVS");
218 ret = MMSYSERR_ERROR;
219 goto error;
221 mix->stereoMask &= WINE_MIXER_MASK_SPEAKER;
223 if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1)
225 perror("ioctl mixer SOUND_MIXER_RECMASK");
226 ret = MMSYSERR_ERROR;
227 goto error;
229 mix->recMask &= WINE_MIXER_MASK_RECORD;
230 /* FIXME: we may need to support both rec lev & igain */
231 if (!WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_RECLEV))
233 WARN("The sound card doesn't support rec level\n");
234 if (WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_IGAIN))
235 WARN("but it does support IGain, please report\n");
237 if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1)
239 perror("ioctl mixer SOUND_MIXER_READ_CAPS");
240 ret = MMSYSERR_ERROR;
241 goto error;
243 mix->singleRecChannel = caps & SOUND_CAP_EXCL_INPUT;
244 TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
245 mix->devMask, mix->recMask, mix->stereoMask,
246 mix->singleRecChannel ? "single" : "multiple");
247 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
249 mix->volume[i] = -1;
251 mix->numCtrl = 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
252 /* FIXME: do we always have RECLEV on all cards ??? */
253 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
255 if (WINE_CHN_SUPPORTS(mix->devMask, i))
256 mix->numCtrl += 2; /* volume & mute */
257 if (WINE_CHN_SUPPORTS(mix->recMask, i))
258 mix->numCtrl += 2; /* volume & onoff */
261 if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), 0, sizeof(mix->ctrl[0]) * mix->numCtrl)))
263 ret = MMSYSERR_NOMEM;
264 goto error;
267 j = 0;
268 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_VOLUME);
269 MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE);
270 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST),
271 mix->singleRecChannel ?
272 MIXERCONTROL_CONTROLTYPE_MUX : MIXERCONTROL_CONTROLTYPE_MIXER);
273 MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
274 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
276 if (WINE_CHN_SUPPORTS(mix->devMask, i))
278 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
279 MIXERCONTROL_CONTROLTYPE_VOLUME);
280 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
281 MIXERCONTROL_CONTROLTYPE_MUTE);
284 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
286 if (WINE_CHN_SUPPORTS(mix->recMask, i))
288 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
289 MIXERCONTROL_CONTROLTYPE_VOLUME);
290 MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
291 MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
294 assert(j == mix->numCtrl);
295 error:
296 close(mixer);
297 return ret;
300 /**************************************************************************
301 * MIX_GetVal [internal]
303 static BOOL MIX_GetVal(struct mixer* mix, int chn, int* val)
305 int mixer;
306 BOOL ret = FALSE;
308 if ((mixer = open(mix->name, O_RDWR)) < 0)
310 /* FIXME: ENXIO => no mixer installed */
311 WARN("mixer device not available !\n");
313 else
315 if (ioctl(mixer, MIXER_READ(chn), val) >= 0)
317 TRACE("Reading volume %x on %d\n", *val, chn);
318 ret = TRUE;
320 close(mixer);
322 return ret;
325 /**************************************************************************
326 * MIX_SetVal [internal]
328 static BOOL MIX_SetVal(struct mixer* mix, int chn, int val)
330 int mixer;
331 BOOL ret = FALSE;
333 TRACE("Writing volume %x on %d\n", val, chn);
335 if ((mixer = open(mix->name, O_RDWR)) < 0)
337 /* FIXME: ENXIO => no mixer installed */
338 WARN("mixer device not available !\n");
340 else
342 if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0)
344 ret = TRUE;
346 close(mixer);
348 return ret;
351 /******************************************************************
352 * MIX_GetRecSrc
356 static BOOL MIX_GetRecSrc(struct mixer* mix, unsigned* mask)
358 int mixer;
359 BOOL ret = FALSE;
361 if ((mixer = open(mix->name, O_RDWR)) >= 0)
363 if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) ret = TRUE;
364 close(mixer);
366 return ret;
369 /******************************************************************
370 * MIX_SetRecSrc
374 static BOOL MIX_SetRecSrc(struct mixer* mix, unsigned mask)
376 int mixer;
377 BOOL ret = FALSE;
379 if ((mixer = open(mix->name, O_RDWR)) >= 0)
381 if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) < 0)
383 ERR("Can't write new mixer settings\n");
385 else
386 ret = TRUE;
387 close(mixer);
389 return ret;
392 /**************************************************************************
393 * MIX_GetDevCaps [internal]
395 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSA lpCaps, DWORD dwSize)
397 struct mixer* mix;
399 TRACE("(%04X, %p, %lu);\n", wDevID, lpCaps, dwSize);
401 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
402 if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
404 lpCaps->wMid = WINE_MIXER_MANUF_ID;
405 lpCaps->wPid = WINE_MIXER_PRODUCT_ID;
406 lpCaps->vDriverVersion = WINE_MIXER_VERSION;
407 strcpy(lpCaps->szPname, WINE_MIXER_NAME);
409 lpCaps->cDestinations = 2; /* speakers & record */
410 lpCaps->fdwSupport = 0; /* No bits defined yet */
412 return MMSYSERR_NOERROR;
415 /**************************************************************************
416 * MIX_GetLineInfoDst [internal]
418 static DWORD MIX_GetLineInfoDst(struct mixer* mix, LPMIXERLINEA lpMl, DWORD dst)
420 unsigned mask;
421 int j;
423 lpMl->dwDestination = dst;
424 switch (dst)
426 case 0:
427 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
428 mask = mix->devMask;
429 j = SOUND_MIXER_VOLUME;
430 break;
431 case 1:
432 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
433 mask = mix->recMask;
434 j = SOUND_MIXER_RECLEV;
435 break;
436 default:
437 FIXME("shouldn't happen\n");
438 return MMSYSERR_ERROR;
440 lpMl->dwSource = 0xFFFFFFFF;
441 lstrcpynA(lpMl->szShortName, MIX_Labels[j], MIXER_SHORT_NAME_CHARS);
442 lstrcpynA(lpMl->szName, MIX_Names[j], MIXER_LONG_NAME_CHARS);
444 /* we have all connections found in the MIX_DevMask */
445 lpMl->cConnections = 0;
446 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
447 if (WINE_CHN_SUPPORTS(mask, j))
448 lpMl->cConnections++;
449 lpMl->cChannels = 1;
450 if (WINE_CHN_SUPPORTS(mix->stereoMask, lpMl->dwLineID))
451 lpMl->cChannels++;
452 lpMl->dwLineID = MAKELONG(dst, LINEID_DST);
453 lpMl->cControls = 0;
454 for (j = 0; j < mix->numCtrl; j++)
455 if (mix->ctrl[j].dwLineID == lpMl->dwLineID)
456 lpMl->cControls++;
458 return MMSYSERR_NOERROR;
461 /**************************************************************************
462 * MIX_GetLineInfoSrc [internal]
464 static DWORD MIX_GetLineInfoSrc(struct mixer* mix, LPMIXERLINEA lpMl, DWORD idx, DWORD dst)
466 int i, j;
467 unsigned mask = (dst) ? mix->recMask : mix->devMask;
469 strcpy(lpMl->szShortName, MIX_Labels[idx]);
470 strcpy(lpMl->szName, MIX_Names[idx]);
471 lpMl->dwLineID = MAKELONG(dst, idx);
472 lpMl->dwDestination = dst;
473 lpMl->cConnections = 1;
474 lpMl->cControls = 0;
475 for (i = 0; i < mix->numCtrl; i++)
476 if (mix->ctrl[i].dwLineID == lpMl->dwLineID)
477 lpMl->cControls++;
479 switch (idx)
481 case SOUND_MIXER_SYNTH:
482 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
483 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
484 break;
485 case SOUND_MIXER_CD:
486 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
487 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
488 break;
489 case SOUND_MIXER_LINE:
490 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
491 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
492 break;
493 case SOUND_MIXER_MIC:
494 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
495 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
496 break;
497 case SOUND_MIXER_PCM:
498 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
499 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
500 break;
501 case SOUND_MIXER_IMIX:
502 lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
503 lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
504 break;
505 default:
506 WARN("Index %ld not handled.\n", idx);
507 return MIXERR_INVALLINE;
509 lpMl->cChannels = 1;
510 if (dst == 0 && WINE_CHN_SUPPORTS(mix->stereoMask, idx))
511 lpMl->cChannels++;
512 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
514 if (WINE_CHN_SUPPORTS(mask, j))
516 if (j == idx) break;
517 i++;
520 lpMl->dwSource = i;
521 return MMSYSERR_NOERROR;
524 /******************************************************************
525 * MIX_CheckLine
527 static BOOL MIX_CheckLine(DWORD lineID)
529 return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) ||
530 (HIWORD(lineID) == LINEID_DST && LOWORD(lineID) < SOUND_MIXER_NRDEVICES));
533 /**************************************************************************
534 * MIX_GetLineInfo [internal]
536 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo)
538 int i, j;
539 DWORD ret = MMSYSERR_NOERROR;
540 unsigned mask;
541 struct mixer* mix;
543 TRACE("(%04X, %p, %lu);\n", wDevID, lpMl, fdwInfo);
545 if (lpMl == NULL || lpMl->cbStruct != sizeof(*lpMl))
546 return MMSYSERR_INVALPARAM;
547 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
549 /* FIXME: set all the variables correctly... the lines below
550 * are very wrong...
552 lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
553 lpMl->dwUser = 0;
555 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
557 case MIXER_GETLINEINFOF_DESTINATION:
558 TRACE("DESTINATION (%08lx)\n", lpMl->dwDestination);
559 if (lpMl->dwDestination >= 2)
560 return MMSYSERR_INVALPARAM;
561 if ((ret = MIX_GetLineInfoDst(mix, lpMl, lpMl->dwDestination)) != MMSYSERR_NOERROR)
562 return ret;
563 break;
564 case MIXER_GETLINEINFOF_SOURCE:
565 TRACE("SOURCE (%08lx), dst=%08lx\n", lpMl->dwSource, lpMl->dwDestination);
566 switch (lpMl->dwDestination)
568 case 0: mask = mix->devMask; break;
569 case 1: mask = mix->recMask; break;
570 default: return MMSYSERR_INVALPARAM;
572 i = lpMl->dwSource;
573 for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
575 if (WINE_CHN_SUPPORTS(mask, j) && (i-- == 0))
576 break;
578 if (j >= SOUND_MIXER_NRDEVICES)
579 return MIXERR_INVALLINE;
580 if ((ret = MIX_GetLineInfoSrc(mix, lpMl, j, lpMl->dwDestination)) != MMSYSERR_NOERROR)
581 return ret;
582 break;
583 case MIXER_GETLINEINFOF_LINEID:
584 TRACE("LINEID (%08lx)\n", lpMl->dwLineID);
586 if (!MIX_CheckLine(lpMl->dwLineID))
587 return MIXERR_INVALLINE;
588 if (HIWORD(lpMl->dwLineID) == LINEID_DST)
589 ret = MIX_GetLineInfoDst(mix, lpMl, LOWORD(lpMl->dwLineID));
590 else
591 ret = MIX_GetLineInfoSrc(mix, lpMl, HIWORD(lpMl->dwLineID), LOWORD(lpMl->dwLineID));
592 if (ret != MMSYSERR_NOERROR)
593 return ret;
594 break;
595 case MIXER_GETLINEINFOF_COMPONENTTYPE:
596 TRACE("COMPONENT TYPE (%08lx)\n", lpMl->dwComponentType);
597 switch (lpMl->dwComponentType)
599 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
600 ret = MIX_GetLineInfoDst(mix, lpMl, 0);
601 break;
602 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
603 ret = MIX_GetLineInfoDst(mix, lpMl, 1);
604 break;
605 case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
606 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_SYNTH, 0);
607 break;
608 case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
609 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_CD, 0);
610 break;
611 case MIXERLINE_COMPONENTTYPE_SRC_LINE:
612 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_LINE, 0);
613 break;
614 case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
615 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_MIC, 1);
616 break;
617 case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
618 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_PCM, 0);
619 break;
620 case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
621 ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1);
622 break;
623 default:
624 FIXME("Unhandled component type (%08lx)\n", lpMl->dwComponentType);
625 return MMSYSERR_INVALPARAM;
627 break;
628 case MIXER_GETLINEINFOF_TARGETTYPE:
629 FIXME("_TARGETTYPE not implemented yet.\n");
630 break;
631 default:
632 WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
633 break;
636 lpMl->Target.dwType = MIXERLINE_TARGETTYPE_AUX; /* FIXME */
637 lpMl->Target.dwDeviceID = 0xFFFFFFFF;
638 lpMl->Target.wMid = WINE_MIXER_MANUF_ID;
639 lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID;
640 lpMl->Target.vDriverVersion = WINE_MIXER_VERSION;
641 strcpy(lpMl->Target.szPname, WINE_MIXER_NAME);
643 return ret;
646 /******************************************************************
647 * MIX_CheckControl
650 static BOOL MIX_CheckControl(struct mixer* mix, DWORD ctrlID)
652 return (ctrlID >= 1 && ctrlID <= mix->numCtrl);
655 /**************************************************************************
656 * MIX_GetLineControls [internal]
658 static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, DWORD flags)
660 DWORD dwRet = MMSYSERR_NOERROR;
661 struct mixer* mix;
663 TRACE("(%04X, %p, %lu);\n", wDevID, lpMlc, flags);
665 if (lpMlc == NULL) return MMSYSERR_INVALPARAM;
666 if (lpMlc->cbStruct < sizeof(*lpMlc) ||
667 lpMlc->cbmxctrl < sizeof(MIXERCONTROLA))
668 return MMSYSERR_INVALPARAM;
669 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
671 switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
673 case MIXER_GETLINECONTROLSF_ALL:
675 int i, j;
677 TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc->dwLineID, lpMlc->cControls);
679 for (i = j = 0; i < mix->numCtrl; i++)
681 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
682 j++;
684 if (!j || lpMlc->cControls != j) dwRet = MMSYSERR_INVALPARAM;
685 else if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
686 else
688 for (i = j = 0; i < mix->numCtrl; i++)
690 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
692 TRACE("[%d] => [%2d]: typ=%08lx\n", j, i + 1, mix->ctrl[i].ctrl.dwControlType);
693 lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl;
698 break;
699 case MIXER_GETLINECONTROLSF_ONEBYID:
700 TRACE("line=%08lx GLCF_ONEBYID (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
702 if (!MIX_CheckControl(mix, lpMlc->u.dwControlID) ||
703 mix->ctrl[lpMlc->u.dwControlID - 1].dwLineID != lpMlc->dwLineID)
704 dwRet = MMSYSERR_INVALPARAM;
705 else
706 lpMlc->pamxctrl[0] = mix->ctrl[lpMlc->u.dwControlID - 1].ctrl;
707 break;
708 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
709 TRACE("line=%08lx GLCF_ONEBYTYPE (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlType);
710 if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
711 else
713 int i;
714 DWORD ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
716 for (i = 0; i < mix->numCtrl; i++)
718 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID &&
719 ct == (mix->ctrl[i].ctrl.dwControlType & MIXERCONTROL_CT_CLASS_MASK))
721 lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl;
722 break;
725 if (i == mix->numCtrl) dwRet = MMSYSERR_INVALPARAM;
727 break;
728 default:
729 ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
730 dwRet = MMSYSERR_INVALPARAM;
733 return dwRet;
736 /**************************************************************************
737 * MIX_GetControlDetails [internal]
739 static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
741 DWORD ret = MMSYSERR_NOTSUPPORTED;
742 DWORD c, chnl;
743 struct mixer* mix;
745 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
747 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
748 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
750 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
752 case MIXER_GETCONTROLDETAILSF_VALUE:
753 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
754 if (MIX_CheckControl(mix, lpmcd->dwControlID))
756 c = lpmcd->dwControlID - 1;
757 chnl = HIWORD(mix->ctrl[c].dwLineID);
758 if (chnl == LINEID_DST)
759 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
760 switch (mix->ctrl[c].ctrl.dwControlType)
762 case MIXERCONTROL_CONTROLTYPE_VOLUME:
764 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
765 int val;
767 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
768 /* return value is 00RL (4 bytes)... */
769 if ((val = mix->volume[chnl]) == -1 && !MIX_GetVal(mix, chnl, &val))
770 return MMSYSERR_INVALPARAM;
772 switch (lpmcd->cChannels)
774 case 1:
775 /* mono... so R = L */
776 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
777 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
778 break;
779 case 2:
780 /* stereo, left is paDetails[0] */
781 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
782 mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100;
783 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
784 mcdu->dwValue = (HIBYTE(LOWORD(val)) * 65536L) / 100;
785 break;
786 default:
787 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
788 return MMSYSERR_INVALPARAM;
790 TRACE("=> %08lx\n", mcdu->dwValue);
792 break;
793 case MIXERCONTROL_CONTROLTYPE_MUTE:
794 case MIXERCONTROL_CONTROLTYPE_ONOFF:
796 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
798 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
799 /* we mute both channels at the same time */
800 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
801 mcdb->fValue = (mix->volume[chnl] != -1);
802 TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
804 break;
805 case MIXERCONTROL_CONTROLTYPE_MIXER:
806 case MIXERCONTROL_CONTROLTYPE_MUX:
808 unsigned mask;
810 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
811 if (!MIX_GetRecSrc(mix, &mask))
813 /* FIXME: ENXIO => no mixer installed */
814 WARN("mixer device not available !\n");
815 ret = MMSYSERR_ERROR;
817 else
819 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
820 int i, j;
822 /* we mute both channels at the same time */
823 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
825 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
827 if (WINE_CHN_SUPPORTS(mix->recMask, j))
829 if (i >= lpmcd->u.cMultipleItems)
830 return MMSYSERR_INVALPARAM;
831 mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j);
836 break;
837 default:
838 WARN("Unsupported\n");
840 ret = MMSYSERR_NOERROR;
842 else
844 ret = MMSYSERR_INVALPARAM;
846 break;
847 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
848 TRACE("LIST TEXT (%08lx)\n", lpmcd->dwControlID);
850 ret = MMSYSERR_INVALPARAM;
851 if (MIX_CheckControl(mix, lpmcd->dwControlID))
853 int c = lpmcd->dwControlID - 1;
855 if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ||
856 mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
858 LPMIXERCONTROLDETAILS_LISTTEXTA mcdlt;
859 int i, j;
861 mcdlt = (LPMIXERCONTROLDETAILS_LISTTEXTA)lpmcd->paDetails;
862 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
864 if (WINE_CHN_SUPPORTS(mix->recMask, j))
866 mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j);
867 mcdlt[i].dwParam2 = 0;
868 strcpy(mcdlt[i].szName, MIX_Names[j]);
869 i++;
872 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
873 ret = MMSYSERR_NOERROR;
876 break;
877 default:
878 WARN("Unknown flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
880 return ret;
883 /**************************************************************************
884 * MIX_SetControlDetails [internal]
886 static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
888 DWORD ret = MMSYSERR_NOTSUPPORTED;
889 DWORD c, chnl;
890 int val;
891 struct mixer* mix;
893 TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
895 if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
896 if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
898 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
900 case MIXER_GETCONTROLDETAILSF_VALUE:
901 TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
902 if (MIX_CheckControl(mix, lpmcd->dwControlID))
904 c = lpmcd->dwControlID - 1;
905 chnl = HIWORD(mix->ctrl[c].dwLineID);
906 if (chnl == LINEID_DST)
907 chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
909 switch (mix->ctrl[c].ctrl.dwControlType)
911 case MIXERCONTROL_CONTROLTYPE_VOLUME:
913 LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
915 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
916 /* val should contain 00RL */
917 switch (lpmcd->cChannels)
919 case 1:
920 /* mono... so R = L */
921 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
922 TRACE("Setting RL to %08ld\n", mcdu->dwValue);
923 val = 0x101 * ((mcdu->dwValue * 100) >> 16);
924 break;
925 case 2:
926 /* stereo, left is paDetails[0] */
927 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 0 * lpmcd->cbDetails);
928 TRACE("Setting L to %08ld\n", mcdu->dwValue);
929 val = ((mcdu->dwValue * 100) >> 16);
930 mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)((char*)lpmcd->paDetails + 1 * lpmcd->cbDetails);
931 TRACE("Setting R to %08ld\n", mcdu->dwValue);
932 val += ((mcdu->dwValue * 100) >> 16) << 8;
933 break;
934 default:
935 WARN("Unknown cChannels (%ld)\n", lpmcd->cChannels);
936 return MMSYSERR_INVALPARAM;
939 if (mix->volume[chnl] == -1)
941 if (!MIX_SetVal(mix, chnl, val))
942 return MMSYSERR_INVALPARAM;
944 else
946 mix->volume[chnl] = val;
949 ret = MMSYSERR_NOERROR;
950 break;
951 case MIXERCONTROL_CONTROLTYPE_MUTE:
952 case MIXERCONTROL_CONTROLTYPE_ONOFF:
954 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
956 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
957 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
958 if (mcdb->fValue)
960 if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) || !MIX_SetVal(mix, chnl, 0))
961 return MMSYSERR_INVALPARAM;
963 else
965 if (mix->volume[chnl] == -1)
967 ret = MMSYSERR_NOERROR;
968 break;
970 if (!MIX_SetVal(mix, chnl, mix->volume[chnl]))
971 return MMSYSERR_INVALPARAM;
972 mix->volume[chnl] = -1;
975 ret = MMSYSERR_NOERROR;
976 break;
977 case MIXERCONTROL_CONTROLTYPE_MIXER:
978 case MIXERCONTROL_CONTROLTYPE_MUX:
980 LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
981 unsigned mask;
982 int i, j;
984 TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
985 /* we mute both channels at the same time */
986 mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
987 mask = 0;
988 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
990 if (WINE_CHN_SUPPORTS(mix->recMask, j) && mcdb[i++].fValue)
992 /* a mux can only select one line at a time... */
993 if (mix->singleRecChannel && mask != 0)
995 FIXME("!!!\n");
996 return MMSYSERR_INVALPARAM;
998 mask |= WINE_CHN_MASK(j);
1001 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
1002 TRACE("writing %04x as rec src\n", mask);
1003 if (!MIX_SetRecSrc(mix, mask))
1004 ERR("Can't write new mixer settings\n");
1005 else
1006 ret = MMSYSERR_NOERROR;
1008 break;
1011 break;
1012 default:
1013 WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
1015 return ret;
1018 /**************************************************************************
1019 * MIX_Init [internal]
1021 static DWORD MIX_Init(void)
1023 int mixer;
1025 #define MIXER_DEV "/dev/mixer"
1026 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
1028 if (errno == ENODEV || errno == ENXIO)
1030 /* no driver present */
1031 return MMSYSERR_NODRIVER;
1033 MIX_NumMixers = 0;
1034 return MMSYSERR_ERROR;
1036 close(mixer);
1037 MIX_NumMixers = 1;
1038 MIX_Mixers[0].name = MIXER_DEV;
1039 MIX_Open(0, NULL, 0); /* FIXME */
1040 #undef MIXER_DEV
1041 return MMSYSERR_NOERROR;
1044 /**************************************************************************
1045 * MIX_GetNumDevs [internal]
1047 static DWORD MIX_GetNumDevs(void)
1049 return MIX_NumMixers;
1052 #endif /* HAVE_OSS */
1054 /**************************************************************************
1055 * mxdMessage (WINEOSS.3)
1057 DWORD WINAPI OSS_mxdMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1058 DWORD dwParam1, DWORD dwParam2)
1060 /* TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); */
1062 #ifdef HAVE_OSS
1063 switch (wMsg)
1065 case DRVM_INIT:
1066 return MIX_Init();
1067 case DRVM_EXIT:
1068 case DRVM_ENABLE:
1069 case DRVM_DISABLE:
1070 /* FIXME: Pretend this is supported */
1071 return 0;
1072 case MXDM_GETDEVCAPS:
1073 return MIX_GetDevCaps(wDevID, (LPMIXERCAPSA)dwParam1, dwParam2);
1074 case MXDM_GETLINEINFO:
1075 return MIX_GetLineInfo(wDevID, (LPMIXERLINEA)dwParam1, dwParam2);
1076 case MXDM_GETNUMDEVS:
1077 return MIX_GetNumDevs();
1078 case MXDM_OPEN:
1079 return MMSYSERR_NOERROR;
1080 /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1081 case MXDM_CLOSE:
1082 return MMSYSERR_NOERROR;
1083 case MXDM_GETLINECONTROLS:
1084 return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSA)dwParam1, dwParam2);
1085 case MXDM_GETCONTROLDETAILS:
1086 return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1087 case MXDM_SETCONTROLDETAILS:
1088 return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
1089 default:
1090 WARN("unknown message %d!\n", wMsg);
1091 return MMSYSERR_NOTSUPPORTED;
1093 #else
1094 return MMSYSERR_NOTENABLED;
1095 #endif