2612 lines
77 KiB
C
2612 lines
77 KiB
C
/*****************************************************************************
|
|
*
|
|
* 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"
|
|
|
|
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)
|
|
return FALSE;
|
|
|
|
lstrcpy(pmxud->szMixer, mxcaps.szPname);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
DWORD dwDst;
|
|
|
|
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);
|
|
|
|
if (mmr == MMSYSERR_NOERROR)
|
|
{
|
|
lstrcpy(achTitle, mlDst.szName);
|
|
}
|
|
else
|
|
{
|
|
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)
|
|
{
|
|
wsprintf(achAccessible,achFmt,pmxul->pvcd->szShortName);
|
|
Static_SetText(ctrl, achAccessible);
|
|
}
|
|
else
|
|
{
|
|
wsprintf(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));
|
|
wsprintf(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));
|
|
wsprintf(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));
|
|
wsprintf(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);
|
|
}
|
|
}
|
|
}
|