Recent

Author Topic: [SOLVED] Sound change notification (mute / unmute)  (Read 3532 times)

pcurtis

  • Hero Member
  • *****
  • Posts: 951
[SOLVED] Sound change notification (mute / unmute)
« on: March 14, 2021, 06:48:04 pm »
Solved here

https://forum.lazarus.freepascal.org/index.php?topic=53726.msg397777#msg397777

Hi All,

how can I get notified of system audio changes? 

Or how to use the callback from
Code: Pascal  [Select][+][-]
  1. unit MMDevAPI;
  2.  
  3. interface
  4.  
  5. uses
  6.   LCLIntf, LCLType, ActiveX, ComObj, Windows;
  7.  
  8. const
  9.   CLASS_IMMDeviceEnumerator             : TGUID = '{BCDE0395-E52F-467C-8E3D-C4579291692E}';
  10.   IID_IMMDeviceEnumerator               : TGUID = '{A95664D2-9614-4F35-A746-DE8DB63617E6}';
  11.   IID_IMMDevice                         : TGUID = '{D666063F-1587-4E43-81F1-B948E807363F}';
  12.   IID_IMMDeviceCollection               : TGUID = '{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}';
  13.   IID_IAudioEndpointVolume              : TGUID = '{5CDF2C82-841E-4546-9722-0CF74078229A}';
  14.   IID_IAudioMeterInformation            : TGUID = '{C02216F6-8C67-4B5B-9D00-D008E73E0064}';
  15.   IID_IAudioEndpointVolumeCallback      : TGUID = '{657804FA-D6AD-4496-8A60-352752AF4F89}';
  16.  
  17.   DEVICE_STATE_ACTIVE                   = $00000001;
  18.   DEVICE_STATE_UNPLUGGED                = $00000002;
  19.   DEVICE_STATE_NOTPRESENT               = $00000004;
  20.   DEVICE_STATEMASK_ALL                  = $00000007;
  21.  
  22. type
  23.   EDataFlow = TOleEnum;
  24.  
  25. const
  26.   eRender                               = $00000000;
  27.   eCapture                              = $00000001;
  28.   eAll                                  = $00000002;
  29.   EDataFlow_enum_count                  = $00000003;
  30.                
  31. type
  32.   ERole = TOleEnum;
  33.  
  34. const
  35.   eConsole                              = $00000000;
  36.   eMultimedia                           = $00000001;
  37.   eCommunications                       = $00000002;
  38.   ERole_enum_count                      = $00000003;
  39.  
  40. type
  41.   IAudioEndpointVolumeCallback = interface(IUnknown)
  42.   ['{657804FA-D6AD-4496-8A60-352752AF4F89}']
  43.   end;
  44.  
  45.   IAudioEndpointVolume = interface(IUnknown)
  46.   ['{5CDF2C82-841E-4546-9722-0CF74078229A}']
  47.     function RegisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): Integer; stdcall;
  48.     function UnregisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): Integer; stdcall;
  49.     function GetChannelCount(out PInteger): Integer; stdcall;
  50.     function SetMasterVolumeLevel(fLevelDB: single; pguidEventContext: PGUID): Integer; stdcall;
  51.     function SetMasterVolumeLevelScalar(fLevelDB: single; pguidEventContext: PGUID): Integer; stdcall;
  52.     function GetMasterVolumeLevel(out fLevelDB: single): Integer; stdcall;
  53.     function GetMasterVolumeLevelScaler(out fLevelDB: single): Integer; stdcall;
  54.     function SetChannelVolumeLevel(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): Integer; stdcall;
  55.     function SetChannelVolumeLevelScalar(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): Integer; stdcall;
  56.     function GetChannelVolumeLevel(nChannel: Integer; out fLevelDB: double): Integer; stdcall;
  57.     function GetChannelVolumeLevelScalar(nChannel: Integer; out fLevel: double): Integer; stdcall;
  58.     function SetMute(bMute: LongBool; pguidEventContext: PGUID): Integer; stdcall;
  59.     function GetMute(out bMute: LongBool): Integer; stdcall;
  60.     function GetVolumeStepInfo(pnStep: Integer; out pnStepCount: Integer): Integer; stdcall;
  61.     function VolumeStepUp(pguidEventContext: PGUID): Integer; stdcall;
  62.     function VolumeStepDown(pguidEventContext: PGUID): Integer; stdcall;
  63.     function QueryHardwareSupport(out pdwHardwareSupportMask): Integer; stdcall;
  64.     function GetVolumeRange(out pflVolumeMindB: double; out pflVolumeMaxdB: double; out pflVolumeIncrementdB: double): Integer; stdcall;
  65.   end;
  66.  
  67.   IAudioMeterInformation = interface(IUnknown)
  68.   ['{C02216F6-8C67-4B5B-9D00-D008E73E0064}']
  69.   end;
  70.  
  71.   IPropertyStore = interface(IUnknown)
  72.   end;
  73.  
  74.   IMMDevice = interface(IUnknown)
  75.   ['{D666063F-1587-4E43-81F1-B948E807363F}']
  76.     function Activate(const refId: TGUID;
  77.                       dwClsCtx: DWORD;
  78.                       pActivationParams: PInteger;
  79.                       out pEndpointVolume: IAudioEndpointVolume): Hresult; stdCall;
  80.     function OpenPropertyStore(stgmAccess: DWORD; out ppProperties: IPropertyStore): Hresult; stdcall;
  81.     function GetId(out ppstrId: PLPWSTR): Hresult; stdcall;
  82.     function GetState(out State: Integer): Hresult; stdcall;
  83.   end;
  84.  
  85.  
  86.   IMMDeviceCollection = interface(IUnknown)
  87.    ['{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}']
  88.    function GetCount(var pcDevices: uint): HRESULT; stdcall;
  89.    function Item(nDevice: uint; out ppDevice: IMMDevice): HRESULT; stdcall;
  90.   end;
  91.  
  92.   IMMNotificationClient = interface(IUnknown)
  93.   ['{7991EEC9-7E89-4D85-8390-6C703CEC60C0}']
  94.   end;
  95.  
  96.   IMMDeviceEnumerator = interface(IUnknown)
  97.   ['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
  98.     function EnumAudioEndpoints(dataFlow: EDataFlow; deviceState: SYSUINT; DevCollection: IMMDeviceCollection): Hresult; stdcall;
  99.     function GetDefaultAudioEndpoint(EDF: SYSUINT; ER: SYSUINT; out Dev :IMMDevice ): Hresult; stdcall;
  100.     function GetDevice(pwstrId: pointer; out Dev: IMMDevice): HResult; stdcall;
  101.     function RegisterEndpointNotificationCallback(pClient: IMMNotificationClient): Hresult; stdcall;
  102.   end;
  103.  
  104. implementation
  105.  
  106. end.
  107.  
« Last Edit: March 16, 2021, 11:07:23 am by pcurtis »
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

Fred vS

  • Hero Member
  • *****
  • Posts: 3158
    • StrumPract is the musicians best friend
Re: Sound change notification (mute / unmute)
« Reply #1 on: March 15, 2021, 12:56:28 am »
Hello.

Here how to get-set master volume with Delphi, maybe you can take some tricks:
 
Code: Pascal  [Select][+][-]
  1. // from https://stackoverflow.com/questions/17612666/controlling-the-master-speaker-volume-in-windows-7
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. uses
  6.   SysUtils,
  7.   Windows,
  8.   ActiveX,
  9.   ComObj;
  10.  
  11. const
  12.   CLASS_IMMDeviceEnumerator : TGUID = '{BCDE0395-E52F-467C-8E3D-C4579291692E}';
  13.   IID_IMMDeviceEnumerator : TGUID = '{A95664D2-9614-4F35-A746-DE8DB63617E6}';
  14.   IID_IAudioEndpointVolume : TGUID = '{5CDF2C82-841E-4546-9722-0CF74078229A}';
  15.  
  16. type
  17.   IAudioEndpointVolumeCallback = interface(IUnknown)
  18.   ['{657804FA-D6AD-4496-8A60-352752AF4F89}']
  19.   end;
  20.  
  21.   IAudioEndpointVolume = interface(IUnknown)
  22.     ['{5CDF2C82-841E-4546-9722-0CF74078229A}']
  23.     function RegisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): HRESULT; stdcall;
  24.     function UnregisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): HRESULT; stdcall;
  25.     function GetChannelCount(out PInteger): HRESULT; stdcall;
  26.     function SetMasterVolumeLevel(fLevelDB: single; pguidEventContext: PGUID): HRESULT; stdcall;
  27.     function SetMasterVolumeLevelScalar(fLevelDB: single; pguidEventContext: PGUID): HRESULT; stdcall;
  28.     function GetMasterVolumeLevel(out fLevelDB: single): HRESULT; stdcall;
  29.     function GetMasterVolumeLevelScaler(out fLevelDB: single): HRESULT; stdcall;
  30.     function SetChannelVolumeLevel(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
  31.     function SetChannelVolumeLevelScalar(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
  32.     function GetChannelVolumeLevel(nChannel: Integer; out fLevelDB: double): HRESULT; stdcall;
  33.     function GetChannelVolumeLevelScalar(nChannel: Integer; out fLevel: double): HRESULT; stdcall;
  34.     function SetMute(bMute: Boolean; pguidEventContext: PGUID): HRESULT; stdcall;
  35.     function GetMute(out bMute: Boolean): HRESULT; stdcall;
  36.     function GetVolumeStepInfo(pnStep: Integer; out pnStepCount: Integer): HRESULT; stdcall;
  37.     function VolumeStepUp(pguidEventContext: PGUID): HRESULT; stdcall;
  38.     function VolumeStepDown(pguidEventContext: PGUID): HRESULT; stdcall;
  39.     function QueryHardwareSupport(out pdwHardwareSupportMask): HRESULT; stdcall;
  40.     function GetVolumeRange(out pflVolumeMindB: double; out pflVolumeMaxdB: double; out pflVolumeIncrementdB: double): HRESULT; stdcall;
  41.   end;
  42.  
  43.   IAudioMeterInformation = interface(IUnknown)
  44.   ['{C02216F6-8C67-4B5B-9D00-D008E73E0064}']
  45.   end;
  46.  
  47.   IPropertyStore = interface(IUnknown)
  48.   end;
  49.  
  50.   IMMDevice = interface(IUnknown)
  51.   ['{D666063F-1587-4E43-81F1-B948E807363F}']
  52.     function Activate(const refId: TGUID; dwClsCtx: DWORD;  pActivationParams: PInteger; out pEndpointVolume: IAudioEndpointVolume): HRESULT; stdCall;
  53.     function OpenPropertyStore(stgmAccess: DWORD; out ppProperties: IPropertyStore): HRESULT; stdcall;
  54.     function GetId(out ppstrId: PLPWSTR): HRESULT; stdcall;
  55.     function GetState(out State: Integer): HRESULT; stdcall;
  56.   end;
  57.  
  58.  
  59.   IMMDeviceCollection = interface(IUnknown)
  60.   ['{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}']
  61.   end;
  62.  
  63.   IMMNotificationClient = interface(IUnknown)
  64.   ['{7991EEC9-7E89-4D85-8390-6C703CEC60C0}']
  65.   end;
  66.  
  67.   IMMDeviceEnumerator = interface(IUnknown)
  68.   ['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
  69.     function EnumAudioEndpoints(dataFlow: TOleEnum; deviceState: SYSUINT; DevCollection: IMMDeviceCollection): HRESULT; stdcall;
  70.     function GetDefaultAudioEndpoint(EDF: SYSUINT; ER: SYSUINT; out Dev :IMMDevice ): HRESULT; stdcall;
  71.     function GetDevice(pwstrId: pointer; out Dev: IMMDevice): HRESULT; stdcall;
  72.     function RegisterEndpointNotificationCallback(pClient: IMMNotificationClient): HRESULT; stdcall;
  73.   end;
  74.  
  75. procedure SetMasterVolume(fLevelDB: single);
  76. var
  77.   pEndpointVolume: IAudioEndpointVolume;
  78.   LDeviceEnumerator: IMMDeviceEnumerator;
  79.   Dev: IMMDevice;
  80. begin
  81.   if not Succeeded(CoCreateInstance(CLASS_IMMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, LDeviceEnumerator)) then
  82.    RaiseLastOSError;
  83.   if not Succeeded(LDeviceEnumerator.GetDefaultAudioEndpoint($00000000, $00000000, Dev)) then
  84.    RaiseLastOSError;
  85.  
  86.   if not Succeeded( Dev.Activate(IID_IAudioEndpointVolume, CLSCTX_INPROC_SERVER, nil, pEndpointVolume)) then
  87.    RaiseLastOSError;
  88.  
  89.   if not Succeeded(pEndpointVolume.SetMasterVolumeLevelScalar(fLevelDB, nil)) then
  90.    RaiseLastOSError;
  91. end;
  92.  
  93.  
  94. begin
  95.  try
  96.     CoInitialize(nil);
  97.     try
  98.       SetMasterVolume(1.0); //set the volume to the max
  99.       Sleep(2000);
  100.       SetMasterVolume(0.5); //set the volume to the 50 %
  101.       Sleep(2000);
  102.       SetMasterVolume(0.1); //set the volume to the 10 %
  103.       Sleep(2000);
  104.     finally
  105.       CoUninitialize;
  106.     end;
  107.  except
  108.     on E:Exception do
  109.         Writeln(E.Classname, ':', E.Message);
  110.  end;
  111.  Writeln('Press Enter to exit');
  112.  Readln;
  113. end.
  114.  

Fre;D
« Last Edit: March 15, 2021, 01:01:10 am by Fred vS »
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

pcurtis

  • Hero Member
  • *****
  • Posts: 951
Re: Sound change notification (mute / unmute)
« Reply #2 on: March 15, 2021, 05:16:12 am »
I already know that. The question was "How to use the callbacks"
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

Fred vS

  • Hero Member
  • *****
  • Posts: 3158
    • StrumPract is the musicians best friend
Re: Sound change notification (mute / unmute)
« Reply #3 on: March 15, 2021, 01:34:06 pm »
I already know that.

Ha, ok, sorry for the noise.

The question was "How to use the callbacks"

I suppose you are talking about a callback done by Windows to tell you that something has changed in the sound-mixer.

If so, I dont know.

But you can mimic it with a timer that check with "GetMasterVolumeLevel" or what ever you need to check, if something was changed.

Fre;D
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

pcurtis

  • Hero Member
  • *****
  • Posts: 951
Re: Sound change notification (mute / unmute)
« Reply #4 on: March 15, 2021, 01:46:02 pm »
A timer is not the right way to go. That's why God created callbacks  :)
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

Fred vS

  • Hero Member
  • *****
  • Posts: 3158
    • StrumPract is the musicians best friend
Re: Sound change notification (mute / unmute)
« Reply #5 on: March 15, 2021, 01:51:28 pm »
A timer is not the right way to go. That's why God created callbacks  :)

So, better to ask Him, I dont know.
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

pcurtis

  • Hero Member
  • *****
  • Posts: 951
Re: Sound change notification (mute / unmute)
« Reply #6 on: March 15, 2021, 04:59:53 pm »
I was given this (see attachment), but I can't get it to compile.
« Last Edit: March 16, 2021, 09:34:10 am by pcurtis »
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

Fred vS

  • Hero Member
  • *****
  • Posts: 3158
    • StrumPract is the musicians best friend
Re: Sound change notification (mute / unmute)
« Reply #7 on: March 15, 2021, 05:04:03 pm »
Hello.

I did find this (but it is in C).
It seems that WaveLib.AudioMixer is needed for this.

Code: C  [Select][+][-]
  1. Getting callback notifications when a line of Mixer has changed:
  2.  
  3. /* From https://www.codeguru.com/csharp/csharp/cs_graphics/sound/article.php/c10931/Windows-Mixer-Control-in-C.htm */
  4.  
  5.     /* Initialization */
  6.     mMixers = new Mixers();
  7.     mMixers.Playback.MixerLineChanged +=
  8.        new WaveLib.AudioMixer.Mixer.
  9.        MixerLineChangeHandler(mMixer_MixerLineChanged);
  10.     mMixers.Recording.MixerLineChanged +=
  11.        new WaveLib.AudioMixer.Mixer.
  12.        MixerLineChangeHandler(mMixer_MixerLineChanged);
  13.     /* Events */
  14.     private void mMixer_MixerLineChanged(Mixer mixer, MixerLine line)
  15.     {
  16.        Console.WriteLine("Mixer: " + mixer.DeviceDetail.MixerName);
  17.        Console.WriteLine("Mixer Type: " + mixer.MixerType);
  18.        Console.WriteLine("Mixer Line: " + line.Name);
  19.     }

« Last Edit: March 15, 2021, 05:18:09 pm by Fred vS »
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

Fred vS

  • Hero Member
  • *****
  • Posts: 3158
    • StrumPract is the musicians best friend
Re: [SOLVED] Sound change notification (mute / unmute)
« Reply #8 on: March 16, 2021, 04:00:01 pm »
Hello.

I am busy to enable sound callback for Linux using only libasound.so.

But I get less luck than PCurtis.
https://forum.lazarus.freepascal.org/index.php/topic,53726.msg397777.html#msg397777

The callback function fires only once.

Maybe somebody has a idea why...

There is the code + a demo here:
https://github.com/fredvs/alsa_mixer/

Fre;D
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

Fred vS

  • Hero Member
  • *****
  • Posts: 3158
    • StrumPract is the musicians best friend
Re: [SOLVED] Sound change notification (mute / unmute)
« Reply #9 on: March 16, 2021, 11:13:42 pm »
Hello.

Ok, now fpc can deal with Windows mixer callback (thanks to Pcurtis) and with Linux mixer callback too (all is fixed).

For Linux it uses libasound.so only and is a independent thread.

There is the code + a demo here:
https://github.com/fredvs/alsa_mixer/

Fre;D
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

 

TinyPortal © 2005-2018