/*========================================================================== * * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved. * * File: MixLine.cpp * Content: Class for managing the mixerLine API. * * History: * Date By Reason * ==== == ====== * 11/30/99 rodtoll Created based on source from dsound * 01/24/2000 rodtoll Mirroring changes from dsound bug #128264 * ***************************************************************************/ #include "dxvutilspch.h" #undef DPF_SUBCOMP #define DPF_SUBCOMP DN_SUBCOMP_VOICE #define DVF_MIXERLINE_PROCEDURE_DEBUG_LEVEL DVF_INFOLEVEL #define DPFLVL_INFO 5 #define DPF_MIXER DPFX #undef DPF_MODNAME #define DPF_MODNAME "CMixerLine::CMixerLine" CMixerLine::CMixerLine( ): m_fAcquiredVolCtrl(FALSE), m_pmxMuxFlags(NULL), m_dwRangeMin(0), m_dwRangeSize(0xFFFF), m_uWaveDeviceId(0), m_pfMicValue(NULL) { ZeroMemory( &m_mxcdMasterVol, sizeof( MIXERCONTROLDETAILS ) ); ZeroMemory( &m_mxcdMasterMute, sizeof( MIXERCONTROLDETAILS ) ); ZeroMemory( &m_mxcdMasterMux, sizeof( MIXERCONTROLDETAILS ) ); ZeroMemory( &m_mxcdMicVol, sizeof( MIXERCONTROLDETAILS ) ); ZeroMemory( &m_mxcdMicMute, sizeof( MIXERCONTROLDETAILS ) ); ZeroMemory( &m_mxVolume, sizeof( MIXERCONTROLDETAILS_UNSIGNED ) ); ZeroMemory( &m_mxMute, sizeof( MIXERCONTROLDETAILS_BOOLEAN ) ); } #undef DPF_MODNAME #define DPF_MODNAME "CMixerLine::~CMixerLine" CMixerLine::~CMixerLine() { if( m_pmxMuxFlags != NULL ) { delete [] m_pmxMuxFlags; } } #undef DPF_MODNAME #define DPF_MODNAME "CMixerLine::Initialize" HRESULT CMixerLine::Initialize( UINT uiDeviceID ) { if( m_fAcquiredVolCtrl ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Initialize has not been called" ); return DVERR_INITIALIZED; } m_uWaveDeviceId = uiDeviceID; // Set up the master waveIn destination mixer line MIXERLINE mxMastLine; ZeroMemory(&mxMastLine, sizeof mxMastLine); mxMastLine.cbStruct = sizeof mxMastLine; mxMastLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN; // Set up the microphone source line MIXERLINE mxMicLine; ZeroMemory(&mxMicLine, sizeof mxMicLine); // Set up the mixer-line control structure MIXERCONTROL mxCtrl; ZeroMemory(&mxCtrl, sizeof mxCtrl); mxCtrl.cbStruct = sizeof mxCtrl; // Set up the 1-element array of controls MIXERLINECONTROLS mxLineCtrls; ZeroMemory(&mxLineCtrls, sizeof mxLineCtrls); mxLineCtrls.cbStruct = sizeof mxLineCtrls; mxLineCtrls.cControls = 1; mxLineCtrls.cbmxctrl = sizeof mxCtrl; mxLineCtrls.pamxctrl = &mxCtrl; // Set up the control details structures m_mxcdMasterVol.cbDetails = sizeof m_mxVolume; m_mxcdMasterVol.paDetails = &m_mxVolume; m_mxcdMasterVol.cChannels = 1; m_mxcdMasterMute.cbDetails = sizeof m_mxMute; m_mxcdMasterMute.paDetails = &m_mxMute; m_mxcdMasterMute.cChannels = 1; m_mxcdMicVol.cbDetails = sizeof m_mxVolume; m_mxcdMicVol.paDetails = &m_mxVolume; m_mxcdMicVol.cChannels = 1; m_mxcdMicMute.cbDetails = sizeof m_mxMute; m_mxcdMicMute.paDetails = &m_mxMute; m_mxcdMicMute.cChannels = 1; // We use the waveIn device ID instead of a "real" mixer device below HMIXEROBJ hMixObj; MMRESULT mmr = mixerGetID((HMIXEROBJ) ((UINT_PTR)m_uWaveDeviceId), (LPUINT)&hMixObj, MIXER_OBJECTF_WAVEIN); if (MMSYSERR_NOERROR == mmr) { DPF_MIXER(DPFPREP, DPFLVL_INFO, "mixerGetID failed."); } // Find the master recording destination line mmr = mixerGetLineInfo(hMixObj, &mxMastLine, MIXER_GETLINEINFOF_COMPONENTTYPE); if (mmr == MMSYSERR_NOERROR) { DPF_MIXER(DPFPREP, DPFLVL_INFO, "Found the master recording mixer line"); // Look for a volume fader control on the master line mxLineCtrls.dwLineID = mxMastLine.dwLineID; mxLineCtrls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mmr = mixerGetLineControls(hMixObj, &mxLineCtrls, MIXER_GETLINECONTROLSF_ONEBYTYPE); if (mmr == MMSYSERR_NOERROR) { // Found it - use the cbStruct field to flag success DPF_MIXER(DPFPREP, DPFLVL_INFO, "Found a volume fader on the master line"); m_mxcdMasterVol.cbStruct = sizeof m_mxcdMasterVol; m_mxcdMasterVol.dwControlID = mxCtrl.dwControlID; m_dwRangeMin = mxCtrl.Bounds.dwMinimum; m_dwRangeSize = mxCtrl.Bounds.dwMaximum - mxCtrl.Bounds.dwMinimum; mmr = mixerGetControlDetails(hMixObj, &m_mxcdMasterVol, MIXER_GETCONTROLDETAILSF_VALUE); } if (mmr != MMSYSERR_NOERROR) m_mxcdMasterVol.cbStruct = 0; // Look for a mute control on the master line mxLineCtrls.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE; mmr = mixerGetLineControls(hMixObj, &mxLineCtrls, MIXER_GETLINECONTROLSF_ONEBYTYPE); if (mmr == MMSYSERR_NOERROR) { DPF_MIXER(DPFPREP, DPFLVL_INFO, "Found a mute control on the master line"); m_mxcdMasterMute.cbStruct = sizeof m_mxcdMasterMute; m_mxcdMasterMute.dwControlID = mxCtrl.dwControlID; mmr = mixerGetControlDetails(hMixObj, &m_mxcdMasterMute, MIXER_GETCONTROLDETAILSF_VALUE); } if (mmr != MMSYSERR_NOERROR) m_mxcdMasterMute.cbStruct = 0; // Look for the microphone source line mxMicLine.cbStruct = sizeof mxMicLine; mxMicLine.dwDestination = mxMastLine.dwDestination; for (UINT i=0; i < mxMastLine.cConnections; ++i) { mxMicLine.dwSource = i; // Note: for some mysterious reason, I had to remove MIXER_OBJECTF_WAVEIN // from this call to mixerGetLineInfo() to make it work. mmr = mixerGetLineInfo(hMixObj, &mxMicLine, MIXER_GETLINEINFOF_SOURCE); if (mmr != MMSYSERR_NOERROR || mxMicLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) break; } if (mxMicLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) { DPF_MIXER(DPFPREP, DPFLVL_INFO, "Found a microphone mixer line"); // Look for a volume fader control on the mic line mxLineCtrls.dwLineID = mxMicLine.dwLineID; mxLineCtrls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mmr = mixerGetLineControls(hMixObj, &mxLineCtrls, MIXER_GETLINECONTROLSF_ONEBYTYPE); if (mmr == MMSYSERR_NOERROR) { DPF_MIXER(DPFPREP, DPFLVL_INFO, "Found a volume fader on the mic line"); m_mxcdMicVol.cbStruct = sizeof m_mxcdMicVol; m_mxcdMicVol.dwControlID = mxCtrl.dwControlID; m_dwRangeMin = mxCtrl.Bounds.dwMinimum; m_dwRangeSize = mxCtrl.Bounds.dwMaximum - mxCtrl.Bounds.dwMinimum; mmr = mixerGetControlDetails(hMixObj, &m_mxcdMicVol, MIXER_GETCONTROLDETAILSF_VALUE); } if (mmr != MMSYSERR_NOERROR) m_mxcdMicVol.cbStruct = 0; // Look for a mute control on the mic line mxLineCtrls.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE; mmr = mixerGetLineControls(hMixObj, &mxLineCtrls, MIXER_GETLINECONTROLSF_ONEBYTYPE); if (mmr == MMSYSERR_NOERROR) { DPF_MIXER(DPFPREP, DPFLVL_INFO, "Found a mute control on the mic line"); m_mxcdMicMute.cbStruct = sizeof m_mxcdMicMute; m_mxcdMicMute.dwControlID = mxCtrl.dwControlID; mmr = mixerGetControlDetails(hMixObj, &m_mxcdMicMute, MIXER_GETCONTROLDETAILSF_VALUE); } if (mmr != MMSYSERR_NOERROR) m_mxcdMicMute.cbStruct = 0; // Look for a MUX or MIXER control on the master line mxLineCtrls.dwLineID = mxMastLine.dwLineID; mxLineCtrls.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX; m_fMasterMuxIsMux = TRUE; mmr = mixerGetLineControls(hMixObj, &mxLineCtrls, MIXER_GETLINECONTROLSF_ONEBYTYPE); if (mmr != MMSYSERR_NOERROR) { mxLineCtrls.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER; m_fMasterMuxIsMux = FALSE; mmr = mixerGetLineControls(hMixObj, &mxLineCtrls, MIXER_GETLINECONTROLSF_ONEBYTYPE); } if (mmr == MMSYSERR_NOERROR) { DPF_MIXER(DPFPREP, DPFLVL_INFO, "Found an item list control on the master line"); m_mxcdMasterMux.cbStruct = sizeof m_mxcdMasterMux; m_mxcdMasterMux.dwControlID = mxCtrl.dwControlID; m_mxcdMasterMux.cMultipleItems = mxCtrl.cMultipleItems; // We save the cChannels value, because some evil VxD drivers (read: Aureal // Vortex) will set it to 0 in the call to mixerGetControlDetails() below int nChannels = (mxCtrl.fdwControl & MIXERCONTROL_CONTROLF_UNIFORM) ? 1 : mxMastLine.cChannels; m_mxcdMasterMux.cChannels = nChannels; // Get the MUX or MIXER list items m_mxcdMasterMux.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT); MIXERCONTROLDETAILS_LISTTEXT *pList = new MIXERCONTROLDETAILS_LISTTEXT [ m_mxcdMasterMux.cbDetails * m_mxcdMasterMux.cChannels * mxCtrl.cMultipleItems ]; if (pList != NULL) { m_mxcdMasterMux.paDetails = pList; mmr = mixerGetControlDetails(hMixObj, &m_mxcdMasterMux, MIXER_GETCONTROLDETAILSF_LISTTEXT); if (mmr == MMSYSERR_NOERROR) { DPF_MIXER(DPFPREP, DPFLVL_INFO, "Got the list controls's LISTTEXT details"); // Get the MUX or MIXER list values m_mxcdMasterMux.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); m_mxcdMasterMux.cChannels = nChannels; m_pmxMuxFlags = new MIXERCONTROLDETAILS_BOOLEAN [ m_mxcdMasterMux.cbDetails * m_mxcdMasterMux.cChannels * mxCtrl.cMultipleItems ]; if (m_pmxMuxFlags != NULL) { m_mxcdMasterMux.paDetails = m_pmxMuxFlags; mmr = mixerGetControlDetails(hMixObj, &m_mxcdMasterMux, MIXER_GETCONTROLDETAILSF_VALUE); if (mmr == MMSYSERR_NOERROR) // Enable the item corresponding to the mic line { DPF_MIXER(DPFPREP, DPFLVL_INFO, "Got the list controls's VALUE details"); for (UINT i=0; i < mxCtrl.cMultipleItems; ++i) { if (pList[i].dwParam1 == mxMicLine.dwLineID) m_pfMicValue = &m_pmxMuxFlags[i].fValue; else if (mxLineCtrls.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX) m_pmxMuxFlags[i].fValue = FALSE; DPF_MIXER(DPFPREP, DPFLVL_INFO, "Set list item %d to %d", i, pList[i].dwParam1 == mxMicLine.dwLineID); } } } } delete[] pList; pList = NULL; } if (!m_pmxMuxFlags || !m_pfMicValue || mmr != MMSYSERR_NOERROR) m_mxcdMasterMux.cbStruct = 0; } } } // To be able to control the recording level, we minimally require // a volume fader on the master line or one on the microphone line: m_fAcquiredVolCtrl = m_mxcdMasterVol.cbStruct || m_mxcdMicVol.cbStruct; HRESULT hr = m_fAcquiredVolCtrl ? DS_OK : DSERR_CONTROLUNAVAIL; return hr; } #undef DPF_MODNAME #define DPF_MODNAME "CMixerLine::SetMicrophoneVolume" HRESULT CMixerLine::SetMicrophoneVolume( LONG lMicrophoneVolume ) { if( !m_fAcquiredVolCtrl ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Initialize has not been called" ); return DVERR_NOTINITIALIZED; } MMRESULT mmr = MMSYSERR_NOTSUPPORTED; // Default return code // Set the microphone recording level control if available if (m_mxcdMicVol.cbStruct) { // Convert the DSBVOLUME level to an amplification factor from 0 to 0xFFFF m_mxVolume.dwValue = DBToAmpFactor(lMicrophoneVolume); // Adjust range if necessary if (m_dwRangeMin != 0 || m_dwRangeSize != 0xFFFF) m_mxVolume.dwValue = DWORD(m_dwRangeMin + m_dwRangeSize*double(m_mxVolume.dwValue)/0xFFFF); mmr = mixerSetControlDetails((HMIXEROBJ)((UINT_PTR)m_uWaveDeviceId), &m_mxcdMicVol, MIXER_OBJECTF_WAVEIN | MIXER_GETCONTROLDETAILSF_VALUE); } HRESULT hr = MMRESULTtoHRESULT(mmr); return hr; } #undef DPF_MODNAME #define DPF_MODNAME "CMixerLine::GetMicrophoneVolume" HRESULT CMixerLine::GetMicrophoneVolume( LPLONG plMicrophoneVolume ) { if( !m_fAcquiredVolCtrl ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Initialize has not been called" ); return DVERR_NOTINITIALIZED; } MMRESULT mmr = MMSYSERR_NOTSUPPORTED; // Default return code DNASSERT(plMicrophoneVolume != NULL); // Get the microphone recording level if available if (m_mxcdMicVol.cbStruct != 0) { mmr = mixerGetControlDetails( (HMIXEROBJ)((UINT_PTR)m_uWaveDeviceId), &m_mxcdMicVol, MIXER_OBJECTF_WAVEIN | MIXER_GETCONTROLDETAILSF_VALUE); if (mmr == MMSYSERR_NOERROR) { DNASSERT(m_mxVolume.dwValue >= m_dwRangeMin && m_mxVolume.dwValue <= m_dwRangeMin + m_dwRangeSize); // Adjust range if necessary if (m_dwRangeMin != 0 || m_dwRangeSize != 0xFFFF) m_mxVolume.dwValue = DWORD(double(m_mxVolume.dwValue-m_dwRangeMin) / m_dwRangeSize * 0xFFFF); // Convert the amplification factor to a DSBVOLUME level *plMicrophoneVolume = AmpFactorToDB(m_mxVolume.dwValue); } } HRESULT hr = MMRESULTtoHRESULT(mmr); return hr; } #undef DPF_MODNAME #define DPF_MODNAME "CMixerLine::SetMasterRecordVolume" HRESULT CMixerLine::SetMasterRecordVolume( LONG lRecordVolume ) { if( !m_fAcquiredVolCtrl ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Initialize has not been called" ); return DVERR_NOTINITIALIZED; } MMRESULT mmr = MMSYSERR_NOTSUPPORTED; // Default return code // Set the master recording level control if available if (m_mxcdMasterVol.cbStruct) { // Convert the DSBVOLUME level to an amplification factor from 0 to 0xFFFF m_mxVolume.dwValue = DBToAmpFactor(lRecordVolume); // Adjust range if necessary if (m_dwRangeMin != 0 || m_dwRangeSize != 0xFFFF) m_mxVolume.dwValue = DWORD(m_dwRangeMin + m_dwRangeSize*double(m_mxVolume.dwValue)/0xFFFF); mmr = mixerSetControlDetails((HMIXEROBJ)((UINT_PTR)m_uWaveDeviceId), &m_mxcdMasterVol, MIXER_OBJECTF_WAVEIN | MIXER_GETCONTROLDETAILSF_VALUE); } HRESULT hr = MMRESULTtoHRESULT(mmr); return hr; } #undef DPF_MODNAME #define DPF_MODNAME "CMixerLine::GetMasterRecordVolume" HRESULT CMixerLine::GetMasterRecordVolume( LPLONG plRecordVolume ) { if( !m_fAcquiredVolCtrl ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Initialize has not been called" ); return DVERR_NOTINITIALIZED; } DNASSERT(plRecordVolume != NULL); MMRESULT mmr = MMSYSERR_NOTSUPPORTED; // Default return code // Get the master recording level if available if (m_mxcdMasterVol.cbStruct != 0) { mmr = mixerGetControlDetails((HMIXEROBJ)((UINT_PTR)m_uWaveDeviceId), &m_mxcdMasterVol, MIXER_OBJECTF_WAVEIN | MIXER_GETCONTROLDETAILSF_VALUE); if (mmr == MMSYSERR_NOERROR) { DNASSERT(m_mxVolume.dwValue >= m_dwRangeMin && m_mxVolume.dwValue <= m_dwRangeMin + m_dwRangeSize); // Adjust range if necessary if (m_dwRangeMin != 0 || m_dwRangeSize != 0xFFFF) m_mxVolume.dwValue = DWORD(double(m_mxVolume.dwValue-m_dwRangeMin) / m_dwRangeSize * 0xFFFF); // Convert the amplification factor to a DSBVOLUME level *plRecordVolume = AmpFactorToDB(m_mxVolume.dwValue); } } HRESULT hr = MMRESULTtoHRESULT(mmr); return hr; } #undef DPF_MODNAME #define DPF_MODNAME "CMixerLine::SelectMicrophone" HRESULT CMixerLine::EnableMicrophone( BOOL fEnable ) { if( !m_fAcquiredVolCtrl ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Initialize has not been called" ); return DVERR_NOTINITIALIZED; } HMIXEROBJ hMixObj = (HMIXEROBJ)((UINT_PTR)m_uWaveDeviceId); MMRESULT mmr = MMSYSERR_NOERROR; HRESULT hr; // Check for presence of microphone controls if (!m_mxcdMasterMux.cbStruct && !m_mxcdMasterMute.cbStruct && !m_mxcdMicMute.cbStruct) { // We cannot do anything to enable the microphone line hr = DSERR_UNSUPPORTED; } else { // Select the mic on the Mux control, if available // if (m_mxcdMasterMux.cbStruct && !(m_fMasterMuxIsMux && !fEnable)) { *m_pfMicValue = fEnable; mmr = mixerSetControlDetails(hMixObj, &m_mxcdMasterMux, MIXER_OBJECTF_WAVEIN | MIXER_GETCONTROLDETAILSF_VALUE); } // Mute/unmute the lines, if mute controls are available m_mxMute.fValue = !fEnable; if (m_mxcdMasterMute.cbStruct && mmr == MMSYSERR_NOERROR) mmr = mixerSetControlDetails(hMixObj, &m_mxcdMasterMute, MIXER_OBJECTF_WAVEIN | MIXER_GETCONTROLDETAILSF_VALUE); if (m_mxcdMicMute.cbStruct && mmr == MMSYSERR_NOERROR) mmr = mixerSetControlDetails(hMixObj, &m_mxcdMicMute, MIXER_OBJECTF_WAVEIN | MIXER_GETCONTROLDETAILSF_VALUE); hr = MMRESULTtoHRESULT(mmr); } return hr; } #undef DPF_MODNAME #define DPF_MODNAME "CMixerLine::MMRESULTtoHRESULT" HRESULT CMixerLine::MMRESULTtoHRESULT( MMRESULT mmr ) { HRESULT hr; switch(mmr) { case MMSYSERR_NOERROR: hr = DS_OK; break; case MMSYSERR_BADDEVICEID: case MMSYSERR_NODRIVER: hr = DSERR_NODRIVER; break; case MMSYSERR_ALLOCATED: hr = DSERR_ALLOCATED; break; case MMSYSERR_NOMEM: hr = DSERR_OUTOFMEMORY; break; case MMSYSERR_NOTSUPPORTED: hr = DSERR_UNSUPPORTED; break; case WAVERR_BADFORMAT: hr = DSERR_BADFORMAT; break; default: hr = DSERR_GENERIC; break; } return hr; }