Delphi - 声音变化通知(静音/取消静音)

时间:2021-03-14 22:34:07

标签: delphi audio callback mute

如何获得系统音频更改的通知?

或者如何使用回调函数

function RegisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): Integer; stdcall;
function UnregisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): Integer; stdcall;
function RegisterEndpointNotificationCallback(pClient: IMMNotificationClient): Hresult; stdcall;

2 个答案:

答案 0 :(得分:2)

首先声明:我不是音频 API 方面的专家。不过,我可以使用 documentation 让它工作。

首先,我们需要使用 CoCreateInstance 获得一个 IMMDeviceEnumerator 接口。然后我们使用 IMMDeviceEnumerator.GetDefaultAudioEndpoint 方法获取默认音频输出 device。使用设备的 Activate 方法,我们请求 IAudioEndpointVolume 接口并调用其 RegisterControlChangeNotify 方法订阅音量通知,包括静音和取消静音。

我们必须为这些通知提供一个接收者,并且该接收者必须实现 IAudioEndpointVolumeCallback 接口,该接口指定接收者对象实际如何接收通知。

在单表单 GUI 应用程序中,就像我为这个答案编写的演示应用程序一样,使用主表单是有意义的。因此,我们必须让表单实现 IAudioEndpointVolumeCallback.OnNotify 方法。当音量改变(或(取消)静音)时,音频系统会调用此方法,并且通知数据以 AUDIO_VOLUME_NOTIFICATION_DATA 结构传递。

我不想在此方法中触摸 GUI 或冒引发异常的风险,因此为了安全起见,我只让此方法将消息发送到包含所需数据的表单。

完整代码:

unit OSD;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, ActiveX,
  ComObj, AudioEndpoint, Gauge;

  // Gauge: https://specials.rejbrand.se/dev/controls/gauge/

const
  WM_VOLNOTIFY = WM_USER + 1;

type
  TSndVolFrm = class(TForm, IAudioEndpointVolumeCallback)
    ArcGauge: TArcGauge;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FDeviceEnumerator: IMMDeviceEnumerator;
    FMMDevice: IMMDevice;
    FAudioEndpointVolume: IAudioEndpointVolume;
    function OnNotify(pNotify: PAUDIO_VOLUME_NOTIFICATION_DATA): HRESULT;
      stdcall;
    procedure WMVolNotify(var Msg: TMessage); message WM_VOLNOTIFY;
  public
  end;

var
  SndVolFrm: TSndVolFrm;

implementation

uses
  Math;

{$R *.dfm}

procedure TSndVolFrm.FormCreate(Sender: TObject);
begin

  if not Succeeded(CoInitialize(nil)) then
    ExitProcess(1);

  OleCheck(CoCreateInstance(CLASS_IMMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER,
    IID_IMMDeviceEnumerator, FDeviceEnumerator));
  OleCheck(FDeviceEnumerator.GetDefaultAudioEndpoint(0, 0, FMMDevice));
  OleCheck(FMMDevice.Activate(IID_IAudioEndpointVolume, CLSCTX_INPROC_SERVER, nil, FAudioEndpointVolume));
  OleCheck(FAudioEndpointVolume.RegisterControlChangeNotify(Self));

end;

procedure TSndVolFrm.FormDestroy(Sender: TObject);
begin
  CoUninitialize;
end;

function TSndVolFrm.OnNotify(pNotify: PAUDIO_VOLUME_NOTIFICATION_DATA): HRESULT;
begin
  if pNotify = nil then
    Exit(E_POINTER);
  try
    PostMessage(Handle, WM_VOLNOTIFY, WPARAM(pNotify.bMuted <> False), LPARAM(Round(100 * pNotify.fMasterVolume)));
    Result := S_OK;
  except
    Result := E_UNEXPECTED;
  end;
end;

procedure TSndVolFrm.WMVolNotify(var Msg: TMessage);
begin

  var LMute := Msg.WParam <> 0;
  var LVolume := Msg.LParam;

  if LMute then
  begin
    ArcGauge.ShowCaption := False;
    ArcGauge.FgBrush.Color := $777777;
  end
  else
  begin
    ArcGauge.ShowCaption := True;
    ArcGauge.FgBrush.Color := clHighlight;
  end;

  ArcGauge.Position := LVolume;

