Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

912 lines
26 KiB

#include "cabinet.h"
#include "mixer.h"
#include <dbt.h>
#include "mmddkp.h"
///////////////////////////////////////
// External interface
//
///////////////////////////////////////
// Definitions
//
#define MMHID_VOLUME_CONTROL 0
#define MMHID_BASS_CONTROL 1
#define MMHID_TREBLE_CONTROL 2
#define MMHID_BALANCE_CONTROL 3
#define MMHID_MUTE_CONTROL 4
#define MMHID_LOUDNESS_CONTROL 5
#define MMHID_BASSBOOST_CONTROL 6
#define MMHID_NUM_CONTROLS 7
typedef struct _LINE_DATA
{
MIXERLINE MixerLine; // The real deal MIXERLINE struct.
DWORD ControlType[MMHID_NUM_CONTROLS];
BOOL ControlPresent[MMHID_NUM_CONTROLS];
MIXERCONTROL Control[MMHID_NUM_CONTROLS];
} LINE_DATA, * PLINE_DATA, FAR * LPLINE_DATA;
typedef struct _MIXER_DATA
{
HMIXER hMixer; // open handle to mixer
HWND hwndCallback; // window to use for mixer callbacks
LPWSTR DeviceInterface; // DeviceInterface that implements the mixer
double* pdblCacheMix; // Dynamic array of relative channel level percentages
LPDWORD pdwLastVolume; // Last volume level set on mixer
MMRESULT mmr; // last result (iff dwReturn == MIXUI_MMSYSERR)
LINE_DATA LineData; // BYDESIGN - putting this here assumes only one
// mixer line for now. (first dest. line)
} MIXER_DATA, *PMIXER_DATA, FAR *LPMIXER_DATA;
/*++
* Globals
--*/
BOOL g_fMixerStartup = TRUE;
HWND g_hwndCallback;
MIXER_DATA g_MixerData;
BOOL g_fMixerPresent = FALSE;
void Mixer_Close(MIXER_DATA *pMixerData);
BOOL Mixer_CheckMissing(void);
/*****************************************************************************
*
* ACTIVE GET/SET CODE
*
*****************************************************************************/
#define VOLUME_MIN 0L
#define VOLUME_MAX 65535L
void RefreshMixCache (PMIXER_DATA pMixerData, LPDWORD padwVolume)
{
if (pMixerData && padwVolume)
{
DWORD cChannels = pMixerData -> LineData.MixerLine.cChannels;
if (1 > cChannels)
return; // Weird!
// Create cache if necessary
if (!pMixerData -> pdblCacheMix)
pMixerData -> pdblCacheMix = (double *)LocalAlloc(LPTR, cChannels * sizeof (double));
// Refresh cache
if (pMixerData -> pdblCacheMix)
{
UINT uiIndx;
double* pdblMixPercent;
DWORD dwVolume;
// Get the maximum volume
DWORD dwMaxVol = 0;
for (uiIndx = 0; uiIndx < cChannels; uiIndx++)
dwMaxVol = max (dwMaxVol, *(padwVolume + uiIndx));
// Caculate the percentage distance each channel is away from the max
// value. Creating this cache allows us to maintain the relative distance
// of the channel levels from each other as the user adjusts the master
// volume level.
for (uiIndx = 0; uiIndx < cChannels; uiIndx++)
{
dwVolume = *(padwVolume + uiIndx);
pdblMixPercent = ((pMixerData -> pdblCacheMix) + uiIndx);
// Caculate the percentage this value is from the max ...
if (dwMaxVol == dwVolume)
{
*pdblMixPercent = 1.0F;
}
else
{
// Note: if 0 == dwMaxVol all values would be zero and this part
// of the "if" statement will never execute.
*pdblMixPercent = ((double) dwVolume / (double) dwMaxVol);
}
}
}
}
}
static
MMRESULT
Mixer_GetVolume(
LPMIXER_DATA pMixerData,
LPDWORD padwVolume
)
/*++
Routine Description:
--*/
{
MIXERCONTROLDETAILS mxcd;
MMRESULT mmr;
if (!pMixerData->LineData.ControlPresent[MMHID_VOLUME_CONTROL]) return MIXERR_INVALCONTROL;
mxcd.cbStruct = sizeof(mxcd);
mxcd.dwControlID = pMixerData->LineData.Control[MMHID_VOLUME_CONTROL].dwControlID;
mxcd.cChannels = pMixerData->LineData.MixerLine.cChannels;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(DWORD);
mxcd.paDetails = (LPVOID)padwVolume;
mmr = mixerGetControlDetails((HMIXEROBJ)pMixerData->hMixer,
&mxcd,
MIXER_OBJECTF_HANDLE | MIXER_GETCONTROLDETAILSF_VALUE);
return mmr;
}
MMRESULT
Mixer_ToggleMute(void)
/*++
Routine Description:
--*/
{
MIXERCONTROLDETAILS mxcd;
DWORD fMute;
MMRESULT mmr;
MIXER_DATA *pMixerData = &g_MixerData;
if (Mixer_CheckMissing())
{
return MMSYSERR_NODRIVER;
}
if (!pMixerData->LineData.ControlPresent[MMHID_MUTE_CONTROL]) return MMSYSERR_NOERROR;
mxcd.cbStruct = sizeof(mxcd);
mxcd.dwControlID = pMixerData->LineData.Control[MMHID_MUTE_CONTROL].dwControlID ;
mxcd.cChannels = 1;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(fMute);
mxcd.paDetails = (LPVOID)&fMute;
mmr = mixerGetControlDetails((HMIXEROBJ)pMixerData->hMixer,
&mxcd,
MIXER_OBJECTF_HANDLE | MIXER_GETCONTROLDETAILSF_VALUE);
if (!mmr) {
fMute = fMute ? 0 : 1;
mmr = mixerSetControlDetails((HMIXEROBJ)pMixerData->hMixer,
&mxcd,
MIXER_OBJECTF_HANDLE | MIXER_SETCONTROLDETAILSF_VALUE);
}
return mmr;
}
MMRESULT
Mixer_ToggleLoudness(
MIXER_DATA * pMixerData
)
/*++
Routine Description:
--*/
{
MIXERCONTROLDETAILS mxcd;
DWORD fEnabled;
MMRESULT mmr;
if (!pMixerData->LineData.ControlPresent[MMHID_LOUDNESS_CONTROL]) return MMSYSERR_NOERROR;
mxcd.cbStruct = sizeof(mxcd);
mxcd.dwControlID = pMixerData->LineData.Control[MMHID_LOUDNESS_CONTROL].dwControlID ;
mxcd.cChannels = 1;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(fEnabled);
mxcd.paDetails = (LPVOID)&fEnabled;
mmr = mixerGetControlDetails((HMIXEROBJ)pMixerData->hMixer,
&mxcd,
MIXER_OBJECTF_HANDLE | MIXER_GETCONTROLDETAILSF_VALUE);
if (!mmr) {
fEnabled = fEnabled ? 0 : 1;
mmr = mixerSetControlDetails((HMIXEROBJ)pMixerData->hMixer,
&mxcd,
MIXER_OBJECTF_HANDLE | MIXER_SETCONTROLDETAILSF_VALUE);
}
return mmr;
}
MMRESULT Mixer_ToggleBassBoost(void)
/*++
Routine Description:
--*/
{
MIXERCONTROLDETAILS mxcd;
DWORD fEnabled;
MMRESULT mmr;
MIXER_DATA *pMixerData = &g_MixerData;
if (Mixer_CheckMissing())
{
return MMSYSERR_NODRIVER;
}
if (!pMixerData->LineData.ControlPresent[MMHID_BASSBOOST_CONTROL]) return MMSYSERR_NOERROR;
mxcd.cbStruct = sizeof(mxcd);
mxcd.dwControlID = pMixerData->LineData.Control[MMHID_BASSBOOST_CONTROL].dwControlID ;
mxcd.cChannels = 1;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(fEnabled);
mxcd.paDetails = (LPVOID)&fEnabled;
mmr = mixerGetControlDetails((HMIXEROBJ)pMixerData->hMixer,
&mxcd,
MIXER_OBJECTF_HANDLE | MIXER_GETCONTROLDETAILSF_VALUE);
if (!mmr) {
fEnabled = fEnabled ? 0 : 1;
mmr = mixerSetControlDetails((HMIXEROBJ)pMixerData->hMixer,
&mxcd,
MIXER_OBJECTF_HANDLE | MIXER_SETCONTROLDETAILSF_VALUE);
}
return mmr;
}
MMRESULT
Mixer_SetVolume(
int Increment // amount of volume change
)
/*++
Routine Description:
Change a mixerControl in response to a user event
--*/
{
MMRESULT mmr;
MIXERCONTROLDETAILS mxcd;
LPVOID pvVolume;
UINT uiIndx;
LPDWORD pdwVolume;
double dblVolume;
MIXER_DATA *pMixerData = &g_MixerData;
PLINE_DATA pLineData;
DWORD cChannels;
if (Mixer_CheckMissing())
{
return MMSYSERR_NODRIVER;
}
pLineData = &pMixerData->LineData;
cChannels = pMixerData -> LineData.MixerLine.cChannels;
if (!pMixerData->LineData.ControlPresent[MMHID_VOLUME_CONTROL]) return MMSYSERR_NOERROR;
//
// get current volume
//
ZeroMemory (&mxcd, sizeof (mxcd));
mxcd.cbDetails = sizeof (DWORD);
mxcd.paDetails = LocalAlloc(LPTR, cChannels * sizeof (DWORD));
if (!mxcd.paDetails)
return MMSYSERR_NOMEM;
pvVolume = LocalAlloc(LPTR, cChannels * sizeof (DWORD));
if (!pvVolume)
{
LocalFree(mxcd.paDetails);
return MMSYSERR_NOMEM;
}
// Note: From here on, do not return without freeing 'mxcd.paDetails'
// and 'pvVolume'.
// Get the current volume and any mix cache
mmr = Mixer_GetVolume (pMixerData, (LPDWORD)mxcd.paDetails);
if (MMSYSERR_NOERROR == mmr)
{
// Create cache if we don't already have one
if (!pMixerData -> pdblCacheMix)
{
RefreshMixCache (pMixerData, (LPDWORD)mxcd.paDetails);
if (!pMixerData -> pdblCacheMix)
mmr = MMSYSERR_NOMEM;
else
{
// Create last set volume cache
if (!pMixerData -> pdwLastVolume)
{
pMixerData -> pdwLastVolume = (DWORD *)LocalAlloc(LPTR, cChannels * sizeof (DWORD));
if (!pMixerData -> pdwLastVolume)
mmr = MMSYSERR_NOMEM;
}
}
}
else
{
// HHMMM, speculating random ass fix for 167948/174466 since this
// is the ONLY branch where pdwLastVolume can be NULL and not
// generate an error. Will have to talk to FrankYe
// -Fwong.
if (!pMixerData -> pdwLastVolume)
{
pMixerData -> pdwLastVolume = (DWORD *)LocalAlloc(LPTR, cChannels * sizeof (DWORD));
if (!pMixerData -> pdwLastVolume)
mmr = MMSYSERR_NOMEM;
}
}
}
// Don't allow incrementing past max volume (channels meet at
// min volume, so need to test that).
if (0 < Increment && MMSYSERR_NOERROR == mmr)
{
for (uiIndx = 0; uiIndx < cChannels; uiIndx++)
{
pdwVolume = (((DWORD*)mxcd.paDetails) + uiIndx);
dblVolume = (*(pMixerData -> pdblCacheMix + uiIndx) * (double) Increment);
if (VOLUME_MAX <= (*pdwVolume) + dblVolume)
Increment = min ((DWORD) Increment, VOLUME_MAX - (*pdwVolume));
}
}
//
// set the volume
//
if (0 != Increment && MMSYSERR_NOERROR == mmr)
{
// Back up the current settings
memcpy (pvVolume, mxcd.paDetails, cChannels * sizeof (DWORD));
// Caculate the new volume level for each of the channels. For volume levels
// at the current max, we simply set the newly requested level (in this case
// the cache value is 1.0). For those less than the max, we set a value that
// is a percentage of the max. This maintains the relative distance of the
// channel levels from each other.
for (uiIndx = 0; uiIndx < cChannels; uiIndx++)
{
pdwVolume = (((DWORD*)mxcd.paDetails) + uiIndx);
dblVolume = (*(pMixerData -> pdblCacheMix + uiIndx) * (double) Increment);
// Ensure positive result
if (VOLUME_MIN >= ((double)(*pdwVolume) + dblVolume))
(*pdwVolume) = VOLUME_MIN;
else
(*pdwVolume) = (DWORD)((double)(*pdwVolume) + dblVolume);
// Ensure that the new value is in range
(*pdwVolume) = (DWORD) min (VOLUME_MAX, (*pdwVolume));
// Disables pesky warning...
#if (VOLUME_MIN != 0L)
(*pdwVolume) = (DWORD) max (VOLUME_MIN, (*pdwVolume));
#endif
}
// Cache last caculated volume..
memcpy (pMixerData -> pdwLastVolume, mxcd.paDetails, cChannels * sizeof (DWORD));
mxcd.cbStruct = sizeof(mxcd);
mxcd.dwControlID = pLineData->Control[MMHID_VOLUME_CONTROL].dwControlID;
mxcd.cChannels = cChannels;
mxcd.cMultipleItems = 0;
// Apply new value only if it is different. This prevents unessary calls to
// mixerSetControlDetails() when we are pegged.
if (memcmp (pvVolume, mxcd.paDetails, cChannels * sizeof (DWORD)))
{
//
// Set the volume control at the mixer.
//
mmr = mixerSetControlDetails((HMIXEROBJ)pMixerData->hMixer,
&mxcd,
MIXER_OBJECTF_HANDLE | MIXER_SETCONTROLDETAILSF_VALUE);
}
}
// Free 'mxcd.paDetails' and 'pvVolume'
LocalFree(mxcd.paDetails);
LocalFree(pvVolume);
return mmr;
}
#define BASS_MIN 0L
#define BASS_MAX 65535L
MMRESULT
Mixer_SetBass(
int Increment // amount of change
)
/*++
Routine Description:
Change a mixerControl in response to a user event
--*/
{
MMRESULT mmr;
MIXERCONTROLDETAILS mxcd;
MIXER_DATA *pMixerData = &g_MixerData;
PLINE_DATA pLineData;
if (Mixer_CheckMissing())
{
return MMSYSERR_NODRIVER;
}
pLineData = &pMixerData->LineData;
LONG lLevel = 0;
if (!pMixerData->LineData.ControlPresent[MMHID_BASS_CONTROL]) return MMSYSERR_NOERROR;
mxcd.cbStruct = sizeof(mxcd);
mxcd.dwControlID = pLineData->Control[MMHID_BASS_CONTROL].dwControlID;
//
// get current setting
//
mxcd.cChannels = 1;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(lLevel);
mxcd.paDetails = (LPVOID)&lLevel;
mmr = mixerGetControlDetails((HMIXEROBJ)pMixerData->hMixer,
&mxcd,
MIXER_OBJECTF_HANDLE | MIXER_GETCONTROLDETAILSF_VALUE);
if (mmr) return mmr;
lLevel += Increment;
lLevel = min( BASS_MAX, lLevel);
lLevel = max( BASS_MIN, lLevel);
mxcd.cChannels = 1;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(lLevel);
mxcd.paDetails = (LPVOID)&lLevel;
//
// Set the bass control at the mixer.
//
mmr = mixerSetControlDetails((HMIXEROBJ)pMixerData->hMixer,
&mxcd,
MIXER_OBJECTF_HANDLE | MIXER_SETCONTROLDETAILSF_VALUE);
return mmr;
}
#define TREBLE_MIN 0L
#define TREBLE_MAX 65535L
MMRESULT
Mixer_SetTreble(
int Increment
)
/*++
Routine Description:
Change a mixerControl in response to a user event
--*/
{
MMRESULT mmr;
MIXERCONTROLDETAILS mxcd;
MIXER_DATA *pMixerData = &g_MixerData;
PLINE_DATA pLineData;
if (Mixer_CheckMissing())
{
return MMSYSERR_NODRIVER;
}
pLineData = &pMixerData->LineData;
LONG lLevel = 0;
if (!pMixerData->LineData.ControlPresent[MMHID_TREBLE_CONTROL]) return MMSYSERR_NOERROR;
mxcd.cbStruct = sizeof(mxcd);
mxcd.dwControlID = pLineData->Control[MMHID_TREBLE_CONTROL].dwControlID;
//
// get current setting
//
mxcd.cChannels = 1;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(lLevel);
mxcd.paDetails = (LPVOID)&lLevel;
mmr = mixerGetControlDetails((HMIXEROBJ)pMixerData->hMixer,
&mxcd,
MIXER_OBJECTF_HANDLE | MIXER_GETCONTROLDETAILSF_VALUE);
if (mmr) return mmr;
lLevel += Increment;
lLevel = min( TREBLE_MAX, lLevel);
lLevel = max( TREBLE_MIN, lLevel);
mxcd.cChannels = 1;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(lLevel);
mxcd.paDetails = (LPVOID)&lLevel;
//
// Set the bass control at the mixer.
//
mmr = mixerSetControlDetails((HMIXEROBJ)pMixerData->hMixer,
&mxcd,
MIXER_OBJECTF_HANDLE | MIXER_SETCONTROLDETAILSF_VALUE);
return mmr;
}
/*****************************************************************************
*
*
*
*****************************************************************************/
MMRESULT
Mixer_GetDefaultMixerID(
int *pid
)
/*++
Routine Description:
Get the default mixer id. We only appear if there is a mixer associated
with the default wave.
--*/
{
MMRESULT mmr;
UINT uWaveID, uMxID;
DWORD dwFlags;
if (0 == waveOutGetNumDevs()) return MMSYSERR_NODRIVER;
mmr = waveOutMessage((HWAVEOUT)(UINT_PTR)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&uWaveID, (DWORD_PTR)&dwFlags);
if (MMSYSERR_NOERROR == mmr)
{
if (WAVE_MAPPER != uWaveID)
{
mmr = mixerGetID((HMIXEROBJ)(UINT_PTR)uWaveID, &uMxID, MIXER_OBJECTF_WAVEOUT);
if (mmr == MMSYSERR_NOERROR)
{
*pid = uMxID;
}
} else {
// Don't return a default mixer id if we don't have a default
// audio driver
mmr = MMSYSERR_NODRIVER;
}
}
return mmr;
}
BOOL
Mixer_GetDestLine(
MIXER_DATA * pMixerData
)
/*++
Routine Description:
--*/
{
MIXERLINE * mlDst = &pMixerData->LineData.MixerLine;
MMRESULT mmr;
mlDst->cbStruct = sizeof ( MIXERLINE );
mlDst->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
mmr = mixerGetLineInfo((HMIXEROBJ)pMixerData->hMixer,
mlDst,
MIXER_OBJECTF_HANDLE | MIXER_GETLINEINFOF_COMPONENTTYPE);
if (mmr != MMSYSERR_NOERROR){
return FALSE;
}
return TRUE;
}
void
Mixer_GetLineControls(
MIXER_DATA * pMixerData,
LINE_DATA * pLineData
)
/*++
Routine Description:
--*/
{
MIXERLINECONTROLS LineControls;
MMRESULT mmr;
DWORD i;
for(i=0; i<MMHID_NUM_CONTROLS; i++){
LineControls.cbStruct = sizeof(LineControls);
LineControls.dwLineID = pLineData->MixerLine.dwLineID;
LineControls.dwControlType = pLineData->ControlType[i];
LineControls.cControls = 1;
LineControls.cbmxctrl = sizeof(MIXERCONTROL);
LineControls.pamxctrl = &pLineData->Control[i];
mmr = mixerGetLineControls((HMIXEROBJ)pMixerData->hMixer,
&LineControls,
MIXER_OBJECTF_HANDLE | MIXER_GETLINECONTROLSF_ONEBYTYPE);
pLineData->ControlPresent[i] = (MMSYSERR_NOERROR == mmr) ? TRUE : FALSE;
if (mmr != MMSYSERR_NOERROR){
//return mmr;
}
}
return;
}
///////////////////////////////////////
//
BOOL
Mixer_Open(
MIXER_DATA * pMixerData
)
/*++
Routine Description:
Finds the default mixer, opens it, and initializes
all data.
--*/
{
PWSTR pwstrDeviceInterface;
ULONG cbDeviceInterface;
int MixerId;
MMRESULT mmr;
BOOL result;
ASSERT(!pMixerData->hMixer);
// Get console mixer ID and open it.
mmr = Mixer_GetDefaultMixerID(&MixerId);
if(mmr) return FALSE;
mmr = mixerOpen(&pMixerData->hMixer, MixerId, (DWORD_PTR)pMixerData->hwndCallback, 0, CALLBACK_WINDOW);
if (!mmr) {
//
// Get our controls for the default destination line.
//
if (Mixer_GetDestLine(pMixerData)) {
Mixer_GetLineControls(pMixerData, &pMixerData->LineData);
// Free any mix cache & volume cache
if (pMixerData->pdblCacheMix) LocalFree(pMixerData->pdblCacheMix);
pMixerData->pdblCacheMix = NULL;
if (pMixerData -> pdwLastVolume) LocalFree(pMixerData -> pdwLastVolume);
pMixerData -> pdwLastVolume = NULL;
// Get the DeviceInterface of the mixer in order to listen
// for relevant PnP device messages
if (pMixerData->DeviceInterface) LocalFree(pMixerData->DeviceInterface);
pMixerData->DeviceInterface = NULL;
mmr = (MMRESULT)mixerMessage(pMixerData->hMixer, DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&cbDeviceInterface, 0);
if (!mmr && (0 != cbDeviceInterface)) {
pwstrDeviceInterface = (PWSTR)LocalAlloc(LPTR, cbDeviceInterface);
if (pwstrDeviceInterface) {
mmr = (MMRESULT)mixerMessage(pMixerData->hMixer, DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)pwstrDeviceInterface, cbDeviceInterface);
if (!mmr) {
pMixerData->DeviceInterface = pwstrDeviceInterface;
} else {
LocalFree(pwstrDeviceInterface);
}
}
}
result = TRUE;
} else {
mixerClose(pMixerData->hMixer);
pMixerData->hMixer = NULL;
TraceMsg(TF_WARNING, "Mixer_Open : Could not find mixer destination line");
result = FALSE;
}
}
return result;
}
void Mixer_Close(MIXER_DATA *pMixerData)
/*++
Routine Description:
Closes the mixer handle.
--*/
{
if (pMixerData->DeviceInterface) LocalFree(pMixerData->DeviceInterface);
pMixerData->DeviceInterface = NULL;
if (pMixerData->pdblCacheMix) LocalFree(pMixerData->pdblCacheMix);
pMixerData->pdblCacheMix = NULL;
if (pMixerData->pdwLastVolume) LocalFree(pMixerData->pdwLastVolume);
pMixerData->pdwLastVolume = NULL;
if (pMixerData->hMixer){
MMRESULT mmr;
mmr = mixerClose(pMixerData->hMixer);
if (mmr) TraceMsg(TF_ERROR, "Mixer_Close : error: mixerClose returned mmr=%08Xh", mmr);
ASSERT(MMSYSERR_NOERROR == mmr);
pMixerData->hMixer = NULL;
}
return;
}
void
Mixer_Refresh(void)
/*++
Routine Description:
Closes the current mixer handle (if one is open), then opens mixer
again.
--*/
{
Mixer_Close(&g_MixerData);
g_fMixerPresent = Mixer_Open(&g_MixerData);
}
void Mixer_SetCallbackWindow(HWND hwndCallback)
{
g_hwndCallback = hwndCallback;
}
void Mixer_Startup(HWND hwndCallback)
/*++
Routine Description:
--*/
{
MIXER_DATA *pMixerData = &g_MixerData;
pMixerData->hMixer = NULL;
pMixerData->hwndCallback = hwndCallback;
pMixerData->DeviceInterface = NULL;
pMixerData->pdblCacheMix = NULL;
pMixerData->pdwLastVolume = NULL;
pMixerData->LineData.ControlType[MMHID_VOLUME_CONTROL] = MIXERCONTROL_CONTROLTYPE_VOLUME;
pMixerData->LineData.ControlType[MMHID_BASS_CONTROL] = MIXERCONTROL_CONTROLTYPE_BASS;
pMixerData->LineData.ControlType[MMHID_TREBLE_CONTROL] = MIXERCONTROL_CONTROLTYPE_TREBLE;
pMixerData->LineData.ControlType[MMHID_BALANCE_CONTROL] = MIXERCONTROL_CONTROLTYPE_PAN;
pMixerData->LineData.ControlType[MMHID_MUTE_CONTROL] = MIXERCONTROL_CONTROLTYPE_MUTE;
pMixerData->LineData.ControlType[MMHID_LOUDNESS_CONTROL] = MIXERCONTROL_CONTROLTYPE_LOUDNESS;
pMixerData->LineData.ControlType[MMHID_BASSBOOST_CONTROL] = MIXERCONTROL_CONTROLTYPE_BASS_BOOST;
pMixerData->LineData.ControlPresent[MMHID_VOLUME_CONTROL] = FALSE;
pMixerData->LineData.ControlPresent[MMHID_BASS_CONTROL] = FALSE;
pMixerData->LineData.ControlPresent[MMHID_TREBLE_CONTROL] = FALSE;
pMixerData->LineData.ControlPresent[MMHID_BALANCE_CONTROL] = FALSE;
pMixerData->LineData.ControlPresent[MMHID_MUTE_CONTROL] = FALSE;
pMixerData->LineData.ControlPresent[MMHID_LOUDNESS_CONTROL] = FALSE;
pMixerData->LineData.ControlPresent[MMHID_BASSBOOST_CONTROL] = FALSE;
Mixer_Refresh();
return;
}
BOOL Mixer_CheckMissing(void)
{
if (g_fMixerStartup)
{
Mixer_Startup(g_hwndCallback);
g_fMixerStartup = FALSE;
}
return !g_fMixerPresent;
}
void Mixer_Shutdown(void)
/*++
Routine Description:
Frees storage for mixer's DeviceInterface, then Mixer_Close().
--*/
{
MIXER_DATA *pMixerData = &g_MixerData;
if (pMixerData->DeviceInterface) LocalFree(pMixerData->DeviceInterface);
pMixerData->DeviceInterface = NULL;
if (pMixerData->pdblCacheMix) LocalFree(pMixerData->pdblCacheMix);
pMixerData->pdblCacheMix = NULL;
if (pMixerData->pdwLastVolume) LocalFree(pMixerData->pdwLastVolume);
pMixerData->pdwLastVolume = NULL;
Mixer_Close(pMixerData);
return;
}
void Mixer_DeviceChange(WPARAM wParam, LPARAM lParam)
{
PDEV_BROADCAST_DEVICEINTERFACE dbdi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
if (!g_MixerData.DeviceInterface) return;
switch (wParam) {
case DBT_DEVICEQUERYREMOVE:
case DBT_DEVICEREMOVEPENDING:
if (dbdi->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE) return;
if (lstrcmpi(dbdi->dbcc_name, g_MixerData.DeviceInterface)) return;
Mixer_Close(&g_MixerData);
return;
case DBT_DEVICEQUERYREMOVEFAILED:
if (dbdi->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE) return;
if (lstrcmpi(dbdi->dbcc_name, g_MixerData.DeviceInterface)) return;
Mixer_Refresh();
return;
}
return;
}
void Mixer_ControlChange(
WPARAM wParam,
LPARAM lParam )
/*++
Routine Description:
Handles mixer callback control change messages. Watches for changes on the
master volume control and recalculates the last mix values.
--*/
{
LPDWORD pdwVolume;
HMIXER hMixer = (HMIXER)wParam;
DWORD dwControlID = lParam;
if (g_MixerData.hMixer != hMixer) return;
if (dwControlID != g_MixerData.LineData.Control[MMHID_VOLUME_CONTROL].dwControlID) return;
// DPF(1, "WinmmShellMixerControlChange");
//
// get current volume
//
pdwVolume = (DWORD *)LocalAlloc(LPTR, g_MixerData.LineData.MixerLine.cChannels * sizeof (DWORD));
if (!pdwVolume)
return;
if (MMSYSERR_NOERROR == Mixer_GetVolume (&g_MixerData, pdwVolume))
{
// Refresh cache only if the volume values have changed (i.e. they
// were set outside of Mixer_SetVolume()).
if (!g_MixerData.pdwLastVolume || memcmp (g_MixerData.pdwLastVolume, pdwVolume, g_MixerData.LineData.MixerLine.cChannels * sizeof (DWORD)))
RefreshMixCache (&g_MixerData, pdwVolume);
}
LocalFree(pdwVolume);
}
void Mixer_MMDeviceChange( void )
{
Mixer_Refresh();
}