/////////////////////////////////////////////////////////////////////////////// // // File: multchan.c // // This file defines the functions that drive the multichannel // volume tab of the Sounds & Multimedia control panel. // // History: // 13 March 2000 RogerW // Created. // // Copyright (C) 2000 Microsoft Corporation All Rights Reserved. // // Microsoft Confidential // /////////////////////////////////////////////////////////////////////////////// //============================================================================= // Include files //============================================================================= #include #include #include #include #include #include "medhelp.h" #include "mmcpl.h" #include "multchan.h" #include "speakers.h" #include "dslevel.h" // Externals extern BOOL DeviceChange_GetHandle(DWORD dwMixerID, HANDLE *phDevice); extern HRESULT DSGetGuidFromName(LPTSTR szName, BOOL fRecord, LPGUID pGuid); extern HRESULT DSGetCplValues(GUID guid, BOOL fRecord, LPCPLDATA pData); // Globals UINT g_uiMCMixID = 0; HMIXER g_hMCMixer = NULL; UINT g_uiMCPageStringID = 0; UINT g_uiMCDescStringID = 0; LPVOID g_paPrevious = NULL; BOOL g_fInternalMCGenerated = FALSE; BOOL g_fMCChanged = FALSE; MIXERCONTROLDETAILS g_mcdMC; MIXERLINE g_mlMCDst; WNDPROC g_fnMCPSProc = NULL; UINT g_uiMCDevChange = 0; HWND g_hWndMC = NULL; static HDEVNOTIFY g_hMCDeviceEventContext= NULL; // Constants #define VOLUME_TICS (500) // VOLUME_TICS * VOLUME_MAX must be less than 0xFFFFFFFF #define VOLUME_MAX (0xFFFF) #define VOLUME_MIN (0) #define MC_SLIDER_COUNT (8) // Update Code & Dialog Template if change this value! static INTCODE aKeyWordIds[] = { IDC_MC_DESCRIPTION, NO_HELP, IDC_MC_ZERO_LOW, IDH_MC_ALL_SLIDERS, IDC_MC_ZERO, IDH_MC_ALL_SLIDERS, IDC_MC_ZERO_VOLUME, IDH_MC_ALL_SLIDERS, IDC_MC_ZERO_HIGH, IDH_MC_ALL_SLIDERS, IDC_MC_ONE_LOW, IDH_MC_ALL_SLIDERS, IDC_MC_ONE, IDH_MC_ALL_SLIDERS, IDC_MC_ONE_VOLUME, IDH_MC_ALL_SLIDERS, IDC_MC_ONE_HIGH, IDH_MC_ALL_SLIDERS, IDC_MC_TWO_LOW, IDH_MC_ALL_SLIDERS, IDC_MC_TWO, IDH_MC_ALL_SLIDERS, IDC_MC_TWO_VOLUME, IDH_MC_ALL_SLIDERS, IDC_MC_TWO_HIGH, IDH_MC_ALL_SLIDERS, IDC_MC_THREE_LOW, IDH_MC_ALL_SLIDERS, IDC_MC_THREE, IDH_MC_ALL_SLIDERS, IDC_MC_THREE_VOLUME, IDH_MC_ALL_SLIDERS, IDC_MC_THREE_HIGH, IDH_MC_ALL_SLIDERS, IDC_MC_FOUR_LOW, IDH_MC_ALL_SLIDERS, IDC_MC_FOUR, IDH_MC_ALL_SLIDERS, IDC_MC_FOUR_VOLUME, IDH_MC_ALL_SLIDERS, IDC_MC_FOUR_HIGH, IDH_MC_ALL_SLIDERS, IDC_MC_FIVE_LOW, IDH_MC_ALL_SLIDERS, IDC_MC_FIVE, IDH_MC_ALL_SLIDERS, IDC_MC_FIVE_VOLUME, IDH_MC_ALL_SLIDERS, IDC_MC_FIVE_HIGH, IDH_MC_ALL_SLIDERS, IDC_MC_SIX_LOW, IDH_MC_ALL_SLIDERS, IDC_MC_SIX, IDH_MC_ALL_SLIDERS, IDC_MC_SIX_VOLUME, IDH_MC_ALL_SLIDERS, IDC_MC_SIX_HIGH, IDH_MC_ALL_SLIDERS, IDC_MC_SEVEN_LOW, IDH_MC_ALL_SLIDERS, IDC_MC_SEVEN, IDH_MC_ALL_SLIDERS, IDC_MC_SEVEN_VOLUME, IDH_MC_ALL_SLIDERS, IDC_MC_SEVEN_HIGH, IDH_MC_ALL_SLIDERS, IDC_MC_MOVE_TOGETHER, IDH_MC_MOVE_TOGETHER, IDC_MC_RESTORE, IDH_MC_RESTORE, 0,0 }; /////////////////////////////////////////////////////////////////////////////// // // %%Function: MCTabProc // // Parameters: hDlg = window handle of dialog window. // uiMessage = message ID. // wParam = message-dependent. // lParam = message-dependent. // // Returns: TRUE if message has been processed, else FALSE // // Description: Dialog proc for multichannel control panel page device change // message. // // /////////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK MCTabProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { if (iMsg == g_uiMCDevChange) { InitMCVolume (g_hWndMC); } return CallWindowProc (g_fnMCPSProc, hwnd, iMsg, wParam, lParam); } /////////////////////////////////////////////////////////////////////////////// // // %%Function: MultichannelDlg // // Parameters: hDlg = window handle of dialog window. // uiMessage = message ID. // wParam = message-dependent. // lParam = message-dependent. // // Returns: TRUE if message has been processed, else FALSE // // Description: Dialog proc for multichannel volume control panel page. // // /////////////////////////////////////////////////////////////////////////////// INT_PTR CALLBACK MultichannelDlg (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_NOTIFY: { OnNotifyMC (hDlg, (LPNMHDR) lParam); } break; case WM_INITDIALOG: { HANDLE_WM_INITDIALOG (hDlg, wParam, lParam, OnInitDialogMC); } break; case WM_DESTROY: { HANDLE_WM_DESTROY (hDlg, wParam, lParam, OnDestroyMC); } break; case WM_COMMAND: { HANDLE_WM_COMMAND (hDlg, wParam, lParam, OnCommandMC); } break; case WM_HSCROLL: { HANDLE_WM_HSCROLL (hDlg, wParam, lParam, MCVolumeScroll); } break; case WM_POWERBROADCAST: { HandleMCPowerBroadcast (hDlg, wParam, lParam); } break; case MM_MIXM_LINE_CHANGE: case MM_MIXM_CONTROL_CHANGE: { if (!g_fInternalMCGenerated) { DisplayMCVolumeControl (hDlg); } g_fInternalMCGenerated = FALSE; } break; case WM_CONTEXTMENU: { WinHelp ((HWND)wParam, NULL, HELP_CONTEXTMENU, (UINT_PTR)(LPTSTR)aKeyWordIds); } break; case WM_HELP: { WinHelp (((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP , (UINT_PTR)(LPTSTR)aKeyWordIds); } break; case WM_DEVICECHANGE: { MCDeviceChange_Change (hDlg, wParam, lParam); } break; case WM_WININICHANGE: case WM_DISPLAYCHANGE : { int iLastSliderID = IDC_MC_ZERO_VOLUME + (MC_SLIDER_COUNT - 1) * 4; int indx = IDC_MC_ZERO_VOLUME; for (; indx <= iLastSliderID; indx += 4) SendDlgItemMessage (hDlg, indx, uMsg, wParam, lParam); } break; } return FALSE; } void InitMCVolume (HWND hDlg) { FreeMCMixer (); if (MMSYSERR_NOERROR == mixerOpen (&g_hMCMixer, g_uiMCMixID, (DWORD_PTR) hDlg, 0L, CALLBACK_WINDOW)) { if (SUCCEEDED (GetMCVolume ()) && g_paPrevious && g_mcdMC.paDetails) { // Copy data so can undo volume changes memcpy (g_paPrevious, g_mcdMC.paDetails, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlMCDst.cChannels); DisplayMCVolumeControl (hDlg); } MCDeviceChange_Init (hDlg, g_uiMCMixID); } } BOOL OnInitDialogMC (HWND hDlg, HWND hwndFocus, LPARAM lParam) { TCHAR szDescription [255]; LoadString (ghInstance, g_uiMCDescStringID, szDescription, sizeof (szDescription)/sizeof (TCHAR)); SetWindowText (GetDlgItem (hDlg, IDC_MC_DESCRIPTION), szDescription); // Init Globals g_fInternalMCGenerated = FALSE; g_fMCChanged = FALSE; g_hWndMC = hDlg; // Set Device Change Notification g_fnMCPSProc = (WNDPROC) SetWindowLongPtr (GetParent (hDlg), GWLP_WNDPROC, (LONG_PTR) MCTabProc); g_uiMCDevChange = RegisterWindowMessage (_T("winmm_devicechange")); // Init Volume InitMCVolume (hDlg); return FALSE; } void OnDestroyMC (HWND hDlg) { // Unregister from notifications MCDeviceChange_Cleanup (); SetWindowLongPtr (GetParent (hDlg), GWLP_WNDPROC, (LONG_PTR) g_fnMCPSProc); FreeAll (); } void OnNotifyMC (HWND hDlg, LPNMHDR pnmh) { if (!pnmh) { DPF ("bad WM_NOTIFY pointer\n"); return; } switch (pnmh->code) { case PSN_KILLACTIVE: FORWARD_WM_COMMAND (hDlg, IDOK, 0, 0, SendMessage); break; case PSN_APPLY: FORWARD_WM_COMMAND (hDlg, ID_APPLY, 0, 0, SendMessage); break; case PSN_RESET: FORWARD_WM_COMMAND (hDlg, IDCANCEL, 0, 0, SendMessage); break; } } BOOL PASCAL OnCommandMC (HWND hDlg, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDC_MC_RESTORE: { // Move all sliders to center UINT uiIndx; for (uiIndx = 0; uiIndx < g_mlMCDst.cChannels; uiIndx++) { ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + uiIndx) -> dwValue = VOLUME_MAX/2; } g_fInternalMCGenerated = FALSE; mixerSetControlDetails ((HMIXEROBJ) g_hMCMixer, &g_mcdMC, MIXER_SETCONTROLDETAILSF_VALUE); if (!g_fMCChanged) { g_fMCChanged = TRUE; PropSheet_Changed (GetParent (hDlg), hDlg); } } break; case ID_APPLY: { if (SUCCEEDED (GetMCVolume ()) && g_paPrevious && g_mcdMC.paDetails) { // Copy data so can undo volume changes memcpy (g_paPrevious, g_mcdMC.paDetails, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlMCDst.cChannels); DisplayMCVolumeControl (hDlg); } g_fMCChanged = FALSE; return TRUE; } break; case IDOK: { } break; case IDCANCEL: { if (g_paPrevious && g_mcdMC.paDetails) { // Undo volume changes memcpy (g_mcdMC.paDetails, g_paPrevious, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlMCDst.cChannels); g_fInternalMCGenerated = TRUE; mixerSetControlDetails ((HMIXEROBJ) g_hMCMixer, &g_mcdMC, MIXER_SETCONTROLDETAILSF_VALUE); } WinHelp (hDlg, gszWindowsHlp, HELP_QUIT, 0L); } break; } return FALSE; } void HandleMCPowerBroadcast (HWND hWnd, WPARAM wParam, LPARAM lParam) { switch (wParam) { case PBT_APMQUERYSUSPEND: { FreeMCMixer (); } break; case PBT_APMQUERYSUSPENDFAILED: case PBT_APMRESUMESUSPEND: { InitMCVolume (hWnd); } break; } } BOOL SliderIDtoChannel (UINT uiSliderID, DWORD* pdwChannel) { if (!pdwChannel) return FALSE; // Determine channel number (index) switch (uiSliderID) { case IDC_MC_ZERO_VOLUME: // Left *pdwChannel = 0; break; case IDC_MC_ONE_VOLUME: // Right *pdwChannel = 1; break; case IDC_MC_TWO_VOLUME: // Center *pdwChannel = 2; break; case IDC_MC_THREE_VOLUME: // Back Left *pdwChannel = 3; break; case IDC_MC_FOUR_VOLUME: // Back Right *pdwChannel = 4; break; case IDC_MC_FIVE_VOLUME: // Low Frequency *pdwChannel = 5; break; case IDC_MC_SIX_VOLUME: // Left of Center *pdwChannel = 6; break; case IDC_MC_SEVEN_VOLUME: // Right of Center *pdwChannel = 7; break; default: return FALSE; } return ((*pdwChannel) < g_mlMCDst.cChannels); } // Called in response to slider movement, computes new volume level and sets it // it also controls the apply state (changed or not) void MCVolumeScroll (HWND hwnd, HWND hwndCtl, UINT code, int pos) { BOOL fSet; BOOL fMoveTogether; DWORD dwChannel; DWORD dwSliderVol; DWORD dwNewMixerVol; DWORD dwOldMixerVol; DWORD dwVolume; fMoveTogether = IsDlgButtonChecked (hwnd, IDC_MC_MOVE_TOGETHER); if (SliderIDtoChannel (GetDlgCtrlID (hwndCtl), &dwChannel)) { // Set the new volume dwSliderVol = (DWORD) SendMessage (hwndCtl, TBM_GETPOS, 0, 0); dwNewMixerVol = (VOLUME_MAX * dwSliderVol + VOLUME_TICS / 2) / VOLUME_TICS; dwOldMixerVol = (g_paPrevious ? ((MIXERCONTROLDETAILS_UNSIGNED*)g_paPrevious + dwChannel) -> dwValue : 0); fSet = SetMCVolume (dwChannel, dwNewMixerVol, fMoveTogether); if (!fSet) { // Restore the correct thumb position. dwVolume = (VOLUME_TICS * ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + dwChannel) -> dwValue + VOLUME_MAX / 2) / VOLUME_MAX; SendMessage (hwndCtl, TBM_SETPOS, TRUE, dwVolume); } if ((fMoveTogether || dwOldMixerVol != dwNewMixerVol) && !g_fMCChanged) { g_fMCChanged = TRUE; PropSheet_Changed (GetParent (hwnd), hwnd); } } } // Sets the volume level // BOOL SetMCVolume (DWORD dwChannel, DWORD dwVol, BOOL fMoveTogether) { BOOL fReturn; UINT uiIndx; DWORD dwValue; long lMoveValue; long lMoveValueActual; fReturn = TRUE; if (dwChannel < g_mlMCDst.cChannels && g_mcdMC.paDetails && g_hMCMixer) { if (fMoveTogether) { // Note: Do not set g_fInternalMCGenerated = TRUE here because we are relying // on the change notification to update the other sliders. lMoveValue = (long)((double)dwVol - (double)(((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + dwChannel) -> dwValue)); lMoveValueActual = lMoveValue; // Don't bother if no move requested. if (lMoveValue == 0) return TRUE; // Already Set // Ensure that the new value is within the range of all the sliders that are // being used. This will ensure that we maintain the distance between sliders // as they are being moved. for (uiIndx = 0; uiIndx < g_mlMCDst.cChannels; uiIndx++) { dwValue = ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + uiIndx) -> dwValue; if (VOLUME_MIN > ((long)dwValue+lMoveValueActual)) { lMoveValueActual = VOLUME_MIN - dwValue; } else { if (VOLUME_MAX < ((long)dwValue+lMoveValueActual)) lMoveValueActual = VOLUME_MAX - dwValue; } } if (lMoveValueActual != 0) { // Update the values for (uiIndx = 0; uiIndx < g_mlMCDst.cChannels; uiIndx++) { dwValue = ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + uiIndx) -> dwValue; ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + uiIndx) -> dwValue = (DWORD)((long) dwValue + lMoveValueActual); } } else { // Let user know that they can move no farther in the current direction. // Note: We use the PC Speaker instead of the mixer because this is an // indicator that they are at either min or max volume for one of // the sliders. Since these are channel volume sliders, if we used // the mixer, the user would either not hear anything (min volume) // or get blown away (max volume). MessageBeep (-1 /*PC Speaker*/); fReturn = FALSE; } } else { g_fInternalMCGenerated = TRUE; ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + dwChannel) -> dwValue = dwVol; } mixerSetControlDetails ((HMIXEROBJ) g_hMCMixer, &g_mcdMC, MIXER_SETCONTROLDETAILSF_VALUE); } return fReturn; } void ShowAndEnableWindow (HWND hwnd, BOOL fEnable) { EnableWindow (hwnd, fEnable); ShowWindow (hwnd, fEnable ? SW_SHOW : SW_HIDE); } void DisplayMCVolumeControl (HWND hDlg) { HWND hwndVol = NULL; HWND hwndLabel = NULL; WCHAR szLabel[MAX_PATH]; BOOL fEnabled; UINT uiIndx; DWORD dwSpeakerType = TYPE_STEREODESKTOP; BOOL fPlayback = (MIXERLINE_COMPONENTTYPE_DST_SPEAKERS == g_mlMCDst.dwComponentType); ZeroMemory (szLabel, sizeof (szLabel)); // Get Speaker Configuration Type if (fPlayback) GetSpeakerType (&dwSpeakerType); for (uiIndx = 0; uiIndx < MC_SLIDER_COUNT; uiIndx++) { fEnabled = (uiIndx < g_mlMCDst.cChannels); // Set up the volume slider hwndVol = GetDlgItem (hDlg, IDC_MC_ZERO_VOLUME + uiIndx * 4); SendMessage (hwndVol, TBM_SETTICFREQ, VOLUME_TICS / 10, 0); SendMessage (hwndVol, TBM_SETRANGE, FALSE, MAKELONG (0, VOLUME_TICS)); // Enable/Disable sliders hwndLabel = GetDlgItem (hDlg, IDC_MC_ZERO + uiIndx * 4); ShowAndEnableWindow (hwndVol, fEnabled); ShowAndEnableWindow (hwndLabel, fEnabled); ShowAndEnableWindow (GetDlgItem (hDlg, IDC_MC_ZERO_LOW + uiIndx * 4), fEnabled); ShowAndEnableWindow (GetDlgItem (hDlg, IDC_MC_ZERO_HIGH + uiIndx * 4), fEnabled); if (fPlayback) { GetSpeakerLabel (dwSpeakerType, uiIndx, szLabel, sizeof(szLabel)/sizeof(TCHAR)); } else { LoadString (ghInstance, IDS_MC_CHANNEL_ZERO + uiIndx, szLabel, MAX_PATH); } SetWindowText (hwndLabel, szLabel); } if (0 < g_mlMCDst.cChannels) { UpdateMCVolumeSliders (hDlg); } } BOOL GetSpeakerType (DWORD* pdwSpeakerType) { BOOL fReturn = FALSE; if (pdwSpeakerType) { MIXERCAPS mc; *pdwSpeakerType = TYPE_STEREODESKTOP; // Init Value if (MMSYSERR_NOERROR == mixerGetDevCaps (g_uiMCMixID, &mc, sizeof (mc))) { GUID guid; if (SUCCEEDED (DSGetGuidFromName (mc.szPname, FALSE, &guid))) { CPLDATA cpldata; if (SUCCEEDED (DSGetCplValues (guid, FALSE, &cpldata))) { *pdwSpeakerType = cpldata.dwSpeakerType; fReturn = TRUE; } } } } return fReturn; } BOOL GetSpeakerLabel (DWORD dwSpeakerType, UINT uiSliderIndx, WCHAR* szLabel, int nSize) { if (uiSliderIndx >= MC_SLIDER_COUNT || !szLabel || nSize <= 0) // Invalid return FALSE; switch (dwSpeakerType) { // // Mono // case TYPE_NOSPEAKERS: case TYPE_MONOLAPTOP: if (0 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_CENTER, szLabel, nSize)); break; // // Stereo // case TYPE_HEADPHONES: case TYPE_STEREODESKTOP: case TYPE_STEREOLAPTOP: case TYPE_STEREOMONITOR: case TYPE_STEREOCPU: case TYPE_MOUNTEDSTEREO: case TYPE_STEREOKEYBOARD: // Left & Right Channel ... if (0 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_LEFT, szLabel, nSize)); else if (1 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_RIGHT, szLabel, nSize)); break; // // Greater than Stereo // case TYPE_SURROUND: // Left & Right Channel ... if (0 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_LEFT, szLabel, nSize)); else if (1 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_RIGHT, szLabel, nSize)); // Center Front & Back else if (2 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_CENTER, szLabel, nSize)); else if (3 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_BACKCENTER, szLabel, nSize)); break; case TYPE_QUADRAPHONIC: // Left & Right Channel ... if (0 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_LEFT, szLabel, nSize)); else if (1 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_RIGHT, szLabel, nSize)); // Back Left & Back Right Channel ... else if (2 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_BACKLEFT, szLabel, nSize)); else if (3 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_BACKRIGHT, szLabel, nSize)); break; case TYPE_SURROUND_5_1: case TYPE_SURROUND_7_1: // Left & Right Channel ... if (0 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_LEFT, szLabel, nSize)); else if (1 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_RIGHT, szLabel, nSize)); // Center Channel ... if (2 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_CENTER, szLabel, nSize)); // Low Frequency ... if (3 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_LOWFREQUENCY, szLabel, nSize)); // Back Left & Back Right Channel ... if (4 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_BACKLEFT, szLabel, nSize)); else if (5 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_BACKRIGHT, szLabel, nSize)); if (TYPE_SURROUND_5_1 == dwSpeakerType) break; // Left of Center & Right of Center Channel ... if (6 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_LEFT_OF_CENTER, szLabel, nSize)); else if (7 == uiSliderIndx) return (LoadString (ghInstance, IDS_MC_SPEAKER_RIGHT_OF_CENTER, szLabel, nSize)); break; } // If we are here, we don't know the speaker type or we have too many // channels for a known type, just use the generic channel text ... return (LoadString (ghInstance, IDS_MC_CHANNEL_ZERO + uiSliderIndx, szLabel, nSize)); } // Called to update the slider when the volume is changed externally // void UpdateMCVolumeSliders (HWND hDlg) { if (g_hMCMixer && g_mcdMC.paDetails && SUCCEEDED (GetMCVolume ())) { UINT uiIndx; DWORD dwVolume; MIXERCONTROLDETAILS_UNSIGNED mcuVolume; for (uiIndx = 0; uiIndx < g_mlMCDst.cChannels; uiIndx++) { mcuVolume = *((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + uiIndx); dwVolume = (VOLUME_TICS * mcuVolume.dwValue + VOLUME_MAX / 2) / VOLUME_MAX; SendMessage (GetDlgItem (hDlg, IDC_MC_ZERO_VOLUME + uiIndx * 4), TBM_SETPOS, TRUE, dwVolume); } } } void FreeAll () { FreeMCMixer (); if (g_mcdMC.paDetails) { LocalFree (g_mcdMC.paDetails); g_mcdMC.paDetails = NULL; } if (g_paPrevious) { LocalFree (g_paPrevious); g_paPrevious = NULL; } ZeroMemory (&g_mcdMC, sizeof (g_mcdMC)); ZeroMemory (&g_mlMCDst, sizeof (g_mlMCDst)); } void FreeMCMixer () { if (g_hMCMixer) { mixerClose (g_hMCMixer); g_hMCMixer = NULL; } } HRESULT SetDevice (UINT uiMixID, DWORD dwDest, DWORD dwVolID) { HMIXER hMixer = NULL; HRESULT hr = E_FAIL; // Free any current mixer stuff FreeAll (); if (MMSYSERR_NOERROR == mixerOpen (&hMixer, uiMixID, 0, 0, MIXER_OBJECTF_MIXER)) { g_mlMCDst.cbStruct = sizeof (g_mlMCDst); g_mlMCDst.dwDestination = dwDest; if (MMSYSERR_NOERROR == mixerGetLineInfo ((HMIXEROBJ) hMixer, &g_mlMCDst, MIXER_GETLINEINFOF_DESTINATION)) { g_mcdMC.cbStruct = sizeof (g_mcdMC); g_mcdMC.dwControlID = dwVolID; g_mcdMC.cChannels = g_mlMCDst.cChannels; g_mcdMC.hwndOwner = 0; g_mcdMC.cMultipleItems = 0; g_mcdMC.cbDetails = sizeof (DWORD); // seems like it would be sizeof(g_mcd), // but actually, it is the size of a single value // and is multiplied by channel in the driver. g_mcdMC.paDetails = (MIXERCONTROLDETAILS_UNSIGNED*) LocalAlloc (LPTR, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlMCDst.cChannels); g_paPrevious = (MIXERCONTROLDETAILS_UNSIGNED*) LocalAlloc (LPTR, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlMCDst.cChannels); if (g_mcdMC.paDetails && g_paPrevious) { hr = S_OK; // Init our other globals g_uiMCMixID = uiMixID; switch (g_mlMCDst.dwComponentType) { case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS: g_uiMCPageStringID = IDS_MC_PLAYBACK; g_uiMCDescStringID = IDS_MC_PLAYBACK_DESC; break; case MIXERLINE_COMPONENTTYPE_DST_WAVEIN: case MIXERLINE_COMPONENTTYPE_DST_VOICEIN: g_uiMCPageStringID = IDS_MC_RECORDING; g_uiMCDescStringID = IDS_MC_RECORDING_DESC; break; default: g_uiMCPageStringID = IDS_MC_OTHER; g_uiMCDescStringID = IDS_MC_OTHER_DESC; break; } } } mixerClose (hMixer); } return hr; } HRESULT GetMCVolume () { HRESULT hr = E_FAIL; if (g_hMCMixer && g_mcdMC.paDetails) { ZeroMemory (g_mcdMC.paDetails, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlMCDst.cChannels); hr = mixerGetControlDetails ((HMIXEROBJ)g_hMCMixer, &g_mcdMC, MIXER_GETCONTROLDETAILSF_VALUE); } return hr; } UINT GetPageStringID () { return g_uiMCPageStringID; } void MCDeviceChange_Cleanup () { if (g_hMCDeviceEventContext) { UnregisterDeviceNotification (g_hMCDeviceEventContext); g_hMCDeviceEventContext = NULL; } } void MCDeviceChange_Init (HWND hWnd, DWORD dwMixerID) { DEV_BROADCAST_HANDLE DevBrodHandle; HANDLE hMixerDevice=NULL; //If we had registered already for device notifications, unregister ourselves. MCDeviceChange_Cleanup(); //If we get the device handle register for device notifications on it. if(DeviceChange_GetHandle(dwMixerID, &hMixerDevice)) { memset(&DevBrodHandle, 0, sizeof(DEV_BROADCAST_HANDLE)); DevBrodHandle.dbch_size = sizeof(DEV_BROADCAST_HANDLE); DevBrodHandle.dbch_devicetype = DBT_DEVTYP_HANDLE; DevBrodHandle.dbch_handle = hMixerDevice; g_hMCDeviceEventContext = RegisterDeviceNotification(hWnd, &DevBrodHandle, DEVICE_NOTIFY_WINDOW_HANDLE); if(hMixerDevice) { CloseHandle(hMixerDevice); hMixerDevice = NULL; } } } // Handle the case where we need to dump mixer handle so PnP can get rid of a device // We assume we will get the WINMM_DEVICECHANGE handle when the dust settles after a remove or add // except for DEVICEQUERYREMOVEFAILED which will not generate that message. // void MCDeviceChange_Change (HWND hDlg, WPARAM wParam, LPARAM lParam) { PDEV_BROADCAST_HANDLE bh = (PDEV_BROADCAST_HANDLE)lParam; if(!g_hMCDeviceEventContext || !bh || bh->dbch_devicetype != DBT_DEVTYP_HANDLE) { return; } switch (wParam) { case DBT_DEVICEQUERYREMOVE: // Must free up Mixer if they are trying to remove the device { FreeMCMixer (); } break; case DBT_DEVICEQUERYREMOVEFAILED: // Didn't happen, need to re-acquire mixer { InitMCVolume (hDlg); } break; } }