From b15504e1221c7e584c78946567a7bd16f68463ce Mon Sep 17 00:00:00 2001 From: Robert Reif Date: Sun, 22 Aug 2004 22:26:48 +0000 Subject: [PATCH] Added support for multiple mixers. Get mixer name from OSS. More tracing. --- dlls/winmm/wineoss/mixer.c | 251 +++++++++++++++++++++++++++++++++------------ 1 file changed, 188 insertions(+), 63 deletions(-) diff --git a/dlls/winmm/wineoss/mixer.c b/dlls/winmm/wineoss/mixer.c index 8f65505b9f3..2b65ee55172 100644 --- a/dlls/winmm/wineoss/mixer.c +++ b/dlls/winmm/wineoss/mixer.c @@ -83,7 +83,8 @@ struct mixerCtrl struct mixer { - const char* name; + char* name; + char* dev_name; int volume[SOUND_MIXER_NRDEVICES]; int devMask; int stereoMask; @@ -131,7 +132,6 @@ static const char * getIoctlCommand(int command) static char str[64]; #define IOCTL_TO_STR(x) case x: return #x; switch (command) { - IOCTL_TO_STR(SOUND_MIXER_NRDEVICES); IOCTL_TO_STR(SOUND_MIXER_VOLUME); IOCTL_TO_STR(SOUND_MIXER_BASS); IOCTL_TO_STR(SOUND_MIXER_TREBLE); @@ -157,8 +157,6 @@ static const char * getIoctlCommand(int command) IOCTL_TO_STR(SOUND_MIXER_VIDEO); IOCTL_TO_STR(SOUND_MIXER_RADIO); IOCTL_TO_STR(SOUND_MIXER_MONITOR); - IOCTL_TO_STR(SOUND_ONOFF_MIN); - IOCTL_TO_STR(SOUND_ONOFF_MAX); } #undef IOCTL_TO_STR sprintf(str, "UNKNOWN(%08x)", command); @@ -207,23 +205,72 @@ static const char * getControlType(DWORD dwControlType) return str; } +static const char * getComponentType(DWORD dwComponentType) +{ + static char str[64]; +#define TYPE_TO_STR(x) case x: return #x; + switch (dwComponentType) { + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY); + TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG); + } +#undef TYPE_TO_STR + sprintf(str, "UNKNOWN(%08lx)", dwComponentType); + return str; +} + +static const char * getTargetType(DWORD dwType) +{ + static char str[64]; +#define TYPE_TO_STR(x) case x: return #x; + switch (dwType) { + TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED); + TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT); + TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN); + TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT); + TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN); + TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX); + } +#undef TYPE_TO_STR + sprintf(str, "UNKNOWN(%08lx)", dwType); + return str; +} + /************************************************************************** * MIX_FillLineControls [internal] */ static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, - DWORD dwType) + DWORD dwControlType) { struct mixerCtrl* mc = &mix->ctrl[c]; int j; - TRACE("(%p, %d, %08lx, %s)\n", mix, c, lineID, getControlType(dwType)); + TRACE("(%p, %d, %08lx, %s)\n", mix, c, lineID, + getControlType(dwControlType)); mc->dwLineID = lineID; mc->ctrl.cbStruct = sizeof(MIXERCONTROLA); mc->ctrl.dwControlID = c + 1; - mc->ctrl.dwControlType = dwType; + mc->ctrl.dwControlType = dwControlType; - switch (dwType) + switch (dwControlType) { case MIXERCONTROL_CONTROLTYPE_VOLUME: mc->ctrl.fdwControl = 0; @@ -266,9 +313,9 @@ static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, break; default: - FIXME("Internal error: unknown type: %08lx\n", dwType); + FIXME("Internal error: unknown type: %08lx\n", dwControlType); } - TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c + 1, dwType, lineID); + TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c + 1, dwControlType, lineID); } /****************************************************************** @@ -280,7 +327,7 @@ static struct mixer* MIX_Get(WORD wDevID) { TRACE("(%04x)\n", wDevID); - if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].name == NULL) + if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].dev_name == NULL) return NULL; return &MIX_Mixers[wDevID]; @@ -302,7 +349,7 @@ static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags) * 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 + * the equivalent of mixer identification * (with a reference to a wave, midi.. handle */ if (!(mix = MIX_Get(wDevID))) { @@ -310,10 +357,10 @@ static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags) return MMSYSERR_BADDEVICEID; } - if ((mixer = open(mix->name, O_RDWR)) < 0) + if ((mixer = open(mix->dev_name, O_RDWR)) < 0) { ERR("open(%s, O_RDWR) failed (%s)\n", - mix->name, strerror(errno)); + mix->dev_name, strerror(errno)); if (errno == ENODEV || errno == ENXIO) { @@ -327,7 +374,7 @@ static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags) if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1) { ERR("ioctl(%s, SOUND_MIXER_DEVMASK) failed (%s)\n", - mix->name, strerror(errno)); + mix->dev_name, strerror(errno)); ret = MMSYSERR_ERROR; goto error; } @@ -343,7 +390,7 @@ static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags) if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1) { ERR("ioctl(%s, SOUND_MIXER_STEREODEVS) failed (%s)\n", - mix->name, strerror(errno)); + mix->dev_name, strerror(errno)); ret = MMSYSERR_ERROR; goto error; } @@ -352,7 +399,7 @@ static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags) if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1) { ERR("ioctl(%s, SOUND_MIXER_RECMASK) failed (%s)\n", - mix->name, strerror(errno)); + mix->dev_name, strerror(errno)); ret = MMSYSERR_ERROR; goto error; } @@ -367,7 +414,7 @@ static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags) if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1) { ERR("ioctl(%s, SOUND_MIXER_READ_CAPS) failed (%s)\n", - mix->name, strerror(errno)); + mix->dev_name, strerror(errno)); ret = MMSYSERR_ERROR; goto error; } @@ -443,7 +490,7 @@ static BOOL MIX_GetVal(struct mixer* mix, int chn, int* val) TRACE("(%p, %s, %p\n", mix, getIoctlCommand(chn), val); - if ((mixer = open(mix->name, O_RDWR)) < 0) { + if ((mixer = open(mix->dev_name, O_RDWR)) < 0) { /* FIXME: ENXIO => no mixer installed */ WARN("mixer device not available !\n"); } else { @@ -452,7 +499,7 @@ static BOOL MIX_GetVal(struct mixer* mix, int chn, int* val) ret = TRUE; } else { ERR("ioctl(%s, MIXER_READ(%s)) failed (%s)\n", - mix->name, getIoctlCommand(chn), strerror(errno)); + mix->dev_name, getIoctlCommand(chn), strerror(errno)); } close(mixer); } @@ -469,7 +516,7 @@ static BOOL MIX_SetVal(struct mixer* mix, int chn, int val) TRACE("(%p, %s, %x)\n", mix, getIoctlCommand(chn), val); - if ((mixer = open(mix->name, O_RDWR)) < 0) { + if ((mixer = open(mix->dev_name, O_RDWR)) < 0) { /* FIXME: ENXIO => no mixer installed */ WARN("mixer device not available !\n"); } else { @@ -478,7 +525,7 @@ static BOOL MIX_SetVal(struct mixer* mix, int chn, int val) ret = TRUE; } else { ERR("ioctl(%s, MIXER_WRITE(%s)) failed (%s)\n", - mix->name, getIoctlCommand(chn), strerror(errno)); + mix->dev_name, getIoctlCommand(chn), strerror(errno)); } close(mixer); } @@ -497,12 +544,12 @@ static BOOL MIX_GetRecSrc(struct mixer* mix, unsigned* mask) TRACE("(%p, %p)\n", mix, mask); - if ((mixer = open(mix->name, O_RDWR)) >= 0) { + if ((mixer = open(mix->dev_name, O_RDWR)) >= 0) { if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) { ret = TRUE; } else { ERR("ioctl(%s, SOUND_MIXER_READ_RECSRC) failed (%s)\n", - mix->name, strerror(errno)); + mix->dev_name, strerror(errno)); } close(mixer); } @@ -521,12 +568,12 @@ static BOOL MIX_SetRecSrc(struct mixer* mix, unsigned mask) TRACE("(%p, %08x)\n", mix, mask); - if ((mixer = open(mix->name, O_RDWR)) >= 0) { + if ((mixer = open(mix->dev_name, O_RDWR)) >= 0) { if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) >= 0) { ret = TRUE; } else { ERR("ioctl(%s, SOUND_MIXER_WRITE_RECSRC) failed (%s)\n", - mix->name, strerror(errno)); + mix->dev_name, strerror(errno)); } close(mixer); } @@ -556,7 +603,10 @@ static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSA lpCaps, DWORD dwSize) capsA.wMid = WINE_MIXER_MANUF_ID; capsA.wPid = WINE_MIXER_PRODUCT_ID; capsA.vDriverVersion = WINE_MIXER_VERSION; - strcpy(capsA.szPname, WINE_MIXER_NAME); + if (mix->name) + strcpy(capsA.szPname, mix->name); + else + strcpy(capsA.szPname, WINE_MIXER_NAME); capsA.cDestinations = 2; /* speakers & record */ capsA.fdwSupport = 0; /* No bits defined yet */ @@ -689,7 +739,7 @@ static BOOL MIX_CheckLine(DWORD lineID) TRACE("(%08lx)\n",lineID); return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) || - (HIWORD(lineID) == LINEID_DST && + (HIWORD(lineID) == LINEID_DST && LOWORD(lineID) < SOUND_MIXER_NRDEVICES)); } @@ -705,8 +755,14 @@ static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo) TRACE("(%04X, %p, %lu);\n", wDevID, lpMl, fdwInfo); - if (lpMl == NULL || lpMl->cbStruct != sizeof(*lpMl)) { - WARN("invalid parameter\n"); + if (lpMl == NULL) { + WARN("invalid parameter: lpMl = NULL\n"); + return MMSYSERR_INVALPARAM; + } + + if (lpMl->cbStruct != sizeof(*lpMl)) { + WARN("invalid parameter: lpMl->cbStruct = %ld != %d\n", + lpMl->cbStruct, sizeof(*lpMl)); return MMSYSERR_INVALPARAM; } @@ -726,7 +782,8 @@ static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo) case MIXER_GETLINEINFOF_DESTINATION: TRACE("MIXER_GETLINEINFOF_DESTINATION (%08lx)\n", lpMl->dwDestination); if (lpMl->dwDestination >= 2) { - WARN("invalid parameter\n"); + WARN("invalid parameter: lpMl->dwDestination = %ld >= 2\n", + lpMl->dwDestination); return MMSYSERR_INVALPARAM; } ret = MIX_GetLineInfoDst(mix, lpMl, lpMl->dwDestination); @@ -780,8 +837,8 @@ static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo) } break; case MIXER_GETLINEINFOF_COMPONENTTYPE: - TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE (%08lx)\n", - lpMl->dwComponentType); + TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE (%s)\n", + getComponentType(lpMl->dwComponentType)); switch (lpMl->dwComponentType) { case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS: @@ -809,12 +866,27 @@ static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo) ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1); break; default: - FIXME("Unhandled component type (%08lx)\n", lpMl->dwComponentType); + FIXME("Unhandled component type (%s)\n", + getComponentType(lpMl->dwComponentType)); return MMSYSERR_INVALPARAM; } break; case MIXER_GETLINEINFOF_TARGETTYPE: FIXME("MIXER_GETLINEINFOF_TARGETTYPE not implemented yet.\n"); + TRACE("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n", + getTargetType(lpMl->Target.dwType)); + switch (lpMl->Target.dwType) { + case MIXERLINE_TARGETTYPE_UNDEFINED: + case MIXERLINE_TARGETTYPE_WAVEOUT: + case MIXERLINE_TARGETTYPE_WAVEIN: + case MIXERLINE_TARGETTYPE_MIDIOUT: + case MIXERLINE_TARGETTYPE_MIDIIN: + case MIXERLINE_TARGETTYPE_AUX: + default: + FIXME("Unhandled target type (%s)\n", + getTargetType(lpMl->Target.dwType)); + return MMSYSERR_INVALPARAM; + } break; default: WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK); @@ -826,7 +898,10 @@ static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEA lpMl, DWORD fdwInfo) lpMl->Target.wMid = WINE_MIXER_MANUF_ID; lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID; lpMl->Target.vDriverVersion = WINE_MIXER_VERSION; - strcpy(lpMl->Target.szPname, WINE_MIXER_NAME); + if (mix->name) + strcpy(lpMl->Target.szPname, mix->name); + else + strcpy(lpMl->Target.szPname, WINE_MIXER_NAME); return ret; } @@ -858,9 +933,15 @@ static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSA lpMlc, return MMSYSERR_INVALPARAM; } - if (lpMlc->cbStruct < sizeof(*lpMlc) || - lpMlc->cbmxctrl < sizeof(MIXERCONTROLA)) { - WARN("invalid parameter: lpMlc\n"); + if (lpMlc->cbStruct < sizeof(*lpMlc)) { + WARN("invalid parameter: lpMlc->cbStruct = %ld < %d\n", + lpMlc->cbStruct, sizeof(*lpMlc)); + return MMSYSERR_INVALPARAM; + } + + if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLA)) { + WARN("invalid parameter: lpMlc->cbmxctrl = %ld < %d\n", + lpMlc->cbmxctrl, sizeof(MIXERCONTROLA)); return MMSYSERR_INVALPARAM; } @@ -986,7 +1067,7 @@ static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, LPMIXERCONTROLDETAILS_UNSIGNED mcdu; int val; - if (lpmcd->cbDetails != + if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) { WARN("invalid parameter: cbDetails != %d\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED)); @@ -1009,16 +1090,17 @@ static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, switch (lpmcd->cChannels) { case 1: - TRACE("mono\n"); /* mono... so R = L */ mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100; + TRACE("Reading RL = %ld\n", mcdu->dwValue); break; case 2: - TRACE("stereo\n"); /* stereo, left is paDetails[0] */ mcdu->dwValue = (LOBYTE(LOWORD(val)) * 65536L) / 100; + TRACE("Reading L = %ld\n", mcdu->dwValue); mcdu++; mcdu->dwValue = (HIBYTE(LOWORD(val)) * 65536L) / 100; + TRACE("Reading R = %ld\n", mcdu->dwValue); break; default: WARN("Unsupported cChannels (%ld)\n", lpmcd->cChannels); @@ -1032,7 +1114,7 @@ static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, { LPMIXERCONTROLDETAILS_BOOLEAN mcdb; - if (lpmcd->cbDetails != + if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) { WARN("invalid parameter: cbDetails != %d\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN)); @@ -1054,7 +1136,7 @@ static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, { unsigned mask; - if (lpmcd->cbDetails != + if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) { WARN("invalid parameter: cbDetails != %d\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN)); @@ -1185,7 +1267,7 @@ static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, { LPMIXERCONTROLDETAILS_UNSIGNED mcdu; - if (lpmcd->cbDetails != + if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) { WARN("invalid parameter: cbDetails != %d\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED)); @@ -1237,7 +1319,7 @@ static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, { LPMIXERCONTROLDETAILS_BOOLEAN mcdb; - if (lpmcd->cbDetails != + if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) { WARN("invalid parameter: cbDetails != %d\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN)); @@ -1281,7 +1363,7 @@ static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, unsigned mask; int i, j; - if (lpmcd->cbDetails != + if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) { WARN("invalid parameter: cbDetails != %d\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN)); @@ -1333,27 +1415,69 @@ static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, */ static DWORD MIX_Init(void) { - int mixer; + int i, mixer; TRACE("()\n"); -#define MIXER_DEV "/dev/mixer" - if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) - { - if (errno == ENODEV || errno == ENXIO) - { - /* no driver present */ - WARN("no driver\n"); - return MMSYSERR_NODRIVER; - } - MIX_NumMixers = 0; - return MMSYSERR_ERROR; + MIX_NumMixers = 0; + + for (i = 0; i < MAX_MIXERDRV; i++) { + char name[32]; + + if (i == 0) + sprintf(name, "/dev/mixer"); + else + sprintf(name, "/dev/mixer%d", i); + + if ((mixer = open(name, O_RDWR)) >= 0) { +#ifdef SOUND_MIXER_INFO + mixer_info info; + if (ioctl(mixer, SOUND_MIXER_INFO, &info) >= 0) { + MIX_Mixers[i].name = HeapAlloc(GetProcessHeap(),0,strlen(info.name) + 1); + strncpy(MIX_Mixers[i].name, info.name, sizeof(info.name)); + } else { + /* FreeBSD up to at least 5.2 provides this ioctl, but does not + * implement it properly, and there are probably similar issues + * on other platforms, so we warn but try to go ahead. + */ + WARN("%s: cannot read SOUND_MIXER_INFO!\n", name); + } +#endif + close(mixer); + + MIX_NumMixers++; + MIX_Mixers[i].dev_name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1); + strcpy(MIX_Mixers[i].dev_name, name); + MIX_Open(i, NULL, 0); /* FIXME */ + } else { + WARN("couldn't open %s\n", name); + } } - close(mixer); - MIX_NumMixers = 1; - MIX_Mixers[0].name = MIXER_DEV; - MIX_Open(0, NULL, 0); /* FIXME */ -#undef MIXER_DEV + + if (MIX_NumMixers == 0) { + WARN("no driver\n"); + return MMSYSERR_NODRIVER; + } + + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * MIX_Exit [internal] + */ +static DWORD MIX_Exit(void) +{ + int i; + + TRACE("()\n"); + + for (i = 0; i < MIX_NumMixers; i++) { + if (MIX_Mixers[i].name) + HeapFree(GetProcessHeap(),0,MIX_Mixers[i].name); + if (MIX_Mixers[i].dev_name) + HeapFree(GetProcessHeap(),0,MIX_Mixers[i].dev_name); + } + return MMSYSERR_NOERROR; } @@ -1384,6 +1508,7 @@ DWORD WINAPI OSS_mxdMessage(UINT wDevID, UINT wMsg, DWORD dwUser, case DRVM_INIT: return MIX_Init(); case DRVM_EXIT: + return MIX_Exit(); case DRVM_ENABLE: case DRVM_DISABLE: /* FIXME: Pretend this is supported */ -- 2.11.4.GIT