|
|
/* (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; }
|