在Delphi中控制应用程序音量

发布于 2024-10-10 15:53:01 字数 479 浏览 7 评论 0原文

我什至不确定这是否适合一个问题,但它是一个单一的问题。我有一个用 Delphi XE 编写的网络广播播放器,使用 BASS 库进行音频流和播放。该应用程序需要在 Windows XP、Vista 和 7 下运行。

低音可以轻松控制全局音量,但没有静音功能,一般来说,最好在每个应用程序的基础上控制音量。

低音还可以轻松控制“通道”(流)的音量,但同样没有静音,这也不是正确的每个应用程序控制。 (Windows 混音器中的应用程序音量控制不受影响。)

我知道,对于 Vista 及以上版本,我需要 ISimpleAudioVolume 和/或 IAudioEndpointVolume,但找不到这些的 Delphi 实现。所以问题的一部分是,它是否作为第三方库存在?

第二部分是,在这些接口不可用的 XP 上控制音量和切换静音(系统范围或每个应用程序)的正确方法是什么?

I'm not even sure if this fits into one question, but it is a single problem. I have an internet radio player written in Delphi XE, using the BASS library for audio streaming and playback. The application needs to run under Windows XP, Vista and 7.

Bass makes it easy to control global volume, but has no facility for muting sound, and in general it's a better idea to control volume on per-application basis.

Bass also makes it easy to control the volume of a "channel" (stream), but again there is no muting, and this isn't the proper per-application control, either. (The application volume control in Windows mixer is unaffected.)

I understand that for Vista and above I need ISimpleAudioVolume and/or IAudioEndpointVolume, but cannot find a Delphi implementation of these. So one part of the question is, does it exist as a 3rd party library?

Part two is, what's the proper way to to control volume and toggle mute (system-wide or per application) on XP, where these interfaces are not available?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

人间不值得 2024-10-17 15:53:01

使用这个简单的代码将我的机器上的主音量静音:

procedure TForm1.Button1Click(Sender: TObject);
var
i:Integer;
begin
for i:=0 to 100 do
begin
  keybd_event($AE, MapVirtualKey($AE,0), 0, 0);
  keybd_event($AE, MapVirtualKey($AE,0), KEYEVENTF_KEYUP, 0);
end;
end;

Use this simple code to mute the main volume it works on my machine:

procedure TForm1.Button1Click(Sender: TObject);
var
i:Integer;
begin
for i:=0 to 100 do
begin
  keybd_event($AE, MapVirtualKey($AE,0), 0, 0);
  keybd_event($AE, MapVirtualKey($AE,0), KEYEVENTF_KEYUP, 0);
end;
end;
我的奇迹 2024-10-17 15:53:01

真是巧合,我也在使用 bass.dll 编写我的个人广播流客户端,顺便说一句,这是一个很棒的库。然而,我仍然想与 Windows 的混音器集成,因此从外部应用程序(例如 EarTrumpet 或 Windows Mixer 本身)修改音量也会自动反映在我的应用程序的音量滑块中。

正如评论所提到的,Windows XP 存在一些缺点,因为 IAudioSession 是从 Vista 开始引入的,而 Windows 7 及更高版本包括 IAudioSessionManager2、IAudioVolumeDuckNotification 等改进。
仍然对于您自己的应用程序,IAudioSessionManager 可以很好地工作,特别是使用 IAudioSimpleVolume。很高兴,Vista+ 中支持这一点。

您可以在 GitHub 和许多其他论坛上获得各种 MMDeviceAPI 实现,但在这里我将剥离必要的接口来实现我们需要的功能。

您将需要 IAudioSessionEvents、IAudioSessionControl、ISimpleAudioVolume、IAudioSessionManager、IMMDevice、IMMDeviceCollection、IMMNotificationClient 和 IMMDeviceEnumerator 接口。

为了响应外部音频事件,我们需要实现扩展 IAudioSessionsEvents 的接口,如下所示:

