1504 lines
39 KiB
C
1504 lines
39 KiB
C
/* (C) Copyright Microsoft Corporation 1993. All Rights Reserved */
|
||
|
||
#include <windows.h>
|
||
#include <mmsystem.h>
|
||
#include <string.h>
|
||
#include "newvol.h"
|
||
#include "volume.h" // for ini file string identifiers
|
||
#include "sndcntrl.h"
|
||
|
||
#ifdef TESTMIX
|
||
#include "mixstub.h"
|
||
#endif
|
||
//
|
||
// Globals
|
||
//
|
||
#define SHOWMUX
|
||
int NumberOfDevices = 0;
|
||
PVOLUME_CONTROL Vol = NULL;
|
||
|
||
UINT FirstMasterIndex;
|
||
|
||
/*
|
||
* Profile file, section and key names
|
||
*/
|
||
TCHAR gszVolumeSection[64];
|
||
TCHAR gszProfileFile[MAX_PATH];
|
||
|
||
DWORD AdjustMaster(WORD v)
|
||
{
|
||
DWORD dwResult;
|
||
|
||
if (bMuted) {
|
||
return 1;
|
||
}
|
||
|
||
dwResult = (v >> 8) + 1;
|
||
|
||
return dwResult;
|
||
}
|
||
|
||
//
|
||
// Add a control to our list
|
||
//
|
||
// Note that the G..Ptr macros in windowsx.h are inadequate and incorrect -
|
||
// especially for multithreaded systems where stuff can move while it is
|
||
// temporarily unlocked.
|
||
//
|
||
|
||
PVOLUME_CONTROL AddNewControl(VOID)
|
||
{
|
||
HGLOBAL hMem;
|
||
PVOLUME_CONTROL pVol;
|
||
|
||
if (Vol == NULL) {
|
||
hMem = GlobalAlloc(GHND, sizeof(VOLUME_CONTROL));
|
||
if (hMem == NULL) {
|
||
return NULL;
|
||
} else {
|
||
Vol = GlobalLock(hMem);
|
||
NumberOfDevices = 1;
|
||
}
|
||
} else {
|
||
HGLOBAL hMemOld;
|
||
|
||
hMemOld = GlobalHandle((LPVOID)Vol);
|
||
GlobalUnlock(hMemOld);
|
||
hMem = GlobalReAlloc(hMemOld,
|
||
sizeof(VOLUME_CONTROL) * (NumberOfDevices + 1),
|
||
GHND);
|
||
if (hMem == NULL) {
|
||
Vol = GlobalLock(hMemOld);
|
||
return NULL;
|
||
}
|
||
|
||
Vol = GlobalLock(hMem);
|
||
NumberOfDevices++;
|
||
}
|
||
pVol = Vol + (NumberOfDevices - 1);
|
||
|
||
/*
|
||
** Finish initialization
|
||
*/
|
||
|
||
pVol->Index = NumberOfDevices - 1;
|
||
|
||
pVol->MixerId = (HMIXEROBJ)-1;
|
||
pVol->ControlId = (DWORD)-1;
|
||
pVol->MuxControlId = (DWORD)-1;
|
||
pVol->MuteControlId = (DWORD)-1;
|
||
pVol->MuxSelectIndex = (DWORD)-1;
|
||
|
||
return pVol;
|
||
}
|
||
|
||
WORD CombineVolume(WORD Master, WORD Slave)
|
||
{
|
||
DWORD Result;
|
||
|
||
//
|
||
// treat both numbers as 8-bit volumes, and multiply them
|
||
//
|
||
|
||
Result = AdjustMaster(Master) * (DWORD)(Slave >> 8);
|
||
|
||
return LOWORD(Result);
|
||
}
|
||
|
||
/*
|
||
** Set the device volume.
|
||
**
|
||
** The master volume (and mute setting) are simulated here by
|
||
** scaling the individual device volumes if there is no mixer
|
||
** or the mixer doesn't support the settings
|
||
*/
|
||
BOOL SetDeviceVolume(PVOLUME_CONTROL pVol, DWORD Volume)
|
||
{
|
||
DWORD dwMaster;
|
||
|
||
/*
|
||
** Mixer volumes get set when we get the notification
|
||
*/
|
||
|
||
if (pVol->VolumeType != VolumeTypeMixerControl) {
|
||
pVol->LRVolume = Volume;
|
||
}
|
||
|
||
/*
|
||
* If it's not the master volume we're setting then
|
||
* combine the setting with the master volume setting
|
||
*/
|
||
|
||
if (pVol->Type != MasterVolume) {
|
||
|
||
/*
|
||
** Only simulate controls which don't have real master controls
|
||
*/
|
||
|
||
if (!pVol->NoMasterSimulation) {
|
||
/*
|
||
* if mute is selected, scale the volume by 1 (not 0)
|
||
* as the master volume. This will still result in an
|
||
* inaudible volume, but will allow us to recover the volume setting
|
||
* from the device when this app restarts.
|
||
*/
|
||
dwMaster = MasterDevice(FALSE)->LRVolume;
|
||
|
||
Volume = CombineVolume(LOWORD(dwMaster),
|
||
LOWORD(Volume)) +
|
||
(CombineVolume(HIWORD(dwMaster),
|
||
HIWORD(Volume)) << 16);
|
||
}
|
||
}
|
||
|
||
switch (pVol->Type) {
|
||
case MasterVolume:
|
||
{
|
||
int i;
|
||
for (i = 0; i < NumberOfDevices; i++) {
|
||
if (!Vol[i].NoMasterSimulation && Vol[i].Type != MasterVolume) {
|
||
SetDeviceVolume(&Vol[i], Vol[i].LRVolume);
|
||
}
|
||
}
|
||
}
|
||
if (pVol->VolumeType == VolumeTypeMixerControl) {
|
||
SetMixerVolume(pVol->MixerId,
|
||
pVol->ControlId,
|
||
pVol->Stereo,
|
||
Volume);
|
||
}
|
||
break;
|
||
|
||
case AuxVolume:
|
||
auxSetVolume(pVol->id, Volume);
|
||
break;
|
||
|
||
case MidiOutVolume:
|
||
#if (WINVER >= 0x0400)
|
||
midiOutSetVolume((HMIDIOUT)pVol->id, Volume);
|
||
#else
|
||
midiOutSetVolume(pVol->id, Volume);
|
||
#endif
|
||
break;
|
||
|
||
case WaveOutVolume:
|
||
#if (WINVER >= 0x0400)
|
||
waveOutSetVolume((HWAVEOUT)pVol->id, Volume);
|
||
#else
|
||
waveOutSetVolume(pVol->id, Volume);
|
||
#endif
|
||
break;
|
||
|
||
case MixerControlVolume:
|
||
SetMixerVolume(pVol->MixerId,
|
||
pVol->ControlId,
|
||
pVol->Stereo,
|
||
Volume);
|
||
break;
|
||
|
||
}
|
||
|
||
if (pVol->VolumeType != VolumeTypeMixerControl) {
|
||
/*
|
||
** Update the slider(s)
|
||
*/
|
||
|
||
UpdateVolume(pVol);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
* Get the volume associated with a mixer device
|
||
*/
|
||
|
||
VOID GetMixerVolume(HMIXEROBJ MixerId, DWORD dwControlId, BOOL Stereo, LPDWORD pVolume)
|
||
{
|
||
MIXERCONTROLDETAILS mxd;
|
||
DWORD Volume[2];
|
||
|
||
Volume[0] = 0;
|
||
Volume[1] = 0;
|
||
|
||
mxd.cbStruct = sizeof(mxd);
|
||
mxd.dwControlID = dwControlId;
|
||
mxd.cChannels = Stereo ? 2 : 1;
|
||
mxd.cMultipleItems = 0;
|
||
mxd.cbDetails = sizeof(DWORD);
|
||
mxd.paDetails = (LPVOID)Volume;
|
||
|
||
mixerGetControlDetails(MixerId, &mxd, MIXER_GETCONTROLDETAILSF_VALUE);
|
||
|
||
if (Stereo) {
|
||
*pVolume = (DWORD)MAKELONG(Volume[0], Volume[1]);
|
||
} else {
|
||
*pVolume = (DWORD)MAKELONG(Volume[0], Volume[0]);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Set the volume associated with a mixer device
|
||
*/
|
||
|
||
VOID SetMixerVolume(HMIXEROBJ MixerId, DWORD dwControlId, BOOL Stereo, DWORD NewVolume)
|
||
{
|
||
MIXERCONTROLDETAILS mxd;
|
||
DWORD Volume[2];
|
||
|
||
Volume[0] = LOWORD(NewVolume);
|
||
Volume[1] = HIWORD(NewVolume);
|
||
|
||
mxd.cbStruct = sizeof(mxd);
|
||
mxd.dwControlID = dwControlId;
|
||
mxd.cChannels = Stereo ? 2 : 1;
|
||
mxd.cMultipleItems = 0;
|
||
mxd.cbDetails = sizeof(DWORD);
|
||
mxd.paDetails = (LPVOID)Volume;
|
||
|
||
mixerSetControlDetails(MixerId, &mxd, MIXER_SETCONTROLDETAILSF_VALUE);
|
||
}
|
||
|
||
|
||
/*
|
||
* Get the volume for a given device. Returns the volume
|
||
* setting packed in a DWORD
|
||
*/
|
||
|
||
DWORD GetDeviceVolume(PVOLUME_CONTROL pVol)
|
||
{
|
||
DWORD Volume;
|
||
DWORD Left;
|
||
DWORD Right;
|
||
DWORD dwMaster;
|
||
PVOLUME_CONTROL pMaster;
|
||
|
||
//
|
||
// Default if calls fail
|
||
//
|
||
|
||
Volume = pVol->LRVolume;
|
||
|
||
switch (pVol->Type) {
|
||
|
||
case AuxVolume:
|
||
auxGetVolume(pVol->id, &Volume);
|
||
break;
|
||
|
||
case MidiOutVolume:
|
||
#if (WINVER >= 0x0400)
|
||
midiOutGetVolume((HMIDIOUT)pVol->id, &Volume);
|
||
#else
|
||
midiOutGetVolume(pVol->id, &Volume);
|
||
#endif
|
||
break;
|
||
|
||
case WaveOutVolume:
|
||
#if (WINVER >= 0x0400)
|
||
waveOutGetVolume((HWAVEOUT)pVol->id, &Volume);
|
||
#else
|
||
waveOutGetVolume(pVol->id, &Volume);
|
||
#endif
|
||
break;
|
||
|
||
case MixerControlVolume:
|
||
case MasterVolume:
|
||
|
||
/*
|
||
** don't scale by master vol in this case
|
||
*/
|
||
|
||
if (pVol->VolumeType != VolumeTypeMixerControl) {
|
||
return Volume;
|
||
}
|
||
|
||
GetMixerVolume(pVol->MixerId,
|
||
pVol->ControlId,
|
||
pVol->Stereo,
|
||
&Volume);
|
||
|
||
if (pVol->NoMasterSimulation || pVol->Type == MasterVolume) {
|
||
return Volume;
|
||
}
|
||
break;
|
||
}
|
||
|
||
/*
|
||
** Translate it back through the master volume
|
||
** Use 1 as the master volume if mute is set (see SetDeviceVolume)
|
||
*/
|
||
|
||
pMaster = MasterDevice(pVol->RecordControl);
|
||
|
||
if (!pVol->NoMasterSimulation && pMaster != NULL) {
|
||
dwMaster = pMaster->LRVolume;
|
||
|
||
Left = ((DWORD)LOWORD(Volume)) / AdjustMaster(LOWORD(dwMaster));
|
||
Left <<= 8;
|
||
if (Left > 65535) {
|
||
Left = 65535;
|
||
}
|
||
|
||
Right = ((DWORD)HIWORD(Volume)) / AdjustMaster(HIWORD(dwMaster));
|
||
Right <<= 8;
|
||
if (Right > 65535) {
|
||
Right = 65535;
|
||
}
|
||
} else {
|
||
if (bMuted &&
|
||
(pMaster == NULL ||
|
||
pMaster->MuteControlId == (DWORD)-1)) {
|
||
|
||
Left = LOWORD(Volume) >> 8;
|
||
Right = HIWORD(Volume) >> 8;
|
||
} else {
|
||
Left = LOWORD(Volume);
|
||
Right = HIWORD(Volume);
|
||
}
|
||
}
|
||
|
||
pVol->LRVolume = (DWORD)MAKELONG(Left, Right);
|
||
|
||
return pVol->LRVolume;
|
||
}
|
||
|
||
/*
|
||
** Update the displayed 'selected' state for a line
|
||
*/
|
||
|
||
VOID UpdateSelected(PVOLUME_CONTROL pVol)
|
||
{
|
||
if (pVol->hCheckBox != NULL) {
|
||
BOOL bSelected = ControlSelected(pVol);
|
||
if (pVol->Type == MasterVolume) {
|
||
SetWindowText(pVol->hCheckBox,
|
||
_string(bSelected ? IDS_MUTE : IDS_UNMUTE));
|
||
} else {
|
||
SendMessage(pVol->hCheckBox,
|
||
BM_SETCHECK,
|
||
(WPARAM)bSelected,
|
||
0L);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
** Update the displayed volume for a slider by getting the actual level from
|
||
** the device and then updating the local values and informing the window
|
||
** control(s)
|
||
*/
|
||
|
||
VOID UpdateVolume(PVOLUME_CONTROL pVol)
|
||
{
|
||
UINT oldVolume, oldBalance;
|
||
DWORD dwVolumes;
|
||
UINT max, min, left, right, temp;
|
||
|
||
oldVolume = pVol->Volume;
|
||
oldBalance = pVol->Balance;
|
||
|
||
dwVolumes = GetDeviceVolume(pVol);
|
||
|
||
/* figure out pan information */
|
||
right = HIWORD(dwVolumes);
|
||
left = LOWORD(dwVolumes);
|
||
max = (right > left) ? right : left;
|
||
min = (right > left) ? left : right;
|
||
|
||
if (max == 0) {
|
||
/* special case since then there's no panning. Therefore
|
||
we dont know what the panning level is, therefore
|
||
dont change the slider balance */
|
||
pVol->Volume = 0;
|
||
pVol->Balance = oldBalance; /* centered */
|
||
} else {
|
||
pVol->Volume = max >> 8;
|
||
temp = (UINT) (((DWORD) (max - min) << 7) / max);
|
||
if (temp > 0x7f) temp = 0x7f;
|
||
|
||
if (right > left)
|
||
pVol->Balance = 0x80 + temp;
|
||
else
|
||
pVol->Balance = 0x7f - temp;
|
||
}
|
||
|
||
/* change the slider if necessary */
|
||
if (oldVolume != pVol->Volume && pVol->hChildWnd && IsWindow(pVol->hChildWnd)) {
|
||
SendMessage(pVol->hChildWnd,SL_PM_SETKNOBPOS,
|
||
pVol->Volume, 0);
|
||
}
|
||
if (oldBalance != pVol->Balance && IsWindow(pVol->hMeterWnd)) {
|
||
SendMessage(pVol->hMeterWnd,MB_PM_SETKNOBPOS,
|
||
pVol->Balance, 0);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Extract pertinent information for a given device type
|
||
* If there is an equivalent mixer device don't bother.
|
||
*/
|
||
BOOL ExtractInfo(UINT id,
|
||
VOLUME_DEVICE_TYPE Type,
|
||
LPBOOL VolSupport,
|
||
LPBOOL StereoSupport,
|
||
LPTSTR lpName,
|
||
PUINT Technology)
|
||
{
|
||
UINT MixerId;
|
||
|
||
switch (Type) {
|
||
case MasterVolume:
|
||
break;
|
||
case AuxVolume:
|
||
if (mixerGetID((HMIXEROBJ)id, &MixerId, MIXER_OBJECTF_AUX) == MMSYSERR_NOERROR) {
|
||
return FALSE;
|
||
} else {
|
||
AUXCAPS ac;
|
||
if (auxGetDevCaps(id, &ac, sizeof(ac)) != MMSYSERR_NOERROR) {
|
||
return FALSE;
|
||
}
|
||
*VolSupport = (ac.dwSupport & AUXCAPS_VOLUME) != 0;
|
||
*StereoSupport = (ac.dwSupport & AUXCAPS_LRVOLUME) != 0;
|
||
lstrcpyn(lpName, ac.szPname, MAXPNAMELEN);
|
||
*Technology =
|
||
ac.wTechnology == AUXCAPS_CDAUDIO ? VolumeTypeCD :
|
||
ac.wTechnology == AUXCAPS_AUXIN ? VolumeTypeLineIn :
|
||
VolumeTypeAux;
|
||
}
|
||
break;
|
||
case MidiOutVolume:
|
||
if (mixerGetID((HMIXEROBJ)id, &MixerId, MIXER_OBJECTF_MIDIOUT) == MMSYSERR_NOERROR) {
|
||
return FALSE;
|
||
} else {
|
||
MIDIOUTCAPS mc;
|
||
if (midiOutGetDevCaps(id, &mc, sizeof(mc)) != MMSYSERR_NOERROR) {
|
||
return FALSE;
|
||
}
|
||
*VolSupport = (mc.dwSupport & MIDICAPS_VOLUME) != 0;
|
||
*StereoSupport = (mc.dwSupport & MIDICAPS_LRVOLUME) != 0;
|
||
lstrcpyn(lpName, mc.szPname, MAXPNAMELEN);
|
||
*Technology =
|
||
mc.wTechnology == MOD_SYNTH || mc.wTechnology == MOD_SQSYNTH ||
|
||
mc.wTechnology == MOD_FMSYNTH ? VolumeTypeSynth :
|
||
VolumeTypeMidi;
|
||
}
|
||
break;
|
||
case WaveOutVolume:
|
||
if (mixerGetID((HMIXEROBJ)id, &MixerId, MIXER_OBJECTF_WAVEOUT) == MMSYSERR_NOERROR) {
|
||
return FALSE;
|
||
} else {
|
||
WAVEOUTCAPS wc;
|
||
if (waveOutGetDevCaps(id, &wc, sizeof(wc)) != MMSYSERR_NOERROR) {
|
||
return FALSE;
|
||
}
|
||
*VolSupport = (wc.dwSupport & WAVECAPS_VOLUME) != 0;
|
||
*StereoSupport = (wc.dwSupport & WAVECAPS_LRVOLUME) != 0;
|
||
lstrcpyn(lpName, wc.szPname, MAXPNAMELEN);
|
||
*Technology = VolumeTypeWave;
|
||
}
|
||
break;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
** NonMixerDevices
|
||
**
|
||
** Search to see if there is a non-mixer device which is not
|
||
** duplicated by a mixer device
|
||
**
|
||
** If there is one return TRUE, otherwise FALSE
|
||
*/
|
||
|
||
BOOL NonMixerDevices()
|
||
{
|
||
VOLUME_DEVICE_TYPE DeviceType;
|
||
|
||
for (DeviceType = WaveOutVolume;
|
||
DeviceType < NumberOfDeviceTypes;
|
||
DeviceType++) {
|
||
UINT DeviceId;
|
||
UINT N;
|
||
|
||
N = DeviceType == AuxVolume ? auxGetNumDevs() :
|
||
DeviceType == MidiOutVolume ? midiOutGetNumDevs() :
|
||
waveOutGetNumDevs();
|
||
|
||
for (DeviceId = 0; DeviceId < N; DeviceId++) {
|
||
BOOL VolumeSupport;
|
||
BOOL StereoSupport;
|
||
TCHAR Pname[MAXPNAMELEN];
|
||
UINT Technology;
|
||
|
||
if (ExtractInfo(DeviceId,
|
||
DeviceType,
|
||
&VolumeSupport,
|
||
&StereoSupport,
|
||
Pname,
|
||
&Technology) &&
|
||
VolumeSupport) {
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/*
|
||
** Returns an allocated array of the controls for a given line
|
||
** Caller must LocalFree it.
|
||
*/
|
||
|
||
PMIXERCONTROL GetMixerLineControls(HMIXEROBJ MixerId,
|
||
DWORD dwLineID,
|
||
DWORD cControls)
|
||
{
|
||
MIXERLINECONTROLS MixerLineControls;
|
||
|
||
MixerLineControls.cbStruct = sizeof(MixerLineControls);
|
||
MixerLineControls.cControls = cControls;
|
||
MixerLineControls.dwLineID = dwLineID;
|
||
MixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
|
||
|
||
MixerLineControls.pamxctrl =
|
||
(LPMIXERCONTROL)LocalAlloc(LPTR, cControls * sizeof(MIXERCONTROL));
|
||
|
||
if (MixerLineControls.pamxctrl == NULL) {
|
||
//
|
||
// Ulp!
|
||
//
|
||
|
||
return NULL;
|
||
}
|
||
|
||
if (mixerGetLineControls(MixerId,
|
||
&MixerLineControls,
|
||
MIXER_GETLINECONTROLSF_ALL) != MMSYSERR_NOERROR) {
|
||
LocalFree((HLOCAL)MixerLineControls.pamxctrl);
|
||
return NULL;
|
||
}
|
||
|
||
return MixerLineControls.pamxctrl;
|
||
}
|
||
|
||
BOOL GetControlByType(
|
||
HMIXEROBJ MixerId,
|
||
DWORD dwLineId,
|
||
DWORD dwControlType,
|
||
PMIXERCONTROL MixerControl
|
||
)
|
||
{
|
||
MIXERLINECONTROLS MixerLineControls;
|
||
|
||
MixerLineControls.cbStruct = sizeof(MixerLineControls);
|
||
MixerLineControls.cControls = 1;
|
||
MixerLineControls.dwLineID = dwLineId;
|
||
MixerLineControls.dwControlType = dwControlType;
|
||
MixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
|
||
|
||
MixerLineControls.pamxctrl = MixerControl;
|
||
|
||
if (mixerGetLineControls(MixerId,
|
||
&MixerLineControls,
|
||
MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR) {
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
** See if a given volume control is selected through its mux/mixer
|
||
** Note that this state can change every time the relevant mux/mixer
|
||
** control changes
|
||
*/
|
||
BOOL ControlSelected(
|
||
PVOLUME_CONTROL pVol
|
||
)
|
||
{
|
||
MIXERCONTROLDETAILS mxd;
|
||
BOOL bResult;
|
||
|
||
if (pVol->Type != MixerControlVolume ||
|
||
pVol->MuxSelectIndex == (DWORD)-1) {
|
||
bResult = TRUE;
|
||
} else {
|
||
|
||
mxd.cbStruct = sizeof(mxd);
|
||
mxd.dwControlID = pVol->MuxControlId;
|
||
mxd.cChannels = 1;
|
||
mxd.cMultipleItems = pVol->MuxItems;
|
||
mxd.cbDetails = sizeof(DWORD);
|
||
mxd.paDetails =
|
||
(LPVOID)LocalAlloc(LPTR, mxd.cbDetails * mxd.cMultipleItems);
|
||
|
||
if (mxd.paDetails == NULL) {
|
||
return FALSE;
|
||
}
|
||
|
||
mixerGetControlDetails(pVol->MixerId, &mxd, MIXER_GETCONTROLDETAILSF_VALUE);
|
||
bResult = ((LPDWORD)mxd.paDetails)[pVol->MuxSelectIndex] != 0;
|
||
LocalFree((HLOCAL)mxd.paDetails);
|
||
}
|
||
|
||
|
||
if (pVol->MuteControlId != (DWORD)-1) {
|
||
bResult = bResult && !GetMixerMute(pVol);
|
||
}
|
||
|
||
return bResult;
|
||
}
|
||
|
||
/*
|
||
** The user wants this device to do its thing
|
||
*/
|
||
|
||
VOID SelectControl(
|
||
PVOLUME_CONTROL pVol,
|
||
BOOL Select
|
||
)
|
||
{
|
||
MIXERCONTROLDETAILS mxd;
|
||
|
||
if (pVol->Type != MixerControlVolume ||
|
||
pVol->MuxSelectIndex == (DWORD)-1 &&
|
||
pVol->MuteControlId == (DWORD)-1) {
|
||
return;
|
||
}
|
||
|
||
if (pVol->MuxSelectIndex == (DWORD)-1) {
|
||
SetMixerMute(pVol, !Select);
|
||
} else {
|
||
mxd.cbStruct = sizeof(mxd);
|
||
mxd.dwControlID = pVol->MuxControlId;
|
||
mxd.cChannels = 1;
|
||
mxd.cMultipleItems = pVol->MuxItems;
|
||
mxd.cbDetails = sizeof(DWORD);
|
||
mxd.paDetails =
|
||
(LPVOID)LocalAlloc(LPTR, mxd.cbDetails * mxd.cMultipleItems);
|
||
|
||
if (mxd.paDetails == NULL) {
|
||
return;
|
||
}
|
||
|
||
if (pVol->MuxOrMixer) {
|
||
/*
|
||
** Mux
|
||
*/
|
||
|
||
ZeroMemory(mxd.paDetails, sizeof(DWORD) * mxd.cMultipleItems);
|
||
} else {
|
||
/*
|
||
** Mixer
|
||
*/
|
||
|
||
mixerGetControlDetails(pVol->MixerId, &mxd, MIXER_GETCONTROLDETAILSF_VALUE);
|
||
}
|
||
|
||
((LPDWORD)mxd.paDetails)[pVol->MuxSelectIndex] = (DWORD)Select;
|
||
mixerSetControlDetails(pVol->MixerId, &mxd, MIXER_SETCONTROLDETAILSF_VALUE);
|
||
|
||
/*
|
||
** If we have both mute and mux then turn off the mute if we
|
||
** activate this device
|
||
*/
|
||
|
||
if (Select && pVol->MuteControlId != (DWORD)-1) {
|
||
SetMixerMute(pVol, FALSE);
|
||
}
|
||
|
||
LocalFree((HLOCAL)mxd.paDetails);
|
||
}
|
||
}
|
||
|
||
BOOL GetMixerMute(PVOLUME_CONTROL pVol)
|
||
{
|
||
MIXERCONTROLDETAILS mxd;
|
||
DWORD dwMute;
|
||
|
||
if (pVol->MuteControlId == (DWORD)-1) {
|
||
return FALSE;
|
||
}
|
||
|
||
mxd.cbStruct = sizeof(mxd);
|
||
mxd.dwControlID = pVol->MuteControlId;
|
||
mxd.cChannels = 1;
|
||
mxd.cMultipleItems = 0;
|
||
mxd.cbDetails = sizeof(DWORD);
|
||
mxd.paDetails = (LPDWORD)&dwMute;
|
||
|
||
mixerGetControlDetails(pVol->MixerId, &mxd, MIXER_GETCONTROLDETAILSF_VALUE);
|
||
|
||
if (pVol->Type == MasterVolume) {
|
||
bMuted = (BOOL)dwMute;
|
||
}
|
||
return (BOOL)dwMute;
|
||
}
|
||
VOID SetMixerMute(PVOLUME_CONTROL pVol, BOOL Set)
|
||
{
|
||
MIXERCONTROLDETAILS mxd;
|
||
|
||
if (pVol->MuteControlId == (DWORD)-1) {
|
||
return;
|
||
}
|
||
|
||
mxd.cbStruct = sizeof(mxd);
|
||
mxd.dwControlID = pVol->MuteControlId;
|
||
mxd.cChannels = 1;
|
||
mxd.cMultipleItems = 0;
|
||
mxd.cbDetails = sizeof(DWORD);
|
||
mxd.paDetails = (LPDWORD)&Set;
|
||
|
||
mixerSetControlDetails(pVol->MixerId, &mxd, MIXER_SETCONTROLDETAILSF_VALUE);
|
||
}
|
||
|
||
/*
|
||
** Add a master control
|
||
**
|
||
** Paramters
|
||
** MixerId - The mixer id
|
||
** dwMaster - The control id for volume setting
|
||
** dwMute - The control id for muting
|
||
** Record - whether it's a record or play master
|
||
*/
|
||
|
||
VOID
|
||
AddMasterControl(
|
||
HMIXEROBJ MixerId,
|
||
LPMIXERLINE LineInfo,
|
||
LPMIXERCONTROL ControlInfo,
|
||
DWORD dwMute,
|
||
BOOL Record
|
||
)
|
||
{
|
||
PVOLUME_CONTROL pVol;
|
||
|
||
pVol = AddNewControl();
|
||
|
||
if (pVol == NULL) {
|
||
return;
|
||
}
|
||
|
||
pVol->Type = MasterVolume;
|
||
pVol->MixerId = MixerId;
|
||
pVol->VolumeType = VolumeTypeMixerControl;
|
||
pVol->Stereo = LineInfo->cChannels > 1;
|
||
pVol->ControlId = ControlInfo->dwControlID;
|
||
pVol->RecordControl = Record;
|
||
pVol->MuteControlId = dwMute;
|
||
pVol->DestLineId = LineInfo->dwLineID;
|
||
lstrcpy(pVol->Name, LineInfo->szShortName);
|
||
|
||
if (FirstMasterIndex == (DWORD)-1) {
|
||
FirstMasterIndex = pVol->Index;
|
||
}
|
||
|
||
if (pVol->MuteControlId != (DWORD)-1) {
|
||
bMuted = GetMixerMute(pVol);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
AddVolumeControl(
|
||
HMIXEROBJ MixerId,
|
||
BOOL NoMasterSimulation,
|
||
LPMIXERLINE LineInfo,
|
||
LPMIXERCONTROL ControlInfo,
|
||
BOOL Record,
|
||
LPMIXERCONTROL MuxControl,
|
||
DWORD MuxSelectIndex,
|
||
BOOL MuxOrMixer,
|
||
DWORD MuteControlId,
|
||
DWORD DestLineId
|
||
)
|
||
{
|
||
PVOLUME_CONTROL pVol;
|
||
|
||
pVol = AddNewControl();
|
||
|
||
if (pVol == NULL) {
|
||
return;
|
||
}
|
||
|
||
pVol->Type = MixerControlVolume;
|
||
pVol->MixerId = MixerId;
|
||
pVol->VolumeType = VolumeTypeMixerControl;
|
||
pVol->Stereo = LineInfo->cChannels > 1;
|
||
#ifndef SHOWMUX
|
||
pVol->ControlId = ControlInfo->dwControlID;
|
||
#else
|
||
if (ControlInfo != NULL)
|
||
pVol->ControlId = ControlInfo->dwControlID;
|
||
else
|
||
pVol->ControlId = (DWORD)-1;
|
||
#endif
|
||
pVol->RecordControl = Record;
|
||
pVol->DestLineId = DestLineId;
|
||
|
||
if (Record) {
|
||
bRecordControllable = TRUE;
|
||
}
|
||
|
||
pVol->NoMasterSimulation = NoMasterSimulation;
|
||
pVol->MuxSelectIndex = MuxSelectIndex;
|
||
pVol->MuteControlId = MuteControlId;
|
||
if (MuxSelectIndex != (DWORD)-1) {
|
||
pVol->MuxControlId = MuxControl->dwControlID;
|
||
pVol->MuxOrMixer = MuxControl->dwControlType ==
|
||
MIXERCONTROL_CONTROLTYPE_MUX;
|
||
|
||
pVol->MuxItems = MuxControl->cMultipleItems;
|
||
}
|
||
|
||
lstrcpy(pVol->Name, LineInfo->szShortName);
|
||
}
|
||
|
||
//
|
||
// Get the mixer stuff we're interested in
|
||
//
|
||
|
||
VOID GetMixerControls(HMIXEROBJ MixerId)
|
||
{
|
||
|
||
MIXERCAPS MixerCaps;
|
||
DWORD DestLineIndex;
|
||
|
||
//
|
||
// Find the number of dest lines
|
||
//
|
||
|
||
if (mixerGetDevCaps((UINT)MixerId, &MixerCaps, sizeof(MixerCaps)) !=
|
||
MMSYSERR_NOERROR) {
|
||
return;
|
||
}
|
||
|
||
/*
|
||
** For each destination :
|
||
** If it's an output
|
||
** Find the master and mute controls if there are any
|
||
** Scan the source lines for suitable devices
|
||
**
|
||
** NB should this just be for speakers?
|
||
*/
|
||
|
||
for (DestLineIndex = 0;
|
||
DestLineIndex < MixerCaps.cDestinations;
|
||
DestLineIndex++) {
|
||
|
||
MIXERLINE DestLineInfo;
|
||
MIXERCONTROL MasterVolumeControl, MasterMuteControl;
|
||
MIXERCONTROL MuxControl;
|
||
DWORD dwMute;
|
||
DWORD dwMaster;
|
||
BOOL MasterFound;
|
||
BOOL IncludeLine;
|
||
BOOL RecordDestination;
|
||
BOOL MuxValid;
|
||
DWORD SourceIndex;
|
||
|
||
MasterFound = FALSE;
|
||
dwMute = (DWORD)-1;
|
||
dwMaster = (DWORD)-1;
|
||
|
||
DestLineInfo.cbStruct = sizeof(DestLineInfo);
|
||
DestLineInfo.dwDestination = DestLineIndex;
|
||
|
||
if (mixerGetLineInfo(MixerId,
|
||
&DestLineInfo,
|
||
MIXER_GETLINEINFOF_DESTINATION) !=
|
||
MMSYSERR_NOERROR) {
|
||
return; // Bad mixer or something
|
||
}
|
||
|
||
if (DestLineInfo.fdwLine & MIXERLINE_LINEF_DISCONNECTED) {
|
||
continue;
|
||
}
|
||
|
||
switch (DestLineInfo.dwComponentType) {
|
||
|
||
case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
|
||
case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES:
|
||
RecordDestination = FALSE;
|
||
IncludeLine = TRUE;
|
||
break;
|
||
|
||
case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
|
||
RecordDestination = TRUE;
|
||
IncludeLine = TRUE;
|
||
break;
|
||
|
||
default:
|
||
IncludeLine = FALSE;
|
||
break;
|
||
}
|
||
|
||
if (!IncludeLine) {
|
||
continue;
|
||
}
|
||
|
||
if (GetControlByType(MixerId,
|
||
DestLineInfo.dwLineID,
|
||
MIXERCONTROL_CONTROLTYPE_MUX,
|
||
&MuxControl) ||
|
||
GetControlByType(MixerId,
|
||
DestLineInfo.dwLineID,
|
||
MIXERCONTROL_CONTROLTYPE_MIXER,
|
||
&MuxControl)) {
|
||
/*
|
||
** Found a mux for this destination.
|
||
*/
|
||
|
||
MuxValid = TRUE;
|
||
} else {
|
||
|
||
/*
|
||
** No Mux
|
||
*/
|
||
|
||
MuxValid = FALSE;
|
||
}
|
||
|
||
/*
|
||
** Master and mute for all dest types
|
||
*/
|
||
|
||
if (GetControlByType(MixerId,
|
||
DestLineInfo.dwLineID,
|
||
MIXERCONTROL_CONTROLTYPE_VOLUME,
|
||
&MasterVolumeControl)) {
|
||
|
||
MasterFound = TRUE;
|
||
dwMaster = MasterVolumeControl.dwControlID;
|
||
|
||
if (GetControlByType(MixerId,
|
||
DestLineInfo.dwLineID,
|
||
MIXERCONTROL_CONTROLTYPE_MUTE,
|
||
&MasterMuteControl)) {
|
||
dwMute = MasterMuteControl.dwControlID;
|
||
}
|
||
|
||
/*
|
||
** Add master information
|
||
*/
|
||
|
||
AddMasterControl(MixerId,
|
||
&DestLineInfo,
|
||
&MasterVolumeControl,
|
||
dwMute,
|
||
RecordDestination);
|
||
|
||
}
|
||
|
||
/*
|
||
** Now find each individual source control we want to
|
||
** control
|
||
*/
|
||
|
||
for (SourceIndex = 0;
|
||
SourceIndex < DestLineInfo.cConnections;
|
||
SourceIndex++) {
|
||
MIXERLINE SourceLineInfo;
|
||
MIXERCONTROL SourceLineVolumeControl;
|
||
LPMIXERCONTROL lpSLVC = &SourceLineVolumeControl;
|
||
|
||
BOOL IncludeLine;
|
||
DWORD MuxSelectIndex;
|
||
DWORD MuteControlId;
|
||
|
||
MuxSelectIndex = (DWORD)-1;
|
||
|
||
SourceLineInfo.cbStruct = sizeof(SourceLineInfo);
|
||
SourceLineInfo.dwDestination = DestLineIndex;
|
||
SourceLineInfo.dwSource = SourceIndex;
|
||
|
||
if (mixerGetLineInfo(MixerId,
|
||
&SourceLineInfo,
|
||
MIXER_GETLINEINFOF_SOURCE) !=
|
||
MMSYSERR_NOERROR) {
|
||
return;
|
||
}
|
||
|
||
if (SourceLineInfo.fdwLine & MIXERLINE_LINEF_DISCONNECTED) {
|
||
continue;
|
||
}
|
||
|
||
|
||
switch (SourceLineInfo.dwComponentType) {
|
||
|
||
/*
|
||
** Only allow things we understand (and remove things
|
||
** like pc speaker to keep the number of sliders down).
|
||
*/
|
||
|
||
case MIXERLINE_COMPONENTTYPE_SRC_LINE:
|
||
case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
|
||
case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
|
||
case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
|
||
case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
|
||
case MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY:
|
||
case MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE:
|
||
case MIXERLINE_COMPONENTTYPE_SRC_DIGITAL:
|
||
IncludeLine = TRUE;
|
||
break;
|
||
|
||
default:
|
||
IncludeLine = TRUE;
|
||
break;
|
||
}
|
||
|
||
if (!IncludeLine) {
|
||
continue;
|
||
}
|
||
|
||
/*
|
||
** Try to get the relevant volume control
|
||
*/
|
||
|
||
if (!GetControlByType(MixerId,
|
||
SourceLineInfo.dwLineID,
|
||
MIXERCONTROL_CONTROLTYPE_VOLUME,
|
||
&SourceLineVolumeControl)) {
|
||
#ifdef SHOWMUX
|
||
lpSLVC = NULL;
|
||
#else
|
||
continue;
|
||
#endif
|
||
}
|
||
|
||
/*
|
||
** See if there's a mute
|
||
*/
|
||
{
|
||
MIXERCONTROL MuteControl;
|
||
|
||
if (GetControlByType(MixerId,
|
||
SourceLineInfo.dwLineID,
|
||
MIXERCONTROL_CONTROLTYPE_MUTE,
|
||
&MuteControl)) {
|
||
MuteControlId = MuteControl.dwControlID;
|
||
} else {
|
||
MuteControlId = (DWORD)-1;
|
||
}
|
||
}
|
||
|
||
/*
|
||
** See if we need an id to switch the recording on or
|
||
** off
|
||
*/
|
||
|
||
if (MuxValid) {
|
||
LPMIXERCONTROLDETAILS_LISTTEXT ListText;
|
||
|
||
ListText = (LPMIXERCONTROLDETAILS_LISTTEXT)
|
||
LocalAlloc(LPTR,
|
||
sizeof(*ListText) *
|
||
MuxControl.cMultipleItems);
|
||
|
||
if (ListText != NULL) {
|
||
MIXERCONTROLDETAILS mxd;
|
||
|
||
mxd.cbStruct = sizeof(mxd);
|
||
mxd.dwControlID = MuxControl.dwControlID;
|
||
mxd.cChannels = 1; // Why the ???
|
||
mxd.cMultipleItems = MuxControl.cMultipleItems;
|
||
mxd.cbDetails = sizeof(*ListText);
|
||
mxd.paDetails = (LPVOID)ListText;
|
||
|
||
if (mixerGetControlDetails(
|
||
MixerId,
|
||
&mxd,
|
||
MIXER_GETCONTROLDETAILSF_LISTTEXT) ==
|
||
MMSYSERR_NOERROR) {
|
||
UINT i;
|
||
|
||
/*
|
||
** Look for our line
|
||
*/
|
||
|
||
for (i = 0; i < MuxControl.cMultipleItems; i++) {
|
||
if (ListText[i].dwParam1 ==
|
||
SourceLineInfo.dwLineID) {
|
||
MuxSelectIndex = i;
|
||
}
|
||
}
|
||
}
|
||
|
||
LocalFree((HLOCAL)ListText);
|
||
}
|
||
}
|
||
|
||
/*
|
||
** Add this volume control to the list
|
||
*/
|
||
|
||
AddVolumeControl(MixerId,
|
||
MasterFound || RecordDestination,
|
||
&SourceLineInfo,
|
||
// &SourceLineVolumeControl,
|
||
lpSLVC,
|
||
RecordDestination,
|
||
MuxValid ? &MuxControl : NULL,
|
||
MuxSelectIndex,
|
||
MuxValid ? FALSE :
|
||
MuxControl.dwControlType ==
|
||
MIXERCONTROL_CONTROLTYPE_MUX,
|
||
MuteControlId,
|
||
DestLineInfo.dwLineID);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Scan through all relevant devices.
|
||
// If pVol is 0 just count them, otherwise save away info
|
||
// about them as well
|
||
//
|
||
|
||
VOID FindDevices(VOLUME_DEVICE_TYPE Type)
|
||
{
|
||
UINT N;
|
||
UINT id;
|
||
|
||
N = Type == MasterVolume ? 0 :
|
||
Type == AuxVolume ? auxGetNumDevs() :
|
||
Type == MidiOutVolume ? midiOutGetNumDevs() :
|
||
Type == WaveOutVolume ? waveOutGetNumDevs() :
|
||
Type == MixerControlVolume ? mixerGetNumDevs() :
|
||
0;
|
||
|
||
|
||
for (id = 0; id < N; id++) {
|
||
if (Type == MixerControlVolume) {
|
||
//
|
||
// Find out how many suitable volume controls this mixer
|
||
// supports.
|
||
//
|
||
// This is incredibly laborious because we can't just enumerate
|
||
// the controls (!).
|
||
//
|
||
// This next call has the side effect of generating the mixer
|
||
// master stuff too and a set of mixer handles.
|
||
//
|
||
|
||
GetMixerControls(MixerId);
|
||
return;
|
||
} else {
|
||
BOOL Volume;
|
||
BOOL Stereo;
|
||
TCHAR Name[MAXPNAMELEN];
|
||
UINT Technology;
|
||
|
||
if (ExtractInfo(id, Type, &Volume, &Stereo, Name, &Technology)) {
|
||
if (Volume) {
|
||
PVOLUME_CONTROL pVol;
|
||
|
||
/*
|
||
** Supports volume setting
|
||
*/
|
||
|
||
pVol = AddNewControl();
|
||
|
||
if (pVol) {
|
||
pVol->id = id;
|
||
pVol->Type = Type;
|
||
pVol->VolumeType = Technology;
|
||
pVol->Stereo = Stereo;
|
||
pVol++;
|
||
}
|
||
}
|
||
} else {
|
||
continue; // Don't use this one
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Create and initialize our volume array
|
||
*
|
||
* On exit
|
||
* NumberOfDevices is set to the number of devices we want
|
||
* Vol is an array of size NumberOfDevices (may be 0)
|
||
*/
|
||
|
||
BOOL VolInit(VOID)
|
||
{
|
||
int i;
|
||
WORD wLeft, wRight, wMax, wMin, wTemp;
|
||
|
||
/*
|
||
** Free any volume stuff currently present
|
||
*/
|
||
|
||
if (Vol) {
|
||
HGLOBAL hVol;
|
||
int i;
|
||
|
||
/*
|
||
** Free all the windows
|
||
*/
|
||
for (i = 0; i < NumberOfDevices; i++) {
|
||
DestroyOurWindow(&Vol[i].hChildWnd);
|
||
DestroyOurWindow(&Vol[i].hMeterWnd);
|
||
DestroyOurWindow(&Vol[i].hStatic);
|
||
DestroyOurWindow(&Vol[i].hCheckBox);
|
||
}
|
||
|
||
/*
|
||
** Free the memory
|
||
*/
|
||
|
||
hVol = GlobalHandle(Vol);
|
||
GlobalUnlock(hVol);
|
||
GlobalFree(hVol);
|
||
Vol = NULL;
|
||
|
||
/*
|
||
** Initialize globals
|
||
*/
|
||
|
||
bRecordControllable = FALSE;
|
||
}
|
||
|
||
/*
|
||
** No master volume controls found yet
|
||
*/
|
||
|
||
FirstMasterIndex = (DWORD)-1;
|
||
|
||
/*
|
||
* Scan all the device types we're interested in :
|
||
* wave out
|
||
* midi out
|
||
* aux
|
||
*/
|
||
|
||
if ((DWORD)MixerId != (DWORD)-1) {
|
||
FindDevices(MixerControlVolume);
|
||
} else {
|
||
for (i = WaveOutVolume; i < NumberOfDeviceTypes; i++) {
|
||
FindDevices(i);
|
||
}
|
||
}
|
||
|
||
if (NumberOfDevices == 0) {
|
||
return FALSE;
|
||
}
|
||
|
||
if (FirstMasterIndex == (DWORD)-1) {
|
||
PVOLUME_CONTROL pMaster;
|
||
BOOL bStereo;
|
||
|
||
/*
|
||
** Find if any devices are stereo
|
||
*/
|
||
|
||
bStereo = FALSE;
|
||
|
||
for (i = 0; i < NumberOfDevices; i++) {
|
||
if (Vol[i].Stereo) {
|
||
bStereo = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
** Create a default volume control
|
||
*/
|
||
|
||
pMaster = AddNewControl();
|
||
if (pMaster == NULL) {
|
||
return FALSE;
|
||
}
|
||
|
||
pMaster->Type = MasterVolume;
|
||
pMaster->VolumeType = -1;
|
||
|
||
pMaster->Stereo = bStereo;
|
||
|
||
FirstMasterIndex = pMaster->Index;
|
||
|
||
wLeft = (WORD)MasterLeft;
|
||
wRight = (WORD)MasterRight;
|
||
|
||
pMaster->LRVolume = MAKELONG(wLeft, wRight);
|
||
|
||
if (wRight > wLeft) {
|
||
wMax = wRight;
|
||
wMin = wLeft;
|
||
} else {
|
||
wMax = wLeft;
|
||
wMin = wRight;
|
||
}
|
||
|
||
if (wMax == 0) {
|
||
|
||
pMaster->Volume = 0;
|
||
pMaster->Balance = 0x80; /* centered */
|
||
|
||
} else {
|
||
|
||
pMaster->Volume = wMax >> 8;
|
||
|
||
wTemp = (UINT) (((DWORD) (wMax - wMin) << 7) / wMax);
|
||
if (wTemp > 0x7f) wTemp = 0x7f;
|
||
|
||
if (wRight > wLeft)
|
||
pMaster->Balance = 0x80 + wTemp;
|
||
else
|
||
pMaster->Balance = 0x7f - wTemp;
|
||
}
|
||
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*
|
||
** Called when a mixer calls us back with a control change
|
||
*/
|
||
|
||
VOID ControlChange(HMIXER hMixer, DWORD ControlId)
|
||
{
|
||
UINT i;
|
||
HMIXEROBJ MixerId;
|
||
MMRESULT mmr;
|
||
|
||
mmr = mixerGetID((HMIXEROBJ)hMixer, (PUINT)&MixerId, MIXER_OBJECTF_HMIXER);
|
||
|
||
if (mmr != MMSYSERR_NOERROR) {
|
||
return;
|
||
}
|
||
for (i = 0; i < (UINT)NumberOfDevices; i++) {
|
||
|
||
if (Vol[i].MixerId == MixerId) {
|
||
if (Vol[i].VolumeType == VolumeTypeMixerControl) {
|
||
if (ControlId == Vol[i].ControlId) {
|
||
UpdateVolume(&Vol[i]);
|
||
|
||
/*
|
||
** Volume controls only affect one control
|
||
** (unlike muxes)
|
||
*/
|
||
|
||
break;
|
||
} else {
|
||
if (ControlId == Vol[i].MuxControlId ||
|
||
ControlId == Vol[i].MuteControlId) {
|
||
|
||
UpdateSelected(&Vol[i]);
|
||
}
|
||
}
|
||
}
|
||
} /* MixerId == Vol[i].MixerId */
|
||
}
|
||
}
|
||
|
||
PVOLUME_CONTROL FirstDevice(BOOL bRecord)
|
||
{
|
||
UINT i;
|
||
for (i = 0; i < (UINT)NumberOfDevices; i++) {
|
||
if (Vol[i].Type != MasterVolume &&
|
||
Vol[i].RecordControl == bRecord) {
|
||
return &Vol[i];
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
PVOLUME_CONTROL LastDevice(BOOL bRecord)
|
||
{
|
||
UINT i;
|
||
for (i = NumberOfDevices; i > 0; i--) {
|
||
if (Vol[i - 1].Type != MasterVolume &&
|
||
Vol[i - 1].RecordControl == bRecord) {
|
||
return &Vol[i - 1];
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
PVOLUME_CONTROL NextDevice(PVOLUME_CONTROL pVol)
|
||
{
|
||
UINT i;
|
||
|
||
for (i = pVol->Index == (UINT)NumberOfDevices - 1 ? 0 : pVol->Index + 1 ;
|
||
i != pVol->Index;
|
||
i = i == (UINT)NumberOfDevices - 1 ? 0 : i + 1) {
|
||
|
||
if (Vol[i].Type != MasterVolume &&
|
||
Vol[i].RecordControl == pVol->RecordControl) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
return &Vol[i];
|
||
}
|
||
|
||
PVOLUME_CONTROL NextDeviceNoWrap(PVOLUME_CONTROL pVol)
|
||
{
|
||
UINT i;
|
||
|
||
for (i = pVol->Index + 1 ;
|
||
i < (UINT)NumberOfDevices;
|
||
i = i + 1) {
|
||
|
||
if (Vol[i].Type != MasterVolume &&
|
||
Vol[i].RecordControl == pVol->RecordControl) {
|
||
return &Vol[i];
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
PVOLUME_CONTROL PrevDevice(PVOLUME_CONTROL pVol)
|
||
{
|
||
UINT i;
|
||
|
||
for (i = pVol->Index == 0 ? NumberOfDevices - 1 : pVol->Index - 1;
|
||
i != pVol->Index;
|
||
i = i == 0 ? NumberOfDevices - 1 : i - 1) {
|
||
|
||
if (Vol[i].Type != MasterVolume &&
|
||
Vol[i].RecordControl == pVol->RecordControl) {
|
||
return &Vol[i];
|
||
}
|
||
}
|
||
|
||
return &Vol[i];
|
||
}
|
||
|
||
PVOLUME_CONTROL PrevDeviceNoWrap(PVOLUME_CONTROL pVol)
|
||
{
|
||
UINT i;
|
||
|
||
for (i = pVol->Index;
|
||
i != 0;
|
||
i = i - 1) {
|
||
|
||
if (Vol[i - 1].Type != MasterVolume &&
|
||
Vol[i - 1].RecordControl == pVol->RecordControl) {
|
||
return &Vol[i - 1];
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
PVOLUME_CONTROL MasterDevice(BOOL bRecord)
|
||
{
|
||
UINT i;
|
||
|
||
for (i = 0 ; i < (UINT)NumberOfDevices; i++) {
|
||
if (Vol[i].Type == MasterVolume &&
|
||
Vol[i].RecordControl == bRecord) {
|
||
return &Vol[i];
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|