[AUDIO] Implement volume control support (#6922)

Implement volume level changing for Aux/MidiOut/WaveOut devices. It's represented the following WINMM functions:
- auxGetVolume,
- auxSetVolume,
- midiOutGetVolume,
- midiOutSetVolume,
- waveOutGetVolume,
- waveOutSetVolume,
which are calling the followind messages appropriately:
- AUXDM_GETVOLUME,
- AUXDM_SETVOLUME,
- MODM_GETVOLUME,
- MODM_SETVOLUME,
- WODM_GETVOLUME,
- WODM_SETVOLUME.
This fixes volume control for several 3rd-party programs (like Fox Audio Player 0.10.2 from Rapps, Winamp 2.95 with WaveOut plugin). However it does not fix changing the volume in system volume mixers (SndVol32, MMSys), since they are using their own functionality instead. They technically do the same things, but apart from the functions mentioned above.
CORE-14780
This commit is contained in:
Oleg Dubinskiy 2024-06-01 12:30:33 +02:00 committed by GitHub
parent e1db293f12
commit 9046cc97ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 323 additions and 0 deletions

View file

@ -850,6 +850,25 @@ WdmAudGetWavePositionByLegacy(
return MMSYSERR_NOERROR; return MMSYSERR_NOERROR;
} }
MMRESULT
WdmAudGetVolumeByLegacy(
_In_ PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
_In_ DWORD DeviceId,
_Out_ PDWORD pdwVolume)
{
/* FIXME */
return MMSYSERR_NOTSUPPORTED;
}
MMRESULT
WdmAudSetVolumeByLegacy(
_In_ PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
_In_ DWORD DeviceId,
_In_ DWORD dwVolume)
{
/* FIXME */
return MMSYSERR_NOTSUPPORTED;
}
MMRESULT MMRESULT
WdmAudResetStreamByLegacy( WdmAudResetStreamByLegacy(

View file

@ -805,6 +805,121 @@ WdmAudGetWavePositionByMMixer(
return MMSYSERR_NOTSUPPORTED; return MMSYSERR_NOTSUPPORTED;
} }
MMRESULT
WdmAudGetVolumeByMMixer(
_In_ PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
_In_ DWORD DeviceId,
_Out_ PDWORD pdwVolume)
{
MMRESULT Result;
MIXERLINE MixLine;
MIXERCONTROL MixControl;
MIXERLINECONTROLS MixLineControls;
MIXERCONTROLDETAILS MixControlDetails;
MIXERCONTROLDETAILS_UNSIGNED MixControlDetailsU[2]; // For 2 (stereo) channels
MixLine.cbStruct = sizeof(MixLine);
MixLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
/* Get line info */
Result = WdmAudGetLineInfo(SoundDeviceInstance->Handle,
DeviceId,
&MixLine,
MIXER_OBJECTF_MIXER | MIXER_GETLINEINFOF_COMPONENTTYPE);
if (!MMSUCCESS(Result))
return TranslateInternalMmResult(Result);
MixLineControls.cbStruct = sizeof(MixLineControls);
MixLineControls.dwLineID = MixLine.dwLineID;
MixLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
MixLineControls.cControls = 1;
MixLineControls.cbmxctrl = sizeof(MixControl);
MixLineControls.pamxctrl = &MixControl;
/* Get line controls */
Result = WdmAudGetLineControls(SoundDeviceInstance->Handle,
DeviceId,
&MixLineControls,
MIXER_OBJECTF_MIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE);
if (!MMSUCCESS(Result))
return TranslateInternalMmResult(Result);
MixControlDetails.cbStruct = sizeof(MixControlDetails);
MixControlDetails.dwControlID = MixControl.dwControlID;
MixControlDetails.cChannels = MixLine.cChannels;
MixControlDetails.cMultipleItems = 0;
MixControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
MixControlDetails.paDetails = MixControlDetailsU;
/* Get volume control details */
Result = WdmAudGetControlDetails(SoundDeviceInstance->Handle,
DeviceId,
&MixControlDetails,
MIXER_OBJECTF_MIXER);
if (MMSUCCESS(Result))
*pdwVolume = MAKELONG(LOWORD(MixControlDetailsU[0].dwValue), HIWORD(MixControlDetailsU[1].dwValue));
return Result;
}
MMRESULT
WdmAudSetVolumeByMMixer(
_In_ PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
_In_ DWORD DeviceId,
_In_ DWORD dwVolume)
{
MMRESULT Result;
MIXERLINE MixLine;
MIXERCONTROL MixControl;
MIXERLINECONTROLS MixLineControls;
MIXERCONTROLDETAILS MixControlDetails;
MIXERCONTROLDETAILS_UNSIGNED MixControlDetailsU[2]; // For 2 (stereo) channels
MixLine.cbStruct = sizeof(MixLine);
MixLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
/* Get line info */
Result = WdmAudGetLineInfo(SoundDeviceInstance->Handle,
DeviceId,
&MixLine,
MIXER_OBJECTF_MIXER | MIXER_GETLINEINFOF_COMPONENTTYPE);
if (!MMSUCCESS(Result))
return TranslateInternalMmResult(Result);
MixLineControls.cbStruct = sizeof(MixLineControls);
MixLineControls.dwLineID = MixLine.dwLineID;
MixLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
MixLineControls.cControls = 1;
MixLineControls.cbmxctrl = sizeof(MixControl);
MixLineControls.pamxctrl = &MixControl;
/* Get line controls */
Result = WdmAudGetLineControls(SoundDeviceInstance->Handle,
DeviceId,
&MixLineControls,
MIXER_OBJECTF_MIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE);
if (!MMSUCCESS(Result))
return TranslateInternalMmResult(Result);
/* Convert volume level to be set */
MixControlDetailsU[0].dwValue = LOWORD(dwVolume); // Left channel
MixControlDetailsU[1].dwValue = HIWORD(dwVolume); // Right channel
MixControlDetails.cbStruct = sizeof(MixControlDetails);
MixControlDetails.dwControlID = MixControl.dwControlID;
MixControlDetails.cChannels = MixLine.cChannels;
MixControlDetails.cMultipleItems = 0;
MixControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
MixControlDetails.paDetails = MixControlDetailsU;
/* Set volume control details */
Result = WdmAudSetControlDetails(SoundDeviceInstance->Handle,
DeviceId,
&MixControlDetails,
MIXER_OBJECTF_MIXER);
return Result;
}
static static
VOID WINAPI VOID WINAPI
CommitWaveBufferApc(PVOID ApcContext, CommitWaveBufferApc(PVOID ApcContext,

View file

@ -76,6 +76,12 @@ PopulateWdmDeviceList(
FuncTable.Close = FUNC_NAME(WdmAudCloseSoundDevice); FuncTable.Close = FUNC_NAME(WdmAudCloseSoundDevice);
FuncTable.GetDeviceInterfaceString = FUNC_NAME(WdmAudGetDeviceInterfaceString); FuncTable.GetDeviceInterfaceString = FUNC_NAME(WdmAudGetDeviceInterfaceString);
if (DeviceType == AUX_DEVICE_TYPE || DeviceType == MIDI_OUT_DEVICE_TYPE || DeviceType == WAVE_OUT_DEVICE_TYPE)
{
FuncTable.GetVolume = FUNC_NAME(WdmAudGetVolume);
FuncTable.SetVolume = FUNC_NAME(WdmAudSetVolume);
}
if (DeviceType == MIXER_DEVICE_TYPE) if (DeviceType == MIXER_DEVICE_TYPE)
{ {
FuncTable.SetWaveFormat = FUNC_NAME(WdmAudSetMixerDeviceFormat); FuncTable.SetWaveFormat = FUNC_NAME(WdmAudSetMixerDeviceFormat);

View file

@ -144,6 +144,18 @@ WdmAudGetWavePositionByMMixer(
IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance, IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
IN MMTIME* Time); IN MMTIME* Time);
MMRESULT
WdmAudGetVolumeByMMixer(
_In_ PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
_In_ DWORD DeviceId,
_Out_ PDWORD pdwVolume);
MMRESULT
WdmAudSetVolumeByMMixer(
_In_ PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
_In_ DWORD DeviceId,
_In_ DWORD dwVolume);
MMRESULT MMRESULT
WdmAudCommitWaveBufferByMMixer( WdmAudCommitWaveBufferByMMixer(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
@ -224,6 +236,18 @@ WdmAudGetWavePositionByLegacy(
IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance, IN struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
IN MMTIME* Time); IN MMTIME* Time);
MMRESULT
WdmAudGetVolumeByLegacy(
_In_ PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
_In_ DWORD DeviceId,
_Out_ PDWORD pdwVolume);
MMRESULT
WdmAudSetVolumeByLegacy(
_In_ PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
_In_ DWORD DeviceId,
_In_ DWORD dwVolume);
MMRESULT MMRESULT
WriteFileEx_Committer2( WriteFileEx_Committer2(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,

View file

@ -190,6 +190,16 @@ typedef MMRESULT(*MMRESETSTREAM_FUNC)(
IN MMDEVICE_TYPE DeviceType, IN MMDEVICE_TYPE DeviceType,
IN BOOLEAN bStartReset); IN BOOLEAN bStartReset);
typedef MMRESULT(*MMGETVOLUME_FUNC)(
_In_ struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
_In_ DWORD DeviceId,
_Out_ PDWORD pdwVolume);
typedef MMRESULT(*MMSETVOLUME_FUNC)(
_In_ struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
_In_ DWORD DeviceId,
_In_ DWORD dwVolume);
typedef struct _MMFUNCTION_TABLE typedef struct _MMFUNCTION_TABLE
{ {
union union
@ -216,6 +226,9 @@ typedef struct _MMFUNCTION_TABLE
MMQUERYDEVICEINTERFACESTRING_FUNC GetDeviceInterfaceString; MMQUERYDEVICEINTERFACESTRING_FUNC GetDeviceInterfaceString;
MMRESETSTREAM_FUNC ResetStream; MMRESETSTREAM_FUNC ResetStream;
MMGETVOLUME_FUNC GetVolume;
MMSETVOLUME_FUNC SetVolume;
// Redundant // Redundant
//MMWAVEHEADER_FUNC PrepareWaveHeader; //MMWAVEHEADER_FUNC PrepareWaveHeader;
//MMWAVEHEADER_FUNC UnprepareWaveHeader; //MMWAVEHEADER_FUNC UnprepareWaveHeader;
@ -369,6 +382,20 @@ MmeGetPosition(
IN MMTIME* Time, IN MMTIME* Time,
IN DWORD Size); IN DWORD Size);
MMRESULT
MmeGetVolume(
_In_ MMDEVICE_TYPE DeviceType,
_In_ DWORD DeviceId,
_In_ DWORD_PTR PrivateHandle,
_Out_ DWORD_PTR pdwVolume);
MMRESULT
MmeSetVolume(
_In_ MMDEVICE_TYPE DeviceType,
_In_ DWORD DeviceId,
_In_ DWORD_PTR PrivateHandle,
_In_ DWORD_PTR dwVolume);
MMRESULT MMRESULT
MmeGetDeviceInterfaceString( MmeGetDeviceInterfaceString(
IN MMDEVICE_TYPE DeviceType, IN MMDEVICE_TYPE DeviceType,

View file

@ -45,6 +45,24 @@ auxMessage(
Parameter2); Parameter2);
break; break;
} }
case AUXDM_GETVOLUME:
{
Result = MmeGetVolume(AUX_DEVICE_TYPE,
DeviceId,
PrivateHandle,
Parameter1);
break;
}
case AUXDM_SETVOLUME:
{
Result = MmeSetVolume(AUX_DEVICE_TYPE,
DeviceId,
PrivateHandle,
Parameter1);
break;
}
} }
SND_TRACE(L"auxMessage returning MMRESULT %d\n", Result); SND_TRACE(L"auxMessage returning MMRESULT %d\n", Result);

View file

@ -75,6 +75,23 @@ modMessage(
break; break;
} }
case MODM_GETVOLUME:
{
Result = MmeGetVolume(MIDI_OUT_DEVICE_TYPE,
DeviceId,
PrivateHandle,
Parameter1);
break;
}
case MODM_SETVOLUME:
{
Result = MmeSetVolume(MIDI_OUT_DEVICE_TYPE,
DeviceId,
PrivateHandle,
Parameter1);
break;
}
} }
SND_TRACE(L"modMessage returning MMRESULT %d\n", Result); SND_TRACE(L"modMessage returning MMRESULT %d\n", Result);

View file

@ -365,3 +365,82 @@ MmeGetPosition(
return Result; return Result;
} }
MMRESULT
MmeGetVolume(
_In_ MMDEVICE_TYPE DeviceType,
_In_ DWORD DeviceId,
_In_ DWORD_PTR PrivateHandle,
_Out_ DWORD_PTR pdwVolume)
{
MMRESULT Result;
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
PSOUND_DEVICE SoundDevice;
PMMFUNCTION_TABLE FunctionTable;
/* Sanity check */
SND_ASSERT(DeviceType == AUX_DEVICE_TYPE ||
DeviceType == MIDI_OUT_DEVICE_TYPE ||
DeviceType == WAVE_OUT_DEVICE_TYPE);
VALIDATE_MMSYS_PARAMETER(PrivateHandle);
SoundDeviceInstance = (PSOUND_DEVICE_INSTANCE)PrivateHandle;
if (!IsValidSoundDeviceInstance(SoundDeviceInstance))
return MMSYSERR_INVALHANDLE;
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
if (!MMSUCCESS(Result))
return TranslateInternalMmResult(Result);
Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
if (!MMSUCCESS(Result))
return TranslateInternalMmResult(Result);
if (!FunctionTable->GetVolume)
return MMSYSERR_NOTSUPPORTED;
/* Call the driver */
Result = FunctionTable->GetVolume(SoundDeviceInstance, DeviceId, (PDWORD)pdwVolume);
return Result;
}
MMRESULT
MmeSetVolume(
_In_ MMDEVICE_TYPE DeviceType,
_In_ DWORD DeviceId,
_In_ DWORD_PTR PrivateHandle,
_In_ DWORD_PTR dwVolume)
{
MMRESULT Result;
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
PSOUND_DEVICE SoundDevice;
PMMFUNCTION_TABLE FunctionTable;
/* Sanity check */
SND_ASSERT(DeviceType == AUX_DEVICE_TYPE ||
DeviceType == MIDI_OUT_DEVICE_TYPE ||
DeviceType == WAVE_OUT_DEVICE_TYPE);
VALIDATE_MMSYS_PARAMETER(PrivateHandle);
SoundDeviceInstance = (PSOUND_DEVICE_INSTANCE)PrivateHandle;
if (!IsValidSoundDeviceInstance(SoundDeviceInstance))
return MMSYSERR_INVALHANDLE;
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
if (!MMSUCCESS(Result))
return TranslateInternalMmResult(Result);
Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
if (!MMSUCCESS(Result))
return TranslateInternalMmResult(Result);
if (!FunctionTable->SetVolume)
return MMSYSERR_NOTSUPPORTED;
/* Call the driver */
Result = FunctionTable->SetVolume(SoundDeviceInstance, DeviceId, (DWORD)dwVolume);
return Result;
}

View file

@ -118,6 +118,24 @@ wodMessage(
break; break;
} }
case WODM_GETVOLUME:
{
Result = MmeGetVolume(WAVE_OUT_DEVICE_TYPE,
DeviceId,
PrivateHandle,
Parameter1);
break;
}
case WODM_SETVOLUME:
{
Result = MmeSetVolume(WAVE_OUT_DEVICE_TYPE,
DeviceId,
PrivateHandle,
Parameter1);
break;
}
case DRV_QUERYDEVICEINTERFACESIZE : case DRV_QUERYDEVICEINTERFACESIZE :
{ {
Result = MmeGetDeviceInterfaceString(WAVE_OUT_DEVICE_TYPE, DeviceId, NULL, 0, (DWORD*)Parameter1); //FIXME DWORD_PTR Result = MmeGetDeviceInterfaceString(WAVE_OUT_DEVICE_TYPE, DeviceId, NULL, 0, (DWORD*)Parameter1); //FIXME DWORD_PTR