TAudioEvent = class(TInterfacedPersistent, IAudioSessionEvents)
  private
    // IAudioSessionEvents
    function OnDisplayNameChanged(NewDisplayName: LPCWSTR; EventContext: PGUID): HRESULT; stdcall;
    function OnIconPathChanged(NewIconPath: LPCWSTR; EventContext: PGUID): HRESULT; stdcall;
    // THIS WILL UPDATE "AUTOMATICALLY" ON VOLUME CHANGES
    function OnSimpleVolumeChanged(NewVolume    : Single;
                                   NewMute      : BOOL;
                                   EventContext : PGUID): HRESULT; stdcall;
    function OnChannelVolumeChanged(ChannelCount    : UINT;
                                    NewChannelArray : PSingle;
                                    ChangedChannel  : UINT;
                                    EventContext    : PGUID): HRESULT; stdcall;
    function OnGroupingParamChanged(NewGroupingParam,
                                    EventContext: PGUID): HRESULT; stdcall;
    function OnStateChanged(NewState: TAudioSessionState): HRESULT; stdcall;
    function OnSessionDisconnected(
              DisconnectReason: TAudioSessionDisconnectReason): HRESULT; stdcall;
  public
//    constructor Create(AppWindow: HWND); <-- left for improvements
//    destructor Destroy; override;
  end;
...
function TAudioEvent.OnSimpleVolumeChanged(NewVolume: Single; NewMute: BOOL;
  EventContext: PGUID): HRESULT;
begin
  // min 0, max 10 for slider, NewVolume is 0 to 1 in Single type
  Form1.Slider1.Value := Round(NewVolume*10);
  if NewMute then
    Form1.MuteButton.Caption := 'Muted'
  else
    Form1.MuteButton.Caption := ' ';
end;

这是事件处理程序,它将更新音量静音状态,任何音量变化从应用程序内或从 Windows 混音器。

注意,它需要改进来处理会话更改,例如,当您切换物理音频设备、重新分配到另一个音频设备等时。

现在,使用全局变量来处理音频会话和相关查询。

  var devicen: IMMDeviceEnumerator;
   device: IMMDevice;
   audiosession: IAudioSessionManager;
   control: IAudioSessionControl;
   audio: ISimpleAudioVolume;
   audioevent: TAudioEvent;
   volume: Single;

加载 Bass.dll 后我们进行初始化。

//HRESULT, I guess you have to do CoInitialize(nil), but Delphi does it by default, at least, that's what they say.
// Get the enumerator for the audio endpoint devices
// on this system.
  hr := CoCreateInstance(CLASS_IMMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, devicen);
// Get the audio endpoint device with the specified data-flow
// direction (eRender or eCapture) and device role.
  devicen.GetDefaultAudioEndpoint(eRender, eMultimedia, device);
// Get the session manager for the endpoint device.
  device.Activate(IID_IAudioSessionManager, CLSCTX_INPROC_SERVER, nil, audiosession);
// Get the control interface for the process-specific audio
// session with session GUID = GUID_NULL. This is the session
// that an audio stream for a DirectSound, DirectShow, waveOut,
// or PlaySound application stream belongs to by default.
// GUID_NULL or nil will assign to the default session 
// (i.e.) our application (bass created, if done previously) session
  audiosession.GetAudioSessionControl(nil, 0, control);
  audioevent := TAudioEvent.Create;
  // register our audio session events 
  control.RegisterAudioSessionNotification(audioevent);
  // to get/set its volume we will use GetSimpleAudioVolume (NIL, too
  audiosession.GetSimpleAudioVolume(nil, 0, audio);
  // retrieve its value 
  audio.GetMasterVolume(volume);
  // show it in our slider
  Slider1.Value := Round(volume*10);

提示:这甚至可能包含在我们之前的自定义界面中,以实现更清晰的代码。

修改我们的应用程序音量:

procedure TForm1.Slider1Change(Sender: TObject);
var
  vol: Single;
begin
  if BASS_ChannelIsActive(chan) = BASS_ACTIVE_PLAYING then
  begin
    vol := Slider1.Value / 10;
    audio.SetMasterVolume(vol, nil);
  end;
end;

与切换静音类似,事件处理程序将在视觉上更新,我们只需切换它即可。

procedure TForm1.MuteButtonClick(Sender: TObject);
var
  ismute: LongBool;
begin
  audio.GetMute(ismute);
  if isMute then
    audio.SetMute(0, nil)
  else
    audio.SetMute(1, nil);  
end;

