|
|
#include "precomp.h"
#include <mixer.h>
//
// private APIs
//
#define MAX_MICROPHONE_DEVS 10
static MMRESULT mixerGetControlValue ( HMIXER, DWORD *, DWORD, UINT ); static MMRESULT mixerSetControlValue ( HMIXER, DWORD *, DWORD, UINT ); static MMRESULT mixerGetControlId ( HMIXER, DWORD *, DWORD, DWORD ); static MMRESULT mixerGetControlByType ( HMIXER, DWORD, DWORD, MIXERCONTROL *pMixerControl);
struct AGCDetails { WORD wMID; // manufacturer ID
WORD wPID; // product ID
DWORD dwAGCID; // AGC ID
};
static const AGCDetails AGCList[] = { // MID PID AGCID
{1, 323, 27}, // Creative Labs (NT)
{1, 104, 21}, // Creative Labs (NT 5)
{2, 409, 27}, // Creative Labs
{21, 42, 13}, // Turtle Beach Tropez
{132, 3, 2072}, // Crystal MMX
{384, 7, 28}, // Xitel Storm 3d PCI
{385, 32, 35} // Aztech PCI-331
};
static BOOL GetAGCID(WORD wMID, WORD wPID, DWORD *pdwAGCID) { int nIndex; int nAGCEntries = sizeof(AGCList) / sizeof(AGCDetails);
for (nIndex = 0; nIndex < nAGCEntries; nIndex++) { if ( (AGCList[nIndex].wMID == wMID) && (AGCList[nIndex].wPID == wPID)) { *pdwAGCID = AGCList[nIndex].dwAGCID; return TRUE; } } return FALSE; }
//
// Init
//
// Enumerate all existing mixers in the system. For each mixer,
// we enumerate all lines with destination Speaker and WaveIn.
// For each such line, we cache the control id and control value
// of volume control. An invalid flag will be tagged to any control
// not supported by this mixer.
// When an application is finished with all mixers operations,
// it must call ReleaseAllMixers to free all memory resources and
// mixers.
//
// THIS MUST BE THE FIRST API TO CALL TO START MIXER OPERATIONS.
//
// Input: The handle of the window which will handle all callback
// messages MM_MIXM_CONTROL_CHANGE and MM_MIXM_LINE_CHANGE.
//
// Output: TRUE if success; otherwise, FALSE.
//
BOOL CMixerDevice::Init( HWND hWnd, UINT_PTR uWaveDevId, DWORD dwFlags) { UINT uDstIdx, uSrcIdx, uMixerIdCheck, uMixerIdx; MMRESULT mmr = MMSYSERR_NOERROR; MIXERLINE mlDst, mlSrc; UINT_PTR nMixers, nWaveInDevs, uIndex;
//get the mixer device corresponding to the wave device
mmr = mixerGetID((HMIXEROBJ)uWaveDevId, &uMixerIdx, dwFlags); if ((mmr != MMSYSERR_NOERROR) && (mmr != MMSYSERR_NODRIVER)) { return FALSE; }
// a simple fix for cheesy sound cards that don't make a
// direct mapping between waveDevice and mixer device
// e.g. MWAVE cards and newer SB NT 4 drivers
// If there is only ONE mixer device and if no other waveIn device
// uses it, then it is probably valid.
if ((mmr == MMSYSERR_NODRIVER) && (dwFlags == MIXER_OBJECTF_WAVEIN)) { nMixers = mixerGetNumDevs(); nWaveInDevs = waveInGetNumDevs(); if (nMixers == 1) { uMixerIdx = 0; for (uIndex = 0; uIndex < nWaveInDevs; uIndex++) { mmr = mixerGetID((HMIXEROBJ)uIndex, &uMixerIdCheck, dwFlags); if ((mmr == MMSYSERR_NOERROR) && (uMixerIdCheck == uMixerIdx)) { return FALSE; // the mixer belongs to another waveIn Device
} } } else { return FALSE; } }
// open the mixer such that we can get notification messages
mmr = mixerOpen ( &m_hMixer, uMixerIdx, (DWORD_PTR) hWnd, 0, (hWnd ? CALLBACK_WINDOW : 0) | MIXER_OBJECTF_MIXER); if (mmr != MMSYSERR_NOERROR) { return FALSE; }
// get mixer caps
mmr = mixerGetDevCaps (uMixerIdx, &(m_mixerCaps), sizeof (MIXERCAPS)); if ((mmr != MMSYSERR_NOERROR) || (0 == m_mixerCaps.cDestinations)) { mixerClose(m_hMixer); return FALSE; }
for (uDstIdx = 0; uDstIdx < m_mixerCaps.cDestinations; uDstIdx++) { ZeroMemory (&mlDst, sizeof (mlDst)); mlDst.cbStruct = sizeof (mlDst); mlDst.dwDestination = uDstIdx;
// get the mixer line for this destination
mmr = mixerGetLineInfo ((HMIXEROBJ)m_hMixer, &mlDst, MIXER_GETLINEINFOF_DESTINATION | MIXER_OBJECTF_HMIXER); if (mmr != MMSYSERR_NOERROR) continue;
// examine the type of this destination line
if (((MIXER_OBJECTF_WAVEOUT == dwFlags) && (MIXERLINE_COMPONENTTYPE_DST_SPEAKERS == mlDst.dwComponentType)) || ((MIXER_OBJECTF_WAVEIN == dwFlags) && (MIXERLINE_COMPONENTTYPE_DST_WAVEIN == mlDst.dwComponentType))) { // fill in more info about DstLine
m_DstLine.ucChannels = mlDst.cChannels; if (!(mlDst.fdwLine & MIXERLINE_LINEF_DISCONNECTED)) { // get id and value of volume control
mmr = mixerGetControlId ( m_hMixer, &m_DstLine.dwControlId, mlDst.dwLineID, MIXERCONTROL_CONTROLTYPE_VOLUME); m_DstLine.fIdValid = (mmr == MMSYSERR_NOERROR);
m_DstLine.dwLineId = mlDst.dwLineID; m_DstLine.dwCompType = mlDst.dwComponentType; m_DstLine.dwConnections = mlDst.cConnections;
// -----------------------------------------------------
// enumerate all sources for this destination
for (uSrcIdx = 0; uSrcIdx < mlDst.cConnections; uSrcIdx++) { // get the info of the line with specific src and dst...
ZeroMemory (&mlSrc, sizeof (mlSrc)); mlSrc.cbStruct = sizeof (mlSrc); mlSrc.dwDestination = uDstIdx; mlSrc.dwSource = uSrcIdx;
mmr = mixerGetLineInfo ( (HMIXEROBJ)m_hMixer, &mlSrc, MIXER_GETLINEINFOF_SOURCE | MIXER_OBJECTF_HMIXER); if (mmr == MMSYSERR_NOERROR) { if (((MIXERLINE_COMPONENTTYPE_DST_SPEAKERS == mlDst.dwComponentType) && (MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT == mlSrc.dwComponentType)) || ((MIXERLINE_COMPONENTTYPE_DST_WAVEIN == mlDst.dwComponentType) && (MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == mlSrc.dwComponentType))) { // fill in more info about this source
m_SrcLine.ucChannels = mlSrc.cChannels;
// get id and value of volume control
mmr = mixerGetControlId ( m_hMixer, &m_SrcLine.dwControlId, mlSrc.dwLineID, MIXERCONTROL_CONTROLTYPE_VOLUME); m_SrcLine.fIdValid = (mmr == MMSYSERR_NOERROR);
m_SrcLine.dwLineId = mlSrc.dwLineID; m_SrcLine.dwCompType = mlSrc.dwComponentType; m_SrcLine.dwConnections = mlSrc.cConnections;
break; } } } } break; } } return TRUE; }
CMixerDevice* CMixerDevice::GetMixerForWaveDevice( HWND hWnd, UINT uWaveDevId, DWORD dwFlags) { DBG_SAVE_FILE_LINE CMixerDevice* pMixerDev = new CMixerDevice; if (NULL != pMixerDev) { if (!pMixerDev->Init(hWnd, uWaveDevId, dwFlags)) { delete pMixerDev; pMixerDev = NULL; } } return pMixerDev; }
BOOL CMixerDevice::SetMainVolume(DWORD dwVolume) { MMRESULT mmr = MMSYSERR_ERROR; DWORD adwVolume[2];
adwVolume[0] = adwVolume[1] = (DWORD) LOWORD (dwVolume);
if (m_DstLine.fIdValid) { mmr = mixerSetControlValue ( m_hMixer, adwVolume, m_DstLine.dwControlId, 2); } return (mmr == MMSYSERR_NOERROR); }
BOOL CMixerDevice::SetSubVolume(DWORD dwVolume) { MMRESULT mmr = MMSYSERR_ERROR; DWORD adwVolume[2];
adwVolume[0] = adwVolume[1] = (DWORD) LOWORD (dwVolume);
if (m_SrcLine.fIdValid) { mmr = mixerSetControlValue ( m_hMixer, adwVolume, m_SrcLine.dwControlId, m_SrcLine.ucChannels); } return (mmr == MMSYSERR_NOERROR); }
//
// Gets the volume (0 - 65535) of the master volume
// returns TRUE if succesful,
// returns FALSE if it fails or if this control is not available
//
BOOL CMixerDevice::GetMainVolume(LPDWORD pdwVolume) { BOOL fRet = FALSE;
if (m_DstLine.fIdValid) { DWORD adwVolume[2]; MMRESULT mmr = ::mixerGetControlValue( m_hMixer, adwVolume, m_DstLine.dwControlId, 2); fRet = (mmr == MMSYSERR_NOERROR); if (fRet) { // BUGBUG: is this the left channel only?
*pdwVolume = LOWORD(adwVolume[0]); } }
return fRet; }
//
// Gets the volume (0 - 65535) of the sub volume
// returns TRUE if succesful,
// returns FALSE if it fails or if this control is not available
//
BOOL CMixerDevice::GetSubVolume(LPDWORD pdwVolume) { BOOL fRet = FALSE;
if (m_SrcLine.fIdValid) { DWORD adwVolume[2]; MMRESULT mmr = ::mixerGetControlValue( m_hMixer, adwVolume, m_SrcLine.dwControlId, m_SrcLine.ucChannels); fRet = (mmr == MMSYSERR_NOERROR); if (fRet) { // BUGBUG: is this the left channel only?
*pdwVolume = LOWORD(adwVolume[0]); } }
return fRet; }
// Return the value of the Auto Gain Control on SB16/AWE32 cards
// Returns FALSE if the control is not supported.
// pfOn is OUTPUT, OPTIONAL - value of AGC
BOOL CMixerDevice::GetAGC(BOOL *pfOn) { MMRESULT mmr; DWORD dwAGCId; DWORD dwValue;
if (FALSE == GetAGCID(m_mixerCaps.wMid, m_mixerCaps.wPid, &dwAGCId)) return FALSE;
mmr = mixerGetControlValue(m_hMixer, &dwValue, dwAGCId, 1); if (mmr != MMSYSERR_NOERROR) return FALSE;
if (pfOn) *pfOn = dwValue; return TRUE; }
/*
Hack API to turn MIC Auto Gain Control on or off. Its a hack because it only works on SB16/AWE32 cards. */ BOOL CMixerDevice::SetAGC(BOOL fOn) { DWORD dwAGCId; DWORD dwValue; MMRESULT mmr;
if (FALSE == GetAGCID(m_mixerCaps.wMid, m_mixerCaps.wPid, &dwAGCId)) return FALSE;
mmr = mixerGetControlValue(m_hMixer, &dwValue, dwAGCId, 1); if (mmr != MMSYSERR_NOERROR) return FALSE; if (dwValue == (DWORD)fOn) return TRUE; dwValue = fOn; mmr = mixerSetControlValue(m_hMixer, &dwValue, dwAGCId, 1); return (mmr == MMSYSERR_NOERROR); }
BOOL CMixerDevice::EnableMicrophone() { MIXERLINE mixerLine; MIXERCONTROL mixerControl; MIXERCONTROLDETAILS mixerControlDetails, mixerControlDetailsOrig; UINT uIndex, numItems, numMics, numMicsSet, fMicFound; UINT uMicIndex = 0; UINT aMicIndices[MAX_MICROPHONE_DEVS]; MIXERCONTROLDETAILS_LISTTEXT *aListText = NULL; MIXERCONTROLDETAILS_BOOLEAN *aEnableList = NULL; MMRESULT mmr;
// check to see if component type is valid (which means the line exists!)
// even if the volume control doesn't exist or isn't slidable,
// there may still be a select switch
if ((m_SrcLine.dwCompType != MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) || (m_DstLine.dwCompType != MIXERLINE_COMPONENTTYPE_DST_WAVEIN)) { return FALSE; }
// try to find the mixer list
if ( (MMSYSERR_NOERROR != mixerGetControlByType(m_hMixer, m_DstLine.dwLineId, MIXERCONTROL_CT_CLASS_LIST, &mixerControl)) && (MMSYSERR_NOERROR != mixerGetControlByType(m_hMixer, m_DstLine.dwLineId, MIXERCONTROL_CONTROLTYPE_MIXER, &mixerControl)) && (MMSYSERR_NOERROR != mixerGetControlByType(m_hMixer, m_DstLine.dwLineId, MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT, &mixerControl)) && (MMSYSERR_NOERROR != mixerGetControlByType(m_hMixer, m_DstLine.dwLineId, MIXERCONTROL_CONTROLTYPE_MUX, &mixerControl)) && (MMSYSERR_NOERROR != mixerGetControlByType(m_hMixer, m_DstLine.dwLineId, MIXERCONTROL_CONTROLTYPE_SINGLESELECT, &mixerControl)) ) { TRACE_OUT(("CMixerDevice::EnableMicrophone-Unable to find mixer list!")); return FALSE; }
ZeroMemory(&mixerControlDetails, sizeof(MIXERCONTROLDETAILS));
mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); mixerControlDetails.dwControlID = mixerControl.dwControlID; if (MIXERCONTROL_CONTROLF_UNIFORM & mixerControl.fdwControl) mixerControlDetails.cChannels = 1; else mixerControlDetails.cChannels = m_DstLine.ucChannels;
if (MIXERCONTROL_CONTROLF_MULTIPLE & mixerControl.fdwControl) mixerControlDetails.cMultipleItems = (UINT)mixerControl.cMultipleItems; else mixerControlDetails.cMultipleItems = 1;
// weirdness - you have to set cbDetails to the size of a single LISTTEXT item
// setting it to anything larger will make the call fail
mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
numItems = mixerControlDetails.cMultipleItems; if (m_DstLine.dwConnections > numItems) numItems = m_DstLine.dwConnections;
DBG_SAVE_FILE_LINE aListText = new MIXERCONTROLDETAILS_LISTTEXT[numItems];
DBG_SAVE_FILE_LINE aEnableList = new MIXERCONTROLDETAILS_BOOLEAN[numItems]; if ((aListText == NULL) || (aEnableList == NULL)) { WARNING_OUT(("CMixerDevice::EnableMicrophone-Out of memory")); return FALSE; }
ZeroMemory(aListText, sizeof(MIXERCONTROLDETAILS_LISTTEXT)*numItems); ZeroMemory(aEnableList, sizeof(MIXERCONTROLDETAILS_BOOLEAN)*numItems);
mixerControlDetails.paDetails = aListText;
// preserve the settings, some values will change after this call
mixerControlDetailsOrig = mixerControlDetails;
// query for the text of the list
mmr = mixerGetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails, MIXER_GETCONTROLDETAILSF_LISTTEXT |MIXER_OBJECTF_HMIXER);
// some sound cards don't specify CONTROLF_MULTIPLE
// try doing what sndvol32 does for MUX controls
if (mmr != MMSYSERR_NOERROR) { mixerControlDetails = mixerControlDetailsOrig; mixerControlDetails.cChannels = 1; mixerControlDetails.cMultipleItems = m_DstLine.dwConnections; mixerControlDetailsOrig = mixerControlDetails; mmr = mixerGetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails, MIXER_GETCONTROLDETAILSF_LISTTEXT |MIXER_OBJECTF_HMIXER); }
if (mmr != MMSYSERR_NOERROR) { delete [] aListText; delete [] aEnableList; return FALSE; }
// enumerate for the microphone
numMics = 0; fMicFound = FALSE; for (uIndex = 0; uIndex < mixerControlDetails.cMultipleItems; uIndex++) { // dwParam1 of the listText structure is the LineID of the source
// dwParam2 should be the component type, but unfoturnately not
// all sound cards obey this rule.
ZeroMemory (&mixerLine, sizeof(MIXERLINE)); mixerLine.cbStruct = sizeof(MIXERLINE); mixerLine.dwLineID = aListText[uIndex].dwParam1;
mmr = mixerGetLineInfo ((HMIXEROBJ)m_hMixer, &mixerLine, MIXER_GETLINEINFOF_LINEID | MIXER_OBJECTF_HMIXER);
if ((mmr == MMSYSERR_NOERROR) && (mixerLine.dwComponentType == m_SrcLine.dwCompType) && (numMics < MAX_MICROPHONE_DEVS)) { aMicIndices[numMics] = uIndex; numMics++; }
if (aListText[uIndex].dwParam1 == m_SrcLine.dwLineId) { uMicIndex = uIndex; fMicFound = TRUE; // can't rely on uIndex or uNumMics not zero
} }
if (fMicFound == FALSE) { delete [] aListText; delete [] aEnableList; return FALSE; }
// now we know which position in the array to set, let's do it.
mixerControlDetails = mixerControlDetailsOrig; mixerControlDetails.paDetails = aEnableList; mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
// find out what's already marked as set.
mmr = mixerGetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER);
if ((mmr == MMSYSERR_NOERROR) && (aEnableList[uMicIndex].fValue != 1)) { // how many microphone's are already enabled ?
// if another microphone is already enabled and if the device is MUX type
// we won't attempt to turn one on.
numMicsSet = 0; for (uIndex = 0; uIndex < numMics; uIndex++) { if ((aEnableList[aMicIndices[uIndex]].fValue == 1) && (uIndex != uMicIndex)) { numMicsSet++; } }
if ( (mixerControl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX) ||(mixerControl.dwControlType == MIXERCONTROL_CONTROLTYPE_SINGLESELECT)) { ZeroMemory(aEnableList, sizeof(aEnableList)*numItems); aEnableList[uMicIndex].fValue = 1; if (numMicsSet == 0) { mmr = mixerSetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER); } else { mmr = MMSYSERR_ERROR; // a mike has already been enabled
} } else { mixerControlDetails = mixerControlDetailsOrig; mixerControlDetails.paDetails = aEnableList; mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); aEnableList[uMicIndex].fValue = 1; mmr = mixerSetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails, MIXER_GETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER); } }
delete []aEnableList; delete []aListText;
return (mmr == MMSYSERR_NOERROR);
}
BOOL CMixerDevice::UnMuteVolume() { MIXERCONTROL mixerControl; MIXERCONTROLDETAILS mixerControlDetails; MIXERCONTROLDETAILS_BOOLEAN mcdb; MMRESULT mmrMaster, mmrSub;
// try to unmute the master volume
// this could be used on both the recording and playback mixers
mmrMaster = mixerGetControlByType(m_hMixer, m_DstLine.dwLineId, MIXERCONTROL_CONTROLTYPE_MUTE, &mixerControl);
if (mmrMaster == MMSYSERR_NOERROR) { ZeroMemory(&mixerControlDetails, sizeof(MIXERCONTROLDETAILS)); mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); mixerControlDetails.dwControlID = mixerControl.dwControlID;
mixerControlDetails.cChannels = 1; mixerControlDetails.cMultipleItems = 0;
mcdb.fValue = 0; mixerControlDetails.paDetails = &mcdb; mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
mmrMaster = mixerSetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER); }
// only try to unmute waveOut
if ( (m_DstLine.dwCompType != MIXERLINE_COMPONENTTYPE_DST_SPEAKERS) || (m_SrcLine.dwCompType == 0)) { return (mmrMaster == MMSYSERR_NOERROR); }
mmrSub = mixerGetControlByType(m_hMixer, m_SrcLine.dwLineId, MIXERCONTROL_CONTROLTYPE_MUTE, &mixerControl);
if (mmrSub == MMSYSERR_NOERROR) { ZeroMemory(&mixerControlDetails, sizeof(MIXERCONTROLDETAILS)); mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); mixerControlDetails.dwControlID = mixerControl.dwControlID;
mixerControlDetails.cChannels = 1; mixerControlDetails.cMultipleItems = 0;
mcdb.fValue = 0; mixerControlDetails.paDetails = &mcdb; mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
mmrSub = mixerSetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER); }
return ((mmrSub == MMSYSERR_NOERROR) || (mmrMaster == MMSYSERR_NOERROR));
}
//////////////////////////////////////////////////
//
// The following are private APIs
//
static MMRESULT mixerGetControlValue ( HMIXER hMixer, DWORD *pdwValue, DWORD dwControlId, UINT ucChannels ) { MIXERCONTROLDETAILS mxcd; MMRESULT mmr;
ZeroMemory (&mxcd, sizeof (mxcd)); mxcd.cbStruct = sizeof (mxcd); mxcd.dwControlID = dwControlId; mxcd.cChannels = ucChannels; mxcd.cbDetails = sizeof (DWORD); mxcd.paDetails = (PVOID) pdwValue; mmr = mixerGetControlDetails ((HMIXEROBJ) hMixer, &mxcd, MIXER_GETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER); return mmr; }
static MMRESULT mixerSetControlValue ( HMIXER hMixer, DWORD *pdwValue, DWORD dwControlId, UINT ucChannels ) { MIXERCONTROLDETAILS mxcd; MMRESULT mmr;
ZeroMemory (&mxcd, sizeof (mxcd)); mxcd.cbStruct = sizeof (mxcd); mxcd.dwControlID = dwControlId; mxcd.cChannels = ucChannels; mxcd.cbDetails = sizeof (DWORD); mxcd.paDetails = (PVOID) pdwValue; mmr = mixerSetControlDetails ((HMIXEROBJ) hMixer, &mxcd, MIXER_SETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER); return mmr; }
static MMRESULT mixerGetControlId ( HMIXER hMixer, DWORD *pdwControlId, DWORD dwLineId, DWORD dwControlType ) { MIXERLINECONTROLS mxlc; MIXERCONTROL mxc; MMRESULT mmr;
ZeroMemory (&mxlc, sizeof (mxlc)); ZeroMemory (&mxc, sizeof (mxc)); mxlc.cbStruct = sizeof (mxlc); mxlc.dwLineID = dwLineId; mxlc.dwControlType = dwControlType; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof (mxc); mxlc.pamxctrl = &mxc; mmr = mixerGetLineControls ((HMIXEROBJ) hMixer, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE | MIXER_OBJECTF_HMIXER); *pdwControlId = mxc.dwControlID; return mmr; }
// similar to above, except returns the whole control
static MMRESULT mixerGetControlByType ( HMIXER hMixer, DWORD dwLineId, DWORD dwControlType, MIXERCONTROL *pMixerControl) { MIXERLINECONTROLS mxlc; MMRESULT mmr;
ZeroMemory (&mxlc, sizeof (mxlc)); ZeroMemory (pMixerControl, sizeof (MIXERCONTROL)); mxlc.cbStruct = sizeof (mxlc); mxlc.dwLineID = dwLineId; mxlc.dwControlType = dwControlType; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof (MIXERCONTROL); mxlc.pamxctrl = pMixerControl; mmr = mixerGetLineControls ((HMIXEROBJ) hMixer, &mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE | MIXER_OBJECTF_HMIXER); return mmr; }
// IUnknown stuff
ULONG __stdcall CMixerDevice::AddRef() { InterlockedIncrement(&m_lRefCount); return m_lRefCount; }
ULONG __stdcall CMixerDevice::Release() { if (0 == InterlockedDecrement(&m_lRefCount)) { delete this; return 0; } return m_lRefCount; }
HRESULT __stdcall CMixerDevice::QueryInterface(const IID& iid, void **ppVoid) {
if ((iid == IID_IUnknown) || (iid == IID_IMixer)) { *ppVoid = this; } else { *ppVoid = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; }
|