OSS mixer fixes
eric pouech
eric.pouech at wanadoo.fr
Thu Nov 1 15:00:18 CST 2001
this patch fixes several issues found in mixer code
- the DST_WAVE_IN wasn't properly handled
- lots of error cases were not checked
- all requests mode weren't supported
- the record part (and settings) wasn't handled
all of this should be better know (at least 3 programs which were broken
before are now fixed, or at least they display reasonable results)
A+
--
---------------
Eric Pouech (http://perso.wanadoo.fr/eric.pouech/)
"The future will be better tomorrow", Vice President Dan Quayle
-------------- next part --------------
Name: mixer
ChangeLog: fixed dst / src lines implementation
added mux/mixer control for master rec
added framework for multiple mixers
GenDate: 2001/11/01 20:57:57 UTC
ModifiedFiles: dlls/winmm/wineoss/mixer.c
AddedFiles:
===================================================================
RCS file: /usr/share/cvs/cvsroot/wine/wine/dlls/winmm/wineoss/mixer.c,v
retrieving revision 1.13
diff -u -u -r1.13 mixer.c
--- dlls/winmm/wineoss/mixer.c 2001/07/11 18:56:45 1.13
+++ dlls/winmm/wineoss/mixer.c 2001/11/01 20:57:53
@@ -4,7 +4,7 @@
* Sample MIXER Wine Driver for Linux
*
* Copyright 1997 Marcus Meissner
- * 1999 Eric Pouech
+ * 1999,2001 Eric Pouech
*/
#include "config.h"
@@ -15,6 +15,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <assert.h>
#include <sys/ioctl.h>
#include "windef.h"
#include "mmddk.h"
@@ -25,8 +26,6 @@
#ifdef HAVE_OSS
-#define MIXER_DEV "/dev/mixer"
-
#define WINE_MIXER_MANUF_ID 0xAA
#define WINE_MIXER_PRODUCT_ID 0x55
#define WINE_MIXER_VERSION 0x0100
@@ -35,34 +34,263 @@
#define WINE_CHN_MASK(_x) (1L << (_x))
#define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))
/* Bass and Treble are no longer in the mask as Windows does not handle them */
-#define WINE_MIXER_MASK (WINE_CHN_MASK(SOUND_MIXER_VOLUME) | \
- WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
+#define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
WINE_CHN_MASK(SOUND_MIXER_PCM) | \
WINE_CHN_MASK(SOUND_MIXER_LINE) | \
+ WINE_CHN_MASK(SOUND_MIXER_MIC) | \
+ WINE_CHN_MASK(SOUND_MIXER_CD) )
+
+#define WINE_MIXER_MASK_RECORD (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \
+ WINE_CHN_MASK(SOUND_MIXER_LINE) | \
WINE_CHN_MASK(SOUND_MIXER_MIC) | \
- WINE_CHN_MASK(SOUND_MIXER_CD))
+ WINE_CHN_MASK(SOUND_MIXER_IMIX) )
/* FIXME: the two following string arrays should be moved to a resource file in a string table */
/* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
static char* MIX_Labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
static char* MIX_Names [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
-static int MIX_Volume[SOUND_MIXER_NRDEVICES];
-static int MIX_DevMask = 0;
-static int MIX_StereoMask = 0;
+struct mixerCtrl
+{
+ DWORD dwLineID;
+ MIXERCONTROLA ctrl;
+};
+
+struct mixer
+{
+ const char* name;
+ int volume[SOUND_MIXER_NRDEVICES];
+ int devMask;
+ int stereoMask;
+ int recMask;
+ BOOL singleRecChannel;
+ struct mixerCtrl* ctrl;
+ int numCtrl;
+};
+
+#define LINEID_DST 0xFFFF
+#define LINEID_SPEAKER 0x0000
+#define LINEID_RECORD 0x0001
+
+static int MIX_NumMixers;
+static struct mixer MIX_Mixers[1];
+
/**************************************************************************
+ * MIX_FillLineControls [internal]
+ */
+static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, DWORD dwType)
+{
+ struct mixerCtrl* mc = &mix->ctrl[c];
+ int j;
+
+ mc->dwLineID = lineID;
+ mc->ctrl.cbStruct = sizeof(MIXERCONTROLA);
+ mc->ctrl.dwControlID = c + 1;
+ mc->ctrl.dwControlType = dwType;
+
+ switch (dwType)
+ {
+ case MIXERCONTROL_CONTROLTYPE_VOLUME:
+ mc->ctrl.fdwControl = 0;
+ mc->ctrl.cMultipleItems = 0;
+ lstrcpynA(mc->ctrl.szShortName, "Vol", MIXER_SHORT_NAME_CHARS);
+ lstrcpynA(mc->ctrl.szName, "Volume", MIXER_LONG_NAME_CHARS);
+ memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
+ /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
+ * [0, 100] is the range supported by OSS
+ * whatever the min and max values are they must match
+ * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
+ */
+ mc->ctrl.Bounds.s1.dwMinimum = 0;
+ mc->ctrl.Bounds.s1.dwMaximum = 65535;
+ memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
+ break;
+ case MIXERCONTROL_CONTROLTYPE_MUTE:
+ case MIXERCONTROL_CONTROLTYPE_ONOFF:
+ mc->ctrl.fdwControl = 0;
+ mc->ctrl.cMultipleItems = 0;
+ lstrcpynA(mc->ctrl.szShortName, "Mute", MIXER_SHORT_NAME_CHARS);
+ lstrcpynA(mc->ctrl.szName, "Mute", MIXER_LONG_NAME_CHARS);
+ memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
+ mc->ctrl.Bounds.s1.dwMinimum = 0;
+ mc->ctrl.Bounds.s1.dwMaximum = 1;
+ memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
+ break;
+ case MIXERCONTROL_CONTROLTYPE_MUX:
+ case MIXERCONTROL_CONTROLTYPE_MIXER:
+ mc->ctrl.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
+ mc->ctrl.cMultipleItems = 0;
+ for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
+ if (WINE_CHN_SUPPORTS(mix->recMask, j))
+ mc->ctrl.cMultipleItems++;
+ lstrcpynA(mc->ctrl.szShortName, "Mixer", MIXER_SHORT_NAME_CHARS);
+ lstrcpynA(mc->ctrl.szName, "Mixer", MIXER_LONG_NAME_CHARS);
+ memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
+ memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
+ break;
+
+ default:
+ FIXME("Internal error: unknown type: %08lx\n", dwType);
+ }
+ TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c + 1, dwType, lineID);
+}
+
+/******************************************************************
+ * MIX_GetMixer
+ *
+ *
+ */
+static struct mixer* MIX_Get(WORD wDevID)
+{
+ if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].name == NULL) return NULL;
+ return &MIX_Mixers[wDevID];
+}
+
+/**************************************************************************
+ * MIX_Open [internal]
+ */
+static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
+{
+ int mixer, i, j;
+ unsigned caps;
+ struct mixer* mix;
+ DWORD ret = MMSYSERR_NOERROR;
+
+ TRACE("(%04X, %p, %lu);\n", wDevID, lpMod, flags);
+
+ /* as we partly init the mixer with MIX_Open, we can allow null open decs */
+ /* EPP if (lpMod == NULL) return MMSYSERR_INVALPARAM; */
+ /* anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer device before sending
+ * messages to it... it seems to be linked to all the equivalent of mixer identification
+ * (with a reference to a wave, midi.. handle
+ */
+ if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
+
+ if ((mixer = open(mix->name, O_RDWR)) < 0)
+ {
+ if (errno == ENODEV || errno == ENXIO)
+ {
+ /* no driver present */
+ return MMSYSERR_NODRIVER;
+ }
+ return MMSYSERR_ERROR;
+ }
+
+ if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1)
+ {
+ perror("ioctl mixer SOUND_MIXER_DEVMASK");
+ ret = MMSYSERR_ERROR;
+ goto error;
+ }
+ mix->devMask &= WINE_MIXER_MASK_SPEAKER;
+ if (mix->devMask == 0)
+ {
+ ret = MMSYSERR_NODRIVER;
+ goto error;
+ }
+
+ if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1)
+ {
+ perror("ioctl mixer SOUND_MIXER_STEREODEVS");
+ ret = MMSYSERR_ERROR;
+ goto error;
+ }
+ mix->stereoMask &= WINE_MIXER_MASK_SPEAKER;
+
+ if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1)
+ {
+ perror("ioctl mixer SOUND_MIXER_RECMASK");
+ ret = MMSYSERR_ERROR;
+ goto error;
+ }
+ mix->recMask &= WINE_MIXER_MASK_RECORD;
+ /* FIXME: we may need to support both rec lev & igain */
+ if (!WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_RECLEV))
+ {
+ WARN("The sound card doesn't support rec level\n");
+ if (WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_IGAIN))
+ WARN("but it does support IGain, please report\n");
+ }
+ if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1)
+ {
+ perror("ioctl mixer SOUND_MIXER_READ_CAPS");
+ ret = MMSYSERR_ERROR;
+ goto error;
+ }
+ mix->singleRecChannel = caps & SOUND_CAP_EXCL_INPUT;
+ TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
+ mix->devMask, mix->recMask, mix->stereoMask,
+ mix->singleRecChannel ? "single" : "multiple");
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ {
+ mix->volume[i] = -1;
+ }
+ mix->numCtrl = 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
+ /* FIXME: do we always have RECLEV on all cards ??? */
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ {
+ if (WINE_CHN_SUPPORTS(mix->devMask, i))
+ mix->numCtrl += 2; /* volume & mute */
+ if (WINE_CHN_SUPPORTS(mix->recMask, i))
+ mix->numCtrl += 2; /* volume & onoff */
+
+ }
+ if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), 0, sizeof(mix->ctrl[0]) * mix->numCtrl)))
+ {
+ ret = MMSYSERR_NOMEM;
+ goto error;
+ }
+
+ j = 0;
+ MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_VOLUME);
+ MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE);
+ MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST),
+ mix->singleRecChannel ?
+ MIXERCONTROL_CONTROLTYPE_MUX : MIXERCONTROL_CONTROLTYPE_MIXER);
+ MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ {
+ if (WINE_CHN_SUPPORTS(mix->devMask, i))
+ {
+ MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
+ MIXERCONTROL_CONTROLTYPE_VOLUME);
+ MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
+ MIXERCONTROL_CONTROLTYPE_MUTE);
+ }
+ }
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ {
+ if (WINE_CHN_SUPPORTS(mix->recMask, i))
+ {
+ MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
+ MIXERCONTROL_CONTROLTYPE_VOLUME);
+ MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
+ MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
+ }
+ }
+ assert(j == mix->numCtrl);
+ error:
+ close(mixer);
+ return ret;
+}
+
+/**************************************************************************
* MIX_GetVal [internal]
*/
-static BOOL MIX_GetVal(int chn, int* val)
+static BOOL MIX_GetVal(struct mixer* mix, int chn, int* val)
{
int mixer;
BOOL ret = FALSE;
- if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
+ if ((mixer = open(mix->name, O_RDWR)) < 0)
+ {
/* FIXME: ENXIO => no mixer installed */
WARN("mixer device not available !\n");
- } else {
- if (ioctl(mixer, MIXER_READ(chn), val) >= 0) {
+ }
+ else
+ {
+ if (ioctl(mixer, MIXER_READ(chn), val) >= 0)
+ {
TRACE("Reading volume %x on %d\n", *val, chn);
ret = TRUE;
}
@@ -74,18 +302,22 @@
/**************************************************************************
* MIX_SetVal [internal]
*/
-static BOOL MIX_SetVal(int chn, int val)
+static BOOL MIX_SetVal(struct mixer* mix, int chn, int val)
{
int mixer;
BOOL ret = FALSE;
TRACE("Writing volume %x on %d\n", val, chn);
- if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
+ if ((mixer = open(mix->name, O_RDWR)) < 0)
+ {
/* FIXME: ENXIO => no mixer installed */
WARN("mixer device not available !\n");
- } else {
- if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0) {
+ }
+ else
+ {
+ if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0)
+ {
ret = TRUE;
}
close(mixer);
@@ -93,40 +325,136 @@
return ret;
}
+/******************************************************************
+ * MIX_GetRecSrc
+ *
+ *
+ */
+static BOOL MIX_GetRecSrc(struct mixer* mix, unsigned* mask)
+{
+ int mixer;
+ BOOL ret = FALSE;
+
+ if ((mixer = open(mix->name, O_RDWR)) >= 0)
+ {
+ if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) ret = TRUE;
+ close(mixer);
+ }
+ return ret;
+}
+
+/******************************************************************
+ * MIX_SetRecSrc
+ *
+ *
+ */
+static BOOL MIX_SetRecSrc(struct mixer* mix, unsigned mask)
+{
+ int mixer;
+ BOOL ret = FALSE;
+
+ if ((mixer = open(mix->name, O_RDWR)) >= 0)
+ {
+ if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) < 0)
+ {
+ ERR("Can't write new mixer settings\n");
+ }
+ else
+ ret = TRUE;
+ close(mixer);
+ }
+ return ret;
+}
+
/**************************************************************************
* MIX_GetDevCaps [internal]
*/
static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSA lpCaps, DWORD dwSize)
{
+ struct mixer* mix;
+
TRACE("(%04X, %p, %lu);\n", wDevID, lpCaps, dwSize);
- if (wDevID != 0) return MMSYSERR_BADDEVICEID;
if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
- if (!MIX_DevMask) return MMSYSERR_NOTENABLED;
+ if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID;
lpCaps->wMid = WINE_MIXER_MANUF_ID;
lpCaps->wPid = WINE_MIXER_PRODUCT_ID;
lpCaps->vDriverVersion = WINE_MIXER_VERSION;
strcpy(lpCaps->szPname, WINE_MIXER_NAME);
- lpCaps->cDestinations = 1;
+ lpCaps->cDestinations = 2; /* speakers & record */
lpCaps->fdwSupport = 0; /* No bits defined yet */
return MMSYSERR_NOERROR;
}
+/**************************************************************************
+ * MIX_GetLineInfoDst [internal]
+ */
+static DWORD MIX_GetLineInfoDst(struct mixer* mix, LPMIXERLINEA lpMl, DWORD dst)
+{
+ unsigned mask;
+ int j;
+
+ lpMl->dwDestination = dst;
+ switch (dst)
+ {
+ case 0:
+ lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
+ mask = mix->devMask;
+ j = SOUND_MIXER_VOLUME;
+ break;
+ case 1:
+ lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
+ mask = mix->recMask;
+ j = SOUND_MIXER_RECLEV;
+ break;
+ default:
+ FIXME("shouldn't happen\n");
+ return MMSYSERR_ERROR;
+ }
+ lpMl->dwSource = 0xFFFFFFFF;
+ lstrcpynA(lpMl->szShortName, MIX_Labels[j], MIXER_SHORT_NAME_CHARS);
+ lstrcpynA(lpMl->szName, MIX_Names[j], MIXER_LONG_NAME_CHARS);
+
+ /* we have all connections found in the MIX_DevMask */
+ lpMl->cConnections = 0;
+ for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
+ if (WINE_CHN_SUPPORTS(mask, j))
+ lpMl->cConnections++;
+ lpMl->cChannels = 1;
+ if (WINE_CHN_SUPPORTS(mix->stereoMask, lpMl->dwLineID))
+ lpMl->cChannels++;
+ lpMl->dwLineID = MAKELONG(dst, LINEID_DST);
+ lpMl->cControls = 0;
+ for (j = 0; j < mix->numCtrl; j++)
+ if (mix->ctrl[j].dwLineID == lpMl->dwLineID)
+ lpMl->cControls++;
+
+ return MMSYSERR_NOERROR;
+}
+
/**************************************************************************
- * MIX_GetLineInfoFromIndex [internal]
+ * MIX_GetLineInfoSrc [internal]
*/
-static DWORD MIX_GetLineInfoFromIndex(LPMIXERLINEA lpMl, int devmask, DWORD idx)
+static DWORD MIX_GetLineInfoSrc(struct mixer* mix, LPMIXERLINEA lpMl, DWORD idx, DWORD dst)
{
+ int i, j;
+ unsigned mask = (dst) ? mix->recMask : mix->devMask;
+
strcpy(lpMl->szShortName, MIX_Labels[idx]);
strcpy(lpMl->szName, MIX_Names[idx]);
- lpMl->dwLineID = idx;
- lpMl->dwDestination = 0; /* index for speakers */
+ lpMl->dwLineID = MAKELONG(dst, idx);
+ lpMl->dwDestination = dst;
lpMl->cConnections = 1;
- lpMl->cControls = 2;
- switch (idx) {
+ lpMl->cControls = 0;
+ for (i = 0; i < mix->numCtrl; i++)
+ if (mix->ctrl[i].dwLineID == lpMl->dwLineID)
+ lpMl->cControls++;
+
+ switch (idx)
+ {
case SOUND_MIXER_SYNTH:
lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
@@ -147,127 +475,132 @@
lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
break;
+ case SOUND_MIXER_IMIX:
+ lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
+ lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
+ break;
default:
WARN("Index %ld not handled.\n", idx);
return MIXERR_INVALLINE;
}
+ lpMl->cChannels = 1;
+ if (dst == 0 && WINE_CHN_SUPPORTS(mix->stereoMask, idx))
+ lpMl->cChannels++;
+ for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
+ {
+ if (WINE_CHN_SUPPORTS(mask, j))
+ {
+ if (j == idx) break;
+ i++;
+ }
+ }
+ lpMl->dwSource = i;
return MMSYSERR_NOERROR;
}
+/******************************************************************
+ * MIX_CheckLine
+ */
+static BOOL MIX_CheckLine(DWORD lineID)
+{
+ return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) ||
+ (HIWORD(lineID) == LINEID_DST && LOWORD(lineID) < SOUND_MIXER_NRDEVICES));
+}
+
/**************************************************************************
* MIX_GetLineInfo [internal]
*/
static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo)
{
int i, j;
- BOOL isDst = FALSE;
DWORD ret = MMSYSERR_NOERROR;
-
+ unsigned mask;
+ struct mixer* mix;
+
TRACE("(%04X, %p, %lu);\n", wDevID, lpMl, fdwInfo);
+
if (lpMl == NULL || lpMl->cbStruct != sizeof(*lpMl))
return MMSYSERR_INVALPARAM;
+ if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
/* FIXME: set all the variables correctly... the lines below
* are very wrong...
*/
lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE;
- lpMl->cChannels = 1;
lpMl->dwUser = 0;
- lpMl->cControls = 2;
- switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
+ switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
+ {
case MIXER_GETLINEINFOF_DESTINATION:
TRACE("DESTINATION (%08lx)\n", lpMl->dwDestination);
- /* FIXME: Linux doesn't seem to support multiple outputs?
- * So we have only one output type: Speaker.
- */
- lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
- lpMl->dwSource = 0xFFFFFFFF;
- lpMl->dwLineID = SOUND_MIXER_VOLUME;
- lstrcpynA(lpMl->szShortName, MIX_Labels[SOUND_MIXER_VOLUME], MIXER_SHORT_NAME_CHARS);
- lstrcpynA(lpMl->szName, MIX_Names[SOUND_MIXER_VOLUME], MIXER_LONG_NAME_CHARS);
-
- /* we have all connections found in the MIX_DevMask */
- lpMl->cConnections = 0;
- for (j = 1; j < SOUND_MIXER_NRDEVICES; j++)
- if (WINE_CHN_SUPPORTS(MIX_DevMask, j))
- lpMl->cConnections++;
- if (WINE_CHN_SUPPORTS(MIX_StereoMask, SOUND_MIXER_VOLUME))
- lpMl->cChannels++;
+ if (lpMl->dwDestination >= 2)
+ return MMSYSERR_INVALPARAM;
+ if ((ret = MIX_GetLineInfoDst(mix, lpMl, lpMl->dwDestination)) != MMSYSERR_NOERROR)
+ return ret;
break;
case MIXER_GETLINEINFOF_SOURCE:
- TRACE("SOURCE (%08lx)\n", lpMl->dwSource);
+ TRACE("SOURCE (%08lx), dst=%08lx\n", lpMl->dwSource, lpMl->dwDestination);
+ switch (lpMl->dwDestination)
+ {
+ case 0: mask = mix->devMask; break;
+ case 1: mask = mix->recMask; break;
+ default: return MMSYSERR_INVALPARAM;
+ }
i = lpMl->dwSource;
- for (j = 1; j < SOUND_MIXER_NRDEVICES; j++) {
- if (WINE_CHN_SUPPORTS(MIX_DevMask, j) && (i-- == 0))
+ for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
+ {
+ if (WINE_CHN_SUPPORTS(mask, j) && (i-- == 0))
break;
}
if (j >= SOUND_MIXER_NRDEVICES)
return MIXERR_INVALLINE;
- if (WINE_CHN_SUPPORTS(MIX_StereoMask, j))
- lpMl->cChannels++;
- if ((ret = MIX_GetLineInfoFromIndex(lpMl, MIX_DevMask, j)) != MMSYSERR_NOERROR)
+ if ((ret = MIX_GetLineInfoSrc(mix, lpMl, j, lpMl->dwDestination)) != MMSYSERR_NOERROR)
return ret;
break;
case MIXER_GETLINEINFOF_LINEID:
TRACE("LINEID (%08lx)\n", lpMl->dwLineID);
- if (lpMl->dwLineID >= SOUND_MIXER_NRDEVICES)
+
+ if (!MIX_CheckLine(lpMl->dwLineID))
return MIXERR_INVALLINE;
- if (WINE_CHN_SUPPORTS(MIX_StereoMask, lpMl->dwLineID))
- lpMl->cChannels++;
- if ((ret = MIX_GetLineInfoFromIndex(lpMl, MIX_DevMask, lpMl->dwLineID)) != MMSYSERR_NOERROR)
+ if (HIWORD(lpMl->dwLineID) == LINEID_DST)
+ ret = MIX_GetLineInfoDst(mix, lpMl, LOWORD(lpMl->dwLineID));
+ else
+ ret = MIX_GetLineInfoSrc(mix, lpMl, HIWORD(lpMl->dwLineID), LOWORD(lpMl->dwLineID));
+ if (ret != MMSYSERR_NOERROR)
return ret;
break;
case MIXER_GETLINEINFOF_COMPONENTTYPE:
TRACE("COMPONENT TYPE (%08lx)\n", lpMl->dwComponentType);
-
- switch (lpMl->dwComponentType) {
+ switch (lpMl->dwComponentType)
+ {
case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
- i = SOUND_MIXER_VOLUME;
- lpMl->dwDestination = 0;
- lpMl->dwSource = 0xFFFFFFFF;
- isDst = TRUE;
+ ret = MIX_GetLineInfoDst(mix, lpMl, 0);
+ break;
+ case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
+ ret = MIX_GetLineInfoDst(mix, lpMl, 1);
break;
case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
- i = SOUND_MIXER_SYNTH;
- lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
+ ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_SYNTH, 0);
break;
case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
- i = SOUND_MIXER_CD;
- lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
+ ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_CD, 0);
break;
case MIXERLINE_COMPONENTTYPE_SRC_LINE:
- i = SOUND_MIXER_LINE;
- lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
+ ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_LINE, 0);
break;
case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
- i = SOUND_MIXER_MIC;
- lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
+ ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_MIC, 1);
break;
case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
- i = SOUND_MIXER_PCM;
- lpMl->fdwLine |= MIXERLINE_LINEF_SOURCE;
+ ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_PCM, 0);
break;
+ case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
+ ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1);
+ break;
default:
FIXME("Unhandled component type (%08lx)\n", lpMl->dwComponentType);
return MMSYSERR_INVALPARAM;
}
-
- if (WINE_CHN_SUPPORTS(MIX_DevMask, i)) {
- strcpy(lpMl->szShortName, MIX_Labels[i]);
- strcpy(lpMl->szName, MIX_Names[i]);
- lpMl->dwLineID = i;
- }
- if (WINE_CHN_SUPPORTS(MIX_StereoMask, i))
- lpMl->cChannels++;
- lpMl->cConnections = 0;
- if (isDst) {
- for (j = 1; j < SOUND_MIXER_NRDEVICES; j++) {
- if (WINE_CHN_SUPPORTS(MIX_DevMask, j)) {
- lpMl->cConnections++;
- }
- }
- }
break;
case MIXER_GETLINEINFOF_TARGETTYPE:
FIXME("_TARGETTYPE not implemented yet.\n");
@@ -277,7 +610,7 @@
break;
}
- lpMl->Target.dwType = MIXERLINE_TARGETTYPE_AUX;
+ lpMl->Target.dwType = MIXERLINE_TARGETTYPE_AUX; /* FIXME */
lpMl->Target.dwDeviceID = 0xFFFFFFFF;
lpMl->Target.wMid = WINE_MIXER_MANUF_ID;
lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID;
@@ -286,85 +619,14 @@
return ret;
}
-
-/**************************************************************************
- * MIX_GetLineInfo [internal]
- */
-static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
-{
- TRACE("(%04X, %p, %lu);\n", wDevID, lpMod, flags);
- if (lpMod == NULL) return MMSYSERR_INVALPARAM;
-
- return (MIX_DevMask == 0) ? MMSYSERR_NODRIVER : MMSYSERR_NOERROR;
-}
-
-/**************************************************************************
- * MIX_MakeControlID [internal]
- */
-static DWORD MIX_MakeControlID(DWORD lineID, DWORD controlType)
-{
- switch (controlType) {
- case MIXERCONTROL_CONTROLTYPE_VOLUME:
- return 2 * lineID + 0;
- case MIXERCONTROL_CONTROLTYPE_MUTE:
- return 2 * lineID + 1;
- }
- FIXME("Internal error");
- return 0x00FADE00;
-}
-
-/**************************************************************************
- * MIX_SplitControlID [internal]
- */
-static BOOL MIX_SplitControlID(DWORD controlID, LPDWORD lineID, LPDWORD controlType)
-{
- *lineID = controlID / 2;
- *controlType = (controlID & 1) ?
- MIXERCONTROL_CONTROLTYPE_MUTE : MIXERCONTROL_CONTROLTYPE_VOLUME;
-
- return *lineID < SOUND_MIXER_NRDEVICES && WINE_CHN_SUPPORTS(MIX_DevMask, *lineID);
-}
-/**************************************************************************
- * MIX_DoGetLineControls [internal]
+/******************************************************************
+ * MIX_CheckControl
+ *
*/
-static void MIX_DoGetLineControls(LPMIXERCONTROLA mc, DWORD lineID, DWORD dwType)
+static BOOL MIX_CheckControl(struct mixer* mix, DWORD ctrlID)
{
- mc->cbStruct = sizeof(MIXERCONTROLA);
-
- switch (dwType) {
- case MIXERCONTROL_CONTROLTYPE_VOLUME:
- TRACE("Returning volume control\n");
- mc->dwControlID = MIX_MakeControlID(lineID, dwType);
- mc->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
- mc->fdwControl = 0;
- mc->cMultipleItems = 0;
- lstrcpynA(mc->szShortName, "Vol", MIXER_SHORT_NAME_CHARS);
- lstrcpynA(mc->szName, "Volume", MIXER_LONG_NAME_CHARS);
- memset(&mc->Bounds, 0, sizeof(mc->Bounds));
- /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
- * [0, 100] is the range supported by OSS
- * whatever the min and max values are they must match
- * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
- */
- mc->Bounds.s1.dwMinimum = 0;
- mc->Bounds.s1.dwMaximum = 65535;
- memset(&mc->Metrics, 0, sizeof(mc->Metrics));
- break;
- case MIXERCONTROL_CONTROLTYPE_MUTE:
- TRACE("Returning mute control\n");
- mc->dwControlID = MIX_MakeControlID(lineID, dwType);
- mc->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
- mc->fdwControl = 0;
- mc->cMultipleItems = 0;
- lstrcpynA(mc->szShortName, "Mute", MIXER_SHORT_NAME_CHARS);
- lstrcpynA(mc->szName, "Mute", MIXER_LONG_NAME_CHARS);
- memset(&mc->Bounds, 0, sizeof(mc->Bounds));
- memset(&mc->Metrics, 0, sizeof(mc->Metrics));
- break;
- default:
- FIXME("Internal error: unknown type: %08lx\n", dwType);
- }
+ return (ctrlID >= 1 && ctrlID <= mix->numCtrl);
}
/**************************************************************************
@@ -373,7 +635,7 @@
static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, DWORD flags)
{
DWORD dwRet = MMSYSERR_NOERROR;
- DWORD lineID, controlType;
+ struct mixer* mix;
TRACE("(%04X, %p, %lu);\n", wDevID, lpMlc, flags);
@@ -381,35 +643,63 @@
if (lpMlc->cbStruct < sizeof(*lpMlc) ||
lpMlc->cbmxctrl < sizeof(MIXERCONTROLA))
return MMSYSERR_INVALPARAM;
+ if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
- switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK) {
+ switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
+ {
case MIXER_GETLINECONTROLSF_ALL:
- TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc->dwLineID, lpMlc->cControls);
- if (lpMlc->cControls != 2) {
- dwRet = MMSYSERR_INVALPARAM;
- } else {
- MIX_DoGetLineControls(&lpMlc->pamxctrl[0], lpMlc->dwLineID, MIXERCONTROL_CONTROLTYPE_VOLUME);
- MIX_DoGetLineControls(&lpMlc->pamxctrl[1], lpMlc->dwLineID, MIXERCONTROL_CONTROLTYPE_MUTE);
+ {
+ int i, j;
+
+ TRACE("line=%08lx GLCF_ALL (%ld)\n", lpMlc->dwLineID, lpMlc->cControls);
+
+ for (i = j = 0; i < mix->numCtrl; i++)
+ {
+ if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
+ j++;
+ }
+ if (!j || lpMlc->cControls != j) dwRet = MMSYSERR_INVALPARAM;
+ else if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
+ else
+ {
+ for (i = j = 0; i < mix->numCtrl; i++)
+ {
+ if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
+ {
+ TRACE("[%d] => [%2d]: typ=%08lx\n", j, i + 1, mix->ctrl[i].ctrl.dwControlType);
+ lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl;
+ }
+ }
+ }
}
break;
case MIXER_GETLINECONTROLSF_ONEBYID:
TRACE("line=%08lx GLCF_ONEBYID (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlID);
- if (MIX_SplitControlID(lpMlc->u.dwControlID, &lineID, &controlType))
- MIX_DoGetLineControls(&lpMlc->pamxctrl[0], lineID, controlType);
- else
+
+ if (!MIX_CheckControl(mix, lpMlc->u.dwControlID) ||
+ mix->ctrl[lpMlc->u.dwControlID - 1].dwLineID != lpMlc->dwLineID)
dwRet = MMSYSERR_INVALPARAM;
+ else
+ lpMlc->pamxctrl[0] = mix->ctrl[lpMlc->u.dwControlID - 1].ctrl;
break;
case MIXER_GETLINECONTROLSF_ONEBYTYPE:
TRACE("line=%08lx GLCF_ONEBYTYPE (%lx)\n", lpMlc->dwLineID, lpMlc->u.dwControlType);
- switch (lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK) {
- case MIXERCONTROL_CT_CLASS_FADER:
- MIX_DoGetLineControls(&lpMlc->pamxctrl[0], lpMlc->dwLineID, MIXERCONTROL_CONTROLTYPE_VOLUME);
- break;
- case MIXERCONTROL_CT_CLASS_SWITCH:
- MIX_DoGetLineControls(&lpMlc->pamxctrl[0], lpMlc->dwLineID, MIXERCONTROL_CONTROLTYPE_MUTE);
- break;
- default:
- dwRet = MMSYSERR_INVALPARAM;
+ if (!MIX_CheckLine(lpMlc->dwLineID)) dwRet = MIXERR_INVALLINE;
+ else
+ {
+ int i;
+ DWORD ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
+
+ for (i = 0; i < mix->numCtrl; i++)
+ {
+ if (mix->ctrl[i].dwLineID == lpMlc->dwLineID &&
+ ct == (mix->ctrl[i].ctrl.dwControlType & MIXERCONTROL_CT_CLASS_MASK))
+ {
+ lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl;
+ break;
+ }
+ }
+ if (i == mix->numCtrl) dwRet = MMSYSERR_INVALPARAM;
}
break;
default:
@@ -425,28 +715,39 @@
*/
static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
{
- DWORD ret = MMSYSERR_NOTSUPPORTED;
- DWORD lineID, controlType;
-
+ DWORD ret = MMSYSERR_NOTSUPPORTED;
+ DWORD c, chnl;
+ struct mixer* mix;
+
TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
+ if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
- switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
+ switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
+ {
case MIXER_GETCONTROLDETAILSF_VALUE:
TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
- if (MIX_SplitControlID(lpmcd->dwControlID, &lineID, &controlType)) {
- switch (controlType) {
+ if (MIX_CheckControl(mix, lpmcd->dwControlID))
+ {
+ c = lpmcd->dwControlID - 1;
+ chnl = HIWORD(mix->ctrl[c].dwLineID);
+ if (chnl == LINEID_DST)
+ chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
+ switch (mix->ctrl[c].ctrl.dwControlType)
+ {
case MIXERCONTROL_CONTROLTYPE_VOLUME:
{
LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
int val;
+ TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
/* return value is 00RL (4 bytes)... */
- if ((val = MIX_Volume[lineID]) == -1 && !MIX_GetVal(lineID, &val))
+ if ((val = mix->volume[chnl]) == -1 && !MIX_GetVal(mix, chnl, &val))
return MMSYSERR_INVALPARAM;
- switch (lpmcd->cChannels) {
+ switch (lpmcd->cChannels)
+ {
case 1:
/* mono... so R = L */
mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
@@ -467,22 +768,88 @@
}
break;
case MIXERCONTROL_CONTROLTYPE_MUTE:
+ case MIXERCONTROL_CONTROLTYPE_ONOFF:
{
LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
+ TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
/* we mute both channels at the same time */
mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
- mcdb->fValue = (MIX_Volume[lineID] != -1);
+ mcdb->fValue = (mix->volume[chnl] != -1);
+ TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
+ }
+ break;
+ case MIXERCONTROL_CONTROLTYPE_MIXER:
+ case MIXERCONTROL_CONTROLTYPE_MUX:
+ {
+ unsigned mask;
+
+ TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
+ if (!MIX_GetRecSrc(mix, &mask))
+ {
+ /* FIXME: ENXIO => no mixer installed */
+ WARN("mixer device not available !\n");
+ ret = MMSYSERR_ERROR;
+ }
+ else
+ {
+ LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
+ int i, j;
+
+ /* we mute both channels at the same time */
+ mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
+
+ for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
+ {
+ if (WINE_CHN_SUPPORTS(mix->recMask, j))
+ {
+ if (i >= lpmcd->u.cMultipleItems)
+ return MMSYSERR_INVALPARAM;
+ mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j);
+ }
+ }
+ }
}
break;
+ default:
+ WARN("Unsupported\n");
}
ret = MMSYSERR_NOERROR;
- } else {
+ }
+ else
+ {
ret = MMSYSERR_INVALPARAM;
}
break;
case MIXER_GETCONTROLDETAILSF_LISTTEXT:
- FIXME("NIY\n");
+ TRACE("LIST TEXT (%08lx)\n", lpmcd->dwControlID);
+
+ ret = MMSYSERR_INVALPARAM;
+ if (MIX_CheckControl(mix, lpmcd->dwControlID))
+ {
+ int c = lpmcd->dwControlID - 1;
+
+ if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ||
+ mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
+ {
+ LPMIXERCONTROLDETAILS_LISTTEXTA mcdlt;
+ int i, j;
+
+ mcdlt = (LPMIXERCONTROLDETAILS_LISTTEXTA)lpmcd->paDetails;
+ for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
+ {
+ if (WINE_CHN_SUPPORTS(mix->recMask, j))
+ {
+ mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j);
+ mcdlt[i].dwParam2 = 0;
+ strcpy(mcdlt[i].szName, MIX_Names[j]);
+ i++;
+ }
+ }
+ if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
+ ret = MMSYSERR_NOERROR;
+ }
+ }
break;
default:
WARN("Unknown flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
@@ -495,25 +862,37 @@
*/
static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
{
- DWORD ret = MMSYSERR_NOTSUPPORTED;
- DWORD lineID, controlType;
- int val;
+ DWORD ret = MMSYSERR_NOTSUPPORTED;
+ DWORD c, chnl;
+ int val;
+ struct mixer* mix;
TRACE("(%04X, %p, %lu);\n", wDevID, lpmcd, fdwDetails);
if (lpmcd == NULL) return MMSYSERR_INVALPARAM;
+ if ((mix = MIX_Get(wDevID)) == NULL) return MMSYSERR_BADDEVICEID;
- switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
+ switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
+ {
case MIXER_GETCONTROLDETAILSF_VALUE:
TRACE("GCD VALUE (%08lx)\n", lpmcd->dwControlID);
- if (MIX_SplitControlID(lpmcd->dwControlID, &lineID, &controlType)) {
- switch (controlType) {
+ if (MIX_CheckControl(mix, lpmcd->dwControlID))
+ {
+ c = lpmcd->dwControlID - 1;
+ chnl = HIWORD(mix->ctrl[c].dwLineID);
+ if (chnl == LINEID_DST)
+ chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
+
+ switch (mix->ctrl[c].ctrl.dwControlType)
+ {
case MIXERCONTROL_CONTROLTYPE_VOLUME:
{
LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
+ TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED), lpmcd->cbDetails);
/* val should contain 00RL */
- switch (lpmcd->cChannels) {
+ switch (lpmcd->cChannels)
+ {
case 1:
/* mono... so R = L */
mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails;
@@ -534,55 +913,83 @@
return MMSYSERR_INVALPARAM;
}
- if (MIX_Volume[lineID] == -1) {
- if (!MIX_SetVal(lineID, val))
+ if (mix->volume[chnl] == -1)
+ {
+ if (!MIX_SetVal(mix, chnl, val))
return MMSYSERR_INVALPARAM;
- } else {
- MIX_Volume[lineID] = val;
}
+ else
+ {
+ mix->volume[chnl] = val;
+ }
}
ret = MMSYSERR_NOERROR;
break;
case MIXERCONTROL_CONTROLTYPE_MUTE:
+ case MIXERCONTROL_CONTROLTYPE_ONOFF:
{
LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
+ TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
- if (mcdb->fValue) {
- if (!MIX_GetVal(lineID, &MIX_Volume[lineID]))
- return MMSYSERR_INVALPARAM;
- if (!MIX_SetVal(lineID, 0))
+ if (mcdb->fValue)
+ {
+ if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) || !MIX_SetVal(mix, chnl, 0))
return MMSYSERR_INVALPARAM;
- } else {
- if (MIX_Volume[lineID] == -1) {
- ret = MMSYSERR_NOERROR;
- break;
+ }
+ else
+ {
+ if (mix->volume[chnl] == -1)
+ {
+ ret = MMSYSERR_NOERROR;
+ break;
}
- if (!MIX_SetVal(lineID, MIX_Volume[lineID]))
+ if (!MIX_SetVal(mix, chnl, mix->volume[chnl]))
return MMSYSERR_INVALPARAM;
- MIX_Volume[lineID] = -1;
+ mix->volume[chnl] = -1;
}
}
ret = MMSYSERR_NOERROR;
break;
+ case MIXERCONTROL_CONTROLTYPE_MIXER:
+ case MIXERCONTROL_CONTROLTYPE_MUX:
+ {
+ LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
+ unsigned mask;
+ int i, j;
+
+ TRACE(" <> %u %lu\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN), lpmcd->cbDetails);
+ /* we mute both channels at the same time */
+ mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails;
+ mask = 0;
+ for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
+ {
+ if (WINE_CHN_SUPPORTS(mix->recMask, j) && mcdb[i++].fValue)
+ {
+ /* a mux can only select one line at a time... */
+ if (mix->singleRecChannel && mask != 0)
+ {
+ FIXME("!!!\n");
+ return MMSYSERR_INVALPARAM;
+ }
+ mask |= WINE_CHN_MASK(j);
+ }
+ }
+ if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
+ TRACE("writing %04x as rec src\n", mask);
+ if (!MIX_SetRecSrc(mix, mask))
+ ERR("Can't write new mixer settings\n");
+ else
+ ret = MMSYSERR_NOERROR;
+ }
+ break;
}
}
break;
- case MIXER_GETCONTROLDETAILSF_LISTTEXT:
- FIXME("NIY\n");
- break;
default:
- WARN("Unknown GetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
+ WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
}
- return (ret);
-}
-
-/**************************************************************************
- * MIX_GetNumDevs [internal]
- */
-static DWORD MIX_GetNumDevs(UINT wDevID)
-{
- return (MIX_DevMask != 0) ? 1 : 0;
+ return ret;
}
/**************************************************************************
@@ -590,51 +997,35 @@
*/
static DWORD MIX_Init(void)
{
- int mixer, i;
+ int mixer;
- if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
- if (errno == ENODEV || errno == ENXIO) {
+#define MIXER_DEV "/dev/mixer"
+ if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
+ {
+ if (errno == ENODEV || errno == ENXIO)
+ {
/* no driver present */
return MMSYSERR_NODRIVER;
}
+ MIX_NumMixers = 0;
return MMSYSERR_ERROR;
}
- if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &MIX_DevMask) == -1) {
- close(mixer);
- perror("ioctl mixer SOUND_MIXER_DEVMASK");
- return MMSYSERR_NOTENABLED;
- }
- MIX_DevMask &= WINE_MIXER_MASK;
- if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &MIX_StereoMask) == -1) {
- close(mixer);
- perror("ioctl mixer SOUND_MIXER_STEREODEVS");
- return MMSYSERR_NOTENABLED;
- }
- MIX_StereoMask &= WINE_MIXER_MASK;
-
-#if 0
- int recsrc, recmask;
-
- if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &recsrc) == -1) {
- close(mixer);
- perror("ioctl mixer SOUND_MIXER_RECSRC");
- return MMSYSERR_NOTENABLED;
- }
-
- if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &recmask) == -1) {
- close(mixer);
- perror("ioctl mixer SOUND_MIXER_RECMASK");
- return MMSYSERR_NOTENABLED;
- }
-#endif
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- MIX_Volume[i] = -1;
- }
close(mixer);
-
+ MIX_NumMixers = 1;
+ MIX_Mixers[0].name = MIXER_DEV;
+ MIX_Open(0, NULL, 0); /* FIXME */
+#undef MIXER_DEV
return MMSYSERR_NOERROR;
}
+/**************************************************************************
+ * MIX_GetNumDevs [internal]
+ */
+static DWORD MIX_GetNumDevs(void)
+{
+ return MIX_NumMixers;
+}
+
#endif /* HAVE_OSS */
/**************************************************************************
@@ -643,14 +1034,13 @@
DWORD WINAPI OSS_mixMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
DWORD dwParam1, DWORD dwParam2)
{
- TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
- wDevID, wMsg, dwUser, dwParam1, dwParam2);
+/* TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); */
#ifdef HAVE_OSS
- switch(wMsg) {
+ switch (wMsg)
+ {
case DRVM_INIT:
return MIX_Init();
- break;
case DRVM_EXIT:
case DRVM_ENABLE:
case DRVM_DISABLE:
@@ -661,9 +1051,10 @@
case MXDM_GETLINEINFO:
return MIX_GetLineInfo(wDevID, (LPMIXERLINEA)dwParam1, dwParam2);
case MXDM_GETNUMDEVS:
- return MIX_GetNumDevs(wDevID);
+ return MIX_GetNumDevs();
case MXDM_OPEN:
- return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2);
+ return MMSYSERR_NOERROR;
+ /* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
case MXDM_CLOSE:
return MMSYSERR_NOERROR;
case MXDM_GETLINECONTROLS:
@@ -674,8 +1065,8 @@
return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
default:
WARN("unknown message %d!\n", wMsg);
+ return MMSYSERR_NOTSUPPORTED;
}
- return MMSYSERR_NOTSUPPORTED;
#else
return MMSYSERR_NOTENABLED;
#endif
More information about the wine-patches
mailing list