diff --git a/dlls/winmm/wineoss/mixer.c b/dlls/winmm/wineoss/mixer.c index c1491ae6d4f..8113c28ebbb 100644 --- a/dlls/winmm/wineoss/mixer.c +++ b/dlls/winmm/wineoss/mixer.c @@ -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 #include #include +#include #include #include "windef.h" #include "mmddk.h" @@ -25,8 +26,6 @@ DEFAULT_DEBUG_CHANNEL(mmaux); #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 @@ DEFAULT_DEBUG_CHANNEL(mmaux); #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)) + 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_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 @@ static BOOL MIX_GetVal(int chn, int* val) /************************************************************************** * 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 @@ static BOOL MIX_SetVal(int chn, int val) 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_GetLineInfoFromIndex [internal] + * MIX_GetLineInfoDst [internal] */ -static DWORD MIX_GetLineInfoFromIndex(LPMIXERLINEA lpMl, int devmask, DWORD idx) +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_GetLineInfoSrc [internal] + */ +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 @@ static DWORD MIX_GetLineInfoFromIndex(LPMIXERLINEA lpMl, int devmask, DWORD idx) 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 @@ static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo) 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; @@ -287,84 +620,13 @@ static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo) return ret; } -/************************************************************************** - * MIX_GetLineInfo [internal] +/****************************************************************** + * MIX_CheckControl + * */ -static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags) +static BOOL MIX_CheckControl(struct mixer* mix, DWORD ctrlID) { - 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] - */ -static void MIX_DoGetLineControls(LPMIXERCONTROLA mc, DWORD lineID, DWORD dwType) -{ - 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 void MIX_DoGetLineControls(LPMIXERCONTROLA mc, DWORD lineID, DWORD dwType 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 @@ static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, DWORD 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_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, DWORD */ 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 @@ static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWO } 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_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWO */ 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 @@ static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWO 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])) + if (mcdb->fValue) + { + if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) || !MIX_SetVal(mix, chnl, 0)) return MMSYSERR_INVALPARAM; - if (!MIX_SetVal(lineID, 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_GetNumDevs(UINT wDevID) */ 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 @@ static DWORD MIX_Init(void) 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 @@ DWORD WINAPI OSS_mixMessage(UINT wDevID, UINT wMsg, DWORD dwUser, 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 @@ DWORD WINAPI OSS_mixMessage(UINT wDevID, UINT wMsg, DWORD dwUser, 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