|  | /* | 
|  | * Sample MIXER Wine Driver for Mac OS X (based on OSS mixer) | 
|  | * | 
|  | * Copyright 	1997 Marcus Meissner | 
|  | * 		1999,2001 Eric Pouech | 
|  | *              2006,2007 Emmanuel Maillard | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "wine/port.h" | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <stdarg.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #ifdef HAVE_UNISTD_H | 
|  | # include <unistd.h> | 
|  | #endif | 
|  |  | 
|  | #define NONAMELESSUNION | 
|  | #define NONAMELESSSTRUCT | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "winnls.h" | 
|  | #include "mmddk.h" | 
|  | #include "coreaudio.h" | 
|  | #include "wine/unicode.h" | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(mixer); | 
|  |  | 
|  | #if defined(HAVE_COREAUDIO_COREAUDIO_H) | 
|  | #include <CoreAudio/CoreAudio.h> | 
|  | #include <CoreFoundation/CoreFoundation.h> | 
|  |  | 
|  | #define	WINE_MIXER_NAME "CoreAudio Mixer" | 
|  |  | 
|  | #define InputDevice (1 << 0) | 
|  | #define OutputDevice (1 << 1) | 
|  |  | 
|  | #define IsInput(dir) ((dir) & InputDevice) | 
|  | #define IsOutput(dir) ((dir) & OutputDevice) | 
|  |  | 
|  | #define ControlsPerLine 2 /* number of control per line : volume & (mute | onoff) */ | 
|  |  | 
|  | #define IDControlVolume 0 | 
|  | #define IDControlMute 1 | 
|  |  | 
|  | typedef struct tagMixerLine | 
|  | { | 
|  | char *name; | 
|  | int direction; | 
|  | int numChannels; | 
|  | int componentType; | 
|  | AudioDeviceID deviceID; | 
|  | } MixerLine; | 
|  |  | 
|  | typedef struct tagMixerCtrl | 
|  | { | 
|  | DWORD dwLineID; | 
|  | MIXERCONTROLW ctrl; | 
|  | } MixerCtrl; | 
|  |  | 
|  | typedef struct tagCoreAudio_Mixer | 
|  | { | 
|  | MIXERCAPSW caps; | 
|  |  | 
|  | MixerCtrl *mixerCtrls; | 
|  | MixerLine *lines; | 
|  | DWORD numCtrl; | 
|  | } CoreAudio_Mixer; | 
|  |  | 
|  | static CoreAudio_Mixer mixer; | 
|  | static int numMixers = 1; | 
|  |  | 
|  | /************************************************************************** | 
|  | */ | 
|  |  | 
|  | static const char * getMessage(UINT uMsg) | 
|  | { | 
|  | static char str[64]; | 
|  | #define MSG_TO_STR(x) case x: return #x; | 
|  | switch (uMsg) { | 
|  | MSG_TO_STR(DRVM_INIT); | 
|  | MSG_TO_STR(DRVM_EXIT); | 
|  | MSG_TO_STR(DRVM_ENABLE); | 
|  | MSG_TO_STR(DRVM_DISABLE); | 
|  | MSG_TO_STR(MXDM_GETDEVCAPS); | 
|  | MSG_TO_STR(MXDM_GETLINEINFO); | 
|  | MSG_TO_STR(MXDM_GETNUMDEVS); | 
|  | MSG_TO_STR(MXDM_OPEN); | 
|  | MSG_TO_STR(MXDM_CLOSE); | 
|  | MSG_TO_STR(MXDM_GETLINECONTROLS); | 
|  | MSG_TO_STR(MXDM_GETCONTROLDETAILS); | 
|  | MSG_TO_STR(MXDM_SETCONTROLDETAILS); | 
|  | } | 
|  | #undef MSG_TO_STR | 
|  | sprintf(str, "UNKNOWN(%08x)", uMsg); | 
|  | return str; | 
|  | } | 
|  |  | 
|  | static const char * getControlType(DWORD dwControlType) | 
|  | { | 
|  | static char str[64]; | 
|  | #define TYPE_TO_STR(x) case x: return #x; | 
|  | switch (dwControlType) { | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME); | 
|  | TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME); | 
|  | } | 
|  | #undef TYPE_TO_STR | 
|  | sprintf(str, "UNKNOWN(%08x)", 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(%08x)", 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(%08x)", dwType); | 
|  | return str; | 
|  | } | 
|  |  | 
|  | /* FIXME is there a better way ? */ | 
|  | static DWORD DeviceComponentType(char *name) | 
|  | { | 
|  | if (strcmp(name, "Built-in Microphone") == 0) | 
|  | return MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE; | 
|  |  | 
|  | if (strcmp(name, "Built-in Line Input") == 0) | 
|  | return MIXERLINE_COMPONENTTYPE_SRC_LINE; | 
|  |  | 
|  | if (strcmp(name, "Built-in Output") == 0) | 
|  | return MIXERLINE_COMPONENTTYPE_DST_SPEAKERS; | 
|  |  | 
|  | return MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED; | 
|  | } | 
|  |  | 
|  | static BOOL DeviceHasMute(AudioDeviceID deviceID, Boolean isInput) | 
|  | { | 
|  | Boolean writable = false; | 
|  | OSStatus err = noErr; | 
|  | err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyMute, NULL, NULL); | 
|  | if (err == noErr) | 
|  | { | 
|  | /* check if we can set it */ | 
|  | err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyMute, NULL, &writable); | 
|  | if (err == noErr) | 
|  | return writable; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Getters | 
|  | */ | 
|  | static BOOL MIX_LineGetVolume(DWORD lineID, DWORD channels, Float32 *left, Float32 *right) | 
|  | { | 
|  | MixerLine *line = &mixer.lines[lineID]; | 
|  | UInt32 size = sizeof(Float32); | 
|  | OSStatus err = noErr; | 
|  | *left = *right = 0.0; | 
|  |  | 
|  | err = AudioDeviceGetProperty(line->deviceID, 1, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, &size, left); | 
|  | if (err != noErr) | 
|  | return FALSE; | 
|  |  | 
|  | if (channels == 2) | 
|  | { | 
|  | size = sizeof(Float32); | 
|  | err = AudioDeviceGetProperty(line->deviceID, 2, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, &size, right); | 
|  | if (err != noErr) | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | TRACE("lineID %d channels %d return left %f right %f\n", lineID, channels, *left, *right); | 
|  | return (err == noErr); | 
|  | } | 
|  |  | 
|  | static BOOL MIX_LineGetMute(DWORD lineID, BOOL *muted) | 
|  | { | 
|  | MixerLine *line = &mixer.lines[lineID]; | 
|  | UInt32 size = sizeof(UInt32); | 
|  | UInt32 val = 0; | 
|  | OSStatus err = noErr; | 
|  | err = AudioDeviceGetProperty(line->deviceID, 0, IsInput(line->direction), kAudioDevicePropertyMute, &size, &val); | 
|  | *muted = val; | 
|  |  | 
|  | return (err == noErr); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Setters | 
|  | */ | 
|  | static BOOL MIX_LineSetVolume(DWORD lineID, DWORD channels, Float32 left, Float32 right) | 
|  | { | 
|  | MixerLine *line = &mixer.lines[lineID]; | 
|  | UInt32 size = sizeof(Float32); | 
|  | OSStatus err = noErr; | 
|  | TRACE("lineID %d channels %d left %f right %f\n", lineID, channels, left, right); | 
|  |  | 
|  | if (channels == 2) | 
|  | { | 
|  | err = AudioDeviceSetProperty(line->deviceID, NULL, 1, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left); | 
|  | if (err != noErr) | 
|  | return FALSE; | 
|  |  | 
|  | err = AudioDeviceSetProperty(line->deviceID, NULL, 2, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &right); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* | 
|  | FIXME Using master channel failed ?? return kAudioHardwareUnknownPropertyError | 
|  | err = AudioDeviceSetProperty(line->deviceID, NULL, 0, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left); | 
|  | */ | 
|  | right = left; | 
|  | err = AudioDeviceSetProperty(line->deviceID, NULL, 1, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &left); | 
|  | if (err != noErr) | 
|  | return FALSE; | 
|  | err = AudioDeviceSetProperty(line->deviceID, NULL, 2, IsInput(line->direction), kAudioDevicePropertyVolumeScalar, size, &right); | 
|  | } | 
|  | return (err == noErr); | 
|  | } | 
|  |  | 
|  | static BOOL MIX_LineSetMute(DWORD lineID, BOOL mute) | 
|  | { | 
|  | MixerLine *line = &mixer.lines[lineID]; | 
|  | UInt32 val = mute; | 
|  | UInt32 size = sizeof(UInt32); | 
|  | OSStatus err = noErr; | 
|  |  | 
|  | err = AudioDeviceSetProperty(line->deviceID, 0, 0, IsInput(line->direction), kAudioDevicePropertyMute, size, &val); | 
|  | return (err == noErr); | 
|  | } | 
|  |  | 
|  | static void MIX_FillControls(void) | 
|  | { | 
|  | int i; | 
|  | int ctrl = 0; | 
|  | MixerLine *line; | 
|  | for (i = 0; i < mixer.caps.cDestinations; i++) | 
|  | { | 
|  | line = &mixer.lines[i]; | 
|  | mixer.mixerCtrls[ctrl].dwLineID = i; | 
|  | mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW); | 
|  | mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; | 
|  | mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl; | 
|  | mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0; | 
|  | mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 65535; | 
|  | mixer.mixerCtrls[ctrl].ctrl.Metrics.cSteps = 656; | 
|  | ctrl++; | 
|  |  | 
|  | mixer.mixerCtrls[ctrl].dwLineID = i; | 
|  | if ( !DeviceHasMute(line->deviceID, IsInput(line->direction)) ) | 
|  | mixer.mixerCtrls[ctrl].ctrl.fdwControl |= MIXERCONTROL_CONTROLF_DISABLED; | 
|  |  | 
|  | mixer.mixerCtrls[ctrl].ctrl.cbStruct = sizeof(MIXERCONTROLW); | 
|  | mixer.mixerCtrls[ctrl].ctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE; | 
|  | mixer.mixerCtrls[ctrl].ctrl.dwControlID = ctrl; | 
|  | mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMinimum = 0; | 
|  | mixer.mixerCtrls[ctrl].ctrl.Bounds.s1.dwMaximum = 1; | 
|  | ctrl++; | 
|  | } | 
|  | assert(ctrl == mixer.numCtrl); | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				CoreAudio_MixerInit | 
|  | */ | 
|  | LONG CoreAudio_MixerInit(void) | 
|  | { | 
|  | OSStatus status; | 
|  | UInt32 propertySize; | 
|  | AudioDeviceID *deviceArray = NULL; | 
|  | char name[MAXPNAMELEN]; | 
|  | int i; | 
|  | int numLines; | 
|  |  | 
|  | AudioStreamBasicDescription streamDescription; | 
|  |  | 
|  | /* Find number of lines */ | 
|  | status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propertySize, NULL); | 
|  | if (status) | 
|  | { | 
|  | ERR("AudioHardwareGetPropertyInfo for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status >> 24), | 
|  | (char) (status >> 16), | 
|  | (char) (status >> 8), | 
|  | (char) status); | 
|  | return DRV_FAILURE; | 
|  | } | 
|  |  | 
|  | numLines = propertySize / sizeof(AudioDeviceID); | 
|  |  | 
|  | mixer.mixerCtrls = NULL; | 
|  | mixer.lines = NULL; | 
|  | mixer.numCtrl = 0; | 
|  |  | 
|  | mixer.caps.cDestinations = numLines; | 
|  | mixer.caps.wMid = 0xAA; | 
|  | mixer.caps.wPid = 0x55; | 
|  | mixer.caps.vDriverVersion = 0x0100; | 
|  |  | 
|  | MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, mixer.caps.szPname, sizeof(mixer.caps.szPname) / sizeof(WCHAR)); | 
|  |  | 
|  | mixer.caps.fdwSupport = 0; /* No bits defined yet */ | 
|  |  | 
|  | mixer.lines = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerLine) * numLines); | 
|  | if (!mixer.lines) | 
|  | goto error; | 
|  |  | 
|  | deviceArray = HeapAlloc(GetProcessHeap(), 0, sizeof(AudioDeviceID) * numLines); | 
|  |  | 
|  | propertySize = sizeof(AudioDeviceID) * numLines; | 
|  | status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propertySize, deviceArray); | 
|  | if (status) | 
|  | { | 
|  | ERR("AudioHardwareGetProperty for kAudioHardwarePropertyDevices return %c%c%c%c\n", (char) (status >> 24), | 
|  | (char) (status >> 16), | 
|  | (char) (status >> 8), | 
|  | (char) status); | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < numLines; i++) | 
|  | { | 
|  | Boolean write; | 
|  | MixerLine *line = &mixer.lines[i]; | 
|  |  | 
|  | line->deviceID = deviceArray[i]; | 
|  |  | 
|  | propertySize = MAXPNAMELEN; | 
|  | status = AudioDeviceGetProperty(line->deviceID, 0 , FALSE, kAudioDevicePropertyDeviceName, &propertySize, name); | 
|  | if (status) { | 
|  | ERR("AudioHardwareGetProperty for kAudioDevicePropertyDeviceName return %c%c%c%c\n", (char) (status >> 24), | 
|  | (char) (status >> 16), | 
|  | (char) (status >> 8), | 
|  | (char) status); | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | line->name = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(name) + 1); | 
|  | if (!line->name) | 
|  | goto error; | 
|  |  | 
|  | memcpy(line->name, name, strlen(name)); | 
|  |  | 
|  | line->componentType = DeviceComponentType(line->name); | 
|  |  | 
|  | /* check for directions */ | 
|  | /* Output ? */ | 
|  | propertySize = sizeof(UInt32); | 
|  | status = AudioDeviceGetPropertyInfo(line->deviceID, 0, FALSE, kAudioDevicePropertyStreams, &propertySize, &write ); | 
|  | if (status) { | 
|  | ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyDataSource return %c%c%c%c\n", (char) (status >> 24), | 
|  | (char) (status >> 16), | 
|  | (char) (status >> 8), | 
|  | (char) status); | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | if ( (propertySize / sizeof(AudioStreamID)) != 0) | 
|  | { | 
|  | line->direction |= OutputDevice; | 
|  |  | 
|  | /* Check the number of channel for the stream */ | 
|  | propertySize = sizeof(streamDescription); | 
|  | status = AudioDeviceGetProperty(line->deviceID, 0, FALSE , kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription); | 
|  | if (status != noErr) { | 
|  | ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24), | 
|  | (char) (status >> 16), | 
|  | (char) (status >> 8), | 
|  | (char) status); | 
|  | goto error; | 
|  | } | 
|  | line->numChannels = streamDescription.mChannelsPerFrame; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Input ? */ | 
|  | propertySize = sizeof(UInt32); | 
|  | status = AudioDeviceGetPropertyInfo(line->deviceID, 0, TRUE, kAudioDevicePropertyStreams, &propertySize, &write ); | 
|  | if (status) { | 
|  | ERR("AudioDeviceGetPropertyInfo for kAudioDevicePropertyStreams return %c%c%c%c\n", (char) (status >> 24), | 
|  | (char) (status >> 16), | 
|  | (char) (status >> 8), | 
|  | (char) status); | 
|  | goto error; | 
|  | } | 
|  | if ( (propertySize / sizeof(AudioStreamID)) != 0) | 
|  | { | 
|  | line->direction |= InputDevice; | 
|  |  | 
|  | /* Check the number of channel for the stream */ | 
|  | propertySize = sizeof(streamDescription); | 
|  | status = AudioDeviceGetProperty(line->deviceID, 0, TRUE, kAudioDevicePropertyStreamFormat, &propertySize, &streamDescription); | 
|  | if (status != noErr) { | 
|  | ERR("AudioHardwareGetProperty for kAudioDevicePropertyStreamFormat return %c%c%c%c\n", (char) (status >> 24), | 
|  | (char) (status >> 16), | 
|  | (char) (status >> 8), | 
|  | (char) status); | 
|  | goto error; | 
|  | } | 
|  | line->numChannels = streamDescription.mChannelsPerFrame; | 
|  | } | 
|  | } | 
|  |  | 
|  | mixer.numCtrl += ControlsPerLine; /* volume & (mute | onoff) */ | 
|  | } | 
|  | mixer.mixerCtrls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MixerCtrl) * mixer.numCtrl); | 
|  | if (!mixer.mixerCtrls) | 
|  | goto error; | 
|  |  | 
|  | MIX_FillControls(); | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, deviceArray); | 
|  | return DRV_SUCCESS; | 
|  |  | 
|  | error: | 
|  | if (mixer.lines) | 
|  | { | 
|  | int i; | 
|  | for (i = 0; i < mixer.caps.cDestinations; i++) | 
|  | { | 
|  | HeapFree(GetProcessHeap(), 0, mixer.lines[i].name); | 
|  | } | 
|  | HeapFree(GetProcessHeap(), 0, mixer.lines); | 
|  | } | 
|  | HeapFree(GetProcessHeap(), 0, deviceArray); | 
|  | if (mixer.mixerCtrls) | 
|  | HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls); | 
|  | return DRV_FAILURE; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				CoreAudio_MixerRelease | 
|  | */ | 
|  | void CoreAudio_MixerRelease(void) | 
|  | { | 
|  | TRACE("()\n"); | 
|  |  | 
|  | if (mixer.lines) | 
|  | { | 
|  | int i; | 
|  | for (i = 0; i < mixer.caps.cDestinations; i++) | 
|  | { | 
|  | HeapFree(GetProcessHeap(), 0, mixer.lines[i].name); | 
|  | } | 
|  | HeapFree(GetProcessHeap(), 0, mixer.lines); | 
|  | } | 
|  | if (mixer.mixerCtrls) | 
|  | HeapFree(GetProcessHeap(), 0, mixer.mixerCtrls); | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				MIX_Open			[internal] | 
|  | */ | 
|  | static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD_PTR flags) | 
|  | { | 
|  | TRACE("wDevID=%d lpMod=%p dwSize=%08lx\n", wDevID, lpMod, flags); | 
|  | if (lpMod == NULL) { | 
|  | WARN("invalid parameter: lpMod == NULL\n"); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  |  | 
|  | if (wDevID >= numMixers) { | 
|  | WARN("bad device ID: %04X\n", wDevID); | 
|  | return MMSYSERR_BADDEVICEID; | 
|  | } | 
|  | return MMSYSERR_NOERROR; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				MIX_GetNumDevs			[internal] | 
|  | */ | 
|  | static DWORD MIX_GetNumDevs(void) | 
|  | { | 
|  | TRACE("()\n"); | 
|  | return numMixers; | 
|  | } | 
|  |  | 
|  | static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD_PTR dwSize) | 
|  | { | 
|  | TRACE("wDevID=%d lpCaps=%p\n", wDevID, lpCaps); | 
|  |  | 
|  | if (lpCaps == NULL) { | 
|  | WARN("Invalid Parameter\n"); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  |  | 
|  | if (wDevID >= numMixers) { | 
|  | WARN("bad device ID : %d\n", wDevID); | 
|  | return MMSYSERR_BADDEVICEID; | 
|  | } | 
|  | memcpy(lpCaps, &mixer.caps, min(dwSize, sizeof(*lpCaps))); | 
|  | return MMSYSERR_NOERROR; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				MIX_GetLineInfo			[internal] | 
|  | */ | 
|  | static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD_PTR fdwInfo) | 
|  | { | 
|  | int i; | 
|  | DWORD ret = MMSYSERR_ERROR; | 
|  | MixerLine *line = NULL; | 
|  |  | 
|  | TRACE("%04X, %p, %08lx\n", wDevID, lpMl, fdwInfo); | 
|  |  | 
|  | if (lpMl == NULL) { | 
|  | WARN("invalid parameter: lpMl = NULL\n"); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  |  | 
|  | if (lpMl->cbStruct != sizeof(*lpMl)) { | 
|  | WARN("invalid parameter: lpMl->cbStruct\n"); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  |  | 
|  | if (wDevID >= numMixers) { | 
|  | WARN("bad device ID: %04X\n", wDevID); | 
|  | return MMSYSERR_BADDEVICEID; | 
|  | } | 
|  |  | 
|  | /* FIXME: set all the variables correctly... the lines below | 
|  | * are very wrong... | 
|  | */ | 
|  | lpMl->dwUser = 0; | 
|  |  | 
|  | switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) | 
|  | { | 
|  | case MIXER_GETLINEINFOF_DESTINATION: | 
|  | TRACE("MIXER_GETLINEINFOF_DESTINATION %d\n", lpMl->dwDestination); | 
|  | if ( (lpMl->dwDestination >= 0) && (lpMl->dwDestination < mixer.caps.cDestinations) ) | 
|  | { | 
|  | lpMl->dwLineID = lpMl->dwDestination; | 
|  | line = &mixer.lines[lpMl->dwDestination]; | 
|  | } | 
|  | else ret = MIXERR_INVALLINE; | 
|  | break; | 
|  | case MIXER_GETLINEINFOF_COMPONENTTYPE: | 
|  | TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE %s\n", getComponentType(lpMl->dwComponentType)); | 
|  | for (i = 0; i < mixer.caps.cDestinations; i++) | 
|  | { | 
|  | if (mixer.lines[i].componentType == lpMl->dwComponentType) | 
|  | { | 
|  | lpMl->dwDestination = lpMl->dwLineID = i; | 
|  | line = &mixer.lines[i]; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (line == NULL) | 
|  | { | 
|  | WARN("can't find component type %s\n", getComponentType(lpMl->dwComponentType)); | 
|  | ret = MIXERR_INVALVALUE; | 
|  | } | 
|  | break; | 
|  | case MIXER_GETLINEINFOF_SOURCE: | 
|  | FIXME("MIXER_GETLINEINFOF_SOURCE %d dst=%d\n", lpMl->dwSource, lpMl->dwDestination); | 
|  | break; | 
|  | case MIXER_GETLINEINFOF_LINEID: | 
|  | TRACE("MIXER_GETLINEINFOF_LINEID %d\n", lpMl->dwLineID); | 
|  | if ( (lpMl->dwLineID >= 0) && (lpMl->dwLineID < mixer.caps.cDestinations) ) | 
|  | { | 
|  | lpMl->dwDestination = lpMl->dwLineID; | 
|  | line = &mixer.lines[lpMl->dwLineID]; | 
|  | } | 
|  | else ret = MIXERR_INVALLINE; | 
|  | break; | 
|  | case MIXER_GETLINEINFOF_TARGETTYPE: | 
|  | FIXME("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); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (line) | 
|  | { | 
|  | lpMl->dwComponentType = line->componentType; | 
|  | lpMl->cChannels = line->numChannels; | 
|  | lpMl->cControls = ControlsPerLine; | 
|  |  | 
|  | /* FIXME check there with CoreAudio */ | 
|  | lpMl->cConnections = 1; | 
|  | lpMl->fdwLine = MIXERLINE_LINEF_ACTIVE; | 
|  |  | 
|  | MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR)); | 
|  | MultiByteToWideChar(CP_ACP, 0, line->name, -1, lpMl->szName, sizeof(lpMl->szName) / sizeof(WCHAR)); | 
|  |  | 
|  | if ( IsInput(line->direction) ) | 
|  | lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN; | 
|  | else | 
|  | lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT; | 
|  |  | 
|  | lpMl->Target.dwDeviceID = line->deviceID; | 
|  | lpMl->Target.wMid = mixer.caps.wMid; | 
|  | lpMl->Target.wPid = mixer.caps.wPid; | 
|  | lpMl->Target.vDriverVersion = mixer.caps.vDriverVersion; | 
|  |  | 
|  | MultiByteToWideChar(CP_ACP, 0, WINE_MIXER_NAME, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR)); | 
|  | ret = MMSYSERR_NOERROR; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				MIX_GetLineControls		[internal] | 
|  | */ | 
|  | static DWORD MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc, DWORD_PTR flags) | 
|  | { | 
|  | DWORD ret = MMSYSERR_NOTENABLED; | 
|  | int ctrl = 0; | 
|  | TRACE("%04X, %p, %08lX\n", wDevID, lpMlc, flags); | 
|  |  | 
|  | if (lpMlc == NULL) { | 
|  | WARN("invalid parameter: lpMlc == NULL\n"); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  |  | 
|  | if (lpMlc->cbStruct < sizeof(*lpMlc)) { | 
|  | WARN("invalid parameter: lpMlc->cbStruct = %d\n", lpMlc->cbStruct); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  |  | 
|  | if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) { | 
|  | WARN("invalid parameter: lpMlc->cbmxctrl = %d\n", lpMlc->cbmxctrl); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  |  | 
|  | if (wDevID >= numMixers) { | 
|  | WARN("bad device ID: %04X\n", wDevID); | 
|  | return MMSYSERR_BADDEVICEID; | 
|  | } | 
|  |  | 
|  | switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK) | 
|  | { | 
|  | case MIXER_GETLINECONTROLSF_ALL: | 
|  | FIXME("dwLineID=%d MIXER_GETLINECONTROLSF_ALL (%d)\n", lpMlc->dwLineID, lpMlc->cControls); | 
|  | if (lpMlc->cControls != ControlsPerLine) | 
|  | { | 
|  | WARN("invalid parameter lpMlc->cControls %d\n", lpMlc->cControls); | 
|  | ret = MMSYSERR_INVALPARAM; | 
|  | } | 
|  | else | 
|  | { | 
|  | if ( (lpMlc->dwLineID >= 0) && (lpMlc->dwLineID < mixer.caps.cDestinations) ) | 
|  | { | 
|  | int i; | 
|  | for (i = 0; i < lpMlc->cControls; i++) | 
|  | { | 
|  | lpMlc->pamxctrl[i] = mixer.mixerCtrls[lpMlc->dwLineID * i].ctrl; | 
|  | } | 
|  | ret = MMSYSERR_NOERROR; | 
|  | } | 
|  | else ret = MIXERR_INVALLINE; | 
|  | } | 
|  | break; | 
|  | case MIXER_GETLINECONTROLSF_ONEBYID: | 
|  | TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYID (%d)\n", lpMlc->dwLineID, lpMlc->u.dwControlID); | 
|  | if ( lpMlc->u.dwControlID >= 0 && lpMlc->u.dwControlID < mixer.numCtrl ) | 
|  | { | 
|  | lpMlc->pamxctrl[0] = mixer.mixerCtrls[lpMlc->u.dwControlID].ctrl; | 
|  | ret = MMSYSERR_NOERROR; | 
|  | } | 
|  | else ret = MIXERR_INVALVALUE; | 
|  | break; | 
|  | case MIXER_GETLINECONTROLSF_ONEBYTYPE: | 
|  | TRACE("dwLineID=%d MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n", lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType)); | 
|  | if ( (lpMlc->dwLineID < 0) || (lpMlc->dwLineID >= mixer.caps.cDestinations) ) | 
|  | { | 
|  | ret = MIXERR_INVALLINE; | 
|  | break; | 
|  | } | 
|  | if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) | 
|  | { | 
|  | ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlVolume; | 
|  | lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl; | 
|  | ret = MMSYSERR_NOERROR; | 
|  | } | 
|  | else | 
|  | if (lpMlc->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) | 
|  | { | 
|  | ctrl = (lpMlc->dwLineID * ControlsPerLine) + IDControlMute; | 
|  | lpMlc->pamxctrl[0] = mixer.mixerCtrls[ctrl].ctrl; | 
|  | ret = MMSYSERR_NOERROR; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK); | 
|  | ret = MMSYSERR_INVALPARAM; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				MIX_GetControlDetails		[internal] | 
|  | */ | 
|  | static DWORD MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails) | 
|  | { | 
|  | DWORD ret = MMSYSERR_NOTSUPPORTED; | 
|  | DWORD dwControlType; | 
|  |  | 
|  | TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails); | 
|  |  | 
|  | if (lpmcd == NULL) { | 
|  | TRACE("invalid parameter: lpmcd == NULL\n"); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  |  | 
|  | if (wDevID >= numMixers) { | 
|  | WARN("bad device ID: %04X\n", wDevID); | 
|  | return MMSYSERR_BADDEVICEID; | 
|  | } | 
|  |  | 
|  | if ( (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE ) | 
|  | { | 
|  | WARN("Unknown/unimplement GetControlDetails flag (%08lx)\n", fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK); | 
|  | return MMSYSERR_NOTSUPPORTED; | 
|  | } | 
|  |  | 
|  | if ( lpmcd->dwControlID < 0 || lpmcd->dwControlID >= mixer.numCtrl ) | 
|  | { | 
|  | WARN("bad control ID: %d\n", lpmcd->dwControlID); | 
|  | return MIXERR_INVALVALUE; | 
|  | } | 
|  |  | 
|  | TRACE("MIXER_GETCONTROLDETAILSF_VALUE %d\n", lpmcd->dwControlID); | 
|  |  | 
|  | dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType; | 
|  | switch (dwControlType) | 
|  | { | 
|  | case MIXERCONTROL_CONTROLTYPE_VOLUME: | 
|  | FIXME("controlType : %s channels %d\n", getControlType(dwControlType), lpmcd->cChannels); | 
|  | { | 
|  | LPMIXERCONTROLDETAILS_UNSIGNED mcdu; | 
|  | Float32 left, right; | 
|  |  | 
|  | if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) { | 
|  | WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  |  | 
|  | if ( MIX_LineGetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, &left, &right) ) | 
|  | { | 
|  | mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails; | 
|  |  | 
|  | switch (lpmcd->cChannels) | 
|  | { | 
|  | case 1: | 
|  | /* mono... so R = L */ | 
|  | mcdu->dwValue = left * 65535; | 
|  | TRACE("Reading RL = %d\n", mcdu->dwValue); | 
|  | break; | 
|  | case 2: | 
|  | /* stereo, left is paDetails[0] */ | 
|  | mcdu->dwValue = left * 65535; | 
|  | TRACE("Reading L = %d\n", mcdu->dwValue); | 
|  | mcdu++; | 
|  | mcdu->dwValue = right * 65535; | 
|  | TRACE("Reading R = %d\n", mcdu->dwValue); | 
|  | break; | 
|  | default: | 
|  | WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  | TRACE("=> %08x\n", mcdu->dwValue); | 
|  | ret = MMSYSERR_NOERROR; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case MIXERCONTROL_CONTROLTYPE_MUTE: | 
|  | case MIXERCONTROL_CONTROLTYPE_ONOFF: | 
|  | FIXME("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels); | 
|  | { | 
|  | LPMIXERCONTROLDETAILS_BOOLEAN mcdb; | 
|  | BOOL muted; | 
|  | if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) { | 
|  | WARN("invalid parameter: lpmcd->cbDetails = %d\n", lpmcd->cbDetails); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  | mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails; | 
|  |  | 
|  | if ( MIX_LineGetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, &muted) ) | 
|  | { | 
|  | mcdb->fValue = muted; | 
|  | TRACE("=> %s\n", mcdb->fValue ? "on" : "off"); | 
|  | ret = MMSYSERR_NOERROR; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case MIXERCONTROL_CONTROLTYPE_MIXER: | 
|  | case MIXERCONTROL_CONTROLTYPE_MUX: | 
|  | default: | 
|  | FIXME("controlType : %s\n", getControlType(dwControlType)); | 
|  | break; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				MIX_SetControlDetails		[internal] | 
|  | */ | 
|  | static DWORD MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd, DWORD_PTR fdwDetails) | 
|  | { | 
|  | DWORD ret = MMSYSERR_NOTSUPPORTED; | 
|  | DWORD dwControlType; | 
|  |  | 
|  | TRACE("%04X, %p, %08lx\n", wDevID, lpmcd, fdwDetails); | 
|  |  | 
|  | if (lpmcd == NULL) { | 
|  | TRACE("invalid parameter: lpmcd == NULL\n"); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  |  | 
|  | if (wDevID >= numMixers) { | 
|  | WARN("bad device ID: %04X\n", wDevID); | 
|  | return MMSYSERR_BADDEVICEID; | 
|  | } | 
|  |  | 
|  | if ( (fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) != MIXER_GETCONTROLDETAILSF_VALUE ) | 
|  | { | 
|  | WARN("Unknown SetControlDetails flag (%08lx)\n", fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK); | 
|  | return MMSYSERR_NOTSUPPORTED; | 
|  | } | 
|  |  | 
|  | TRACE("MIXER_SETCONTROLDETAILSF_VALUE dwControlID=%d\n", lpmcd->dwControlID); | 
|  | dwControlType = mixer.mixerCtrls[lpmcd->dwControlID].ctrl.dwControlType; | 
|  | switch (dwControlType) | 
|  | { | 
|  | case MIXERCONTROL_CONTROLTYPE_VOLUME: | 
|  | FIXME("controlType : %s\n", getControlType(dwControlType)); | 
|  | { | 
|  | LPMIXERCONTROLDETAILS_UNSIGNED mcdu; | 
|  | Float32 left, right = 0; | 
|  |  | 
|  | if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED)) { | 
|  | WARN("invalid parameter: lpmcd->cbDetails == %d\n", lpmcd->cbDetails); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  |  | 
|  | mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)lpmcd->paDetails; | 
|  |  | 
|  | switch (lpmcd->cChannels) | 
|  | { | 
|  | case 1: | 
|  | /* mono... so R = L */ | 
|  | TRACE("Setting RL to %d\n", mcdu->dwValue); | 
|  | left = (Float32) mcdu->dwValue / 65535.0; | 
|  | break; | 
|  | case 2: | 
|  | /* stereo, left is paDetails[0] */ | 
|  | TRACE("Setting L to %d\n", mcdu->dwValue); | 
|  | left = (Float32) mcdu->dwValue / 65535.0; | 
|  | mcdu++; | 
|  | TRACE("Setting R to %d\n", mcdu->dwValue); | 
|  | right = (Float32) mcdu->dwValue / 65535.0; | 
|  | break; | 
|  | default: | 
|  | WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  | if ( MIX_LineSetVolume(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, lpmcd->cChannels, left, right) ) | 
|  | ret = MMSYSERR_NOERROR; | 
|  | } | 
|  | break; | 
|  | case MIXERCONTROL_CONTROLTYPE_MUTE: | 
|  | case MIXERCONTROL_CONTROLTYPE_ONOFF: | 
|  | TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(dwControlType), lpmcd->cChannels); | 
|  | { | 
|  | LPMIXERCONTROLDETAILS_BOOLEAN	mcdb; | 
|  |  | 
|  | if (lpmcd->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN)) { | 
|  | WARN("invalid parameter: cbDetails\n"); | 
|  | return MMSYSERR_INVALPARAM; | 
|  | } | 
|  | mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)lpmcd->paDetails; | 
|  | if ( MIX_LineSetMute(mixer.mixerCtrls[lpmcd->dwControlID].dwLineID, mcdb->fValue) ) | 
|  | ret = MMSYSERR_NOERROR; | 
|  | } | 
|  | break; | 
|  | case MIXERCONTROL_CONTROLTYPE_MIXER: | 
|  | case MIXERCONTROL_CONTROLTYPE_MUX: | 
|  | default: | 
|  | FIXME("controlType : %s\n", getControlType(dwControlType)); | 
|  | ret = MMSYSERR_NOTSUPPORTED; | 
|  | break; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * 				mxdMessage | 
|  | */ | 
|  | DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser, | 
|  | DWORD_PTR dwParam1, DWORD_PTR dwParam2) | 
|  | { | 
|  | TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg), | 
|  | dwUser, dwParam1, dwParam2); | 
|  |  | 
|  | switch (wMsg) | 
|  | { | 
|  | case DRVM_INIT: | 
|  | case DRVM_EXIT: | 
|  | case DRVM_ENABLE: | 
|  | case DRVM_DISABLE: | 
|  | /* FIXME: Pretend this is supported */ | 
|  | return 0; | 
|  | case MXDM_OPEN: | 
|  | return MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); | 
|  | case MXDM_CLOSE: | 
|  | return MMSYSERR_NOERROR; | 
|  | case MXDM_GETNUMDEVS: | 
|  | return MIX_GetNumDevs(); | 
|  | case MXDM_GETDEVCAPS: | 
|  | return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2); | 
|  | case MXDM_GETLINEINFO: | 
|  | return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2); | 
|  | case MXDM_GETLINECONTROLS: | 
|  | return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2); | 
|  | case MXDM_GETCONTROLDETAILS: | 
|  | return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2); | 
|  | case MXDM_SETCONTROLDETAILS: | 
|  | return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2); | 
|  | default: | 
|  | WARN("unknown message %d!\n", wMsg); | 
|  | return MMSYSERR_NOTSUPPORTED; | 
|  | } | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | DWORD WINAPI CoreAudio_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser, | 
|  | DWORD_PTR dwParam1, DWORD_PTR dwParam2) | 
|  | { | 
|  | TRACE("(%04X, %04x, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); | 
|  | return MMSYSERR_NOTENABLED; | 
|  | } | 
|  | #endif /* HAVE_COREAUDIO_COREAUDIO_H */ |