Source code of Windows XP (NT5)
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
39 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;
}