You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1504 lines
40 KiB
1504 lines
40 KiB
/* (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;
|
|
}
|
|
|