Author Topic: Realtime FFT audio input analysis with BASS in Delphi  (Read 265 times)

silentrider

  • Posts: 2
Hi,
i've just tried to work with this BASS library, but I counldn't understand how to process the data in order to create a functional Delphi app to show me the FFT values inside a TImage control in Delphi.
I asked ChatGPT to help me, but it provided me some code that is not working.
Also I have searched for many other examples in the internet and I couldn't figured out how to use BASS library, unfortunately.
In the past (more than 20 years) I have used MMTools from Swiftsoft, but it cannot be used now with modern Embarcadero versions of Delphi or with Lazarus.
I have created such an app using MMTools but it was so simple... but unfortunately I didn't managed to create it much flexible from the GUI point of view, with rescalation and so on...
Now I have decided after many years to use this BASS library that I understood it is somehow the only one library I can use with modern Delphi Compilers.
I just wanted to create a simple DFFT spectrum realtime analysis from the input port of my soundcard, showing me logarithmic / linear frequency values domain and logarithmic / linear values for amplitudes of the FFT.
Also I would like to modify the lenght of the FFT (1024, 2048, 4096, 8192) and by dragging the bottom-right corner of the main window, the internal black picture with the FFT analysis inside it to auto-rescalate with the window and also the values inside it to be streched-out and fitted accordingly.
I have a piece of code that I have written to provide here, but it is a bit confusing.
Does anybody is willing to help me in finalizing my hobby project, please?
Thank you in advance.
« Last Edit: 30 May '24 - 10:36 by silentrider »

silentrider

  • Posts: 2
I think I made it, finnaly:
-----------------------------

unit myFFTunit;

interface

uses Windows, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Math, ExtCtrls, StdCtrls, Buttons, ComCtrls, Bass;

const
  SAMPLE_RATE = 44100;
  FFT_SIZE = 4096;

type
  TForm1 = class(TForm)
    Image1: TImage;
    CheckBoxFreqAspect: TCheckBox;
    CheckBoxAmpAspect: TCheckBox;
    Timer1: TTimer;
    ON_OFF: TBitBtn;
    Combo_FFTSize: TComboBox;
    StaticText1: TStaticText;
    Slider_Speed: TTrackBar;
    StaticText2: TStaticText;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure ON_OFFClick(Sender: TObject);
    procedure Slider_SpeedChange(Sender: TObject);
  private
    procedure InitializeBass;
  public
    { Public declarations }
  end;

var
  Stream: HRECORD;
  FFTData: array[0..FFT_SIZE - 1] of Single;
  Form1: TForm1;
  Started : BOOLEAN;
  FrameDrawn : BOOLEAN;

implementation

{$R *.dfm}

procedure TForm1.InitializeBass;
var
  errorCode: Integer;
begin
 BASS_RecordInit(-1);
 Stream := BASS_RecordStart(SAMPLE_RATE, 1, 0, nil, nil);
  if BASS_ChannelGetData(Stream, @FFTData, BASS_DATA_FFT4096) = -1 then
    raise Exception.Create('Error capturing FFT data: ' + IntToStr(BASS_ErrorGetCode));
 end;

procedure DrawSpectrum;
var
  i, x, y: Integer;
  Amp: Single;
  Width, Height: Integer;
  LineThick : Word;
begin
  Width := Form1.Image1.Width;
  Height := Form1.Image1.Height;
  Form1.Image1.Picture.Bitmap.Width := Width;
  Form1.Image1.Picture.Bitmap.Height := Height;
  if FrameDrawn = TRUE then
  begin
   Form1.Image1.Canvas.Brush.Color := clBlack;
   Form1.Image1.Canvas.FillRect(Rect(0, 0, Width, Height));
  end;
  Form1.Image1.Canvas.Pen.Color := clGreen;
  LineThick := 1;
  for i := 1 to (FFT_Size div 2) - 1 do
  begin
    if Form1.CheckBoxFreqAspect.Checked then
    begin
      x := Round((Log10(i+1) / Log10(FFT_Size)) * Width);
      LineThick := x DIV 2;
      if i>1 then
       LineThick := (x - (Round((Log10(i-1 + 1) / Log10(FFT_Size)) * Width))) DIV 2;
    end
    else
      x := Round(i * Width / (FFT_Size div 2));
    Amp := FFTData;
      if Form1.CheckBoxAmpAspect.Checked then
      begin
        Amp := Amp * Sqrt(i);
        Amp := Log10(Amp + 1);
      end;

    y := Round(Height * (1 - Amp * 10));
    if y < 0 then y := 0;
    if y > Height then y := Height;

    Form1.Image1.Canvas.Pen.Width := LineThick;
    Form1.Image1.Canvas.MoveTo(x, Height);
    Form1.Image1.Canvas.LineTo(x, y);
   end;
  FrameDrawn := TRUE;
end;

procedure TForm1.ON_OFFClick(Sender: TObject);
begin
 if Started = FALSE then
 begin
    Started := TRUE;
    InitializeBass;
    Form1.ON_OFF.Glyph.LoadFromFile('Started.BMP');
 end
 else
 begin
  BASS_RecordFree;
  BASS_Free;
  Started := FALSE;
  Form1.ON_OFF.Glyph.LoadFromFile('Stopped.BMP');
  Form1.Image1.Canvas.Brush.Color := clBlack;
  Form1.Image1.Picture.Bitmap.Canvas.FillRect(Rect(0, 0, Form1.Image1.Width, Form1.Image1.Height));
 end;
end;

procedure TForm1.Slider_SpeedChange(Sender: TObject);
begin
  Timer1.Interval := 100-Form1.Slider_Speed.Position+10;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  BASS_RecordFree;
  BASS_Free;
  Started := FALSE;
  FrameDrawn := FALSE;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Started := FALSE;
  FrameDrawn := FALSE;
  Form1.ON_OFF.Glyph.LoadFromFile('Stopped.BMP');
  Form1.Image1.Canvas.Brush.Color := clBlack;
  Form1.Image1.Picture.Bitmap.Canvas.FillRect(Rect(0, 0, Form1.Image1.Width, Form1.Image1.Height));
  Timer1.Interval := 100-Form1.Slider_Speed.Position+10;
  Timer1.Enabled := True;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
 if Started = TRUE then
 begin
  if BASS_ChannelGetData(Stream, @FFTData, BASS_DATA_FFT4096) = -1 then
    raise Exception.Create('Error capturing FFT data: ' + IntToStr(BASS_ErrorGetCode));
  DrawSpectrum;
 end;
end;

end.
« Last Edit: 2 Jun '24 - 04:47 by silentrider »