2

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

MarMar
  • 35
  • 1
  • 9
  • If you want to record software audio from another program on Windows Vista or newer then I'm afraid that you will probably have to find custom sound drivers for soundard first. Why? Microsoft is disalowing hardware maufacturers to provide oficial drivers which allow to record audio from software channerls due the fact that this was comonly used to bypas the copyright protection of basically any digital audio. Now you might be able to get audio volume from windows audio API but I'm not sure how. – SilverWarior Sep 22 '14 at 06:48
  • I want to get data from mixer in windows with api. In widows 7 every appliction have own mixer and i dont have a problem to get this. But i need get the windows generel mixer – MarMar Sep 22 '14 at 06:56
  • 1
    The simplest solution is probably to use a hardware loop - plug the audio output into an audio input (ensuring it's not in pass-through mode) and read the signal from that input. Otherwise, for a software implementation, @mg30rg has the correct answer that this requires a kernel-mode driver. – J... Sep 22 '14 at 09:27
  • @J... It is funny how I concentrated on the software solution. I did not even think about a hardware sound loop. My only excuse is that I thought of solving a problem where seemingly direct wave out is still necessary. – mg30rg Sep 22 '14 at 09:39
  • @mg30rg If you need the audio to also still go out to speakers then you can always use a splitter... you can buy them for a pittance. – J... Sep 22 '14 at 09:44
  • @J... _You are right but..._ If _MarMar_ wishes to provide some volume-based service instead of an actual music ripper tool (which was my _primary assumption_), then creating a program which _requires_ a(n external) _hardware component_ (even if it is only a jack-to-jack cable) would reduce the _usability_ of his/her program. – mg30rg Sep 22 '14 at 10:30
  • @mg30rg Agreed. It's not a viable solution for any sort of professional software. The purpose of the project was not made clear so I offered the suggestion in the case that this might be a personal project with limited scope and a potential need for an easy, if inelegant, solution. Even with a kernel-mode driver I'm not sure this could work, if not technically at least legally; surely not as commercial software given that such a driver would open so many security and copyright loopholes. This is why pro-audio software so heavily uses a plug-in based architecture for this sort of thing. – J... Sep 22 '14 at 10:36
  • @J... Since I'm living in a country with **completely different copyright laws** compared to the **USA** or **Canada** I have no clue about the legal issues. (i.e.: In **Hungary** you are free to make copies for personal purpose of any media you own, and you can download _anything_ for personal use.) However I'm **pretty aware** of the technical side of the solution I suggested, and its _more then possible_. The only thing that might prevent it is if the media playing SW also installs a kernel mode component to detect audio loop software, and it is very unlikely. – mg30rg Sep 22 '14 at 10:51
  • Perhaps some of this could be of any help to you : http://www.torry.net/pages.php?id=167 – Jens Borrisholt Sep 22 '14 at 11:53
  • @MarMar do you mean get peak meters for speakers? IF yes i can paste my code for that. Works on Vista/7 and 8 – mca64 Sep 22 '14 at 21:55

2 Answers2

5

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.
mca64
  • 534
  • 1
  • 5
  • 17
3

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.

mg30rg
  • 1,311
  • 13
  • 24
  • I have to mention, that _my solution is heavily based on the "do it yourself" attitude_ of the **hobby coder** . You _might_ find _oven-ready_ solution of loopback audio driver(s) on the market, (maybe even for free) and utilise them. Only I would not choose that solution because it is less "fun". :v – mg30rg Sep 22 '14 at 11:00
  • @J... Exactly what I thought of. – mg30rg Sep 22 '14 at 12:15
  • "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." I dont agree. Windows provide access and you can copy audio stream from your speakers. You can see egxample directshow filter for that https://github.com/rdp/virtual-audio-capture-grabber-device/tree/master/source_code/acam – mca64 Sep 22 '14 at 23:31