|
|
/*****************************************************************************
* * Component: sndvol32.exe * File: mixer.c * Purpose: mixer api specific implementations * * Copyright (c) 1985-1999 Microsoft Corporation * *****************************************************************************/ #include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <commctrl.h>
#include <math.h>
#include "volumei.h"
#include "volids.h"
#include "vu.h"
#define STRSAFE_LIB
#include <strsafe.h>
extern void Mixer_Multichannel(PMIXUIDIALOG pmxud, DWORD dwVolumeID); extern void Mixer_Advanced(PMIXUIDIALOG pmxud, DWORD dwLineID, LPTSTR szName); extern HRESULT GetDestination(DWORD mxid, int *piDest); extern BOOL DeviceChange_Init(HWND hWnd, DWORD dwMixerID);
/*****************************************************************************
* * INIT SPECIFIC CODE * *****************************************************************************/
/*
* Mixer_GetNumDevs * * */ int Mixer_GetNumDevs() { return mixerGetNumDevs(); }
/*
* Mixer_GetDeviceName() * * */ BOOL Mixer_GetDeviceName( PMIXUIDIALOG pmxud) { MIXERCAPS mxcaps; MMRESULT mmr;
mmr = mixerGetDevCaps( pmxud->mxid, &mxcaps, sizeof(mxcaps)); if( mmr == MMSYSERR_NOERROR ) { HRESULT hr; hr = StringCchCopy( pmxud->szMixer, SIZEOF(pmxud->szMixer), mxcaps.szPname ); if( hr == S_OK ) { return TRUE; } }
pmxud->szMixer[0] = TEXT('\0'); return FALSE; }
BOOL Mixer_AreChannelsAtMinimum(MIXERCONTROLDETAILS_UNSIGNED* pmcdVolume, DWORD cChannels) { UINT uiIndx; if (pmcdVolume && cChannels > 0) { for (uiIndx = 0; uiIndx < cChannels; uiIndx++) { if ((pmcdVolume + uiIndx) -> dwValue != 0) { return (FALSE); } } return (TRUE); // Volume of all channels equals zero since we haven't returned yet.
} else return (FALSE);
}
void Mixer_RefreshMixCache (PVOLCTRLDESC pvcd, MIXERCONTROLDETAILS_UNSIGNED* pmcdVolume, DWORD cChannels) {
if (pmcdVolume && cChannels > 0) {
// Create cache if necessary
if (!pvcd->pdblCacheMix) pvcd->pdblCacheMix = (double*) GlobalAllocPtr(GHND, sizeof (double) * cChannels);
// Refresh cache
if (pvcd->pdblCacheMix) {
UINT uiIndx; double* pdblMixPercent; DWORD dwVolume;
// Get the maximum volume
DWORD dwMaxVol = 0; for (uiIndx = 0; uiIndx < cChannels; uiIndx++) dwMaxVol = max (dwMaxVol, (pmcdVolume + uiIndx) -> dwValue);
// 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 = (pmcdVolume + uiIndx) -> dwValue; pdblMixPercent = (pvcd->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); } } } } }
/*
* Mixer_SetLines * * Locate mixer/mux relationships. Fix up uninitialized volume description * information. * * */ static void Mixer_SetLines( HMIXEROBJ hmx, PVOLCTRLDESC pvcd, UINT cPvcd) { LPMIXERCONTROLDETAILS_LISTTEXT pmcd_lt; PMIXERCONTROLDETAILS_BOOLEAN pmcd_b; MIXERCONTROLDETAILS mxd; MMRESULT mmr; UINT i,j; MIXERLINE mxl; DWORD dwDst;
//
// Another test for drivers. Some drivers (Mediavision)
// don't return the proper destination / source index in the
// mixerGetLineInfo call. Tag a workaround.
//
mxl.cbStruct = sizeof(mxl); mxl.dwLineID = pvcd[0].dwLineID;
mmr = mixerGetLineInfo(hmx , &mxl , MIXER_GETLINEINFOF_LINEID);
if (mmr == MMSYSERR_NOERROR) { dwDst = mxl.dwDestination; for (i = 1; i < cPvcd; i++) { mxl.cbStruct = sizeof(mxl); mxl.dwLineID = pvcd[i].dwLineID;
mmr = mixerGetLineInfo(hmx , &mxl , MIXER_GETLINEINFOF_LINEID); if (mmr != MMSYSERR_NOERROR) { pvcd[0].dwSupport |= VCD_SUPPORTF_BADDRIVER; break; } if (mxl.dwDestination != dwDst) { pvcd[0].dwSupport |= VCD_SUPPORTF_BADDRIVER; break; } } }
//
// for the first pvcd (destination), propogate the mixer/mux control
// id's to those controls that are part of the list. 0 out the rest.
// The UI can just do a mixerXXXControlDetails on the control ID to
// locate the state information
//
if (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MIXER) { pmcd_lt = GlobalAllocPtr(GHND, sizeof(MIXERCONTROLDETAILS_LISTTEXT) * pvcd->cMixer); pmcd_b = GlobalAllocPtr(GHND, sizeof(MIXERCONTROLDETAILS_BOOLEAN) * pvcd->cMixer);
if (!pmcd_lt || !pmcd_b) return;
mxd.cbStruct = sizeof(mxd); mxd.dwControlID = pvcd->dwMixerID; mxd.cChannels = 1; mxd.cMultipleItems = pvcd->cMixer; mxd.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT); mxd.paDetails = pmcd_lt; mmr = mixerGetControlDetails(hmx , &mxd , MIXER_GETCONTROLDETAILSF_LISTTEXT);
if (mmr == MMSYSERR_NOERROR) { //
// iterate over all source lines s.t. dwMixerID points to the
// correct control id on the destination and iMixer is the
// correct index into the value list
//
pvcd[0].amcd_bMixer = pmcd_b; for (i = 1; i < cPvcd; i++) { for (j = 0; j < pvcd->cMixer; j++) { if (!lstrcmp(pmcd_lt[j].szName,pvcd[i].szName)) { pvcd[i].dwMixerID = pvcd->dwMixerID; pvcd[i].iMixer = j; pvcd[i].cMixer = pvcd->cMixer; pvcd[i].dwSupport |= VCD_SUPPORTF_MIXER_MIXER; pvcd[i].dwVisible |= VCD_VISIBLEF_MIXER_MIXER; pvcd[i].dwVisible &= ~VCD_VISIBLEF_MIXER_MUTE; pvcd[i].amcd_bMixer = pmcd_b; } } } } GlobalFreePtr(pmcd_lt); }
if (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUX) { pmcd_lt = GlobalAllocPtr(GHND, sizeof(MIXERCONTROLDETAILS_LISTTEXT) * pvcd->cMux); pmcd_b = GlobalAllocPtr(GHND, sizeof(MIXERCONTROLDETAILS_BOOLEAN) * pvcd->cMux);
if (!pmcd_lt || !pmcd_b) return;
mxd.cbStruct = sizeof(mxd); mxd.dwControlID = pvcd->dwMuxID; mxd.cChannels = 1; mxd.cMultipleItems = pvcd->cMux; mxd.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT); mxd.paDetails = pmcd_lt;
mmr = mixerGetControlDetails(hmx , &mxd , MIXER_GETCONTROLDETAILSF_LISTTEXT);
if (mmr == MMSYSERR_NOERROR) { //
// iterate over all source lines s.t. dwMuxID points to the
// correct control id on the destination and iMux is the
// correct index into the value list
//
pvcd[0].amcd_bMux = pmcd_b; for (i = 1; i < cPvcd; i++) { for (j = 0; j < pvcd->cMux; j++) { if (!lstrcmp(pmcd_lt[j].szName,pvcd[i].szName)) { pvcd[i].dwMuxID = pvcd->dwMuxID; pvcd[i].iMux = j; pvcd[i].cMux = pvcd->cMux; pvcd[i].dwSupport |= VCD_SUPPORTF_MIXER_MUX; pvcd[i].dwVisible |= VCD_VISIBLEF_MIXER_MUX; pvcd[i].dwVisible &= ~VCD_VISIBLEF_MIXER_MUTE; pvcd[i].amcd_bMux = pmcd_b; } } } } GlobalFreePtr(pmcd_lt); } }
/*
* Mixer_CheckdDriver * * Consistency check for bad mixer drivers. * */ static DWORD Mixer_CheckBadDriver( HMIXEROBJ hmx, PMIXERLINECONTROLS pmxlc, PMIXERCONTROL pmxc, DWORD dwControlID, DWORD dwLineID) { MMRESULT mmr;
pmxlc->cbStruct = sizeof(MIXERLINECONTROLS); pmxlc->dwControlID = dwControlID; pmxlc->cControls = 1; pmxlc->cbmxctrl = sizeof(MIXERCONTROL); pmxlc->pamxctrl = pmxc;
mmr = mixerGetLineControls(hmx , pmxlc , MIXER_GETLINECONTROLSF_ONEBYID);
if (mmr != MMSYSERR_NOERROR) return VCD_SUPPORTF_BADDRIVER;
if (pmxlc->dwLineID != dwLineID) return VCD_SUPPORTF_BADDRIVER;
return 0L; }
/*
* IsDestinationMux * * Helper function to determine if a source line has a mux on its associated * destination line * * */ BOOL IsDestinationMux( HMIXEROBJ hmx, DWORD dwLineID ) { MIXERLINE mxl; MIXERLINECONTROLS mxlc; MIXERCONTROL mxc; MMRESULT mmr;
mxl.cbStruct = sizeof(mxl); mxl.dwLineID = dwLineID;
// Get the destination number for this line
mmr = mixerGetLineInfo(hmx , &mxl , MIXER_GETLINEINFOF_LINEID); if (mmr != MMSYSERR_NOERROR) { return FALSE; }
//
// Get the LineId for this destination number
//
// mxl.dwDestination will been filled in by the last
// call to mixerGetLineInfo
//
mmr = mixerGetLineInfo(hmx , &mxl , MIXER_GETLINEINFOF_DESTINATION); if (mmr != MMSYSERR_NOERROR) { return FALSE; }
mxlc.cbStruct = sizeof(mxlc); mxlc.dwLineID = mxl.dwLineID; // use the dwLineId obtained from mixerGetLinInfo
mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof(mxc); mxlc.pamxctrl = &mxc;
mmr = mixerGetLineControls(hmx , &mxlc , MIXER_GETLINECONTROLSF_ONEBYTYPE); if (mmr == MMSYSERR_NOERROR) { return TRUE; }
return FALSE; }
/*
* Mixer_InitLineControls * * Initialize the mixer api specific part of the volume control description * mark hidden lines. * */ static void Mixer_InitLineControls( HMIXEROBJ hmx, PVOLCTRLDESC pvcd, DWORD dwLineID) { MIXERLINECONTROLS mxlc; MIXERCONTROL mxc; MMRESULT mmr; int iType;
const DWORD dwAdvTypes[] = { MIXERCONTROL_CONTROLTYPE_BOOLEAN, MIXERCONTROL_CONTROLTYPE_ONOFF, MIXERCONTROL_CONTROLTYPE_MONO, MIXERCONTROL_CONTROLTYPE_LOUDNESS, MIXERCONTROL_CONTROLTYPE_STEREOENH, MIXERCONTROL_CONTROLTYPE_BASS, MIXERCONTROL_CONTROLTYPE_TREBLE };
pvcd->dwLineID = dwLineID; pvcd->dwMeterID = 0; pvcd->dwVolumeID = 0; pvcd->dwMuteID = 0; pvcd->dwMixerID = 0; pvcd->dwMuxID = 0;
//
// advanced controls
//
for (iType = 0; iType < SIZEOF(dwAdvTypes); iType++) { mxlc.cbStruct = sizeof(mxlc); mxlc.dwLineID = dwLineID; mxlc.dwControlType = dwAdvTypes[iType]; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof(mxc); mxlc.pamxctrl = &mxc;
mmr = mixerGetLineControls(hmx , &mxlc , MIXER_GETLINECONTROLSF_ONEBYTYPE); if (mmr == MMSYSERR_NOERROR) { pvcd->dwSupport |= VCD_SUPPORTF_MIXER_ADVANCED; break; } }
//
// stock controls
//
//
// peakmeter
//
mxlc.cbStruct = sizeof(mxlc); mxlc.dwLineID = dwLineID; mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_PEAKMETER; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof(mxc); mxlc.pamxctrl = &mxc;
mmr = mixerGetLineControls(hmx , &mxlc , MIXER_GETLINECONTROLSF_ONEBYTYPE); if (mmr == MMSYSERR_NOERROR) { pvcd->dwMeterID = mxc.dwControlID; pvcd->dwSupport |= VCD_SUPPORTF_MIXER_METER; pvcd->dwSupport |= Mixer_CheckBadDriver(hmx , &mxlc , &mxc , mxc.dwControlID , dwLineID); }
//
// mute
//
mxlc.cbStruct = sizeof(mxlc); mxlc.dwLineID = dwLineID; mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof(mxc); mxlc.pamxctrl = &mxc;
mmr = mixerGetLineControls(hmx , &mxlc , MIXER_GETLINECONTROLSF_ONEBYTYPE); if (mmr == MMSYSERR_NOERROR) { pvcd->fdwMuteControl = mxc.fdwControl; pvcd->dwMuteID = mxc.dwControlID; pvcd->dwSupport |= VCD_SUPPORTF_MIXER_MUTE; pvcd->dwVisible |= VCD_VISIBLEF_MIXER_MUTE;
pvcd->dwSupport |= Mixer_CheckBadDriver(hmx , &mxlc , &mxc , mxc.dwControlID , dwLineID); }
//
// volume
//
mxlc.cbStruct = sizeof(mxlc); mxlc.dwLineID = dwLineID; mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof(mxc); mxlc.pamxctrl = &mxc;
mmr = mixerGetLineControls(hmx , &mxlc , MIXER_GETLINECONTROLSF_ONEBYTYPE); if (mmr == MMSYSERR_NOERROR) { pvcd->fdwVolumeControl = mxc.fdwControl; pvcd->dwVolumeID = mxc.dwControlID; pvcd->dwSupport |= VCD_SUPPORTF_MIXER_VOLUME; pvcd->dwSupport |= Mixer_CheckBadDriver(hmx , &mxlc , &mxc , mxc.dwControlID , dwLineID); }
//
// mixer
//
mxlc.cbStruct = sizeof(mxlc); mxlc.dwLineID = dwLineID; mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof(mxc); mxlc.pamxctrl = &mxc;
mmr = mixerGetLineControls(hmx , &mxlc , MIXER_GETLINECONTROLSF_ONEBYTYPE); if (mmr == MMSYSERR_NOERROR) { pvcd->dwMixerID = mxc.dwControlID; pvcd->cMixer = mxc.cMultipleItems; pvcd->dwSupport |= VCD_SUPPORTF_MIXER_MIXER; pvcd->dwSupport |= Mixer_CheckBadDriver(hmx , &mxlc , &mxc , mxc.dwControlID , dwLineID); }
//
// mux
//
mxlc.cbStruct = sizeof(mxlc); mxlc.dwLineID = dwLineID; mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof(mxc); mxlc.pamxctrl = &mxc;
mmr = mixerGetLineControls(hmx , &mxlc , MIXER_GETLINECONTROLSF_ONEBYTYPE); if (mmr == MMSYSERR_NOERROR) { pvcd->dwMuxID = mxc.dwControlID; pvcd->cMux = mxc.cMultipleItems; pvcd->dwSupport |= VCD_SUPPORTF_MIXER_MUX; pvcd->dwSupport |= Mixer_CheckBadDriver(hmx , &mxlc , &mxc , mxc.dwControlID , dwLineID); } if (!(pvcd->dwSupport & ( VCD_SUPPORTF_MIXER_MUTE | VCD_SUPPORTF_MIXER_METER | VCD_SUPPORTF_MIXER_VOLUME))) { if (IsDestinationMux(hmx, dwLineID) && !(pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUX)) { //
// Visible, and not hidden
//
pvcd->dwSupport |= VCD_SUPPORTF_VISIBLE; pvcd->dwSupport &= ~VCD_SUPPORTF_DEFAULT; } else { //
// make it invisible in the UI.
//
pvcd->dwSupport |= VCD_SUPPORTF_HIDDEN; } } else { //
// Visible, and not hidden
//
pvcd->dwSupport |= VCD_SUPPORTF_VISIBLE; }
}
/*
* Mixer_CreateVolumeDescription * * */ PVOLCTRLDESC Mixer_CreateVolumeDescription( HMIXEROBJ hmx, int iDest, DWORD* pcvd ) { MMRESULT mmr; PVOLCTRLDESC pvcdPrev = NULL, pvcd = NULL; MIXERLINE mlDst; DWORD cLines = 0; DWORD dwSupport = 0L; UINT iSrc; int newDest=0;
ZeroMemory(&mlDst, sizeof(mlDst));
mlDst.cbStruct = sizeof(mlDst); mlDst.dwDestination = iDest;
mmr = mixerGetLineInfo(hmx , &mlDst , MIXER_GETLINEINFOF_DESTINATION);
if(!mlDst.cConnections) { //No lines to list. Try with a different mixer ID.
GetDestination(0, &newDest); mlDst.dwDestination = newDest;
mmr = mixerGetLineInfo(hmx , &mlDst , MIXER_GETLINEINFOF_DESTINATION);
//Even if we do not get any connections here lets continue. Nothing more we can do.
//This will be taken care of before opening the dialog.
}
if (mmr == MMSYSERR_NOERROR) { if (mlDst.cChannels == 1L) dwSupport |= VCD_SUPPORTF_MONO;
if (mlDst.fdwLine & MIXERLINE_LINEF_DISCONNECTED) dwSupport |= VCD_SUPPORTF_DISABLED;
//
// a default type
//
dwSupport |= VCD_SUPPORTF_DEFAULT; } else { //
// we need to add it anyway s.t. a UI comes up
//
dwSupport = VCD_SUPPORTF_DISABLED; }
pvcd = PVCD_AddLine(NULL , iDest , VCD_TYPE_MIXER , mlDst.szShortName , mlDst.szName , dwSupport , &cLines );
if (!pvcd) return NULL;
Mixer_InitLineControls( hmx, pvcd, mlDst.dwLineID );
pvcdPrev = pvcd;
for (iSrc = 0; iSrc < mlDst.cConnections; iSrc++) { MIXERLINE mlSrc;
mlSrc.cbStruct = sizeof(mlSrc); mlSrc.dwDestination = iDest; mlSrc.dwSource = iSrc;
mmr = mixerGetLineInfo(hmx , &mlSrc , MIXER_GETLINEINFOF_SOURCE); dwSupport = 0L;
if (mmr == MMSYSERR_NOERROR) { if (mlSrc.cChannels == 1L) { dwSupport |= VCD_SUPPORTF_MONO; }
if (mlSrc.fdwLine & MIXERLINE_LINEF_DISCONNECTED) dwSupport |= VCD_SUPPORTF_DISABLED;
//
// Mark these types as "default" just to lessen the shock on
// some advanced sound cards.
//
if (mlDst.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS || mlDst.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_HEADPHONES) { switch (mlSrc.dwComponentType) { case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT: case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC: case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER: case MIXERLINE_COMPONENTTYPE_SRC_LINE: dwSupport |= VCD_SUPPORTF_DEFAULT; break; } } else if (mlDst.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN || mlDst.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_VOICEIN) { switch (mlSrc.dwComponentType) { case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE: case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC: case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER: case MIXERLINE_COMPONENTTYPE_SRC_LINE: case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED: dwSupport |= VCD_SUPPORTF_DEFAULT; break; } } } else { //
// we need to add it anyway s.t. lookups aren't under counted
//
dwSupport = VCD_SUPPORTF_DISABLED; } pvcd = PVCD_AddLine(pvcdPrev , iDest , VCD_TYPE_MIXER , mlSrc.szShortName , mlSrc.szName , dwSupport , &cLines ); if (pvcd) { Mixer_InitLineControls( hmx, &pvcd[cLines-1], mlSrc.dwLineID ); pvcdPrev = pvcd; } }
//
// Fixup dependencies
//
Mixer_SetLines(hmx, pvcdPrev, cLines);
*pcvd = cLines; return pvcdPrev; }
/*
* Mixer_IsValidRecordingDestination * * */ BOOL Mixer_IsValidRecordingDestination (HMIXEROBJ hmx, MIXERLINE* pmlDst) {
BOOL fReturn = FALSE;
if (pmlDst && MIXERLINE_COMPONENTTYPE_DST_WAVEIN == pmlDst -> dwComponentType) {
UINT uiSrc; MIXERLINE mlSrc;
for (uiSrc = 0; uiSrc < pmlDst -> cConnections; uiSrc++) {
mlSrc.cbStruct = sizeof (mlSrc); mlSrc.dwDestination = pmlDst -> dwDestination; mlSrc.dwSource = uiSrc;
if (SUCCEEDED (mixerGetLineInfo (hmx, &mlSrc, MIXER_GETLINEINFOF_SOURCE))) { switch (mlSrc.dwComponentType) { case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE: case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC: case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER: case MIXERLINE_COMPONENTTYPE_SRC_LINE: case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED: fReturn = TRUE; break; } } } }
return fReturn;
}
/*
* Mixer_CleanupVolumeDescription * * */ void Mixer_CleanupVolumeDescription( PVOLCTRLDESC avcd, DWORD cvcd) { if (cvcd == 0) return;
if (avcd[0].pdblCacheMix) { GlobalFreePtr(avcd[0].pdblCacheMix); }
if (avcd[0].dwSupport & VCD_SUPPORTF_MIXER_MIXER) { if (avcd[0].amcd_bMixer) GlobalFreePtr(avcd[0].amcd_bMixer); }
if (avcd[0].dwSupport & VCD_SUPPORTF_MIXER_MUX) { if (avcd[0].amcd_bMux) GlobalFreePtr(avcd[0].amcd_bMux); }
} /*****************************************************************************
* * ACTIVE GET/SET CODE * *****************************************************************************/
static MMRESULT Mixer_GetMixerLineInfo( HMIXEROBJ hmx, // handle to mixer
LPMIXERLINE pml, // Returns destination info
DWORD dwLineID //
) { if (!pml || !hmx) return MMSYSERR_INVALPARAM;
// Get mixerline info
ZeroMemory( pml, sizeof(*pml) ); pml->cbStruct = sizeof(*pml); pml->dwLineID = dwLineID;
return (mixerGetLineInfo (hmx, pml, MIXER_GETLINEINFOF_LINEID)); }
/*
* Mixer_GetMixerVolume * * */ static MMRESULT Mixer_GetMixerVolume( PMIXUIDIALOG pmxud, // app instance
PVOLCTRLDESC pvcd, // volume to change
MIXERCONTROLDETAILS_UNSIGNED* pmcdVolume, // array for volume levels
LPDWORD lpSize // size of array (or return size needed)
) {
MMRESULT mmr; MIXERLINE ml; MIXERCONTROLDETAILS mxcd; DWORD cChannels;
if (!lpSize || !pmxud) return MMSYSERR_INVALPARAM;
// Get mixerline info
if (pvcd->fdwVolumeControl & MIXERCONTROL_CONTROLF_UNIFORM) { cChannels = 1; } else { mmr = Mixer_GetMixerLineInfo ((HMIXEROBJ)(pmxud->hmx), &ml, pvcd->dwLineID); if (MMSYSERR_NOERROR != mmr) { return mmr; } cChannels = ml.cChannels; }
if (!pmcdVolume) { // Just return size needed
*lpSize = cChannels * sizeof (MIXERCONTROLDETAILS_UNSIGNED); return MMSYSERR_NOERROR; } else { // Verify passed array size
if (*lpSize < cChannels * sizeof (MIXERCONTROLDETAILS_UNSIGNED)) return MMSYSERR_INVALPARAM; }
// Get volume levels
ZeroMemory (&mxcd, sizeof (mxcd)); mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pvcd->dwVolumeID; mxcd.cChannels = cChannels; mxcd.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED); mxcd.paDetails = (LPVOID) pmcdVolume;
mmr = mixerGetControlDetails((HMIXEROBJ)(pmxud->hmx), &mxcd, MIXER_OBJECTF_HANDLE | MIXER_GETCONTROLDETAILSF_VALUE); return mmr;
}
static MMRESULT Mixer_Mute( HMIXEROBJ hmx, PVOLCTRLDESC pvcd, PMIXERCONTROLDETAILS pmxcd, DWORD fMute) { MIXERLINE ml; DWORD cChannels; DWORD dwSize; LPDWORD lpdwCurrent; UINT uiIndx; MMRESULT mmr;
// Check the parameters
if (!hmx || !pvcd || !pmxcd) return MMSYSERR_INVALPARAM;
// Get mixerline info
if (pvcd->fdwMuteControl & MIXERCONTROL_CONTROLF_UNIFORM) { cChannels = 1; } else { mmr = Mixer_GetMixerLineInfo(hmx, &ml, pvcd->dwLineID); if (MMSYSERR_NOERROR != mmr) { return mmr; } cChannels = ml.cChannels; }
dwSize = (DWORD)(cChannels * sizeof(DWORD));
pmxcd->paDetails = LocalAlloc (LPTR, dwSize); if (!pmxcd->paDetails) return MMSYSERR_NOMEM;
for (uiIndx = 0; uiIndx < cChannels; uiIndx++) { lpdwCurrent = ((LPDWORD)pmxcd->paDetails + uiIndx); *lpdwCurrent = fMute; }
pmxcd->cbStruct = sizeof(*pmxcd); pmxcd->dwControlID = pvcd->dwMuteID ; pmxcd->cChannels = cChannels; pmxcd->cMultipleItems = 0; pmxcd->cbDetails = sizeof(DWORD);
mmr = mixerSetControlDetails(hmx , pmxcd , MIXER_SETCONTROLDETAILSF_VALUE);
LocalFree (pmxcd->paDetails); return mmr; }
/*
* Mixer_GetControl * * Change a UI control in response to a device or initialization event * * */
void Mixer_GetControl( PMIXUIDIALOG pmxud, HWND hctl, int imxul, int itype) { PMIXUILINE pmxul = &pmxud->amxul[imxul]; PVOLCTRLDESC pvcd = pmxul->pvcd; DWORD dwID = 0L; BOOL fSet = FALSE;
switch (itype) { case MIXUI_VUMETER: fSet = (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_METER); if (fSet) dwID = pmxul->pvcd->dwMeterID; break;
case MIXUI_SWITCH: fSet = (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUTE) && (pmxul->pvcd->dwVisible & VCD_VISIBLEF_MIXER_MUTE); if (fSet) { dwID = pmxul->pvcd->dwMuteID; break; }
fSet = (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUX) && (pmxul->pvcd->dwVisible & VCD_VISIBLEF_MIXER_MUX); if (fSet) { dwID = pmxul->pvcd->dwMuxID; break; }
fSet = (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_MIXER) && (pmxul->pvcd->dwVisible & VCD_VISIBLEF_MIXER_MIXER); if (fSet) { dwID = pmxul->pvcd->dwMixerID; break; } break;
case MIXUI_VOLUME: case MIXUI_BALANCE: fSet = (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_VOLUME); if (fSet) dwID = pmxul->pvcd->dwVolumeID; break;
default: return; } if (fSet) Mixer_GetControlFromID(pmxud, dwID);
}
/*
* Mixer_SetVolume * * - Change a mixerControl in response to a user event * */ MMRESULT Mixer_SetVolume ( PMIXUIDIALOG pmxud, // app instance
PVOLCTRLDESC pvcd, // volume to change
DWORD dwVolume, // volume value VOLUME_MAX to VOLUME_MIN
LPDWORD lpdwBalance // Balance desired (NULL == No Balance) 0 to 64
) {
MIXERLINE ml; DWORD cChannels; DWORD dwSize; MIXERCONTROLDETAILS_UNSIGNED* pmcdVolume; MMRESULT mmr;
// Check the parameters
if ( !pvcd || !pmxud || (dwVolume > VOLUME_MAX) ) return MMSYSERR_INVALPARAM;
// Find needed buffer size for volumes
if (pvcd->fdwVolumeControl & MIXERCONTROL_CONTROLF_UNIFORM) { cChannels = 1; } else { mmr = Mixer_GetMixerLineInfo ((HMIXEROBJ)(pmxud->hmx), &ml, pvcd->dwLineID); if (MMSYSERR_NOERROR != mmr) { return mmr; }
cChannels = ml.cChannels; }
dwSize = (DWORD)(cChannels * sizeof (MIXERCONTROLDETAILS_UNSIGNED));
// Create volume buffer
pmcdVolume = LocalAlloc (LPTR, dwSize); if (!pmcdVolume) return MMSYSERR_NOMEM;
// Note: From here on, do not return without releasing 'pmcdVolume'.
mmr = Mixer_GetMixerVolume (pmxud, pvcd, pmcdVolume, &dwSize); if (MMSYSERR_NOERROR == mmr) {
MIXERCONTROLDETAILS mcd; ZeroMemory (&mcd, sizeof (mcd));
// Create volume mix cache if necessary
// if we have no cache we make one of course
// other wise we first check that not all the volumes of the channels are equal to zero
if (!pvcd->pdblCacheMix || !Mixer_AreChannelsAtMinimum(pmcdVolume,cChannels)) { Mixer_RefreshMixCache (pvcd, pmcdVolume, cChannels); }
// Create volume buffer for new values
mcd.paDetails = LocalAlloc (LPTR, dwSize); if (!mcd.paDetails || !pvcd->pdblCacheMix) mmr = MMSYSERR_NOMEM;
// Caculate the new volume & balance
if (MMSYSERR_NOERROR == mmr) {
UINT uiIndx; MIXERCONTROLDETAILS_UNSIGNED* pmcdCurrent;
// Account for Balance (only for Stereo)
if ( lpdwBalance && (cChannels == 2) && (*lpdwBalance <= 64) ) { MIXERCONTROLDETAILS_UNSIGNED* pmcdLeft = ((MIXERCONTROLDETAILS_UNSIGNED*)mcd.paDetails); MIXERCONTROLDETAILS_UNSIGNED* pmcdRight = ((MIXERCONTROLDETAILS_UNSIGNED*)mcd.paDetails + 1); long lBalance = *lpdwBalance;
lBalance -= 32; // -32 to 32 range
// Caculate volume based on balance and refresh mix cache
if (lBalance > 0) // Balance Right
{ // Left
if (lBalance == 32) // Pegged Right
pmcdLeft -> dwValue = 0; else pmcdLeft -> dwValue = dwVolume - (lBalance * (dwVolume - VOLUME_MIN))/32;
// Right
pmcdRight -> dwValue = dwVolume; } if (lBalance < 0) // Balance Left
{ // Left
pmcdLeft -> dwValue = dwVolume; // Right
if (lBalance == -32) // Pegged Left
pmcdRight -> dwValue = 0; else pmcdRight -> dwValue = dwVolume - (-lBalance * (dwVolume - VOLUME_MIN))/32; } if (lBalance == 0) // Balance Centered
{ // Left
pmcdLeft -> dwValue = dwVolume; // Right
pmcdRight -> dwValue = dwVolume; } Mixer_RefreshMixCache (pvcd, mcd.paDetails, cChannels); } else { // 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++) { pmcdCurrent = ((MIXERCONTROLDETAILS_UNSIGNED*)mcd.paDetails + uiIndx); // The 0.5f forces rounding (instead of truncation)
pmcdCurrent -> dwValue = (DWORD)(*(pvcd->pdblCacheMix + uiIndx) * (double) dwVolume + 0.5f); } }
mcd.cbStruct = sizeof (mcd); mcd.dwControlID = pvcd -> dwVolumeID; mcd.cChannels = cChannels; mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED); // seems like it would be sizeof(mcd.paDetails),
// but actually, it is the size of a single value
// and is multiplied by channel in the driver.
// Apply new value only if it is different. This prevents unessary calls to
// mixerSetControlDetails() when we are pegged.
if (memcmp (pmcdVolume, mcd.paDetails, dwSize)) { mixerSetControlDetails ((HMIXEROBJ)(pmxud->hmx), &mcd, MIXER_SETCONTROLDETAILSF_VALUE); }
} // Free new volume array
if (mcd.paDetails) LocalFree (mcd.paDetails); }
// Free volume array
LocalFree (pmcdVolume);
return mmr;
}
/*
* Mixer_GetControlFromID * * */ void Mixer_GetControlFromID( PMIXUIDIALOG pmxud, DWORD dwControlID) { MIXERLINE mxl; MIXERLINECONTROLS mxlc; MIXERCONTROL mxc; MIXERCONTROLDETAILS mxcd; PMIXUILINE pmxul; PMIXUICTRL pmxuc; PVOLCTRLDESC pvcd; DWORD ivcd; BOOL fBarf = FALSE; MMRESULT mmr;
//
// Retrieve the control information
//
mxlc.cbStruct = sizeof(mxlc); mxlc.dwControlID = dwControlID; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof(mxc); mxlc.pamxctrl = &mxc;
mmr = mixerGetLineControls((HMIXEROBJ)(pmxud->hmx) , &mxlc , MIXER_GETLINECONTROLSF_ONEBYID); if (mmr != MMSYSERR_NOERROR) return;
if (!(pmxud->dwFlags & MXUD_FLAGSF_BADDRIVER)) { //
// The *correct* code for this lookup using the mixer API.
//
// Is this our current destination line?
//
mxl.cbStruct = sizeof(mxl); mxl.dwLineID = mxlc.dwLineID;
mmr = mixerGetLineInfo((HMIXEROBJ)(pmxud->hmx) , &mxl , MIXER_GETLINEINFOF_LINEID); if (mmr != MMSYSERR_NOERROR) return;
if (mxl.dwDestination != pmxud->iDest) return;
//
// Is this a source line or a destination line?
//
ivcd = (mxl.fdwLine & MIXERLINE_LINEF_SOURCE)? 1 + mxl.dwSource : 0; pvcd = &pmxud->avcd[ivcd];
//
// a bad driver was detected!
//
if (pvcd->dwLineID != mxlc.dwLineID) { pmxud->dwFlags |= MXUD_FLAGSF_BADDRIVER; } } if (pmxud->dwFlags & MXUD_FLAGSF_BADDRIVER) { PVOLCTRLDESC pvcdTmp; //
// take evasive action if this was a bad driver by doing a brute force
// search.
//
pvcd = NULL; for (ivcd = 0; ivcd < pmxud->cvcd; ivcd ++) { pvcdTmp = &pmxud->avcd[ivcd]; if ( ( (pvcdTmp->dwSupport & VCD_SUPPORTF_MIXER_VOLUME) && pvcdTmp->dwVolumeID == dwControlID ) || ( (pvcdTmp->dwSupport & VCD_SUPPORTF_MIXER_MUTE) && pvcdTmp->dwMuteID == dwControlID ) || ( (pvcdTmp->dwSupport & VCD_SUPPORTF_MIXER_MIXER) && pvcdTmp->dwMixerID == dwControlID ) || ( (pvcdTmp->dwSupport & VCD_SUPPORTF_MIXER_MUX) && pvcdTmp->dwMuxID == dwControlID ) || ( (pvcdTmp->dwSupport & VCD_SUPPORTF_MIXER_METER) && pvcdTmp->dwMeterID == dwControlID ) ) { pvcd = pvcdTmp; break; } } if (pvcd == NULL) return; }
pmxul = pvcd->pmxul;
//
// Go through our visible lines to determine if this control affects
// any visible control and change them.
//
switch (mxc.dwControlType) { case MIXERCONTROL_CONTROLTYPE_VOLUME: { MIXERCONTROLDETAILS_UNSIGNED* pmcdVolume; DWORD cChannels; DWORD dwSize; MIXERLINE ml;
//
// A nonvisible line should be shunned
//
if (pmxul == NULL) return;
// Find needed buffer size for volumes
if (pvcd->fdwVolumeControl & MIXERCONTROL_CONTROLF_UNIFORM) { cChannels = 1; } else { mmr = Mixer_GetMixerLineInfo ((HMIXEROBJ)(pmxud->hmx), &ml, pvcd->dwLineID); if (MMSYSERR_NOERROR != mmr) { return; } cChannels = ml.cChannels; }
dwSize = (DWORD)(cChannels * sizeof (MIXERCONTROLDETAILS_UNSIGNED)); // Create volume buffer
pmcdVolume = LocalAlloc (LPTR, dwSize); if (!pmcdVolume) return;
// Note : Do not return without releasing 'pmcdVolume'.
if (Mixer_GetMixerVolume (pmxud, pvcd, pmcdVolume, &dwSize) == MMSYSERR_NOERROR) { UINT uindx; DWORD dwVolume; DWORD dwMax = 0;
// Set Volume slider
for (uindx = 0; uindx < cChannels; uindx++) dwMax = max (dwMax, (pmcdVolume + uindx) -> dwValue); dwVolume = VOLUME_TO_SLIDER(dwMax); dwVolume = VOLUME_TICS - dwVolume;
pmxuc = &pmxul->acr[MIXUI_VOLUME]; if (pmxuc->state) { SendMessage(pmxuc->hwnd, TBM_SETPOS, TRUE, dwVolume); }
// Set Balance Slider
pmxuc = &pmxul->acr[MIXUI_BALANCE]; if (dwVolume < VOLUME_TICS && pmxuc->state && 2 >= cChannels) { long lBalance; double dblBalance;
if (1 >= cChannels) lBalance = 0; else { // Stereo
dblBalance = (double)(32 * (long)(pmcdVolume -> dwValue - (pmcdVolume + 1) -> dwValue)) / (double)(dwMax - VOLUME_MIN); lBalance = (long)((32.0F - dblBalance) + 0.5F); // 0.5 forces rounding
}
SendMessage(pmxuc->hwnd, TBM_SETPOS, TRUE, lBalance); } }
LocalFree (pmcdVolume);
break; }
case MIXERCONTROL_CONTROLTYPE_MIXER: { DWORD i;
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pvcd->dwMixerID ; mxcd.cChannels = 1; mxcd.cMultipleItems = pvcd->cMixer; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); mxcd.paDetails = (LPVOID)pvcd->amcd_bMixer;
mmr = mixerGetControlDetails((HMIXEROBJ)(pmxud->hmx) , &mxcd , MIXER_GETCONTROLDETAILSF_VALUE);
if (mmr == MMSYSERR_NOERROR) { for (i = 0; i < pmxud->cvcd; i++) { pvcd = &pmxud->avcd[i]; if ( (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MIXER) && (pvcd->dwVisible & VCD_VISIBLEF_MIXER_MIXER) && pvcd->pmxul) { pmxuc = &pvcd->pmxul->acr[MIXUI_SWITCH]; if (pmxuc->state == MIXUI_CONTROL_INITIALIZED) { SendMessage(pmxuc->hwnd , BM_SETCHECK , pvcd->amcd_bMixer[pvcd->iMixer].fValue, 0); } } } } break; }
case MIXERCONTROL_CONTROLTYPE_MUX: { DWORD i;
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pvcd->dwMuxID ; mxcd.cChannels = 1; mxcd.cMultipleItems = pvcd->cMux; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); mxcd.paDetails = (LPVOID)pvcd->amcd_bMux;
mmr = mixerGetControlDetails((HMIXEROBJ)(pmxud->hmx) , &mxcd , MIXER_GETCONTROLDETAILSF_VALUE);
if (mmr == MMSYSERR_NOERROR) { for (i = 0; i < pmxud->cvcd; i++) { pvcd = &pmxud->avcd[i]; if ( (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUX) && (pvcd->dwVisible & VCD_VISIBLEF_MIXER_MUX) && pvcd->pmxul) { pmxuc = &pvcd->pmxul->acr[MIXUI_SWITCH]; if (pmxuc->state == MIXUI_CONTROL_INITIALIZED) SendMessage(pmxuc->hwnd , BM_SETCHECK , pvcd->amcd_bMux[pvcd->iMux].fValue, 0); } } } break; }
case MIXERCONTROL_CONTROLTYPE_MUTE: { DWORD fChecked;
//
// A nonvisible line should be shunned
//
if (pmxul == NULL) return;
if (! (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUTE && pvcd->dwVisible & VCD_VISIBLEF_MIXER_MUTE)) return;
pmxuc = &pmxul->acr[MIXUI_SWITCH]; if (pmxuc->state != MIXUI_CONTROL_INITIALIZED) break;
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pvcd->dwMuteID; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(DWORD); mxcd.paDetails = (LPVOID)&fChecked;
mmr = mixerGetControlDetails((HMIXEROBJ)(pmxud->hmx) , &mxcd , MIXER_GETCONTROLDETAILSF_VALUE);
if (mmr != MMSYSERR_NOERROR) break;
SendMessage(pmxuc->hwnd, BM_SETCHECK, fChecked, 0); break; }
case MIXERCONTROL_CONTROLTYPE_PEAKMETER: { LONG lVol; DWORD dwVol;
//
// A nonvisible line should be shunned
//
if (pmxul == NULL) return;
pmxuc = &pmxul->acr[MIXUI_VUMETER]; if (pmxuc->state != MIXUI_CONTROL_INITIALIZED) break;
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pvcd->dwMeterID; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(DWORD); mxcd.paDetails = (LPVOID)&lVol;
mmr = mixerGetControlDetails((HMIXEROBJ)(pmxud->hmx) , &mxcd , MIXER_GETCONTROLDETAILSF_VALUE);
if (mmr != MMSYSERR_NOERROR) break;
dwVol = (DWORD)abs(lVol); dwVol = (VOLUME_TICS * dwVol) / 32768;
SendMessage(pmxuc->hwnd, VU_SETPOS, 0, dwVol); break; }
default: return; } }
/*
* Mixer_SetControl * * - Change a mixerControl in response to a user event * */ void Mixer_SetControl( PMIXUIDIALOG pmxud, // app instance
HWND hctl, // hwnd of control that changed
int iLine, // visible line index of control that changed
int iCtl) // control id%line of control that changed
{ MMRESULT mmr; MIXERCONTROLDETAILS mxcd; PMIXUILINE pmxul; PMIXUICTRL pmxuc; PVOLCTRLDESC pvcd = NULL;
if ((DWORD)iLine >= pmxud->cmxul) return;
pmxul = &pmxud->amxul[iLine]; pvcd = pmxul->pvcd;
if (iCtl <= MIXUI_LAST) { pmxuc = &pmxul->acr[iCtl]; }
switch (iCtl) { case MIXUI_ADVANCED: Mixer_Advanced(pmxud, pvcd->dwLineID, pvcd->szName); break;
case MIXUI_MULTICHANNEL: // Note: This will always be true:
// (MXUL_STYLEF_DESTINATION & pmxul->dwStyle)
Mixer_Multichannel(pmxud, pvcd->dwVolumeID); break;
case MIXUI_VOLUME: case MIXUI_BALANCE: { // Make sure we have a volume slider
if ( pmxul->acr[MIXUI_VOLUME].state != MIXUI_CONTROL_UNINITIALIZED) { DWORD dwVolume; DWORD dwBalance; LPDWORD lpdwBalance = NULL;
dwVolume = (DWORD)SendMessage( pmxul->acr[MIXUI_VOLUME].hwnd , TBM_GETPOS , 0 , 0 );
dwVolume = VOLUME_TICS - dwVolume; dwVolume = SLIDER_TO_VOLUME(dwVolume);
// See if we have a balance slider as well
if ( pmxul->acr[MIXUI_BALANCE].state != MIXUI_CONTROL_UNINITIALIZED) { dwBalance = (DWORD)SendMessage(pmxul->acr[MIXUI_BALANCE].hwnd , TBM_GETPOS , 0 , 0); lpdwBalance = &dwBalance;
} Mixer_SetVolume (pmxud, pvcd, dwVolume, lpdwBalance); }
break; }
case MIXUI_SWITCH: { LONG fChecked;
if (pmxuc->state != MIXUI_CONTROL_INITIALIZED) break;
fChecked = (LONG)SendMessage(pmxuc->hwnd, BM_GETCHECK, 0, 0);
//
// it's unlikely that there is a mixer and a mux and a mute
// representing the same line. It's most important that when a line
// is selected that the user gets a response. If there is a mute
// but no mux, then mute and mixer should be OFF and ON
// respectively and vice versa. If there is a mux and a mute the
// same is true.
// If there is a mux and a mixer... then the mux select should
// correspond.
//
if ( pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUTE && pvcd->dwVisible & VCD_VISIBLEF_MIXER_MUTE ) { mmr = Mixer_Mute((HMIXEROBJ)(pmxud->hmx), pvcd, &mxcd, fChecked); }
if (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MIXER && pvcd->dwVisible & VCD_VISIBLEF_MIXER_MIXER ) { //
// get all other mixer settings, make sure this one is checked
//
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pvcd->dwMixerID ; mxcd.cChannels = 1; mxcd.cMultipleItems = pvcd->cMixer; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); mxcd.paDetails = (LPVOID)pvcd->amcd_bMixer;
mmr = mixerGetControlDetails((HMIXEROBJ)(pmxud->hmx) , &mxcd , MIXER_GETCONTROLDETAILSF_VALUE);
if (mmr == MMSYSERR_NOERROR) { pvcd->amcd_bMixer[pvcd->iMixer].fValue = fChecked; mmr = mixerSetControlDetails((HMIXEROBJ)(pmxud->hmx) , &mxcd , MIXER_SETCONTROLDETAILSF_VALUE); }
if (fChecked && pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUTE) { mmr = Mixer_Mute((HMIXEROBJ)(pmxud->hmx), pvcd, &mxcd, FALSE); } }
if (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUX && pvcd->dwVisible & VCD_VISIBLEF_MIXER_MUX ) { DWORD i; //
// get all other mux settings, make sure this one is checked
// or unchecked and all others are not.
//
for (i = 0; i < pvcd->cMux; i++) pvcd->amcd_bMux[i].fValue = FALSE;
pvcd->amcd_bMux[pvcd->iMux].fValue = TRUE;
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pvcd->dwMuxID ; mxcd.cChannels = 1; mxcd.cMultipleItems = pvcd->cMux; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); mxcd.paDetails = (LPVOID)pvcd->amcd_bMux;
mmr = mixerSetControlDetails((HMIXEROBJ)(pmxud->hmx) , &mxcd , MIXER_SETCONTROLDETAILSF_VALUE);
if (fChecked && pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUTE) { mmr = Mixer_Mute((HMIXEROBJ)(pmxud->hmx), pvcd, &mxcd, FALSE); } }
break; } default: break; } }
/*
* Mixer_PollingUpdate * * Controls that need to be updated by a timer. * * */ void Mixer_PollingUpdate( PMIXUIDIALOG pmxud) { DWORD i; MMRESULT mmr; MIXERLINE mxl; //
// For all visible mixer lines, locate the control id's that need to be
// updated.
//
for (i = 0; i < pmxud->cmxul; i++) { PMIXUICTRL pmxuc = &pmxud->amxul[i].acr[MIXUI_VUMETER]; PVOLCTRLDESC pvcd = pmxud->amxul[i].pvcd;
if (pmxuc->state == MIXUI_CONTROL_UNINITIALIZED) continue;
if (!(pvcd->dwSupport & VCD_SUPPORTF_MIXER_METER)) continue;
//
// Is the line active?
//
mxl.cbStruct = sizeof(MIXERLINE); mxl.dwLineID = pvcd->dwLineID;
mmr = mixerGetLineInfo((HMIXEROBJ)(pmxud->hmx) , &mxl , MIXER_GETLINEINFOF_LINEID); //
// Force non active or invalid lines to 0
//
if (mmr != MMSYSERR_NOERROR || !(mxl.fdwLine & MIXERLINE_LINEF_ACTIVE)) { SendMessage(pmxuc->hwnd, VU_SETPOS, 0, 0L); continue; }
//
// Force a visible update
//
Mixer_GetControlFromID(pmxud, pvcd->dwMeterID); } }
void ShowAndEnableWindow (HWND hWnd, BOOL fEnable) { ShowWindow (hWnd, fEnable ? SW_SHOW : SW_HIDE); EnableWindow (hWnd, fEnable); }
/*
* Mixer_Init * * Control initialization * */ BOOL Mixer_Init( PMIXUIDIALOG pmxud) { MMRESULT mmr; MIXERLINE mlDst; DWORD iline; TCHAR achFmt[256]; TCHAR achTitle[256]; TCHAR achAccessible[256]; int x;
ZeroMemory (achFmt, sizeof (achFmt)); // Inital value for prefix
mmr = mixerOpen((LPHMIXER)&pmxud->hmx , pmxud->mxid , (DWORD_PTR)pmxud->hwnd , 0 , CALLBACK_WINDOW);
if (mmr != MMSYSERR_NOERROR) { return FALSE; } else { DeviceChange_Init(pmxud->hwnd, pmxud->mxid); }
if (mixerMessage((HMIXER)ULongToPtr(pmxud->mxid), DRV_QUERYDEVNODE, (DWORD_PTR)&pmxud->dwDevNode, 0L)) pmxud->dwDevNode = 0L;
LoadString(pmxud->hInstance, IDS_APPTITLE, achFmt, SIZEOF(achFmt));
mlDst.cbStruct = sizeof ( mlDst ); mlDst.dwDestination = pmxud->iDest;
mmr = mixerGetLineInfo((HMIXEROBJ)ULongToPtr(pmxud->mxid) , &mlDst , MIXER_GETLINEINFOF_DESTINATION);
achTitle[0] = TEXT('\0'); if( mmr == MMSYSERR_NOERROR ) { HRESULT hr = StringCchCopy( achTitle, SIZEOF(achTitle), mlDst.szName ); if( hr != S_OK ) { achTitle[0] = TEXT('\0'); } } if( !achTitle[0] ) { LoadString(pmxud->hInstance, IDS_APPBASE, achTitle, SIZEOF(achTitle)); }
SetWindowText(pmxud->hwnd, achTitle);
//
// since we cannot get a WM_PARENTNOTIFY, we need to run through
// all controls and make appropriate modifications.
//
for ( iline = 0 ; iline < pmxud->cmxul ; iline++ ) { PMIXUILINE pmxul = &pmxud->amxul[iline]; PMIXUICTRL amxuc = pmxul->acr; HWND ctrl;
ctrl = Volume_GetLineItem(pmxud->hwnd, iline, IDC_LINELABEL); if (ctrl) { if (pmxud->dwStyle & MXUD_STYLEF_SMALL) Static_SetText(ctrl, pmxul->pvcd->szShortName); else Static_SetText(ctrl, pmxul->pvcd->szName); }
// for MSAA (accessibility), we need to put the control name on the sliders
for (x = IDC_ACCESS_BALANCE; x <= IDC_ACCESS_VOLUME; x++) { ctrl = Volume_GetLineItem(pmxud->hwnd, iline, x); if (ctrl) { Static_GetText(ctrl, achFmt, sizeof(achFmt)/sizeof(TCHAR));
if (pmxud->dwStyle & MXUD_STYLEF_SMALL) { StringCchPrintf(achAccessible,SIZEOF(achAccessible),achFmt,pmxul->pvcd->szShortName); Static_SetText(ctrl, achAccessible); } else { StringCchPrintf(achAccessible,SIZEOF(achAccessible),achFmt,pmxul->pvcd->szName); Static_SetText(ctrl, achAccessible); } } }
//
// Master Control Multichannel Support
//
// Init multichannel support for master control if available. Note that if a master
// control exisits on the dialog, it is currently in the first position, but we do
// NOT rely on that fact here.
// Note: Not only must there be multiple channels, but Volume must also be
// Supported to manipulate the channels.
if (mlDst.cChannels > 2L && MXUL_STYLEF_DESTINATION & pmxul->dwStyle && pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_VOLUME) { int idc; for (idc = IDC_MASTER_BALANCE_ICON_2; idc >= IDC_MULTICHANNEL; idc--) { ctrl = Volume_GetLineItem (pmxud->hwnd, iline, idc); if (ctrl) ShowAndEnableWindow (ctrl, (IDC_MULTICHANNEL == idc)); } ctrl = Volume_GetLineItem (pmxud->hwnd, iline, IDC_BALANCE); if (ctrl) ShowAndEnableWindow (ctrl, FALSE);
switch (mlDst.dwComponentType) { case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS: // No Change
break;
case MIXERLINE_COMPONENTTYPE_DST_WAVEIN: case MIXERLINE_COMPONENTTYPE_DST_VOICEIN: // Recording
LoadString(pmxud->hInstance, IDS_MC_RECORDING, achFmt, SIZEOF(achFmt)); SetWindowText (ctrl, achFmt); break;
default: // Anything else...
LoadString(pmxud->hInstance, IDS_MC_LEVEL, achFmt, SIZEOF(achFmt)); SetWindowText (ctrl, achFmt); break;
} }
//
// Advanced escape
//
if (MXUD_ADVANCED(pmxud) && !(pmxud->dwStyle & MXUD_STYLEF_SMALL))
{ HWND hadv = Volume_GetLineItem(pmxud->hwnd, iline, IDC_ADVANCED); if (hadv) { ShowWindow(hadv,(pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_ADVANCED)?SW_SHOW:SW_HIDE); EnableWindow(hadv, (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_ADVANCED)?TRUE:FALSE); } }
if (pmxul->pvcd->dwSupport & VCD_SUPPORTF_DISABLED) continue;
//
// allow init of control structures
//
if (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_VOLUME) { amxuc[MIXUI_VOLUME].state = MIXUI_CONTROL_ENABLED; if (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MONO) { amxuc[MIXUI_BALANCE].state = MIXUI_CONTROL_UNINITIALIZED; } else amxuc[MIXUI_BALANCE].state = MIXUI_CONTROL_ENABLED;
} if (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_METER) amxuc[MIXUI_VUMETER].state = MIXUI_CONTROL_ENABLED;
if (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUTE) amxuc[MIXUI_SWITCH].state = MIXUI_CONTROL_ENABLED;
if ((pmxul->pvcd->dwSupport & ( VCD_SUPPORTF_MIXER_MIXER | VCD_SUPPORTF_MIXER_MUX)) && (pmxul->pvcd->dwVisible & ( VCD_VISIBLEF_MIXER_MIXER | VCD_VISIBLEF_MIXER_MUX))) { //
// No longer make the mute visible
//
pmxul->pvcd->dwVisible &= ~VCD_VISIBLEF_MIXER_MUTE;
amxuc[MIXUI_SWITCH].state = MIXUI_CONTROL_ENABLED; ctrl = Volume_GetLineItem(pmxud->hwnd, iline, IDC_SWITCH); if (ctrl) { TCHAR ach[256]; if (LoadString(pmxud->hInstance, IDS_SELECT, ach, SIZEOF(ach))) Button_SetText(ctrl, ach); } } } return TRUE; }
/*
* Mixer_Shutdown * * Close handles, etc.. * */ void Mixer_Shutdown( PMIXUIDIALOG pmxud) { if (pmxud->hmx) { mixerClose(pmxud->hmx); pmxud->hmx = NULL; }
Mixer_CleanupVolumeDescription(pmxud->avcd, pmxud->cvcd); }
/* - - - - - - - - - */
typedef struct tagAdv { PMIXUIDIALOG pmxud; // IN
DWORD dwLineID; // IN
HMIXER hmx; // IN
LPTSTR szName; // IN
DWORD dwSupport; DWORD dwBassID; DWORD dwTrebleID; DWORD dwSwitch1ID; DWORD dwSwitch2ID;
} ADVPARAM, *PADVPARAM;
#define GETPADVPARAM(x) (ADVPARAM *)GetWindowLongPtr(x, DWLP_USER)
#define SETPADVPARAM(x,y) SetWindowLongPtr(x, DWLP_USER, y)
#define ADV_HAS_BASS 0x00000001
#define ADV_HAS_TREBLE 0x00000002
#define ADV_HAS_SWITCH1 0x00000004
#define ADV_HAS_SWITCH2 0x00000008
void Mixer_Advanced_Update( PADVPARAM pap, HWND hwnd) { MIXERCONTROLDETAILS mxcd; DWORD dwValue = 0; MMRESULT mmr;
if (pap->dwSupport & ADV_HAS_TREBLE) { mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pap->dwTrebleID ; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(DWORD); mxcd.paDetails = (LPVOID)&dwValue;
mmr = mixerGetControlDetails((HMIXEROBJ)(pap->hmx) , &mxcd , MIXER_GETCONTROLDETAILSF_VALUE);
if (mmr == MMSYSERR_NOERROR) { dwValue = VOLUME_TO_SLIDER(dwValue); SendMessage(GetDlgItem(hwnd, IDC_TREBLE), TBM_SETPOS, TRUE, dwValue); } }
if (pap->dwSupport & ADV_HAS_BASS) { mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pap->dwBassID; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(DWORD); mxcd.paDetails = (LPVOID)&dwValue;
mmr = mixerGetControlDetails((HMIXEROBJ)(pap->hmx) , &mxcd , MIXER_GETCONTROLDETAILSF_VALUE);
if (mmr == MMSYSERR_NOERROR) { dwValue = VOLUME_TO_SLIDER(dwValue); SendMessage(GetDlgItem(hwnd, IDC_BASS), TBM_SETPOS, TRUE, dwValue); } }
if (pap->dwSupport & ADV_HAS_SWITCH1) { mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pap->dwSwitch1ID; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(DWORD); mxcd.paDetails = (LPVOID)&dwValue;
mmr = mixerGetControlDetails((HMIXEROBJ)(pap->hmx) , &mxcd , MIXER_GETCONTROLDETAILSF_VALUE);
if (mmr == MMSYSERR_NOERROR) { Button_SetCheck(GetDlgItem(hwnd,IDC_SWITCH1),dwValue); }
}
if (pap->dwSupport & ADV_HAS_SWITCH2) { mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pap->dwSwitch2ID; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(DWORD); mxcd.paDetails = (LPVOID)&dwValue;
mmr = mixerGetControlDetails((HMIXEROBJ)(pap->hmx) , &mxcd , MIXER_GETCONTROLDETAILSF_VALUE);
if (mmr == MMSYSERR_NOERROR) { Button_SetCheck(GetDlgItem(hwnd,IDC_SWITCH2),dwValue); } } }
void Mixer_Advanced_OnMixmControlChange( HWND hwnd, HMIXER hmx, DWORD dwControlID) { PADVPARAM pap = GETPADVPARAM(hwnd);
if (!pap) return;
if ( ((pap->dwSupport & ADV_HAS_BASS) && dwControlID == pap->dwBassID) || ((pap->dwSupport & ADV_HAS_TREBLE) && dwControlID == pap->dwTrebleID) || ((pap->dwSupport & ADV_HAS_SWITCH1) && dwControlID == pap->dwSwitch1ID) || ((pap->dwSupport & ADV_HAS_SWITCH2) && dwControlID == pap->dwSwitch2ID) ) { Mixer_Advanced_Update(pap,hwnd); } }
BOOL Mixer_Advanced_OnInitDialog( HWND hwnd, HWND hwndFocus, LPARAM lParam) { PADVPARAM pap; MIXERLINECONTROLS mxlc; MIXERCONTROL *pmxc; MIXERLINE ml; MMRESULT mmr; DWORD iCtrl, iSwitch1, iSwitch2; TCHAR ach[MIXER_LONG_NAME_CHARS + 24]; TCHAR achFmt[256];
HWND hBass,hTreble,hSwitch1,hSwitch2;
SETPADVPARAM(hwnd, lParam); pap = GETPADVPARAM(hwnd); if (!pap) EndDialog(hwnd, FALSE);
//
// clone the mixer handle to catch callbacks
//
#ifndef _WIN64
mmr = mixerOpen((LPHMIXER)&pap->hmx , (UINT)pap->pmxud->hmx , (DWORD_PTR)hwnd , 0 , CALLBACK_WINDOW | MIXER_OBJECTF_HMIXER ); #else
mmr = mixerOpen((LPHMIXER)&pap->hmx , (UINT)pap->pmxud->mxid , (DWORD_PTR)hwnd , 0 , CALLBACK_WINDOW | MIXER_OBJECTF_HMIXER ); #endif
if (mmr != MMSYSERR_NOERROR) EndDialog(hwnd, FALSE);
//
// Get all controls.
//
ml.cbStruct = sizeof(ml); ml.dwLineID = pap->dwLineID;
mmr = mixerGetLineInfo((HMIXEROBJ)pap->hmx , &ml , MIXER_GETLINEINFOF_LINEID);
if (mmr != MMSYSERR_NOERROR || ml.cControls == 0L) EndDialog(hwnd, FALSE);
pmxc = (MIXERCONTROL *)GlobalAllocPtr(GHND, sizeof(MIXERCONTROL) * ml.cControls); if (!pmxc) { EndDialog(hwnd, FALSE); return FALSE; // Bail on error
}
mxlc.cbStruct = sizeof(mxlc); mxlc.dwLineID = pap->dwLineID; mxlc.cControls = ml.cControls; mxlc.cbmxctrl = sizeof(MIXERCONTROL); mxlc.pamxctrl = pmxc;
mmr = mixerGetLineControls((HMIXEROBJ)(pap->hmx) , &mxlc , MIXER_GETLINECONTROLSF_ALL); if (mmr != MMSYSERR_NOERROR) { GlobalFreePtr(pmxc); EndDialog(hwnd, FALSE); }
pap->dwSupport = 0L; for (iCtrl = 0; iCtrl < ml.cControls; iCtrl++) { switch (pmxc[iCtrl].dwControlType) { case MIXERCONTROL_CONTROLTYPE_BASS: if (!(pap->dwSupport & ADV_HAS_BASS)) { pap->dwBassID = pmxc[iCtrl].dwControlID; pap->dwSupport |= ADV_HAS_BASS; } break; case MIXERCONTROL_CONTROLTYPE_TREBLE: if (!(pap->dwSupport & ADV_HAS_TREBLE)) { pap->dwTrebleID = pmxc[iCtrl].dwControlID; pap->dwSupport |= ADV_HAS_TREBLE; } break;
case MIXERCONTROL_CONTROLTYPE_BOOLEAN: case MIXERCONTROL_CONTROLTYPE_MONO: case MIXERCONTROL_CONTROLTYPE_STEREOENH: case MIXERCONTROL_CONTROLTYPE_ONOFF: case MIXERCONTROL_CONTROLTYPE_LOUDNESS: if (!(pap->dwSupport & ADV_HAS_SWITCH1)) { pap->dwSwitch1ID = pmxc[iCtrl].dwControlID; pap->dwSupport |= ADV_HAS_SWITCH1; iSwitch1 = iCtrl; } else if (!(pap->dwSupport & ADV_HAS_SWITCH2)) { pap->dwSwitch2ID = pmxc[iCtrl].dwControlID; pap->dwSupport |= ADV_HAS_SWITCH2; iSwitch2 = iCtrl; } break; } }
//
//
//
hBass = GetDlgItem(hwnd, IDC_BASS); hTreble = GetDlgItem(hwnd, IDC_TREBLE); hSwitch1 = GetDlgItem(hwnd, IDC_SWITCH1); hSwitch2 = GetDlgItem(hwnd, IDC_SWITCH2);
SendMessage(hBass, TBM_SETRANGE, 0, MAKELONG(0, VOLUME_TICS)); SendMessage(hBass, TBM_SETTICFREQ, (VOLUME_TICS + 5)/6, 0 );
SendMessage(hTreble, TBM_SETRANGE, 0, MAKELONG(0, VOLUME_TICS)); SendMessage(hTreble, TBM_SETTICFREQ, (VOLUME_TICS + 5)/6, 0 );
if (!(pap->dwSupport & ADV_HAS_BASS)) { SendMessage(hBass, TBM_SETPOS, 64, 0 ); EnableWindow(GetDlgItem(hwnd, IDC_TXT_LOW1), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_TXT_HI1), FALSE); } EnableWindow(hBass, (pap->dwSupport & ADV_HAS_BASS));
if (!(pap->dwSupport & ADV_HAS_TREBLE)) { SendMessage(hTreble, TBM_SETPOS, 64, 0 ); EnableWindow(GetDlgItem(hwnd, IDC_TXT_LOW2), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_TXT_HI2), FALSE); } EnableWindow(hTreble, (pap->dwSupport & ADV_HAS_TREBLE));
if (pap->dwSupport & ADV_HAS_SWITCH1) { LoadString(pap->pmxud->hInstance, IDS_ADV_SWITCH1, achFmt, SIZEOF(achFmt)); StringCchPrintf(ach, SIZEOF(ach), achFmt, pmxc[iSwitch1].szName);
SetWindowText(hSwitch1, ach); ShowWindow(hSwitch1, SW_SHOW); ShowWindow(GetDlgItem(hwnd, IDC_TXT_SWITCHES), SW_SHOW); ShowWindow(GetDlgItem(hwnd, IDC_GRP_OTHER), SW_SHOW); } EnableWindow(hSwitch1, (pap->dwSupport & ADV_HAS_SWITCH1));
if (pap->dwSupport & ADV_HAS_SWITCH2) { LoadString(pap->pmxud->hInstance, IDS_ADV_SWITCH2, achFmt, SIZEOF(achFmt)); StringCchPrintf(ach, SIZEOF(ach), achFmt, pmxc[iSwitch2].szName);
SetWindowText(hSwitch2, ach); ShowWindow(hSwitch2, SW_SHOW); }
EnableWindow(hSwitch2, (pap->dwSupport & ADV_HAS_SWITCH2));
if (pap->dwSupport & (ADV_HAS_SWITCH1 | ADV_HAS_SWITCH2)) { RECT rcGrp,rcGrp2,rcClose,rcWnd; DWORD dwDY=0L; POINT pos; HWND hClose = GetDlgItem(hwnd, IDOK); HWND hGrp2 = GetDlgItem(hwnd, IDC_GRP_OTHER);
GetWindowRect(GetDlgItem(hwnd, IDC_GRP_TONE), &rcGrp); GetWindowRect(GetDlgItem(hwnd, IDC_GRP_OTHER), &rcGrp2); GetWindowRect(hClose, &rcClose); GetWindowRect(hwnd, &rcWnd);
if (pap->dwSupport & ADV_HAS_SWITCH2) { RECT rc1, rc2; GetWindowRect(hSwitch1,&rc1); GetWindowRect(hSwitch2,&rc2);
rcGrp2.bottom += rc2.bottom - rc1.bottom; }
dwDY = rcGrp2.bottom - rcGrp.bottom;
//
// resize our main window
//
MoveWindow(hwnd, rcWnd.left , rcWnd.top , rcWnd.right - rcWnd.left , (rcWnd.bottom - rcWnd.top) + dwDY , FALSE);
//
// move the close button
//
MapWindowPoints(NULL, hwnd, (LPPOINT)&rcClose, 2); pos.x = rcClose.left; pos.y = rcClose.top;
MoveWindow(hClose, pos.x , pos.y + dwDY , rcClose.right - rcClose.left , rcClose.bottom - rcClose.top , FALSE);
//
// resize our group box if necessary
//
if (pap->dwSupport & ADV_HAS_SWITCH2) { MapWindowPoints(NULL, hwnd, (LPPOINT)&rcGrp2, 2); pos.x = rcGrp2.left; pos.y = rcGrp2.top;
MoveWindow(hGrp2, pos.x , pos.y , rcGrp2.right - rcGrp2.left , rcGrp2.bottom - rcGrp2.top , FALSE); } }
GlobalFreePtr(pmxc);
{ TCHAR achTitle[MIXER_LONG_NAME_CHARS+256]; LoadString(pap->pmxud->hInstance, IDS_ADV_TITLE, achFmt, SIZEOF(achFmt)); StringCchPrintf(achTitle, SIZEOF(achTitle), achFmt, pap->szName); SetWindowText(hwnd, achTitle); }
Mixer_Advanced_Update(pap, hwnd);
return TRUE; }
void Mixer_Advanced_OnXScroll( HWND hwnd, HWND hwndCtl, UINT code, int pos) { PADVPARAM pap; MIXERCONTROLDETAILS mxcd; DWORD dwVol; MMRESULT mmr;
pap = GETPADVPARAM(hwnd);
if (!pap) return;
if (pap->dwSupport & ADV_HAS_TREBLE) { dwVol = (DWORD)SendMessage( GetDlgItem(hwnd, IDC_TREBLE) , TBM_GETPOS , 0 , 0 );
dwVol = SLIDER_TO_VOLUME(dwVol);
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pap->dwTrebleID ; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(DWORD); mxcd.paDetails = (LPVOID)&dwVol;
mixerSetControlDetails((HMIXEROBJ)(pap->hmx) , &mxcd , MIXER_SETCONTROLDETAILSF_VALUE); }
if (pap->dwSupport & ADV_HAS_BASS) { dwVol = (DWORD)SendMessage( GetDlgItem(hwnd, IDC_BASS) , TBM_GETPOS , 0 , 0 );
dwVol = SLIDER_TO_VOLUME(dwVol);
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = pap->dwBassID; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(DWORD); mxcd.paDetails = (LPVOID)&dwVol;
mmr = mixerSetControlDetails((HMIXEROBJ)(pap->hmx) , &mxcd , MIXER_SETCONTROLDETAILSF_VALUE); } }
void Mixer_Advanced_OnSwitch( HWND hwnd, int id, HWND hwndCtl) { PADVPARAM pap; MIXERCONTROLDETAILS mxcd; DWORD dwValue; MMRESULT mmr;
pap = GETPADVPARAM(hwnd);
if (!pap) return;
dwValue = Button_GetCheck(hwndCtl);
mxcd.cbStruct = sizeof(mxcd); mxcd.dwControlID = (id == IDC_SWITCH1)?pap->dwSwitch1ID:pap->dwSwitch2ID; mxcd.cChannels = 1; mxcd.cMultipleItems = 0; mxcd.cbDetails = sizeof(DWORD); mxcd.paDetails = (LPVOID)&dwValue;
mmr = mixerSetControlDetails((HMIXEROBJ)(pap->hmx) , &mxcd , MIXER_SETCONTROLDETAILSF_VALUE);
}
BOOL Mixer_Advanced_OnCommand( HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDOK: EndDialog(hwnd, TRUE); break;
case IDCANCEL: EndDialog(hwnd, FALSE); break;
case IDC_SWITCH1: Mixer_Advanced_OnSwitch(hwnd, id, hwndCtl); break;
case IDC_SWITCH2: Mixer_Advanced_OnSwitch(hwnd, id, hwndCtl); break;
} return FALSE; }
INT_PTR CALLBACK Mixer_Advanced_Proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_INITDIALOG: HANDLE_WM_INITDIALOG(hwnd, wparam, lparam, Mixer_Advanced_OnInitDialog); return TRUE;
case MM_MIXM_CONTROL_CHANGE: HANDLE_MM_MIXM_CONTROL_CHANGE(hwnd , wparam , lparam , Mixer_Advanced_OnMixmControlChange); break;
case WM_CLOSE: EndDialog(hwnd, FALSE); break;
case WM_HSCROLL: HANDLE_WM_XSCROLL(hwnd, wparam, lparam, Mixer_Advanced_OnXScroll); break;
case WM_COMMAND: HANDLE_WM_COMMAND(hwnd, wparam, lparam, Mixer_Advanced_OnCommand); break;
case WM_DESTROY: { PADVPARAM pap = GETPADVPARAM(hwnd); if (pap) { if (pap->hmx) mixerClose(pap->hmx); } break; }
default: break; }
return FALSE; }
/*
* Advanced Features for specific mixer lines. */ void Mixer_Advanced( PMIXUIDIALOG pmxud, DWORD dwLineID, LPTSTR szName) { ADVPARAM advp;
ZeroMemory(&advp, sizeof(ADVPARAM)); advp.pmxud = pmxud; advp.dwLineID = dwLineID; advp.szName = szName;
DialogBoxParam(pmxud->hInstance , MAKEINTRESOURCE(IDD_ADVANCED) , pmxud->hwnd , Mixer_Advanced_Proc , (LPARAM)(LPVOID)&advp); }
typedef void (*MULTICHANNELFUNC)(HWND, UINT, DWORD, DWORD); void Mixer_Multichannel (PMIXUIDIALOG pmxud, DWORD dwVolumeID) { HMODULE hModule; MULTICHANNELFUNC fnMultiChannel;
if (pmxud) { hModule = (HMODULE) LoadLibrary (TEXT ("mmsys.cpl")); if (hModule) { fnMultiChannel = (MULTICHANNELFUNC) GetProcAddress (hModule, "Multichannel"); if (fnMultiChannel) { (*fnMultiChannel)(pmxud->hwnd, pmxud->mxid, pmxud->iDest, dwVolumeID); } FreeLibrary (hModule); } } }
|