Skip to content

Commit 27fde6f

Browse files
[MMIXER] Further imrovements to volume control support (follow-up of 376708b)
Add more improvements and fixes to volume control implementation. - Don't allocate and don't use an array for storing volume level values. Also, get rid from some stuff, which is not used any more (some fields of MIXERVOLUME_DATA structure and MMixerGetVOlumeControlIndex() function). - Use the following formulas to properly convert the volume level values from the logical units range (0 - 65535) to the hardware Decibel (DB) range (defined by audio miniport driver): <decibels> = <units> * <range_in_db> / <range_in_units> + <minimal_level> (for setting the new value) and <units> = (<decibels> - <mimimal_level> + 1) * <range_in_db> / <range_in_units> (for getting the previous value), where <decibels> is a DB hardware value, <units> is logical units value, <rang_in_db> is the hardware range (DB), <range_in_units> is range in the logical units and <minimal_level> is the most minimum volume level value defined by an audio miniport driver. - I've created this formula myself basing on my calculations and investigations (with some help from Hermes Belusca-Maito), so it's tested and confirmed to be working for all possible values range (at least for our official Intel AC97 driver, and, as tested later, Realtek HD audio). - Do this in both cases when setting the new and when getting the previous volume value as well. - Fallback to default values range -96 - 0 DB in case either the volume level property is not supported by audio miniport driver, or the values range is empty (SignedMinimum is equal to SignedMaximum and both of them typically have a 0 (zero) value). Realtek HD audio codec is one of such a drivers, so this fixes the volume control on real hardware too (tested on Asus-F5R notebook with Realtek ALC660 audio controller). Moreover, the volume values set by user are even properly saved (aren't lost) after reboot (unlike with Intel AC97 in VirtualBox or SoundBlaster in VMware)! Realtek probably uses another mechanism to write/read the value(s) to/from Registry, which is handled by the miniport (codec) driver instead. This fixes some remaining bugs when changing the volume level, so now 1) min/max position of the volume bar can be reached correctly and 2) left/right balance sliders are now behaving properly (they don't affect position of each other anymore when moving them manually). CORE-19189, CORE-19190
1 parent c9911d3 commit 27fde6f

File tree

3 files changed

+33
-72
lines changed

3 files changed

+33
-72
lines changed

sdk/lib/drivers/sound/mmixer/controls.c

Lines changed: 24 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ MMixerAddMixerControl(
159159
PKSPROPERTY_DESCRIPTION Desc;
160160
PKSPROPERTY_MEMBERSHEADER Members;
161161
PKSPROPERTY_STEPPING_LONG Range;
162+
LPMIXERVOLUME_DATA VolumeData;
162163

163164
MixerControl->Control.Bounds.dwMinimum = 0;
164165
MixerControl->Control.Bounds.dwMaximum = 0xFFFF;
@@ -179,54 +180,38 @@ MMixerAddMixerControl(
179180
/* get node volume level info */
180181
Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), Desc, Length, &BytesReturned);
181182

183+
VolumeData = (LPMIXERVOLUME_DATA)MixerContext->Alloc(sizeof(*VolumeData));
184+
if (!VolumeData)
185+
return MM_STATUS_NO_MEMORY;
186+
182187
if (Status == MM_STATUS_SUCCESS)
183188
{
184-
LPMIXERVOLUME_DATA VolumeData;
185-
ULONG Steps, MaxRange, Index;
186-
LONG Value;
187-
188189
Members = (PKSPROPERTY_MEMBERSHEADER)(Desc + 1);
189190
Range = (PKSPROPERTY_STEPPING_LONG)(Members + 1);
190191

191192
DPRINT("NodeIndex %u Range Min %d Max %d Steps %x UMin %x UMax %x\n", NodeIndex, Range->Bounds.SignedMinimum, Range->Bounds.SignedMaximum, Range->SteppingDelta, Range->Bounds.UnsignedMinimum, Range->Bounds.UnsignedMaximum);
192193

193-
MaxRange = Range->Bounds.UnsignedMaximum - Range->Bounds.UnsignedMinimum;
194+
/* Store mixer control info there */
195+
VolumeData->Header.dwControlID = MixerControl->Control.dwControlID;
196+
VolumeData->SignedMinimum = Range->Bounds.SignedMinimum;
197+
VolumeData->SignedMaximum = Range->Bounds.SignedMaximum;
194198

195-
if (MaxRange)
199+
/* Fallback to defaults if: 1) the range is not defined (typically is 0) */
200+
if (VolumeData->SignedMinimum == VolumeData->SignedMaximum)
196201
{
197-
ASSERT(MaxRange);
198-
VolumeData = (LPMIXERVOLUME_DATA)MixerContext->Alloc(sizeof(MIXERVOLUME_DATA));
199-
if (!VolumeData)
200-
return MM_STATUS_NO_MEMORY;
201-
202-
Steps = MaxRange / Range->SteppingDelta + 1;
203-
204-
/* store mixer control info there */
205-
VolumeData->Header.dwControlID = MixerControl->Control.dwControlID;
206-
VolumeData->SignedMaximum = Range->Bounds.SignedMaximum;
207-
VolumeData->SignedMinimum = Range->Bounds.SignedMinimum;
208-
VolumeData->SteppingDelta = Range->SteppingDelta;
209-
VolumeData->ValuesCount = Steps;
210-
VolumeData->InputSteppingDelta = 0x10000 / Steps;
211-
212-
VolumeData->Values = (PLONG)MixerContext->Alloc(sizeof(LONG) * Steps);
213-
if (!VolumeData->Values)
214-
{
215-
MixerContext->Free(Desc);
216-
MixerContext->Free(VolumeData);
217-
return MM_STATUS_NO_MEMORY;
218-
}
219-
220-
Value = Range->Bounds.SignedMinimum;
221-
for (Index = 0; Index < Steps; Index++)
222-
{
223-
VolumeData->Values[Index] = Value;
224-
Value += Range->SteppingDelta;
225-
}
226-
MixerControl->ExtraData = VolumeData;
227-
}
228-
}
229-
MixerContext->Free(Desc);
202+
VolumeData->SignedMinimum = -96 * 0x10000; // -96 DB
203+
VolumeData->SignedMaximum = 0; // 0 DB
204+
}
205+
}
206+
else
207+
{
208+
/* or 2) when some failure occurs */
209+
VolumeData->Header.dwControlID = MixerControl->Control.dwControlID;
210+
VolumeData->SignedMinimum = -96 * 0x10000; // -96 DB
211+
VolumeData->SignedMaximum = 0; // 0 DB
212+
}
213+
MixerControl->ExtraData = VolumeData;
214+
MixerContext->Free(Desc);
230215
}
231216

