Blog. Just Blog

Управление громкостью звука в системе

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Управление громкостью звука в системе
Управление громкостью звука в системе

Сегодня разберем пример, как программно изменить громкость звука в Windows. В старых версиях Windows для этого использовались waveform-функции с очень неудобными параметрами. Управлять громкостью с помощью эмуляции мультимедийных клавиш тоже не самое лучшее решение. Но, к счастью, прогресс не стоит на месте.

Начиная с Windows Vista, для управления звуком появился специализированный COM-интерфейс IAudioEndpointVolume. Как и в случае с другими COM-объектами, для работы нам понадобится целая пачка GUID'ов и описаний интерфейсов.
  1. ; GUID {BCDE0395-E52F-467C-8E3D-C4579291692E}
  2. CLSID_MMDeviceEnumerator dd 0BCDE0395h
  3.                          dw 0E52Fh
  4.                          dw 0467Ch
  5.                          db 08Eh, 03Dh, 0C4h, 057h, 092h, 091h, 069h, 02Eh
  6.  
  7. ; GUID {A95664D2-9614-4F35-A746-DE8DB63617E6}
  8. IID_IMMDeviceEnumerator dd 0A95664D2h
  9.                         dw 09614h
  10.                         dw 04F35h
  11.                         db 0A7h, 046h, 0DEh, 08Dh, 0B6h, 036h, 017h, 0E6h
  12.  
  13. ; GUID {5CDF2C82-841E-4546-9722-0CF74078229A}
  14. IID_IAudioEndpointVolume dd 05CDF2C82h
  15.                          dw 0841Eh
  16.                          dw 04546h
  17.                          db 097h, 022h, 00Ch, 0F7h, 040h, 078h, 022h, 09Ah
  18.  
  19. ; IID_IMMDeviceEnumerator Interface
  20. struct IMMDeviceEnumerator
  21.     ; IUnknown
  22.     QueryInterface                         dd ?   ; 000h
  23.     AddRef                                 dd ?   ; 004h
  24.     Release                                dd ?   ; 008h
  25.     ; IMMDeviceEnumerator
  26.     EnumAudioEndpoints                     dd ?   ; 00Ch
  27.     GetDefaultAudioEndpoint                dd ?   ; 010h
  28.     GetDevice                              dd ?   ; 014h
  29.     RegisterEndpointNotificationCallback   dd ?   ; 018h
  30.     UnregisterEndpointNotificationCallback dd ?   ; 01Ch
  31. ends
  32.  
  33. ; IID_IMMDevice Interface
  34. struct IMMDevice
  35.     ; IUnknown
  36.     QueryInterface    dd ?   ; 000h
  37.     AddRef            dd ?   ; 004h
  38.     Release           dd ?   ; 008h
  39.     ; IMMDevice
  40.     Activate          dd ?   ; 00Ch
  41.     OpenPropertyStore dd ?   ; 010h
  42.     GetId             dd ?   ; 014h
  43.     GetState          dd ?   ; 018h
  44. ends
  45.  
  46. ; IID_IAudioEndpointVolume Interface
  47. struct IAudioEndpointVolume
  48.     ; IUnknown
  49.     QueryInterface                dd ?   ; 000h
  50.     AddRef                        dd ?   ; 004h
  51.     Release                       dd ?   ; 008h
  52.     ; IAudioEndpointVolume
  53.     RegisterControlChangeNotify   dd ?   ; 00Ch
  54.     UnregisterControlChangeNotify dd ?   ; 010h
  55.     GetChannelCount               dd ?   ; 014h
  56.     SetMasterVolumeLevel          dd ?   ; 018h
  57.     SetMasterVolumeLevelScalar    dd ?   ; 01Ch
  58.     GetMasterVolumeLevel          dd ?   ; 020h
  59.     GetMasterVolumeLevelScalar    dd ?   ; 024h
  60.     SetChannelVolumeLevel         dd ?   ; 028h
  61.     SetChannelVolumeLevelScalar   dd ?   ; 02Ch
  62.     GetChannelVolumeLevel         dd ?   ; 030h
  63.     GetChannelVolumeLevelScalar   dd ?   ; 034h
  64.     SetMute                       dd ?   ; 038h
  65.     GetMute                       dd ?   ; 03Ch
  66.     GetVolumeStepInfo             dd ?   ; 040h
  67.     VolumeStepUp                  dd ?   ; 044h
  68.     VolumeStepDown                dd ?   ; 048h
  69.     QueryHardwareSupport          dd ?   ; 04Ch
  70.     GetVolumeRange                dd ?   ; 050h
  71. ends
