alnitak / flutter_recorder

A low-level audio recorder plugin which uses miniaudio as backend and supporting all the platforms. It can detect silence and save to WAV audio file. Audio wave and FFT data can be get in real-time as for the volume level.
Apache License 2.0
20 stars 2 forks source link

fix: android device frequance calculate issue #10

Open murtazayucal opened 2 days ago

murtazayucal commented 2 days ago

Hello, I encountered a problem while using this library. I am trying to calculate the frequency using the getFft() method. When running in the emulator, human voice frequencies are detected correctly, but when I install it on my Android device, the received human voice frequency is detected more than it should be.

My init :

    _recorder.init(
      format: PCMFormat.f32le,
      sampleRate: 16000,
      channels: RecorderChannels.mono,
    );
    _recorder.start();
    _recorder.startStreamingData();

My frequency calculation method:

  Future<double> _calculateFrequency() async {
    Float32List fftData = await Future.microtask(() => _recorder.getFft());

    int maxIndex = 0;
    double maxValue = -double.infinity;

    for (int i = 0; i < fftData.length; i++) {
      if (fftData[i] > maxValue) {
        maxValue = fftData[i];
        maxIndex = i;
      }
    }

    const double sampleRate = 22050.0;
    const int fftSize = 256;
    return (maxIndex * sampleRate) / fftSize;
  }

Emulator Results : Frekans: 86.13 Hz, Desibel: -30.02 dB Frekans: 86.13 Hz, Desibel: -30.73 dB Frekans: 86.13 Hz, Desibel: -32.09 dB Frekans: 86.13 Hz, Desibel: -33.56 dB

Android Device Results: Frekans: 3562.50 Hz, Desibel: -28.54 dB Frekans: 2125.00 Hz, Desibel: -28.05 dB Frekans: 2125.00 Hz, Desibel: -27.52 dB Frekans: 2125.00 Hz, Desibel: -26.94 dB Frekans: 4250.00 Hz, Desibel: -26.47 dB Frekans: 4250.00 Hz, Desibel: -26.97 dB Frekans: 2125.00 Hz, Desibel: -26.24 dB

Where am I making a mistake?

alnitak commented 2 days ago

Let me start by saying that I am not an audio engineer.

One thing I noticed is that you initialize the recorder with a sample rate of 16000, but then in _calculateFrequency you use 22050. Another thing I can suggest is to not use startStreamingData if you don't need it. The getFft you use in the method doesn't need streaming of audio data. getFft requests the FFT data of the current audio chunk and it is not necessarily the one after the last call.

That said, if I understand the _calculateFrequency function correctly, you are taking the maximum value of the FFT data and then returning its frequency. But what if a hammer fell on the floor at that moment? You would have a different peak at a different frequency than a voice frequency.

I did some research and found that the human voice has frequencies between 300 and 3000 Hz. So in a 256 FFT array, those values are in between 5-48 indexes when using a samplerate of 16000: 300Hz index = (300 256) / samplerate 3000Hz index = (3000 256) / samplerate

So I would also suggest you to check only those range in the for loop.

In conclusion, I don't know why you get so different Hz values from an emulator and a real device. On emulator, you are using the PC microphone which could have a way more different hardware specifications. So please, if you find some of my considerations useful and you get better results, let me know.