EZAudio: How do you separate the buffersize from the FFT window size(desire higher frequency bin resolution).

StackOverflow https://stackoverflow.com/questions/23506325

  •  16-07-2023
  •  | 
  •  

Question

https://github.com/syedhali/EZAudio

I've been having success using this audio library, but now I'd like to increase the resolution of the microphone data that's read in, so that the FFT resolution, or frequency bin size goes down to 10Hz. To do that, I need a buffersize of 8820 instead of 512. Is the buffersize of the microphone and FFT windowing size separable? I can't see a way to separate it.

How do I set up the audio stream description, so that it can calculate the FFT with a larger window?

Any help would be much appreciated.

Was it helpful?

Solution 3

Thanks Jaket for suggestion. Buffer is the way to go and here is my working implementation of that same function now with adjustable FFT window:

    -(void)microphone:(EZMicrophone *)microphone
            hasAudioReceived:(float **)buffer
            withBufferSize:(UInt32)bufferSize
            withNumberOfChannels:(UInt32)numberOfChannels {

dispatch_async(dispatch_get_main_queue(),^{

    [self.audioPlot updateBuffer:buffer[0] withBufferSize:bufferSize];

    // Decibel Calculation.
    float one       = 1.0;
    float meanVal   = 0.0;
    float tiny      = 0.1;
    vDSP_vsq(buffer[0], 1, buffer[0], 1, bufferSize);
    vDSP_meanv(buffer[0], 1, &meanVal, bufferSize);
    vDSP_vdbcon(&meanVal, 1, &one, &meanVal, 1, 1, 0);
    // Exponential moving average to dB level to only get continous sounds.
    float currentdb = 1.0 - (fabs(meanVal)/100);
    if (lastdbValue == INFINITY || lastdbValue == -INFINITY || isnan(lastdbValue)) {
        lastdbValue = 0.0;
    }
    dbValue =   ((1.0 - tiny)*lastdbValue) + tiny*currentdb;
    lastdbValue = dbValue;
    // NSLog(@"dbval:  %f",dbValue);
    //
    // Setup the FFT if it's not already setup
    int samplestoCopy = fmin(bufferSize, FFTLEN - _fftBufIndex);
    for ( size_t i = 0; i < samplestoCopy; i++ ) {
        _fftBuf[_fftBufIndex+i] = buffer[0][i];
    }
    _fftBufIndex        += samplestoCopy;
   _samplesRemaining    -= samplestoCopy;
    if (_fftBufIndex == FFTLEN) {
        if( !_isFFTSetup ){
            [self createFFTWithBufferSize:FFTLEN withAudioData:_fftBuf];
            _isFFTSetup = YES;
        }
        [self updateFFTWithBufferSize:FFTLEN withAudioData:_fftBuf];
        _fftBufIndex        = 0;
        _samplesRemaining   = FFTLEN;
    } 
});  

}

OTHER TIPS

The FFT size and the audio buffer size should be completely independent. You can just save multiple audio input buffers (perhaps in a circular FIFO or queue), without processing them until you have enough samples for your desired length FFT.

Saving audio buffers this way also allows you to FFT overlapped frames for more time resolution.

Having browsed the source of the linked project, it appears that the audio callback passes a buffer size that is the preferred buffer size of the microphone device. I would recommend you buffer up the desired number of samples before calling the FFT. The following code is modified from FFTViewController.m in EZAudioFFTExample:

#pragma mark - EZMicrophoneDelegate
-(void)    microphone:(EZMicrophone *)microphone
     hasAudioReceived:(float **)buffer
       withBufferSize:(UInt32)bufferSize
 withNumberOfChannels:(UInt32)numberOfChannels {
  dispatch_async(dispatch_get_main_queue(), ^{

    // Update time domain plot
    [self.audioPlotTime updateBuffer:buffer[0]
                      withBufferSize:bufferSize];

    // Setup the FFT if it's not already setup
    if( !_isFFTSetup ){
      [self createFFTWithBufferSize:bufferSize withAudioData:buffer[0]];
      _isFFTSetup = YES;
    }

    int samplesRemaining = bufferSize;
    while (samplesRemaining > 0)
    {
        int samplestoCopy = max(bufferSize, FFTLEN - _fftBufIndex);
        memcpy(_fftBuf, buffer[0], samplesToCopy*sizeof(float));       
        _fftBufIndex += samplesToCopy;
        samplesRemaining -= samplesToCopy;

        if (_fftBufIndex == FFTLEN)
        {
            _fftBufIndex = 0;
            [self updateFFTWithBufferSize:FFTLEN withAudioData:_fftBuf];
        }
    }
  });
 }

In the modified program, FFTLEN a value that you defined, _fftBuf is an array of floats that you allocate and it needs to hold FFTLEN elements, and _fftBufIndex is an integer to track the write position into the array.

On a separate note, I would recommend you make a copy of the buffer parameter before calling the async delegate. The reason I say this is because looking at the source for EZMicrophone it looks like it's recycling the buffer so you'll have a race condition.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top