Чтобы выйти на методы интерфейса IAudioEndpointVolume, сперва потребуется получить интерфейс дефолтного звукового устройства, затем активировать его, и только потом у нас будет доступ к функциям управления звуком.
  1.         ; Инициализировать COM-объект
  2.         invoke  CoInitialize,NULL
  3.  
  4.         ; Создать объект
  5.         invoke  CoCreateInstance,CLSID_MMDeviceEnumerator,NULL,\
  6.                 CLSCTX_INPROC_SERVER,\
  7.                 IID_IMMDeviceEnumerator,deviceEnumerator
  8.  
  9.         ; Инициализировать объект
  10.         mov     eax, [deviceEnumerator]
  11.         mov     eax, [eax]
  12.         stdcall [eax+IMMDeviceEnumerator.GetDefaultAudioEndpoint],\
  13.                 [deviceEnumerator],[eRender],[eConsole],defaultDevice
  14.  
  15.         mov     eax, [deviceEnumerator]
  16.         mov     eax, [eax]
  17.         stdcall [eax+IMMDeviceEnumerator.Release],[deviceEnumerator]
  18.  
  19.         mov     eax, [defaultDevice]
  20.         mov     eax, [eax]
  21.         stdcall [eax+IMMDevice.Activate],[defaultDevice],\
  22.                 IID_IAudioEndpointVolume,CLSCTX_INPROC_SERVER,\
  23.                 NULL,endpointVolume
  24.  
  25.         mov     eax, [defaultDevice]
  26.         mov     eax, [eax]
  27.         stdcall [eax+IMMDevice.Release],[defaultDevice]
Если посмотреть документацию, то там будет достаточно много различных методов, вот ассемблерная реализация некоторых из них. Получение текущего уровня громкости системы. Значение возвращается в формате вещественного числа в интервале от 0.0 до 1.0. Для получения привычного процентного значения придется умножить его на 100.
  1. ; Текущее значение громкости
  2. fLevel           dd 0.0
  3. ; Множитель для нормализации значения 
  4. fMult            dd 100
  5. ...
  6. ...
  7. .get_volume:
  8.         ; Получить значение громкости
  9.         mov     eax, [endpointVolume]
  10.         mov     eax, [eax]
  11.         stdcall [eax+IAudioEndpointVolume.GetMasterVolumeLevelScalar],\
  12.                 [endpointVolume],fLevel
  13.  
  14.         ; Привести значение к процентному и целочисленному
  15.         fld     [fLevel]
  16.         fimul   [fMult]
  17.         fistp   [result]
  18.         mov     eax,[result]
  19.         ; EAX - текущее значение громкости [0..100]
Увеличение и уменьшение громкости системы на один шаг. Значение шага можно узнать с помощью метода GetVolumeStepInfo.
  1. .volume_up:
  2.         ; Увеличить громкость
  3.         mov     eax, [endpointVolume]
  4.         mov     eax, [eax]
  5.         stdcall [eax+IAudioEndpointVolume.VolumeStepUp],\
  6.                 [endpointVolume],NULL
  7.  
  8. .volume_down:
  9.         ; Уменьшить громкость
  10.         mov     eax, [endpointVolume]
  11.         mov     eax, [eax]
  12.         stdcall [eax+IAudioEndpointVolume.VolumeStepDown],\
  13.                 [endpointVolume],NULL