就是这样,由你来改进它。我知道,这是一团糟的代码,当然它需要清理和处理安全释放接口等。

最后,这是所需的接口:

const
  CLASS_IMMDeviceEnumerator             : TGUID = '{BCDE0395-E52F-467C-8E3D-C4579291692E}';
  IID_IMMDeviceEnumerator               : TGUID = '{A95664D2-9614-4F35-A746-DE8DB63617E6}';
  IID_IAudioSessionManager              : TGUID = '{BFA971F1-4D5E-40BB-935E-967039BFBEE4}';
const
  eRender                               = 0;
const
  eConsole                              = 0;
  eMultimedia                           = eConsole + 1;
type
  TAudioSessionDisconnectReason = (DisconnectReasonDeviceRemoval,
    DisconnectReasonServerShutdown, DisconnectReasonFormatChanged,
    DisconnectReasonSessionLogoff, DisconnectReasonSessionDisconnected,
    DisconnectReasonExclusiveModeOverride);
  TAudioSessionState = (AudioSessionStateInactive, AudioSessionStateActive,
                       AudioSessionStateExpired);
  IAudioSessionEvents = interface(IUnknown)
  ['{24918ACC-64B3-37C1-8CA9-74A66E9957A8}']
    function OnDisplayNameChanged(NewDisplayName: LPCWSTR; EventContext: PGUID): HRESULT; stdcall;
    function OnIconPathChanged(NewIconPath: LPCWSTR; EventContext: PGUID): HRESULT; stdcall;
    function OnSimpleVolumeChanged(NewVolume    : Single;
                                   NewMute      : BOOL;
                                   EventContext : PGUID): HRESULT; stdcall;
    function OnChannelVolumeChanged(ChannelCount    : UINT;
                                    NewChannelArray : PSingle;
                                    ChangedChannel  : UINT;
                                    EventContext    : PGUID): HRESULT; stdcall;
    function OnGroupingParamChanged(NewGroupingParam,
                                    EventContext: PGUID): HRESULT; stdcall;
    function OnStateChanged(NewState: TAudioSessionState): HRESULT; stdcall;
    function OnSessionDisconnected(
              DisconnectReason: TAudioSessionDisconnectReason): HRESULT; stdcall;
  end;

  IAudioSessionControl = interface(IUnknown)
  ['{F4B1A599-7266-4319-A8CA-E70ACB11E8CD}']
    function GetState(out pRetVal: TAudioSessionState): HRESULT; stdcall;
    function GetDisplayName(out pRetVal: LPWSTR): HRESULT; stdcall; // pRetVal must be freed by CoTaskMemFree
    function SetDisplayName(Value: LPCWSTR; EventContext: PGUID): HRESULT; stdcall;
    function GetIconPath(out pRetVal: LPWSTR): HRESULT; stdcall;  // pRetVal must be freed by CoTaskMemFree
    function SetIconPath(Value: LPCWSTR; EventContext: PGUID): HRESULT; stdcall;
    function GetGroupingParam(pRetVal: PGUID): HRESULT; stdcall;
    function SetGroupingParam(OverrideValue, EventContext: PGUID): HRESULT; stdcall;
    function RegisterAudioSessionNotification(
                 const NewNotifications: IAudioSessionEvents): HRESULT; stdcall;
    function UnregisterAudioSessionNotification(
                 const NewNotifications: IAudioSessionEvents): HRESULT; stdcall;
  end;

  ISimpleAudioVolume = interface(IUnknown)
  ['{87CE5498-68D6-44E5-9215-6DA47EF883D8}']
    function SetMasterVolume(fLevel: Single; EventContext: PGUID): HRESULT; stdcall;
    function GetMasterVolume(out fLevel: Single): HRESULT; stdcall;
    // bMute either TRUE = 1 or FALSE = 0 !
    function SetMute(bMute: Longint; EventContext: PGUID): HRESULT; stdcall;
    function GetMute(out bMute: BOOL): HRESULT; stdcall;
  end;

  IAudioSessionManager = interface(IUnknown)
  ['{BFA971F1-4D5E-40BB-935E-967039BFBEE4}']
    function GetAudioSessionControl(AudioSessionGuid: PGUID; StreamFlag : UINT;
                    out SessionControl: IAudioSessionControl): HRESULT; stdcall;
    function GetSimpleAudioVolume(AudioSessionGuid: PGUID; StreamFlag: UINT;
                         out AudioVolume: ISimpleAudioVolume): HRESULT; stdcall;
  end;

  EDataFlow = TOleEnum;
  ERole = TOleEnum;
{$IF CompilerVersion >= 21.0}  //Winapi.PropSys
  IPropertyStore  = Winapi.PropSys.IPropertyStore;
  {$EXTERNALSYM IPropertyStore}
{$ELSE}
  IPropertyStore  = ShlObj.IPropertyStore;
{$ENDIF}

  IMMDevice = interface(IUnknown)
  ['{D666063F-1587-4E43-81F1-B948E807363F}']
    function Activate(const iid: TGUID; dwClsCtx: DWORD; pActivationParams: PPropVariant; out ppInterface): HRESULT; stdcall;
    function OpenPropertyStore(stgmAccess: DWORD; out ppProperties: IPropertyStore): HRESULT; stdcall;
    function GetId(out ppstrId: LPWSTR): HRESULT; stdcall;
    function GetState(out pdwState: DWORD): HRESULT; stdcall;
  end;

  IMMDeviceCollection = interface(IUnknown)
  ['{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}']
    function GetCount(out pcDevices: UINT): HRESULT; stdcall;
    function Item(nDevice: UINT; out ppDevice: IMMDevice): HRESULT; stdcall;
  end;

  IMMNotificationClient = interface(IUnknown)
  ['{7991EEC9-7E89-4D85-8390-6C703CEC60C0}']
    function OnDeviceStateChanged(pwstrDeviceId: LPCWSTR; dwNewState: DWORD): HRESULT; stdcall;
    function OnDeviceAdded(pwstrDeviceId: LPCWSTR): HRESULT; stdcall;
    function OnDeviceRemoved(pwstrDeviceId: LPCWSTR): HRESULT; stdcall;
    function OnDefaultDeviceChanged(flow: EDataFlow; role: ERole; pwstrDefaultDeviceId: LPCWSTR): HRESULT; stdcall;
    function OnPropertyValueChanged(pwstrDeviceId: LPCWSTR; {const} key: PROPERTYKEY): HRESULT; stdcall;
  end;

  IMMDeviceEnumerator = interface(IUnknown)
  ['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
    function EnumAudioEndpoints(dataFlow: EDataFlow; dwStateMask: DWORD;
      out ppDevices: IMMDeviceCollection): HRESULT; stdcall;
    function GetDefaultAudioEndpoint(dataFlow: EDataFlow; role: ERole;
      out ppEndpoint: IMMDevice): HRESULT; stdcall;
    function GetDevice(pwstrId: LPCWSTR; out ppDevice: IMMDevice): HRESULT; stdcall;
    function RegisterEndpointNotificationCallback(const pClient: IMMNotificationClient): HRESULT; stdcall;
    function UnregisterEndpointNotificationCallback(const pClient: IMMNotificationClient): HRESULT; stdcall;
  end;

我猜 MfPack 和其他库包括 MMDeviceAPI.pas ,它拥有所有这些接口。

demoapp

来源:https://learn.microsoft.com/en-us/windows/win32/coreaudio/audio-events-for-legacy-audio-applications

如果您想修改其他应用程序的音频会话并找到其图标、可执行文件等。最好使用 IAudioSessionManager2 (Windows 7+)
这是我的实现 https://github.com/vhanla/snotify/blob/ 596d22f5bd89e58297d15ffff6df2d0f69bd0351/AudioSessionService.pas

What a coincidence, I'm also writing my personal radio stream client using bass.dll, which is a great library BTW. However, I still wanted to integrate with Windows' mixer, so modifying volume from external applications (like EarTrumpet or Windows Mixer itself) would reflect in my application's volume slider too automatically.

As comments mentioned, there are some disadvantages with Windows XP, since IAudioSession was introduced from Vista onwards, and Windows 7 and newer included improvementes like IAudioSessionManager2, IAudioVolumeDuckNotification, etc).
Still for your own application, IAudioSessionManager will work well, specially using IAudioSimpleVolume. Gladly, that's supported in Vista+.

