Reactos

[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

+33 -72
+24 -39
sdk/lib/drivers/sound/mmixer/controls.c
··· 159 159 PKSPROPERTY_DESCRIPTION Desc; 160 160 PKSPROPERTY_MEMBERSHEADER Members; 161 161 PKSPROPERTY_STEPPING_LONG Range; 162 + LPMIXERVOLUME_DATA VolumeData; 162 163 163 164 MixerControl->Control.Bounds.dwMinimum = 0; 164 165 MixerControl->Control.Bounds.dwMaximum = 0xFFFF; ··· 179 180 /* get node volume level info */ 180 181 Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), Desc, Length, &BytesReturned); 181 182 183 + VolumeData = (LPMIXERVOLUME_DATA)MixerContext->Alloc(sizeof(*VolumeData)); 184 + if (!VolumeData) 185 + return MM_STATUS_NO_MEMORY; 186 + 182 187 if (Status == MM_STATUS_SUCCESS) 183 188 { 184 - LPMIXERVOLUME_DATA VolumeData; 185 - ULONG Steps, MaxRange, Index; 186 - LONG Value; 187 - 188 189 Members = (PKSPROPERTY_MEMBERSHEADER)(Desc + 1); 189 190 Range = (PKSPROPERTY_STEPPING_LONG)(Members + 1); 190 191 191 192 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); 192 193 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; 194 198 195 - if (MaxRange) 199 + /* Fallback to defaults if: 1) the range is not defined (typically is 0) */ 200 + if (VolumeData->SignedMinimum == VolumeData->SignedMaximum) 196 201 { 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); 230 215 } 231 216 232 217 DPRINT("Status %x Name %S\n", Status, MixerControl->Control.szName);
-4
sdk/lib/drivers/sound/mmixer/precomp.h
··· 102 102 MIXERCONTROL_DATA Header; 103 103 LONG SignedMinimum; 104 104 LONG SignedMaximum; 105 - LONG SteppingDelta; 106 - ULONG InputSteppingDelta; 107 - ULONG ValuesCount; 108 - PLONG Values; 109 105 }MIXERVOLUME_DATA, *LPMIXERVOLUME_DATA; 110 106 111 107 typedef struct
+9 -29
sdk/lib/drivers/sound/mmixer/sup.c
··· 315 315 return MM_STATUS_UNSUCCESSFUL; 316 316 } 317 317 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 - 335 318 VOID 336 319 MMixerNotifyControlChange( 337 320 IN PMIXER_CONTEXT MixerContext, ··· 672 655 LPMIXERLINE_EXT MixerLine) 673 656 { 674 657 LPMIXERCONTROLDETAILS_UNSIGNED Input; 675 - LONG Value; 676 - ULONG Index, Channel; 658 + LONG MaxRange, Value; 659 + ULONG Channel; 677 660 MIXER_STATUS Status; 678 661 LPMIXERVOLUME_DATA VolumeData; 679 662 ··· 689 672 if (!Input) 690 673 return MM_STATUS_UNSUCCESSFUL; /* To prevent dereferencing NULL */ 691 674 675 + /* Get maximum available range */ 676 + MaxRange = VolumeData->SignedMaximum - VolumeData->SignedMinimum; 677 + 692 678 /* Loop for each channel */ 693 679 for (Channel = 0; Channel < MixerControlDetails->cChannels; Channel++) 694 680 { 695 681 if (bSet) 696 682 { 697 683 /* 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); 707 686 } 708 687 709 688 /* Get/set control details */ ··· 712 691 if (!bSet) 713 692 { 714 693 /* 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); 716 696 } 717 697 } 718 698