|
|
///////////////////////////////////////////////////////////////////////////////
//
// 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 <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <regstr.h>
#include <dbt.h>
#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; } }
|