You can get a variety of MMDeviceAPI implementations at GitHub and many other forums, but here I will strip the necessary interfaces to achieve what we need.

You will need IAudioSessionEvents, IAudioSessionControl, ISimpleAudioVolume, IAudioSessionManager, IMMDevice, IMMDeviceCollection, IMMNotificationClient, and IMMDeviceEnumerator interfaces.

To respond to external audio events, we need to implement our interface extending IAudioSessionsEvents as follows:

TAudioEvent = class(TInterfacedPersistent, IAudioSessionEvents)
  private
    // IAudioSessionEvents
    function OnDisplayNameChanged(NewDisplayName: LPCWSTR; EventContext: PGUID): HRESULT; stdcall;
    function OnIconPathChanged(NewIconPath: LPCWSTR; EventContext: PGUID): HRESULT; stdcall;
    // THIS WILL UPDATE "AUTOMATICALLY" ON VOLUME CHANGES
    function OnSimpleVolumeChanged(NewVolume    : Single;
                                   NewMute      : BOOL;
                                   EventContext : PGUID): HRESULT; stdcall;
    function OnChannelVolumeChanged(ChannelCount    : UINT;
                                    NewChannelArray : PSingle;
                                    ChangedChannel  : UINT;
                                    EventContext    : PGUID): HRESULT; stdcall;
    function OnGroupingParamChanged(NewGroupingParam,
                                    EventContext: PGUID): HRESULT; stdcall;
    function OnStateChanged(NewState: TAudioSessionState): HRESULT; stdcall;
    function OnSessionDisconnected(
              DisconnectReason: TAudioSessionDisconnectReason): HRESULT; stdcall;
  public
