我甚至不确定这是否符合一个问题,但这是一个单一的问题。我有一个用Delphi编写的网络无线电播放器,它使用贝司库进行音频流和回放。应用程序需要在Windows、Vista和7下运行。
低音使它很容易控制全球音量,但没有设施静音,一般来说,这是一个更好的想法控制音量的基础上。
低音也使它很容易控制一个“通道”(流)的音量,但同样没有静音,这也不是适当的每个应用程序的控制。( Windows混频器中的应用程序卷控制不受影响。)
我知道,对于Vista和更高版本,我需要ISimpleAudioVolume和/或IAudioEndpointVolume,但无法找到这些实现的Delphi。所以问题的一部分是,它是否以第三方图书馆的形式存在?
第二部分是,在XP上控制卷和切换静音(系统范围或每个应用程序)的正确方法是什么,而这些接口是不可用的?
发布于 2011-05-27 12:40:47
使用这个简单的代码来静音它在我的机器上工作的主卷:
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;
发布于 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的接口,如下所示:
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,它拥有所有的库。
来源: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
https://stackoverflow.com/questions/4616108
复制相似问题