首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Delphi中应用声量的控制

Delphi中应用声量的控制
EN

Stack Overflow用户
提问于 2011-01-06 14:44:29
回答 2查看 3.4K关注 0票数 8

我甚至不确定这是否符合一个问题,但这是一个单一的问题。我有一个用Delphi编写的网络无线电播放器,它使用贝司库进行音频流和回放。应用程序需要在Windows、Vista和7下运行。

低音使它很容易控制全球音量,但没有设施静音,一般来说,这是一个更好的想法控制音量的基础上。

低音也使它很容易控制一个“通道”(流)的音量,但同样没有静音,这也不是适当的每个应用程序的控制。( Windows混频器中的应用程序卷控制不受影响。)

我知道,对于Vista和更高版本,我需要ISimpleAudioVolume和/或IAudioEndpointVolume,但无法找到这些实现的Delphi。所以问题的一部分是,它是否以第三方图书馆的形式存在?

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

EN

回答 2

Stack Overflow用户

发布于 2011-05-27 12:40:47

使用这个简单的代码来静音它在我的机器上工作的主卷:

代码语言:javascript
运行
复制
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;
票数 2
EN

Stack Overflow用户

发布于 2021-08-16 17:05:50

太巧了,我还在用bass.dll编写我的个人无线电流客户端,这是一个很棒的图书馆BTW。但是,我仍然希望与Windows的混频器集成,因此从外部应用程序(如EarTrumpet或Windows本身)修改卷也会自动反映在我的应用程序的卷滑块中。

正如评论所提到的,Windows有一些缺点,因为IAudioSession是从Vista开始引入的,而Windows7和更高版本包括了IAudioSessionManager2、IAudioVolumeDuckNotification等改进。不过,对于您自己的应用程序,IAudioSessionManager将运行良好,特别是使用IAudioSimpleVolume。很高兴,这在Vista+中得到了支持。

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

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

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

代码语言:javascript
运行
复制
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混合器中更改任何卷。

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

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

代码语言:javascript
运行
复制
  var devicen: IMMDeviceEnumerator;
   device: IMMDevice;
   audiosession: IAudioSessionManager;
   control: IAudioSessionControl;
   audio: ISimpleAudioVolume;
   audioevent: TAudioEvent;
   volume: Single;

让我们在加载Bass.dll之后初始化。

代码语言:javascript
运行
复制
//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);

提示:为了更干净的代码,这甚至可能包含在我们以前的自定义界面中。

若要修改应用程序卷,请执行以下操作:

代码语言:javascript
运行
复制
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;

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

代码语言:javascript
运行
复制
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;

就这样,这取决于你改进它。我知道,这是一个混乱的代码,当然,它需要清理和处理安全的释放接口,等等。

最后,以下是所需的接口:

代码语言:javascript
运行
复制
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,它拥有所有的库。

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

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4616108

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档