syedhali / EZAudio

An iOS and macOS audio visualization framework built upon Core Audio useful for anyone doing real-time, low-latency audio processing and visualizations.
Other
4.94k stars 822 forks source link

Low pass filter / High pass filter #258

Open trkfabi opened 8 years ago

trkfabi commented 8 years ago

Hi. I know this has been asked & answered before (you said it's out of the scope of EZAudio what we do with the data) but I just wanted to ask where would I put the filter if I meant to do it?

I read about Apple's vDSP functions and a lot about filtering in stackoverflow but I don't get how/where to put the vDSP_hann_window and how to modify the data to apply simple filtering.

I believe it's in EZAudioFFT.m in function:

- (float *)computeFFTWithBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize
{
    if (buffer == NULL)
    {
        return NULL;
    }

    //
    // Calculate real + imaginary components and normalize
    //
    vDSP_Length log2n = log2f(bufferSize);
    long nOver2 = bufferSize / 2;
    float mFFTNormFactor = 10.0 / (2 * bufferSize);
    vDSP_ctoz((COMPLEX*)buffer, 2, &(self.info->complexA), 1, nOver2);
    vDSP_fft_zrip(self.info->fftSetup, &(self.info->complexA), 1, log2n, FFT_FORWARD);
    vDSP_vsmul(self.info->complexA.realp, 1, &mFFTNormFactor, self.info->complexA.realp, 1, nOver2);
    vDSP_vsmul(self.info->complexA.imagp, 1, &mFFTNormFactor, self.info->complexA.imagp, 1, nOver2);
    vDSP_zvmags(&(self.info->complexA), 1, self.info->outFFTData, 1, nOver2);
    vDSP_fft_zrip(self.info->fftSetup, &(self.info->complexA), 1, log2n, FFT_INVERSE);
    vDSP_ztoc(&(self.info->complexA), 1, (COMPLEX *) self.info->inversedFFTData , 2, nOver2);
    self.info->outFFTDataLength = nOver2;

    //
    // Calculate max freq
    //
    if (self.sampleRate > 0.0f)
    {
        vDSP_maxvi(self.info->outFFTData, 1, &self.info->maxFrequencyMangitude, &self.info->maxFrequencyIndex, nOver2);
        self.info->maxFrequency = [self frequencyAtIndex:self.info->maxFrequencyIndex];
    }

    //
    // Notify delegate
    //
    if ([self.delegate respondsToSelector:@selector(fft:updatedWithFFTData:bufferSize:)])
    {
        [self.delegate fft:self
        updatedWithFFTData:self.info->outFFTData
                bufferSize:nOver2];
    }

    //
    // Return the FFT
    //
    return self.info->outFFTData;
}

I just don't understand what all those functions are calculating. Another question is what is this factor and why do you calculate it like this?

float mFFTNormFactor = 10.0 / (2 * bufferSize);

Thank you so much.

trkfabi commented 8 years ago

I'm using NVDSP library to perform filtering, but I'm not sure if I'm doing this right. Any comment is appreciated. This is EZAudioFFT.m. I have included

#import "NVDSP.h"
#import "NVLowpassFilter.h"
- (float *)computeFFTWithBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize
{
    if (buffer == NULL)
    {
        return NULL;
    }

    //NVLowpassFilter *LPF = [[NVLowpassFilter alloc] initWithSamplingRate:self.sampleRate];
    //LPF.cornerFrequency = 400.0f;
    //LPF.Q = 0.8f;
    [self.LPF filterData:buffer numFrames:bufferSize numChannels:1];

    //
    // Calculate real + imaginary components and normalize
    //
    vDSP_Length log2n = log2f(bufferSize);
    long nOver2 = bufferSize / 2;
    float mFFTNormFactor = 10.0 / (2 * bufferSize);

    // fabi
    //float *window = (float *)malloc(sizeof(float) * nOver2);
    //vDSP_hann_window(window, nOver2, 0); // Window the samples
    /////////

    vDSP_ctoz((COMPLEX*)buffer, 2, &(self.info->complexA), 1, nOver2);
    vDSP_fft_zrip(self.info->fftSetup, &(self.info->complexA), 1, log2n, FFT_FORWARD);

    vDSP_vsmul(self.info->complexA.realp, 1, &mFFTNormFactor, self.info->complexA.realp, 1, nOver2);
    vDSP_vsmul(self.info->complexA.imagp, 1, &mFFTNormFactor, self.info->complexA.imagp, 1, nOver2);

    // fabi
    //vDSP_vmul(self.info->complexA.realp, 1, window, 1, self.info->complexA.realp, 1, nOver2);
    //vDSP_vmul(self.info->complexA.imagp, 1, window, 1, self.info->complexA.imagp, 1, nOver2);
    /////////

    vDSP_zvmags(&(self.info->complexA), 1, self.info->outFFTData, 1, nOver2);
    vDSP_fft_zrip(self.info->fftSetup, &(self.info->complexA), 1, log2n, FFT_INVERSE);
    vDSP_ztoc(&(self.info->complexA), 1, (COMPLEX *) self.info->inversedFFTData , 2, nOver2);
    self.info->outFFTDataLength = nOver2;

    //
    // Calculate max freq
    //
    if (self.sampleRate > 0.0f)
    {
        vDSP_maxvi(self.info->outFFTData, 1, &self.info->maxFrequencyMangitude, &self.info->maxFrequencyIndex, nOver2);
        self.info->maxFrequency = [self frequencyAtIndex:self.info->maxFrequencyIndex];
    }

    //
    // Notify delegate
    //
    if ([self.delegate respondsToSelector:@selector(fft:updatedWithFFTData:bufferSize:)])
    {
        [self.delegate fft:self
        updatedWithFFTData:self.info->outFFTData
                bufferSize:nOver2];
    }

    //
    // Return the FFT
    //
    return self.info->outFFTData;
}

I read that filtering should be done in time domain before doing the fft, so I think this is the right place

steve21124 commented 8 years ago

@ trkfabi do you figure it out?