end;

end.

Screen recorder of application in action, responding to volume changes including mute and unmute.

接口单元:

unit AudioEndpoint;

interface

uses
  Windows,
  Messages,
  SysUtils,
  ActiveX,
  ComObj;

const
  CLASS_IMMDeviceEnumerator : TGUID = '{BCDE0395-E52F-467C-8E3D-C4579291692E}';
  IID_IMMDeviceEnumerator : TGUID = '{A95664D2-9614-4F35-A746-DE8DB63617E6}';
  IID_IAudioEndpointVolume : TGUID = '{5CDF2C82-841E-4546-9722-0CF74078229A}';

type
  PAUDIO_VOLUME_NOTIFICATION_DATA = ^AUDIO_VOLUME_NOTIFICATION_DATA;
  AUDIO_VOLUME_NOTIFICATION_DATA = record
    guidEventContext: TGUID;
    bMuted: BOOL;
    fMasterVolume: Single;
    nChannels: UINT;
    afChannelVolumes: Single;
  end;

  IAudioEndpointVolumeCallback = interface(IUnknown)
    ['{657804FA-D6AD-4496-8A60-352752AF4F89}']
    function OnNotify(pNotify: PAUDIO_VOLUME_NOTIFICATION_DATA): HRESULT; stdcall;
  end;

  IAudioEndpointVolume = interface(IUnknown)
    ['{5CDF2C82-841E-4546-9722-0CF74078229A}']
    function RegisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): HRESULT; stdcall;
    function UnregisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): HRESULT; stdcall;
    function GetChannelCount(out PInteger): HRESULT; stdcall;
    function SetMasterVolumeLevel(fLevelDB: single; pguidEventContext: PGUID): HRESULT; stdcall;
    function SetMasterVolumeLevelScalar(fLevelDB: single; pguidEventContext: PGUID): HRESULT; stdcall;
    function GetMasterVolumeLevel(out fLevelDB: single): HRESULT; stdcall;
    function GetMasterVolumeLevelScaler(out fLevelDB: single): HRESULT; stdcall;
    function SetChannelVolumeLevel(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
    function SetChannelVolumeLevelScalar(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
    function GetChannelVolumeLevel(nChannel: Integer; out fLevelDB: double): HRESULT; stdcall;
    function GetChannelVolumeLevelScalar(nChannel: Integer; out fLevel: double): HRESULT; stdcall;
    function SetMute(bMute: Boolean; pguidEventContext: PGUID): HRESULT; stdcall;
    function GetMute(out bMute: Boolean): HRESULT; stdcall;
    function GetVolumeStepInfo(pnStep: Integer; out pnStepCount: Integer): HRESULT; stdcall;
    function VolumeStepUp(pguidEventContext: PGUID): HRESULT; stdcall;
    function VolumeStepDown(pguidEventContext: PGUID): HRESULT; stdcall;
    function QueryHardwareSupport(out pdwHardwareSupportMask): HRESULT; stdcall;
    function GetVolumeRange(out pflVolumeMindB: double; out pflVolumeMaxdB: double; out pflVolumeIncrementdB: double): HRESULT; stdcall;
  end;

  IAudioMeterInformation = interface(IUnknown)
  ['{C02216F6-8C67-4B5B-9D00-D008E73E0064}']
  end;

  IPropertyStore = interface(IUnknown)
  end;

  IMMDevice = interface(IUnknown)
  ['{D666063F-1587-4E43-81F1-B948E807363F}']
    function Activate(const refId: TGUID; dwClsCtx: DWORD;  pActivationParams: PInteger; out pEndpointVolume: IAudioEndpointVolume): HRESULT; stdCall;
    function OpenPropertyStore(stgmAccess: DWORD; out ppProperties: IPropertyStore): HRESULT; stdcall;
    function GetId(out ppstrId: PLPWSTR): HRESULT; stdcall;
    function GetState(out State: Integer): HRESULT; stdcall;
  end;


  IMMDeviceCollection = interface(IUnknown)
  ['{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}']
  end;

  IMMNotificationClient = interface(IUnknown)
  ['{7991EEC9-7E89-4D85-8390-6C703CEC60C0}']
  end;

  IMMDeviceEnumerator = interface(IUnknown)
  ['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
    function EnumAudioEndpoints(dataFlow: TOleEnum; deviceState: SYSUINT; DevCollection: IMMDeviceCollection): HRESULT; stdcall;
    function GetDefaultAudioEndpoint(EDF: SYSUINT; ER: SYSUINT; out Dev :IMMDevice ): HRESULT; stdcall;
    function GetDevice(pwstrId: pointer; out Dev: IMMDevice): HRESULT; stdcall;
    function RegisterEndpointNotificationCallback(pClient: IMMNotificationClient): HRESULT; stdcall;
  end;

implementation

end.

答案 1 :(得分:1)

我有一些代码给你,3 个源代码文件:一个带有处理音量控制通知的类的单元,一个与 Windows API 接口的单元和一个简单的演示程序。该演示实际上是您需要详细查看的全部内容。其余的可以被认为是晦涩的支持例程:-)

让我们看看演示程序。它是一个简单的 VCL 表格,上面只有一个 TMemo。它注册音量控制通知并在备忘录中显示一条简单的消息(您可能想要一个漂亮的用户界面)。

代码真的很简单:创建一个指向 TVolumeControl 的接口,为 OnVolumeChange 分配一个事件处理程序并调用 Initialize 方法。当事件触发时,调用 GetLevelInfo 来获取信息并显示它。当表单被销毁时,调用 Dispose 方法停止接收通知。

unit SoundChangeNotificationDemoMain;

interface

uses
    Winapi.Windows, Winapi.Messages, Winapi.ActiveX,
    System.SysUtils, System.Variants, System.Classes, System.SyncObjs,
    System.Win.ComObj,
    Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
    Ovb.VolumeMonitor,
    Ovb.MMDevApi;

type
    TSoundChangeDemoForm = class(TForm)
        Memo1: TMemo;
    protected
        FVolumeMonitor  : IVolumeMonitor;
        procedure VolumeMonitorVolumeChange(Sender : TObject);
    public
        constructor Create(AOwner : TComponent); override;
        destructor  Destroy; override;
    end;

var
    SoundChangeDemoForm: TSoundChangeDemoForm;

implementation
{$R *.dfm}

constructor TSoundChangeDemoForm.Create(AOwner: TComponent);
var
    HR : HRESULT;
begin
    inherited;
    FVolumeMonitor                := TVolumeMonitor.Create;
    FVolumeMonitor.OnVolumeChange := VolumeMonitorVolumeChange;
    HR                            := FVolumeMonitor.Initialize();
    if not SUCCEEDED(HR) then
        ShowMessage('Volume control initialization failed');
end;

destructor TSoundChangeDemoForm.Destroy;
begin
    FVolumeMonitor.Dispose;
    inherited Destroy;
end;

procedure TSoundChangeDemoForm.VolumeMonitorVolumeChange(Sender: TObject);
var
    Info: TVOLUME_INFO;
begin
    FVolumeMonitor.GetLevelInfo(Info);
    Memo1.Lines.Add(Format('Volume change: nStep=%d cSteps=%d Mute=%d',
                           [Info.nStep, Info.cSteps, Ord(Info.bMuted)]));
end;

辛苦的工作是我命名为 Ovb.VolumeMonitor 的单元。此单元与 Windows API 交互以在默认音频设备上更改音量时请求通知。

请注意,这不是组件而是类,您可以通过接口使用此类。请参阅上面的演示应用。

unit Ovb.VolumeMonitor;

interface

uses
    Winapi.Windows, Winapi.Messages, Winapi.ActiveX,
    System.SysUtils, System.Variants, System.Classes, System.SyncObjs,
    System.Win.ComObj,
    Ovb.MMDevApi;

const
    WM_VOLUMECHANGE     = (WM_USER + 12);
    WM_ENDPOINTCHANGE   = (WM_USER + 13);   // Not implemented yet

type
    IVolumeMonitor = interface
        ['{B06EE2E9-E707-4086-829A-D5664978069F}']
        function  Initialize() : HRESULT;
        procedure Dispose;
        function  GetLevelInfo(var Info: TVOLUME_INFO) : HRESULT;
        function  GetOnVolumeChange: TNotifyEvent;
        procedure SetOnVolumeChange(const Value: TNotifyEvent);
        property OnVolumeChange : TNotifyEvent   read  GetOnVolumeChange
                                                 write SetOnVolumeChange;
    end;

    TVolumeMonitor = class(TInterfacedObject,
                           IVolumeMonitor,
                           IMMNotificationClient,
                           IAudioEndpointVolumeCallback)
    private
        FRegisteredForEndpointNotifications : BOOL;
        FRegisteredForVolumeNotifications   : BOOL;
        FDeviceEnumerator                   : IMMDeviceEnumerator;
        FAudioEndpoint                      : IMMDevice;
        FAudioEndpointVolume                : IAudioEndpointVolume;
        FEndPointCritSect                   : TRTLCriticalSection;
        FWindowHandle                       : HWND;
        FOnVolumeChange                     : TNotifyEvent;
        procedure WndProc(var Msg: TMessage);
        procedure WMVolumeChange(var Msg: TMessage);
        function  GetOnVolumeChange: TNotifyEvent;
        procedure SetOnVolumeChange(const Value: TNotifyEvent);
        function  AttachToDefaultEndpoint() : HRESULT;
        function  OnNotify(pNotify : PAUDIO_VOLUME_NOTIFICATION_DATA) : HRESULT; stdcall;
    public
        constructor Create; virtual;
        destructor  Destroy; override;
        function  Initialize() : HRESULT;
        procedure DetachFromEndpoint();
        procedure Dispose;
        function GetLevelInfo(var Info: TVOLUME_INFO) : HRESULT;
        property OnVolumeChange : TNotifyEvent   read  GetOnVolumeChange
                                                 write SetOnVolumeChange;
    end;

implementation

{ TVolumeMonitor }

constructor TVolumeMonitor.Create;
begin
    inherited Create;
    FWindowHandle                         := AllocateHWnd(WndProc);
    FRegisteredForEndpointNotifications := FALSE;
    FRegisteredForVolumeNotifications   := FALSE;
    FEndPointCritSect.Initialize();
end;

destructor TVolumeMonitor.Destroy;
begin
    if FWindowHandle <> INVALID_HANDLE_VALUE then begin
        DeallocateHWnd(FWindowHandle);
        FWindowHandle := INVALID_HANDLE_VALUE;
    end;
    FEndPointCritSect.Free;
    inherited Destroy;
end;

//  Initialize this object.  Call after constructor.
function TVolumeMonitor.Initialize: HRESULT;
var
    hr : HRESULT;
begin
    hr := CoCreateInstance(CLASS_IMMDeviceEnumerator,
                           nil,
                           CLSCTX_INPROC_SERVER,
                           IID_IMMDeviceEnumerator,
                           FDeviceEnumerator);
    if SUCCEEDED(hr) then begin
        hr := FDeviceEnumerator.RegisterEndpointNotificationCallback(Self);
        if SUCCEEDED(hr) then
            hr := AttachToDefaultEndpoint();
    end;
    Result := hr;
end;

function TVolumeMonitor.AttachToDefaultEndpoint: HRESULT;
var
    hr : HRESULT;
begin
    FEndPointCritSect.Enter();

    // Get the default music & movies playback device
    hr := FDeviceEnumerator.GetDefaultAudioEndpoint(eRender, eMultimedia, FAudioEndpoint);
    if SUCCEEDED(hr) then begin
        // Get the volume control for it
        hr := FAudioEndpoint.Activate(IAudioEndpointVolume, CLSCTX_INPROC_SERVER, nil, FAudioEndpointVolume);
        if SUCCEEDED(hr) then begin
            // Register for callbacks
            hr := FAudioEndpointVolume.RegisterControlChangeNotify(self);
            FRegisteredForVolumeNotifications := SUCCEEDED(hr);
        end;
    end;

    FEndPointCritSect.Leave();

    Result := hr;
end;

//  Stop monitoring the device and release all associated references
procedure TVolumeMonitor.DetachFromEndpoint();
begin
    FEndPointCritSect.Enter();

    if FAudioEndpointVolume <> nil then begin
        // be sure to unregister...
        if FRegisteredForVolumeNotifications then begin
            FAudioEndpointVolume.UnregisterControlChangeNotify(Self);
            FRegisteredForVolumeNotifications := FALSE;
        end;

        FAudioEndpointVolume := nil
    end;

    if FAudioEndpoint <> nil then
        FAudioEndpoint := nil;

    FEndPointCritSect.Leave();
end;

//  Call when the app is done with this object before calling release.
//  This detaches from the endpoint and releases all audio service references.
procedure TVolumeMonitor.Dispose;
begin
    DetachFromEndpoint();

    if FRegisteredForEndpointNotifications then begin
        FDeviceEnumerator.UnregisterEndpointNotificationCallback(Self);
        FRegisteredForEndpointNotifications := FALSE;
    end;
end;

function TVolumeMonitor.GetLevelInfo(var Info: TVOLUME_INFO): HRESULT;
var
    hr : HRESULT;
begin
    hr := E_FAIL;
    FEndPointCritSect.Enter();
    if FAudioEndpointVolume <> nil then begin
        hr := FAudioEndpointVolume.GetMute(Info.bMuted);
        if SUCCEEDED(hr) then
            hr := FAudioEndpointVolume.GetVolumeStepInfo(Info.nStep, Info.cSteps);
    end;
    FEndPointCritSect.Leave();
    Result := hr;
end;

function TVolumeMonitor.GetOnVolumeChange: TNotifyEvent;
begin
    Result := FOnVolumeChange;
end;

// Callback for Windows API
function TVolumeMonitor.OnNotify(
    pNotify: PAUDIO_VOLUME_NOTIFICATION_DATA): HRESULT;
begin
    if FWindowHandle <> INVALID_HANDLE_VALUE then
        PostMessage(FWindowHandle, WM_VOLUMECHANGE, 0, 0);
    Result := S_OK;
end;

procedure TVolumeMonitor.SetOnVolumeChange(const Value: TNotifyEvent);
begin
    FOnVolumeChange := Value;
end;

procedure TVolumeMonitor.WMVolumeChange(var Msg: TMessage);
begin
    if Assigned(FOnVolumeChange) then
        FOnVolumeChange(Self);
end;

procedure TVolumeMonitor.WndProc(var Msg: TMessage);
begin
    case Msg.Msg of
    WM_VOLUMECHANGE  : WMVolumeChange(Msg);
    else
        Winapi.Windows.DefWindowProc(FWindowHandle, Msg.Msg, Msg.WParam, Msg.LParam);
    end;
end;

最后,为了与 Windows API 交互,我们需要对 Windows 使用的结构和接口进行一些声明。

unit Ovb.MMDevApi;

interface

uses
    WinApi.Windows,
    WinApi.ActiveX;

const
    CLASS_IMMDeviceEnumerator : TGUID = '{BCDE0395-E52F-467C-8E3D-C4579291692E}';
    IID_IMMDeviceEnumerator   : TGUID = '{A95664D2-9614-4F35-A746-DE8DB63617E6}';
    IID_IAudioEndpointVolume  : TGUID = '{5CDF2C82-841E-4546-9722-0CF74078229A}';

    // Data-flow direction
    eRender         = $00000000;
    eCapture        = $00000001;
    eAll            = $00000002;

    // Role constant
    eConsole        = $00000000;
    eMultimedia     = $00000001;
    eCommunications = $00000002;

type
    TAUDIO_VOLUME_NOTIFICATION_DATA = record
        guidEventContext : TGUID;
        Muted            : BOOL;
        fMasterVolume    : Single;
        nChannels        : UINT;
        afChannelVolumes : array [1..1] of Single;
    end;
    PAUDIO_VOLUME_NOTIFICATION_DATA = ^TAUDIO_VOLUME_NOTIFICATION_DATA;

    TVOLUME_INFO = record
        nStep  : UINT;
        cSteps : UINT;
        bMuted : BOOL;
    end;
    PVOLUME_INFO = ^TVOLUME_INFO;

    IAudioEndpointVolumeCallback = interface(IUnknown)
        ['{657804FA-D6AD-4496-8A60-352752AF4F89}']
        function OnNotify(pNotify : PAUDIO_VOLUME_NOTIFICATION_DATA) : HRESULT; stdcall;
    end;

    IAudioEndpointVolume = interface(IUnknown)
        ['{5CDF2C82-841E-4546-9722-0CF74078229A}']
        function RegisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): HRESULT; stdcall;
        function UnregisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): HRESULT; stdcall;
        function GetChannelCount(out PInteger): HRESULT; stdcall;
        function SetMasterVolumeLevel(fLevelDB: single; pguidEventContext: PGUID): HRESULT; stdcall;
        function SetMasterVolumeLevelScalar(fLevelDB: single; pguidEventContext: PGUID): HRESULT; stdcall;
        function GetMasterVolumeLevel(out fLevelDB: single): HRESULT; stdcall;
        function GetMasterVolumeLevelScaler(out fLevelDB: single): HRESULT; stdcall;
        function SetChannelVolumeLevel(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
        function SetChannelVolumeLevelScalar(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
        function GetChannelVolumeLevel(nChannel: Integer; out fLevelDB: double): HRESULT; stdcall;
        function GetChannelVolumeLevelScalar(nChannel: Integer; out fLevel: double): HRESULT; stdcall;
        function SetMute(bMute: Boolean; pguidEventContext: PGUID): HRESULT; stdcall;
        function GetMute(out bMute: BOOL): HRESULT; stdcall;
        function GetVolumeStepInfo(out pnStep: UINT; out pnStepCount: UINT): HRESULT; stdcall;
        function VolumeStepUp(pguidEventContext: PGUID): HRESULT; stdcall;
        function VolumeStepDown(pguidEventContext: PGUID): HRESULT; stdcall;
        function QueryHardwareSupport(out pdwHardwareSupportMask): HRESULT; stdcall;
        function GetVolumeRange(out pflVolumeMindB: double; out pflVolumeMaxdB: double; out pflVolumeIncrementdB: double): HRESULT; stdcall;
    end;

    IAudioMeterInformation = interface(IUnknown)
        ['{C02216F6-8C67-4B5B-9D00-D008E73E0064}']
    end;

    IPropertyStore = interface(IUnknown)
    end;

    IMMDevice = interface(IUnknown)
        ['{D666063F-1587-4E43-81F1-B948E807363F}']
        function Activate(const refId: TGUID; dwClsCtx: DWORD;  pActivationParams: PInteger; out pEndpointVolume: IAudioEndpointVolume): HRESULT; stdCall;
        function OpenPropertyStore(stgmAccess: DWORD; out ppProperties: IPropertyStore): HRESULT; stdcall;
        function GetId(out ppstrId: PLPWSTR): HRESULT; stdcall;
        function GetState(out State: Integer): HRESULT; stdcall;
    end;


    IMMDeviceCollection = interface(IUnknown)
      ['{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}']
    end;

    IMMNotificationClient = interface(IUnknown)
        ['{7991EEC9-7E89-4D85-8390-6C703CEC60C0}']
    end;

    IMMDeviceEnumerator = interface(IUnknown)
        ['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
        function EnumAudioEndpoints(dataFlow: TOleEnum; deviceState: SYSUINT; DevCollection: IMMDeviceCollection): HRESULT; stdcall;
        function GetDefaultAudioEndpoint(EDF: SYSUINT; ER: SYSUINT; out Dev :IMMDevice ): HRESULT; stdcall;
        function GetDevice(pwstrId: pointer; out Dev: IMMDevice): HRESULT; stdcall;
        function RegisterEndpointNotificationCallback(pClient: IMMNotificationClient): HRESULT; stdcall;
        function UnregisterEndpointNotificationCallback(pClient: IMMNotificationClient): HRESULT; stdcall;
    end;

implementation

end.