Delphi - Get Wave amplitude

僤鯓⒐⒋嵵緔 提交于 2019-12-23 02:43:05

问题


I need to make Delphi library / component that takes the currently playing sound ( it does not play my apps , just the general sound of what goes on loud-speakers ) returns me the data ( the amplitude of the left and right channels ) . Currently I have it processed by scanning from the microphone. He was looking for and I tried different VU meters that are on the net ( Torry ... ) , but they are not compatible with Win7 and higher. Anyone know of a solution? Thanks


回答1:


dont know if i understood corectly, if you mean how to get peak meter for default playback device you may try this:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Winapi.ActiveX, System.Win.ComObj, MMSystem,
  Vcl.ComCtrls, Vcl.ExtCtrls;

type
  EDATAFLOW = TOleEnum;
  EROLE = TOleEnum;

  IMMDevice = interface(IUnknown)
    ['{D666063F-1587-4E43-81F1-B948E807363F}']
    function Activate(const iid: TGUID; const dwClsCtx: UINT; const pActivationParams: PPropVariant; out ppInterface: IUnknown)
      : HRESULT; stdcall;
  end;

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

  IMMDeviceEnumerator = interface(IUnknown)
    ['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
    function EnumAudioEndpoints(const dataFlow: EDATAFLOW; const dwStateMask: DWORD; out ppDevices: IMMDeviceCollection): HRESULT; stdcall;
    function GetDefaultAudioEndpoint(const dataFlow: EDATAFLOW; const role: EROLE; out ppEndpoint: IMMDevice): HRESULT; stdcall;
  end;

  IAudioMeterInformation = interface(IUnknown)
    ['{C02216F6-8C67-4B5B-9D00-D008E73E0064}']
    function GetPeakValue(out pfPeak: Single): HRESULT; stdcall;
    function GetMeteringChannelCount(out pnChannelCount: UINT): HRESULT; stdcall;
    function GetChannelsPeakValues(u32ChannelCount: UINT; out afPeakValues: pSingle): HRESULT; stdcall;
    function QueryHardwareSupport(out pdwHardwareSupportMask: UINT): HRESULT; stdcall;
  end;

  TForm1 = class(TForm)
    ProgressBar1: TProgressBar;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

const
  IID_IMMDeviceEnumerator: TGUID = '{A95664D2-9614-4F35-A746-DE8DB63617E6}';
  CLASS_IMMDeviceEnumerator: TGUID = '{BCDE0395-E52F-467C-8E3D-C4579291692E}';
  IID_IAudioMeterInformation: TGUID = '{C02216F6-8C67-4B5B-9D00-D008E73E0064}';
  eRender = $00000000;
  eConsole = $00000000;

var
  Form1: TForm1;
  peak: IAudioMeterInformation = nil;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);

var
  device: IMMDevice;
  deviceEnumerator: IMMDeviceEnumerator;
begin
  Timer1.Enabled := False;
  ProgressBar1.Max := 65535; 
  CoCreateInstance(CLASS_IMMDeviceEnumerator, nil, CLSCTX_ALL, IID_IMMDeviceEnumerator, deviceEnumerator);
  deviceEnumerator.GetDefaultAudioEndpoint(eRender, eConsole, device);
  device.Activate(IID_IAudioMeterInformation, CLSCTX_ALL, nil, IUnknown(peak));
  Timer1.Enabled := true;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  Temp: Single;
begin
  peak.GetPeakValue(Temp);
  ProgressBar1.position := Round(Temp * 65535);
end;

end.



回答2:


As far as I know what you are looking for is not possible directly.


Windows does not provide you information of the sound output of other programs. Neither as a direct input, nor as access to their voice mixer. The cause of this behavior is that such access would enable bypassing copyright protection. (You could record the wave output of the media player or media streaming programs.) Windows does not normally provide API s for copyright infringement. This time I will assume you want to perform something law-obedient (like a volume normalizing application) and I will share a solution to bypass this copyright logic, but this solution is not easy and requires extensive programming skills.

So the only way I'm aware of to perform such tasks is to create your own virtual audio device, then treat it as the default wave output device. It could record the received audio data while also channelling it to the audio device you normally use for wave-out. The drawback of this solution is that you have to write a sound card driver. AFAIK Delphi does not provide a way to write kernel mode drivers, so you will have to use C/C++. You also will have to use the WDK compiler.

Writing a driver is not easy. If you choose to do so, installing MS Visual Studio (express) would be a wise (but not necessary) choice. The WDK has plenty of example codes and knowledge base articles that will show you the way to perform this. A sound card driver for your purposes can be hacked together using the example codes in about 10-12 man-hours.

p.s.: Please don't forget to write error-prone code! Don't forget that kernel-mode software could not fail gracefully. In kernel mode an unhandled exception or buffer overrun could cause BSOD.



来源:https://stackoverflow.com/questions/25967462/delphi-get-wave-amplitude

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!