//    constructor Create(AppWindow: HWND); <-- left for improvements
//    destructor Destroy; override;
  end;
...
function TAudioEvent.OnSimpleVolumeChanged(NewVolume: Single; NewMute: BOOL;
  EventContext: PGUID): HRESULT;
begin
  // min 0, max 10 for slider, NewVolume is 0 to 1 in Single type
  Form1.Slider1.Value := Round(NewVolume*10);
  if NewMute then
    Form1.MuteButton.Caption := 'Muted'
  else
    Form1.MuteButton.Caption := ' ';
end;

That's the event handler, it will update volume and mute status as well, any volume changes from within the application or from Windows mixer.

NOTICE, it needs improvements to handle session changes, e.g. when you switch physical audio device, reassign to another audio device, etc.

Now, use a global variables to handle the audio sessions and related queries.

  var devicen: IMMDeviceEnumerator;
   device: IMMDevice;
   audiosession: IAudioSessionManager;
   control: IAudioSessionControl;
   audio: ISimpleAudioVolume;
   audioevent: TAudioEvent;
   volume: Single;

Let's initialize after loading Bass.dll.

//HRESULT, I guess you have to do CoInitialize(nil), but Delphi does it by default, at least, that's what they say.
// Get the enumerator for the audio endpoint devices
// on this system.
  hr := CoCreateInstance(CLASS_IMMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, devicen);
// Get the audio endpoint device with the specified data-flow
// direction (eRender or eCapture) and device role.
  devicen.GetDefaultAudioEndpoint(eRender, eMultimedia, device);
// Get the session manager for the endpoint device.
  device.Activate(IID_IAudioSessionManager, CLSCTX_INPROC_SERVER, nil, audiosession);
// Get the control interface for the process-specific audio
// session with session GUID = GUID_NULL. This is the session
// that an audio stream for a DirectSound, DirectShow, waveOut,
// or PlaySound application stream belongs to by default.
// GUID_NULL or nil will assign to the default session 
// (i.e.) our application (bass created, if done previously) session
  audiosession.GetAudioSessionControl(nil, 0, control);
  audioevent := TAudioEvent.Create;
  // register our audio session events 
  control.RegisterAudioSessionNotification(audioevent);
  // to get/set its volume we will use GetSimpleAudioVolume (NIL, too
  audiosession.GetSimpleAudioVolume(nil, 0, audio);
  // retrieve its value 
  audio.GetMasterVolume(volume);
  // show it in our slider
  Slider1.Value := Round(volume*10);

Hint: this might even be included in our previous custom interface, for cleaner code.

To modify our application volume:

