windows-nt/Source/XPSP1/NT/multimedia/directx/dplay/dvoice/dxvutils/mixline.cpp
2020-09-26 16:20:57 +08:00

486 lines
18 KiB
C++

/*==========================================================================
*
* 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;
}