### Author Topic: DetectPeakFrequency  (Read 872 times)

#### Ed1966

• Posts: 248
##### DetectPeakFrequency
« on: 8 Feb '23 - 10:34 »
Hi

Is there a DetectPeakFrequency for Delphi using Windows?
Or how to do that myself.

Regards,
Eduard.

#### Ian @ un4seen

• Posts: 26035
##### Re: DetectPeakFrequency
« Reply #1 on: 8 Feb '23 - 15:29 »
You can get it from FFT data, ie. check which FFT bin has the highest value. I'm not a Delphi user myself, but it'll be basically the same in any language, and here's how it could look in C/C++:

Code: [Select]
`float fft[2048];BASS_ChannelGetData(handle, fft, BASS_DATA_FFT4096); // get FFT data (4096 sample)int peakbin = 0;float peakval = 0;for (int a = 1; a < 2048; a++) { if (peakval < fft[a]) { // found a new peak peakval = fft[a]; peakbin = a; }}float rate;BASS_ChannelGetAttribute(handle, BASS_ATTRIB_FREQ, &rate); // get sample ratefloat peakfreq = peakbin * rate / 4096; // get frequency of peak bin`

#### Ed1966

• Posts: 248
##### Re: DetectPeakFrequency
« Reply #2 on: 9 Feb '23 - 08:16 »
Thank you. I still see some wrong frequencies so I'm going to check that out first. Do not know why. I'll come back to this next week.

#### Ed1966

• Posts: 248
##### Re: DetectPeakFrequency
« Reply #3 on: 9 Feb '23 - 12:03 »
I produce something like this.

Code: [Select]
`var  MasterFreq0,  PeakFreq0: FLOAT;function GetRealBitrateFrom(FullArtist: string): Cardinal;var  PeakBin: Integer;  Rate: FLOAT;  PeakVal: FLOAT;  FFTData0: array [0..2048] of Single;var  AStream: HSTREAM;  A0: Integer;begin  AStream := BASS_StreamCreateFile(False, PChar(FullArtist), 0, 0, BASS_SAMPLE_FLOAT or BASS_STREAM_PRESCAN or BASS_STREAM_DECODE or BASS_UNICODE);  BASS_ChannelGetAttribute(AStream, BASS_ATTRIB_FREQ, Rate); // get sample rate  if (BASS_ErrorGetCode > 0) then    Exit;  PeakBin := 0;  PeakVal := 0.0;  BASS_ChannelGetData(AStream, @FFTData0, BASS_DATA_FFT4096); // get FFT data (4096 sample)  for A0 := 2 to 2047 do  begin    if (Peakval < FFTData0[A0]) then // found a new peak    begin      PeakVal := FFTData0[A0];      PeakBin := A0;    end;  end;  PeakFreq0 := PeakBin * (Rate / 4096); // get frequency of peak bin  BASS_StreamFree(AStream);end;function GetRealBitrateFull(FullArtist: string): Cardinal;var  PeakBin: Integer;  Rate: FLOAT;  PeakVal: FLOAT;  FFTData0: array [0..2048] of Single;var  AStream: HSTREAM;  A0: Integer;begin  AStream := BASS_StreamCreateFile(False, PChar(FullArtist), 0, 0, BASS_SAMPLE_FLOAT or BASS_STREAM_PRESCAN or BASS_STREAM_DECODE or BASS_UNICODE);  BASS_ChannelGetAttribute(AStream, BASS_ATTRIB_FREQ, Rate); // get sample rate  if (BASS_ErrorGetCode > 0) then    Exit;  MasterFreq0 := 0;  while BASS_ChannelIsActive(AStream) <> 0 do  begin    PeakBin := 0;    PeakVal := 0.0;    BASS_ChannelGetData(AStream, @FFTData0, BASS_DATA_FFT4096); // get FFT data (4096 sample)    for A0 := 2 to 2047 do    begin      if (Peakval < FFTData0[A0]) then // found a new peak      begin        PeakVal := FFTData0[A0];        PeakBin := A0;      end;    end;    PeakFreq0 := PeakBin * (Rate / 4096); // get frequency of peak bin    if (MasterFreq0 < PeakFreq0) then      MasterFreq0 := PeakFreq0;  end;  BASS_StreamFree(AStream);end;`
Ignore title proc 'GetRealBitrateFull' because that what i want to reaches. Not important for now.
The first is not always correct and de second is better. But there is a max. High frequency situation wilt most mp3 songs.
A0 start with 2 is better dan start whit 0 or 1.

Thank you.

#### Ian @ un4seen

• Posts: 26035
##### Re: DetectPeakFrequency
« Reply #4 on: 9 Feb '23 - 17:52 »
I forgot to include the BASS_DATA_FFT_REMOVEDC flag in the BASS_ChannelGetData call. Adding that will make the FFT bin 1 value more useful.

If you want to get the peak frequency over the entire file then you should sum the FFT data from all of the BASS_ChannelGetData calls, and then check the summed values at the end. Something like this:

Code: [Select]
`float fft[2048] = {0};while (true) { float temp[2048]; if (BASS_ChannelGetData(handle, temp, BASS_DATA_FFT4096 | BASS_DATA_FFT_REMOVEDC) == -1) // get FFT data (4096 sample) break; for (int a = 1; a < 2048; a++) fft[a] += temp[a]; // add to the previous data}// check fft array here like before...`
Btw, you don't need the BASS_STREAM_PRESCAN flag for this (because you're not getting the length or seeking), so you can remove that from the BASS_StreamCreateFile calls to save a bit of time.

#### Ed1966

• Posts: 248
##### Re: DetectPeakFrequency
« Reply #5 on: 10 Feb '23 - 11:27 »
I think it's going well.
Maybe you have an idea to ignore anything lower than -80dB?
So only the peak between 0 .. -60db as an example.

#### Ian @ un4seen

• Posts: 26035
##### Re: DetectPeakFrequency
« Reply #6 on: 10 Feb '23 - 14:35 »
You can calculate a threshold value like this:

Code: [Select]
`float threshold = pow(10, db / 20.0);`
And then use it here:

Code: [Select]
` for (int a = 1; a < 2048; a++) if (temp[a] >= threshold) fft[a] += temp[a]; // add to the previous data`

#### Ed1966

• Posts: 248
##### Re: DetectPeakFrequency
« Reply #7 on: 10 Feb '23 - 16:21 »
Thank you

#### Ed1966

• Posts: 248
##### Re: DetectPeakFrequency
« Reply #8 on: 20 Feb '23 - 09:57 »
One question?
How many milliseconds is one call to BASS_ChannelGetData?

Code: [Select]
`BASS_ChannelGetData(AStream, @FFTData0, BASS_DATA_FFT4096 or BASS_DATA_FFT_REMOVEDC); // get FFT data (4096 sample)`
10ms or 20ms ? OR?