procedure TForm1.Slider1Change(Sender: TObject);
var
  vol: Single;
begin
  if BASS_ChannelIsActive(chan) = BASS_ACTIVE_PLAYING then
  begin
    vol := Slider1.Value / 10;
    audio.SetMasterVolume(vol, nil);
  end;
end;

Similarly to toggle mute, the event handler will update visually, we just need to toggle it.

procedure TForm1.MuteButtonClick(Sender: TObject);
var
  ismute: LongBool;
begin
  audio.GetMute(ismute);
  if isMute then
    audio.SetMute(0, nil)
  else
    audio.SetMute(1, nil);  
end;

And that's it, it is up to you improving it. I know, this was a mess of a code, of course it needs cleaning and handling safe releasing interfaces, etc.

Finally, here are the interfaces required:

const
  CLASS_IMMDeviceEnumerator             : TGUID = '{BCDE0395-E52F-467C-8E3D-C4579291692E}';
  IID_IMMDeviceEnumerator               : TGUID = '{A95664D2-9614-4F35-A746-DE8DB63617E6}';
  IID_IAudioSessionManager              : TGUID = '{BFA971F1-4D5E-40BB-935E-967039BFBEE4}';
const
  eRender                               = 0;
const
  eConsole                              = 0;
  eMultimedia                           = eConsole + 1;
