913 lines
26 KiB
C++
913 lines
26 KiB
C++
#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();
|
|
}
|