the frequency spectrum of an audio

edited August 2013

Hum,I want to do an audio spectrum, I look for some information, it should be using FFT to get the job done. But I do not know hwo to use TAAE complete it.imagehttp://a4.mzstatic.com/us/r1000/117/Purple/v4/f5/11/8d/f5118df2-75ec-1839-3ef8-931e13177e15/mzl.tjcpmauv.320x480-75.jpg

Comments

  • There are some performant FFT functions in the accelerate framework. You should be able to google for some sample code.

    One way to use TAAE would be to set up an AEAudioReceiver that processes the audio signal via FFT

  • edited November 2015

    Here's some (admittedly pretty basic) code I have lying around that'll pick the dominant frequency from audio samples - it also automatically buffers audio so it can perform the analysis in the appropriate-sized blocks. Note that this is just a chunk of code, you'll need to figure out what to do with it (but that's half the fun!):

    /*
     * Based on code from the audioGraph project
     * https://github.com/tkzic/audiograph
     */
    
    #import 
    #import "TPCircularBuffer.h"
    
    static const int kProcessingBlockSize = 1024;
    static const int kMagnitudeThreshold = 50;
    static const int kFrequencyCutoff = 8000;
    
    @interface MyClassName () {
        TPCircularBuffer _buffer;
        int _log2n;
        int _n;
        int _nOver2;
        COMPLEX_SPLIT _complexBuffer;
        FFTSetup _fftSetup;
    }
    @end
    
    @implementation MyClassName
    
    - (id)init {
       if ( !(self = [super init]) ) return nil;
        TPCircularBufferInit(&_buffer, (kProcessingBlockSize + 1024 /* A little extra to store overflow */) * sizeof(float));
        _log2n = log2f(kProcessingBlockSize);
        _n = 1 << _log2n;
        assert(_n == kProcessingBlockSize);
        _nOver2 = kProcessingBlockSize/2;
        _complexBuffer.realp = (float *)malloc(_nOver2 * sizeof(float));
        _complexBuffer.imagp = (float *)malloc(_nOver2 * sizeof(float));
        _fftSetup = vDSP_create_fftsetup(_log2n, FFT_RADIX2);
       return self;
    }
    
    static float findDominantFrequency(MyClassName *THIS, float *samples, UInt32 length, float *outMagnitude) {
        int32_t availableBytes;
        TPCircularBufferTail(&THIS->_buffer, &availableBytes);
        
        UInt32 availableFrames = availableBytes/sizeof(float);
        if ( availableFrames+length > kProcessingBlockSize ) {
            // Throw away the excess frames
            int32_t discardBytes = MIN(availableBytes, (availableFrames+length-kProcessingBlockSize) * sizeof(float));
            TPCircularBufferConsumeNoBarrier(&THIS->_buffer, discardBytes);
        }
        
        // Add in the new frames
        TPCircularBufferProduceBytes(&THIS->_buffer, samples, length * sizeof(float));
        
        float *buffer = (float*)TPCircularBufferTail(&THIS->_buffer, &availableBytes);
        availableFrames = availableBytes / sizeof(float);
        
        // Wait till we have kProcessingBlockSize frames available to process
        if ( availableFrames < kProcessingBlockSize ) return THIS->_frequency;
        
        // Put our real signal into a split complex buffer, as required by the FFT
        vDSP_ctoz((COMPLEX*)buffer, 2, &THIS->_complexBuffer, 1, THIS->_nOver2);
        
        // Carry out a forward FFT
        vDSP_fft_zrip(THIS->_fftSetup, &THIS->_complexBuffer, 1, THIS->_log2n, FFT_FORWARD);
        
        // Determine the dominant frequency by taking the magnitude squared and
        // saving the bin which it resides in.
        float dominantFrequencyIntensity = 0;
        int bin = -1;
        int cutoff = kFrequencyCutoff / (44100.0/kProcessingBlockSize);
        for (int i=0; i_n && i < cutoff; i++) {
            float frequencyIntensity = magnitudeSquared(THIS->_complexBuffer.realp[i], THIS->_complexBuffer.imagp[i]);
            if (frequencyIntensity > dominantFrequencyIntensity) {
                dominantFrequencyIntensity = frequencyIntensity;
                bin = i+1;
            }
        }
        
        TPCircularBufferConsumeNoBarrier(&THIS->_buffer, kProcessingBlockSize * sizeof(float));
    
        float frequency = bin * (44100.0/kProcessingBlockSize);
        
        *outMagnitude = dominantFrequencyIntensity;
        
        return frequency;
    }
    
    static float magnitudeSquared(float x, float y) {
        return ((x*x) + (y*y));
    }
    
    @end
    
  • Hey Matt,

    Thanks a lot for the sample code, it's really helping me get a grasp on these functions, but I was wondering if you could explain why bin = (i+1)/2 instead of i+1?

    Thanks,

    Josh

  • edited October 2013

    Um...Mostly because I'm an idiot, @joshglick =) I think I put that in while I was messing around and forgot to take it out. It means it was returning an octave lower than the actual frequency.

    I've patched it up - and damn, that's a great catch!

    I should also add that the above is an imprecise implementation, as the frequencies returned are truncated to just 1024 separate values. There's an approach you can then take to get the actual exact frequency by looking at the phase values, but I've not done that above.

  • Thanks for sharing this code. It's really helping me get a grasp on how to work with audio buffers.

    The documentation for vDSP_ctoz says it takes complex interleaved data as the input. Is that the typical format of the incoming AudioBuffer in a playback callback? There's an audio format flag called kAudioFormatFlagIsNonInterleaved, but I think that's referring to whether the stereo channels are interleaved.

    Also, how do you determine what strides to use for the two strides in vDSP_ctoz and the one in vDSP_fft_zrip?

  • Nevermind, I think I got it. vDSP_ctoz takes an input stride of two because it's treating linear real data as interleaved complex numbers, so it's taking all the odds and putting them into the imaginary component of the output.

  • I'm working on a spectrum analyzer and spectrum analyzer view if anybody is interested? Might be helpful.

  • I would definitely be interested in that, @RepublicOfApps would be a very handy addition!

  • @macguy - I'll let you know when it's available. I'll likely release it as a Cocoapod that depends on AmazingAudioEngine (which also comes as a cocoapod).

  • I'm about to dive into trying to build a pitch detector using FFT and phase detection Michael mentioned (roughly using this recently published blog post as my guide.) Has anyone's had success since this thread went dormant?

  • I think there's a mistake in setting discardBytes:

    int32_t discardBytes = MIN(availableFrames, availableBytes+length - kProcessingBlockSize) * sizeof(float);
    

    (Should availableBytes be availableFrames)

    int32_t discardBytes = MIN(availableFrames, availableFrames+length - kProcessingBlockSize) * sizeof(float);
    
  • Right you are, although I think it's meant to be:

            int32_t discardBytes = MIN(availableBytes, (availableFrames+length-kProcessingBlockSize) * sizeof(float));
    
Sign In or Register to comment.