type
  TAudioSessionDisconnectReason = (DisconnectReasonDeviceRemoval,
    DisconnectReasonServerShutdown, DisconnectReasonFormatChanged,
    DisconnectReasonSessionLogoff, DisconnectReasonSessionDisconnected,
    DisconnectReasonExclusiveModeOverride);
  TAudioSessionState = (AudioSessionStateInactive, AudioSessionStateActive,
                       AudioSessionStateExpired);
  IAudioSessionEvents = interface(IUnknown)
  ['{24918ACC-64B3-37C1-8CA9-74A66E9957A8}']
    function OnDisplayNameChanged(NewDisplayName: LPCWSTR; EventContext: PGUID): HRESULT; stdcall;
    function OnIconPathChanged(NewIconPath: LPCWSTR; EventContext: PGUID): HRESULT; stdcall;
    function OnSimpleVolumeChanged(NewVolume    : Single;
                                   NewMute      : BOOL;
                                   EventContext : PGUID): HRESULT; stdcall;
    function OnChannelVolumeChanged(ChannelCount    : UINT;
                                    NewChannelArray : PSingle;
                                    ChangedChannel  : UINT;
                                    EventContext    : PGUID): HRESULT; stdcall;
    function OnGroupingParamChanged(NewGroupingParam,
                                    EventContext: PGUID): HRESULT; stdcall;
    function OnStateChanged(NewState: TAudioSessionState): HRESULT; stdcall;
    function OnSessionDisconnected(
              DisconnectReason: TAudioSessionDisconnectReason): HRESULT; stdcall;
  end;

  IAudioSessionControl = interface(IUnknown)
  ['{F4B1A599-7266-4319-A8CA-E70ACB11E8CD}']
    function GetState(out pRetVal: TAudioSessionState): HRESULT; stdcall;
    function GetDisplayName(out pRetVal: LPWSTR): HRESULT; stdcall; // pRetVal must be freed by CoTaskMemFree
    function SetDisplayName(Value: LPCWSTR; EventContext: PGUID): HRESULT; stdcall;
    function GetIconPath(out pRetVal: LPWSTR): HRESULT; stdcall;  // pRetVal must be freed by CoTaskMemFree
    function SetIconPath(Value: LPCWSTR; EventContext: PGUID): HRESULT; stdcall;
    function GetGroupingParam(pRetVal: PGUID): HRESULT; stdcall;
    function SetGroupingParam(OverrideValue, EventContext: PGUID): HRESULT; stdcall;
    function RegisterAudioSessionNotification(
                 const NewNotifications: IAudioSessionEvents): HRESULT; stdcall;
    function UnregisterAudioSessionNotification(
                 const NewNotifications: IAudioSessionEvents): HRESULT; stdcall;
  end;

  ISimpleAudioVolume = interface(IUnknown)
  ['{87CE5498-68D6-44E5-9215-6DA47EF883D8}']
    function SetMasterVolume(fLevel: Single; EventContext: PGUID): HRESULT; stdcall;
    function GetMasterVolume(out fLevel: Single): HRESULT; stdcall;
    // bMute either TRUE = 1 or FALSE = 0 !
    function SetMute(bMute: Longint; EventContext: PGUID): HRESULT; stdcall;
    function GetMute(out bMute: BOOL): HRESULT; stdcall;
  end;

  IAudioSessionManager = interface(IUnknown)
  ['{BFA971F1-4D5E-40BB-935E-967039BFBEE4}']
    function GetAudioSessionControl(AudioSessionGuid: PGUID; StreamFlag : UINT;
                    out SessionControl: IAudioSessionControl): HRESULT; stdcall;
    function GetSimpleAudioVolume(AudioSessionGuid: PGUID; StreamFlag: UINT;
                         out AudioVolume: ISimpleAudioVolume): HRESULT; stdcall;
  end;

  EDataFlow = TOleEnum;
  ERole = TOleEnum;
{$IF CompilerVersion >= 21.0}  //Winapi.PropSys
  IPropertyStore  = Winapi.PropSys.IPropertyStore;
  {$EXTERNALSYM IPropertyStore}
{$ELSE}
  IPropertyStore  = ShlObj.IPropertyStore;
{$ENDIF}

  IMMDevice = interface(IUnknown)
  ['{D666063F-1587-4E43-81F1-B948E807363F}']
    function Activate(const iid: TGUID; dwClsCtx: DWORD; pActivationParams: PPropVariant; out ppInterface): HRESULT; stdcall;
    function OpenPropertyStore(stgmAccess: DWORD; out ppProperties: IPropertyStore): HRESULT; stdcall;
    function GetId(out ppstrId: LPWSTR): HRESULT; stdcall;
    function GetState(out pdwState: DWORD): HRESULT; stdcall;
  end;

  IMMDeviceCollection = interface(IUnknown)
  ['{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}']
    function GetCount(out pcDevices: UINT): HRESULT; stdcall;
    function Item(nDevice: UINT; out ppDevice: IMMDevice): HRESULT; stdcall;
  end;

  IMMNotificationClient = interface(IUnknown)
  ['{7991EEC9-7E89-4D85-8390-6C703CEC60C0}']
    function OnDeviceStateChanged(pwstrDeviceId: LPCWSTR; dwNewState: DWORD): HRESULT; stdcall;
    function OnDeviceAdded(pwstrDeviceId: LPCWSTR): HRESULT; stdcall;
    function OnDeviceRemoved(pwstrDeviceId: LPCWSTR): HRESULT; stdcall;
    function OnDefaultDeviceChanged(flow: EDataFlow; role: ERole; pwstrDefaultDeviceId: LPCWSTR): HRESULT; stdcall;
    function OnPropertyValueChanged(pwstrDeviceId: LPCWSTR; {const} key: PROPERTYKEY): HRESULT; stdcall;
  end;

  IMMDeviceEnumerator = interface(IUnknown)
  ['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
    function EnumAudioEndpoints(dataFlow: EDataFlow; dwStateMask: DWORD;
      out ppDevices: IMMDeviceCollection): HRESULT; stdcall;
    function GetDefaultAudioEndpoint(dataFlow: EDataFlow; role: ERole;
      out ppEndpoint: IMMDevice): HRESULT; stdcall;
    function GetDevice(pwstrId: LPCWSTR; out ppDevice: IMMDevice): HRESULT; stdcall;
    function RegisterEndpointNotificationCallback(const pClient: IMMNotificationClient): HRESULT; stdcall;
    function UnregisterEndpointNotificationCallback(const pClient: IMMNotificationClient): HRESULT; stdcall;
  end;

I guess MfPack and other libraries include MMDeviceAPI.pas which has all of them.

demoapp

Source: https://learn.microsoft.com/en-us/windows/win32/coreaudio/audio-events-for-legacy-audio-applications

If you want to modify other applications' audio sessions and also find its icon, executable, etc. It is better to use IAudioSessionManager2 (Windows 7+)
here is my implementation https://github.com/vhanla/snotify/blob/596d22f5bd89e58297d15ffff6df2d0f69bd0351/AudioSessionService.pas

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文