#include "cabinet.h" #include "mixer.h" #include #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; iMixerLine.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(); }