Fixed dst / src lines implementation.
[wine] / dlls / winmm / wineoss / mixer.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * Sample MIXER Wine Driver for Linux
5  *
6  * Copyright    1997 Marcus Meissner
7  *              1999,2001 Eric Pouech
8  */
9
10 #include "config.h"
11
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"
24
25 DEFAULT_DEBUG_CHANNEL(mmaux);
26
27 #ifdef HAVE_OSS
28
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"
33
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)     )
42
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)   )
47
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;
52
53 struct mixerCtrl
54 {
55     DWORD               dwLineID;
56     MIXERCONTROLA       ctrl;
57 };
58
59 struct mixer 
60 {
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;
69 };
70
71 #define LINEID_DST      0xFFFF
72 #define LINEID_SPEAKER  0x0000
73 #define LINEID_RECORD   0x0001
74
75 static int              MIX_NumMixers;
76 static struct mixer     MIX_Mixers[1];
77
78 /**************************************************************************
79  *                              MIX_FillLineControls            [internal]
80  */
81 static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, DWORD dwType)
82 {
83     struct mixerCtrl*   mc = &mix->ctrl[c];
84     int                 j;
85
86     mc->dwLineID = lineID;
87     mc->ctrl.cbStruct = sizeof(MIXERCONTROLA);
88     mc->ctrl.dwControlID = c + 1;
89     mc->ctrl.dwControlType = dwType;
90     
91     switch (dwType) 
92     {
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
103          */
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;
131         
132     default:
133         FIXME("Internal error: unknown type: %08lx\n", dwType);
134     }
135     TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c + 1, dwType, lineID);
136 }
137
138 /******************************************************************
139  *              MIX_GetMixer
140  *
141  *
142  */
143 static struct mixer*    MIX_Get(WORD wDevID)
144 {
145     if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].name == NULL) return NULL;
146     return &MIX_Mixers[wDevID];
147 }
148
149 /**************************************************************************
150  *                              MIX_Open                        [internal]
151  */
152 static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
153 {
154     int                 mixer, i, j;
155     unsigned            caps;
156     struct mixer*       mix;
157     DWORD               ret = MMSYSERR_NOERROR;
158
159     TRACE("(%04X, %p, %lu);\n", wDevID, lpMod, flags);
160     
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
166      */
167     if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
168
169     if ((mixer = open(mix->name, O_RDWR)) < 0) 
170     {
171         if (errno == ENODEV || errno == ENXIO) 
172         {       
173             /* no driver present */
174             return MMSYSERR_NODRIVER;
175         }
176         return MMSYSERR_ERROR;
177     }
178     
179     if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1) 
180     {
181         perror("ioctl mixer SOUND_MIXER_DEVMASK");
182         ret = MMSYSERR_ERROR;
183         goto error;
184     }
185     mix->devMask &= WINE_MIXER_MASK_SPEAKER;
186     if (mix->devMask == 0)
187     {
188         ret = MMSYSERR_NODRIVER;
189         goto error;
190     }
191
192     if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1) 
193     {
194         perror("ioctl mixer SOUND_MIXER_STEREODEVS");
195         ret = MMSYSERR_ERROR;
196         goto error;
197     }
198     mix->stereoMask &= WINE_MIXER_MASK_SPEAKER;
199
200     if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1) 
201     {
202         perror("ioctl mixer SOUND_MIXER_RECMASK");
203         ret = MMSYSERR_ERROR;
204         goto error;
205     }
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))
209     {
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");
213     }
214     if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1) 
215     {
216         perror("ioctl mixer SOUND_MIXER_READ_CAPS");
217         ret = MMSYSERR_ERROR;
218         goto error;
219     }
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++) 
225     {
226         mix->volume[i] = -1;
227     }
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++)
231     {
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 */
236         
237     }
238     if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), 0, sizeof(mix->ctrl[0]) * mix->numCtrl)))
239     {
240         ret = MMSYSERR_NOMEM;
241         goto error;
242     }
243     
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++)
252     {
253         if (WINE_CHN_SUPPORTS(mix->devMask, i))
254         {
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);
259         }
260     }
261     for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
262     {
263         if (WINE_CHN_SUPPORTS(mix->recMask, i))
264         {
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*/);
269         }
270     }
271     assert(j == mix->numCtrl);
272  error:
273     close(mixer);
274     return ret;
275 }
276
277 /**************************************************************************
278  *                              MIX_GetVal                      [internal]
279  */
280 static  BOOL    MIX_GetVal(struct mixer* mix, int chn, int* val)
281 {
282     int         mixer;
283     BOOL        ret = FALSE;
284
285     if ((mixer = open(mix->name, O_RDWR)) < 0) 
286     {
287         /* FIXME: ENXIO => no mixer installed */
288         WARN("mixer device not available !\n");
289     } 
290     else 
291     {
292         if (ioctl(mixer, MIXER_READ(chn), val) >= 0) 
293         {
294             TRACE("Reading volume %x on %d\n", *val, chn);
295             ret = TRUE;
296         }
297         close(mixer);
298     }
299     return ret;
300 }
301
302 /**************************************************************************
303  *                              MIX_SetVal                      [internal]
304  */
305 static  BOOL    MIX_SetVal(struct mixer* mix, int chn, int val)
306 {
307     int         mixer;
308     BOOL        ret = FALSE;
309
310     TRACE("Writing volume %x on %d\n", val, chn);
311
312     if ((mixer = open(mix->name, O_RDWR)) < 0) 
313     {
314         /* FIXME: ENXIO => no mixer installed */
315         WARN("mixer device not available !\n");
316     }
317     else 
318     {
319         if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0) 
320         {
321             ret = TRUE;
322         }
323         close(mixer);
324     }
325     return ret;
326 }
327
328 /******************************************************************
329  *              MIX_GetRecSrc
330  *
331  *
332  */
333 static BOOL     MIX_GetRecSrc(struct mixer* mix, unsigned* mask)
334 {
335     int         mixer;
336     BOOL        ret = FALSE;
337
338     if ((mixer = open(mix->name, O_RDWR)) >= 0)
339     {
340         if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) ret = TRUE;
341         close(mixer);
342     }
343     return ret;
344 }
345
346 /******************************************************************
347  *              MIX_SetRecSrc
348  *
349  *
350  */
351 static BOOL     MIX_SetRecSrc(struct mixer* mix, unsigned mask)
352 {
353     int         mixer;
354     BOOL        ret = FALSE;
355
356     if ((mixer = open(mix->name, O_RDWR)) >= 0) 
357     {
358         if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) < 0) 
359         {
360             ERR("Can't write new mixer settings\n");
361         }
362         else
363             ret = TRUE;
364         close(mixer);
365     }
366     return ret;
367 }
368
369 /**************************************************************************
370  *                              MIX_GetDevCaps                  [internal]
371  */
372 static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSA lpCaps, DWORD dwSize)
373 {
374     struct mixer*       mix;
375
376     TRACE("(%04X, %p, %lu);\n", wDevID, lpCaps, dwSize);
377     
378     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
379     if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
380
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);
385
386     lpCaps->cDestinations = 2; /* speakers & record */
387     lpCaps->fdwSupport = 0; /* No bits defined yet */
388     
389     return MMSYSERR_NOERROR;
390 }
391
392 /**************************************************************************
393  *                              MIX_GetLineInfoDst      [internal]
394  */
395 static  DWORD   MIX_GetLineInfoDst(struct mixer* mix, LPMIXERLINEA lpMl, DWORD dst)
396 {       
397     unsigned mask;
398     int j;
399
400     lpMl->dwDestination = dst;
401     switch (dst)
402     {
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;
416     }
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);
420         
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++;
434
435     return MMSYSERR_NOERROR;
436 }
437
438 /**************************************************************************
439  *                              MIX_GetLineInfoSrc      [internal]
440  */
441 static  DWORD   MIX_GetLineInfoSrc(struct mixer* mix, LPMIXERLINEA lpMl, DWORD idx, DWORD dst)
442 {
443     int         i, j;
444     unsigned    mask = (dst) ? mix->recMask : mix->devMask;
445
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++;
455
456     switch (idx) 
457     {
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;
485     }
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++) 
490     {
491         if (WINE_CHN_SUPPORTS(mask, j))
492         {
493             if (j == idx) break;
494             i++;
495         }
496     }   
497     lpMl->dwSource = i;
498     return MMSYSERR_NOERROR;
499 }
500
501 /******************************************************************
502  *              MIX_CheckLine
503  */
504 static BOOL MIX_CheckLine(DWORD lineID)
505 {
506     return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) || 
507             (HIWORD(lineID) == LINEID_DST && LOWORD(lineID) < SOUND_MIXER_NRDEVICES));
508 }
509
510 /**************************************************************************
511  *                              MIX_GetLineInfo                 [internal]
512  */
513 static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo)
514 {
515     int                 i, j;
516     DWORD               ret = MMSYSERR_NOERROR;
517     unsigned            mask;
518     struct mixer*       mix;
519
520     TRACE("(%04X, %p, %lu);\n", wDevID, lpMl, fdwInfo);
521
522     if (lpMl == NULL || lpMl->cbStruct != sizeof(*lpMl)) 
523         return MMSYSERR_INVALPARAM;
524     if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
525     
526     /* FIXME: set all the variables correctly... the lines below
527      * are very wrong...
528      */
529     lpMl->fdwLine       = MIXERLINE_LINEF_ACTIVE;
530     lpMl->dwUser        = 0;
531     
532     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) 
533     {
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)
544         {
545         case 0: mask = mix->devMask; break;
546         case 1: mask = mix->recMask; break;
547         default: return MMSYSERR_INVALPARAM;
548         }
549         i = lpMl->dwSource;
550         for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) 
551         {
552             if (WINE_CHN_SUPPORTS(mask, j) && (i-- == 0))
553                 break;
554         }
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);
562
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) 
575         {
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;
603         }
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;
611     }
612     
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);
619     
620     return ret;
621 }
622
623 /******************************************************************
624  *              MIX_CheckControl
625  *
626  */
627 static BOOL     MIX_CheckControl(struct mixer* mix, DWORD ctrlID)
628 {
629     return (ctrlID >= 1 && ctrlID <= mix->numCtrl);
630 }
631
632 /**************************************************************************
633  *                              MIX_GetLineControls             [internal]
634  */
635 static  DWORD   MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, DWORD flags)
636 {
637     DWORD               dwRet = MMSYSERR_NOERROR;
638     struct mixer*       mix;
639
640     TRACE("(%04X, %p, %lu);\n", wDevID, lpMlc, flags);
641     
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;
647
648     switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK) 
649     {
650     case MIXER_GETLINECONTROLSF_ALL:
651         {
652             int         i, j;
653
654             TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc->dwLineID, lpMlc->cControls);
655         
656             for (i = j = 0; i < mix->numCtrl; i++)
657             {
658                 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
659                     j++;
660             }
661             if (!j || lpMlc->cControls != j)            dwRet = MMSYSERR_INVALPARAM;
662             else if (!MIX_CheckLine(lpMlc->dwLineID))   dwRet = MIXERR_INVALLINE;
663             else 
664             {
665                 for (i = j = 0; i < mix->numCtrl; i++)
666                 {
667                     if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
668                     {
669                         TRACE("[%d] => [%2d]: typ=%08lx\n", j, i + 1, mix->ctrl[i].ctrl.dwControlType);
670                         lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl;
671                     }
672                 }
673             }
674         }
675         break;
676     case MIXER_GETLINECONTROLSF_ONEBYID:
677         TRACE("line=%08lx GLCF_ONEBYID (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
678
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 
689         {
690             int         i;
691             DWORD       ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
692
693             for (i = 0; i < mix->numCtrl; i++)
694             {
695                 if (mix->ctrl[i].dwLineID == lpMlc->dwLineID &&
696                     ct == (mix->ctrl[i].ctrl.dwControlType & MIXERCONTROL_CT_CLASS_MASK))
697                 {
698                     lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl;
699                     break;
700                 }
701             }
702             if (i == mix->numCtrl) dwRet = MMSYSERR_INVALPARAM;
703         }
704         break;
705     default:
706         ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
707         dwRet = MMSYSERR_INVALPARAM;
708     }
709
710     return dwRet;
711 }
712
713 /**************************************************************************
714  *                              MIX_GetControlDetails           [internal]
715  */
716 static  DWORD   MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
717 {
718     DWORD               ret = MMSYSERR_NOTSUPPORTED;
719     DWORD               c, chnl;
720     struct mixer*       mix;
721     
722     TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
723     
724     if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
725     if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
726
727     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) 
728     {
729     case MIXER_GETCONTROLDETAILSF_VALUE:
730         TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
731         if (MIX_CheckControl(mix, lpmcd->dwControlID))
732         {
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) 
738             {
739             case MIXERCONTROL_CONTROLTYPE_VOLUME:
740                 {
741                     LPMIXERCONTROLDETAILS_UNSIGNED      mcdu;
742                     int                                 val;
743
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;
748             
749                     switch (lpmcd->cChannels) 
750                     {
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;
766                     }
767                     TRACE("=> %08lx\n", mcdu->dwValue);
768                 }
769                 break;
770             case MIXERCONTROL_CONTROLTYPE_MUTE:
771             case MIXERCONTROL_CONTROLTYPE_ONOFF:
772                 {
773                     LPMIXERCONTROLDETAILS_BOOLEAN       mcdb;
774                     
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");
780                 }
781                 break;
782             case MIXERCONTROL_CONTROLTYPE_MIXER:
783             case MIXERCONTROL_CONTROLTYPE_MUX:
784                 {
785                     unsigned                            mask;
786
787                     TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
788                     if (!MIX_GetRecSrc(mix, &mask))
789                     {
790                         /* FIXME: ENXIO => no mixer installed */
791                         WARN("mixer device not available !\n");
792                         ret = MMSYSERR_ERROR;
793                     }
794                     else 
795                     {
796                         LPMIXERCONTROLDETAILS_BOOLEAN   mcdb;
797                         int                             i, j;
798
799                         /* we mute both channels at the same time */
800                         mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
801
802                         for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
803                         {
804                             if (WINE_CHN_SUPPORTS(mix->recMask, j))
805                             {
806                                 if (i >= lpmcd->u.cMultipleItems) 
807                                     return MMSYSERR_INVALPARAM;
808                                 mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j);
809                             }
810                         }
811                     }
812                 }
813                 break;
814             default:
815                 WARN("Unsupported\n");
816             }
817             ret = MMSYSERR_NOERROR;
818         }
819         else 
820         {
821             ret = MMSYSERR_INVALPARAM;
822         }
823         break;
824     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
825         TRACE("LIST TEXT (%08lx)\n", lpmcd->dwControlID);
826
827         ret = MMSYSERR_INVALPARAM;
828         if (MIX_CheckControl(mix, lpmcd->dwControlID))
829         {
830             int c = lpmcd->dwControlID - 1;
831
832             if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ||
833                 mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
834             {
835                 LPMIXERCONTROLDETAILS_LISTTEXTA mcdlt;
836                 int i, j;
837
838                 mcdlt = (LPMIXERCONTROLDETAILS_LISTTEXTA)lpmcd->paDetails;
839                 for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
840                 {
841                     if (WINE_CHN_SUPPORTS(mix->recMask, j))
842                     {
843                         mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j);
844                         mcdlt[i].dwParam2 = 0;
845                         strcpy(mcdlt[i].szName, MIX_Names[j]);
846                         i++;
847                     }
848                 }
849                 if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
850                 ret = MMSYSERR_NOERROR;
851             }
852         }
853         break;
854     default:
855         WARN("Unknown flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
856     }
857     return ret;
858 }
859
860 /**************************************************************************
861  *                              MIX_SetControlDetails           [internal]
862  */
863 static  DWORD   MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
864 {
865     DWORD               ret = MMSYSERR_NOTSUPPORTED;
866     DWORD               c, chnl;
867     int                 val;
868     struct mixer*       mix;
869     
870     TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
871     
872     if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
873     if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
874     
875     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) 
876     {
877     case MIXER_GETCONTROLDETAILSF_VALUE:
878         TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
879         if (MIX_CheckControl(mix, lpmcd->dwControlID))
880         {
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;
885
886             switch (mix->ctrl[c].ctrl.dwControlType) 
887             {
888             case MIXERCONTROL_CONTROLTYPE_VOLUME:
889                 {
890                     LPMIXERCONTROLDETAILS_UNSIGNED      mcdu;
891                     
892                     TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
893                     /* val should contain 00RL */
894                     switch (lpmcd->cChannels) 
895                     {
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;
914                     }
915                     
916                     if (mix->volume[chnl] == -1) 
917                     {
918                         if (!MIX_SetVal(mix, chnl, val))
919                             return MMSYSERR_INVALPARAM;
920                     }
921                     else 
922                     {
923                         mix->volume[chnl] = val;
924                     }
925                 }
926                 ret = MMSYSERR_NOERROR;
927                 break;
928             case MIXERCONTROL_CONTROLTYPE_MUTE:
929             case MIXERCONTROL_CONTROLTYPE_ONOFF:
930                 {
931                     LPMIXERCONTROLDETAILS_BOOLEAN       mcdb;
932                     
933                     TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
934                     mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
935                     if (mcdb->fValue) 
936                     {
937                         if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) || !MIX_SetVal(mix, chnl, 0))
938                             return MMSYSERR_INVALPARAM;
939                     }   
940                     else 
941                     {
942                         if (mix->volume[chnl] == -1) 
943                         {
944                             ret = MMSYSERR_NOERROR;
945                             break;
946                         }
947                         if (!MIX_SetVal(mix, chnl, mix->volume[chnl]))
948                             return MMSYSERR_INVALPARAM;
949                         mix->volume[chnl] = -1;
950                     }
951                 }
952                 ret = MMSYSERR_NOERROR;
953                 break;
954             case MIXERCONTROL_CONTROLTYPE_MIXER:
955             case MIXERCONTROL_CONTROLTYPE_MUX:
956                 {
957                     LPMIXERCONTROLDETAILS_BOOLEAN       mcdb;
958                     unsigned                            mask;
959                     int                                 i, j;
960
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++)
966                     {
967                         if (WINE_CHN_SUPPORTS(mix->recMask, j) && mcdb[i++].fValue)
968                         {
969                             /* a mux can only select one line at a time... */
970                             if (mix->singleRecChannel && mask != 0)     
971                             {
972                                 FIXME("!!!\n");
973                                 return MMSYSERR_INVALPARAM;
974                             }
975                             mask |= WINE_CHN_MASK(j);
976                         }
977                     }
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;
984                 }
985                 break;
986             }
987         }
988         break;
989     default:
990         WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
991     }
992     return ret;
993 }
994
995 /**************************************************************************
996  *                              MIX_Init                        [internal]
997  */
998 static  DWORD   MIX_Init(void)
999 {
1000     int mixer;
1001
1002 #define MIXER_DEV "/dev/mixer"
1003     if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) 
1004     {
1005         if (errno == ENODEV || errno == ENXIO) 
1006         {       
1007             /* no driver present */
1008             return MMSYSERR_NODRIVER;
1009         }
1010         MIX_NumMixers = 0;
1011         return MMSYSERR_ERROR;
1012     }
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;
1019 }
1020
1021 /**************************************************************************
1022  *                              MIX_GetNumDevs                  [internal]
1023  */
1024 static  DWORD   MIX_GetNumDevs(void)
1025 {
1026     return MIX_NumMixers;
1027 }
1028
1029 #endif /* HAVE_OSS */
1030
1031 /**************************************************************************
1032  *                              mixMessage (WINEOSS.3)
1033  */
1034 DWORD WINAPI OSS_mixMessage(UINT wDevID, UINT wMsg, DWORD dwUser, 
1035                             DWORD dwParam1, DWORD dwParam2)
1036 {
1037 /* TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); */
1038     
1039 #ifdef HAVE_OSS
1040     switch (wMsg) 
1041     {
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;
1069     }
1070 #else
1071     return MMSYSERR_NOTENABLED;
1072 #endif
1073 }
1074
1075