Установка фиксированного значения громкости. Новый уровень громкости должен быть в формате вещественного числа и иметь значение в интервале от 0.0 до 1.0.
  1. ; Фиксированная громкость 20%
  2. fFixed           dd 0.2
  3. ...
  4. ...
  5. .volume_fix:
  6.         ; Громкость 20%
  7.         mov     eax, [endpointVolume]
  8.         mov     eax, [eax]
  9.         stdcall [eax+IAudioEndpointVolume.SetMasterVolumeLevelScalar],\
  10.                 [endpointVolume],[fFixed],NULL
Включение и отключение звука, а также получение текущего состояния Mute. Нулевое значение - звук включен, любое другое значение - звук выключен.
  1. .volume_mute:
  2.         ; Отключить/включить звук
  3.         mov     eax, [endpointVolume]
  4.         mov     eax, [eax]
  5.         stdcall [eax+IAudioEndpointVolume.GetMute],\
  6.                 [endpointVolume],bMute
  7.  
  8.         ; Поменять значение на противоположное
  9.         xor     ebx,ebx
  10.         cmp     [bMute],0
  11.         sete    bl
  12.  
  13.         ; Установить состояние Mute
  14.         mov     eax, [endpointVolume]
  15.         mov     eax, [eax]
  16.         stdcall [eax+IAudioEndpointVolume.SetMute],\
  17.                 [endpointVolume],ebx,NULL
Конечно, это не все методы для управления уровнем громкости звука в системе, но их вполне достаточно для большинства прикладных программ. Остальные методы вы можете реализовать по аналогии с описанными выше.

В приложении пример программы с исходным текстом, которая позволяет управлять общей громкостью звуков в системе, а также отображает текущее значение громкости.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Volume.Control.Demo.zip (3,780 bytes)


Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 445 | Комментариев: 8

Комментарии

Отзывы посетителей сайта о статье
ManHunter (17.11.2021 в 11:42):
Потому что все уже починено.
wet (17.11.2021 в 11:02):
ЦитатаНачиная с Windows Vista, для управления звуком появился специализированный COM-интерфейс

У меня ваш пример отлично работает на win7 x64
ManHunter (16.11.2021 в 17:50):
Вроде нашел в чем была проблема. Архив обновлен.
SMaSm-94 (16.11.2021 в 16:22):
Nemo, подтверждаю. У меня аналогично на Win8.1x64
Nemo (15.11.2021 в 23:04):
Левые 2 кнопки аварийно завершают процесс (win8.1-64)
Тоже самое через COM на С: https://transfiles.ru/hob8d
DRON (15.11.2021 в 23:04):
Так тоже не очень: сишный BOOL (не путать с bool) это четыре байта, а не один. Так что выделить bMute через dd, а затем:
        cmp     [bMute],1
        sbb     edx,edx
        neg     edx
...
        stdcall [eax+IAudioEndpointVolume.SetMute],\
                [endpointVolume],edx,NULL
ManHunter (15.11.2021 в 22:00):
Жаль, такая хорошая оптимизация пропадает. Ну ничо, заменю на что-нибудь чуть менее красивое.
DRON (15.11.2021 в 20:42):
Так делать нельзя:
dec     [bMute]
neg     [bMute]

Дело в том, что винда не проверяет параметры SetMute и запоминает то что ей дали, то есть после вызова SetMute(2) вызов GetMute вернёт именно 2, а dec+neg вернёт -1, что приравнивается к TRUE. Интересно, что эта ошибка присутствует и в самой винде, то есть можно подбором величин выключить звук, но иконка в трее будет без красного значка (или наоборот, уже не помню).

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2021
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.1 сек. / MySQL: 2 (0.0046 сек.) / Память: 5 Mb
Наверх