/* (C) Copyright Microsoft Corporation 1993. All Rights Reserved */ #include #include #include #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; }