232217
DPRINT("Status %x Name %S\n", Status, MixerControl->Control.szName);

sdk/lib/drivers/sound/mmixer/precomp.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,6 @@ typedef struct
102102
MIXERCONTROL_DATA Header;
103103
LONG SignedMinimum;
104104
LONG SignedMaximum;
105-
LONG SteppingDelta;
106-
ULONG InputSteppingDelta;
107-
ULONG ValuesCount;
108-
PLONG Values;
109105
}MIXERVOLUME_DATA, *LPMIXERVOLUME_DATA;
110106

111107
typedef struct

sdk/lib/drivers/sound/mmixer/sup.c

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -315,23 +315,6 @@ MMixerGetMixerControlById(
315315
return MM_STATUS_UNSUCCESSFUL;
316316
}
317317

318-
ULONG
319-
MMixerGetVolumeControlIndex(
320-
LPMIXERVOLUME_DATA VolumeData,
321-
LONG Value)
322-
{
323-
ULONG Index;
324-
325-
for(Index = 0; Index < VolumeData->ValuesCount; Index++)
326-
{
327-
if (VolumeData->Values[Index] > Value)
328-
{
329-
return VolumeData->InputSteppingDelta * Index;
330-
}
331-
}
332-
return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1);
333-
}
334-
335318
VOID
336319
MMixerNotifyControlChange(
337320
IN PMIXER_CONTEXT MixerContext,
@@ -672,8 +655,8 @@ MMixerSetGetVolumeControlDetails(
672655
LPMIXERLINE_EXT MixerLine)
673656
{
674657
LPMIXERCONTROLDETAILS_UNSIGNED Input;
675-
LONG Value;
676-
ULONG Index, Channel;
658+
LONG MaxRange, Value;
659+
ULONG Channel;
677660
MIXER_STATUS Status;
678661
LPMIXERVOLUME_DATA VolumeData;
679662

@@ -689,21 +672,17 @@ MMixerSetGetVolumeControlDetails(
689672
if (!Input)
690673
return MM_STATUS_UNSUCCESSFUL; /* To prevent dereferencing NULL */
691674

675+
/* Get maximum available range */
676+
MaxRange = VolumeData->SignedMaximum - VolumeData->SignedMinimum;
677+
692678
/* Loop for each channel */
693679
for (Channel = 0; Channel < MixerControlDetails->cChannels; Channel++)
694680
{
695681
if (bSet)
696682
{
697683
/* FIXME SEH */
698-
Index = Input[Channel].dwValue / VolumeData->InputSteppingDelta;
699-
700-
if (Index >= VolumeData->ValuesCount)
701-
{
702-
DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
703-
return MM_STATUS_INVALID_PARAMETER;
704-
}
705-
706-
Value = VolumeData->Values[Index];
684+
/* Convert from logical units to hardware range (DB) */
685+
Value = (LONG)((INT64)Input[Channel].dwValue * MaxRange / 0x10000 + VolumeData->SignedMinimum);
707686
}
708687

709688
/* Get/set control details */
@@ -712,7 +691,8 @@ MMixerSetGetVolumeControlDetails(
712691
if (!bSet)
713692
{
714693
/* FIXME SEH */
715-
Input[Channel].dwValue = MMixerGetVolumeControlIndex(VolumeData, Value);
694+
/* Convert from hardware range (DB) to logical units */
695+
Input[Channel].dwValue = (ULONG)(((INT64)Value - VolumeData->SignedMinimum + 1) * 0x10000 / MaxRange);
716696
}
717697
}
718698

0 commit comments

Comments
 (0)