I am working on an App for the iPad, and I want to analyze the Audio from a Video that I am playing. This is all going well, using the MTAudioProcessingTap. Currently I have some test code to test/measure the volume of the left and right channel. This is all going pretty well:
void process(MTAudioProcessingTapRef tap, CMItemCount numberFrames,
MTAudioProcessingTapFlags flags, AudioBufferList *bufferListInOut,
CMItemCount *numberFramesOut, MTAudioProcessingTapFlags *flagsOut)
{
OSStatus err = MTAudioProcessingTapGetSourceAudio(tap, numberFrames, bufferListInOut,
flagsOut, NULL, numberFramesOut);
if (err)
NSLog(@"Error from GetSourceAudio: %ld", err);
float leftVolume, rightVolume;
for (CMItemCount i = 0; i < bufferListInOut->mNumberBuffers; i++)
{
AudioBuffer *pBuffer = &bufferListInOut->mBuffers[i];
int cSamples = numberFrames * pBuffer->mNumberChannels;
float *pData = (float *)pBuffer->mData;
float rms = 0.0f;
for (int j = 0; j < cSamples; j++)
{
rms += pData[j] * pData[j];
}
if (cSamples > 0)
{
rms = sqrtf(rms / cSamples);
}
if (0 == i)
{
leftVolume = rms;
}
if (1 == i || (0 == i && 1 == bufferListInOut->mNumberBuffers))
{
rightVolume = rms;
}
}
NSLog(@"Left / Right Volume: %f / %f", leftVolume, rightVolume);
}
But for the purpose of this App, I want it to just measure the RMS ("intensity") of the range of 0-80Hz (as example). Therefore, I need a Low-Pass filter.
I have been Googling for a long time now, but my problem is that I can't find any clear post, tutorial or solution that is obvious. Almost every problem that sounds somewhat like mine has a random piece of code underneath it with crappy or lack of comments so I can't figure out what all the magic numbers are doing there, and what they mean..
Could someone push me in the right direction here? Note that in my case I do want to understand the code and not just run off with a working sample.
Thanks
If you could just run off with a working example, you would be lucky. :-)
Signal processing is a complicated and deep area. It is complicated when you do it theoretically, and it is also complicated when you want to do it practically.
You want to have a low pass filter. There are many options with different advantages and disadvantages.
The most fundamental concepts you need to deal with when you want to understand what is going on:
Frequency domain and time domain: Frequency domain is when you talk about frequency intervals like 0..80Hz for example. Time domain is normal time, or for example the individual sample values you have in your sampling buffer. The above code calculates the RMS in the time domain.
Fundamental rule: Frequency domain and time domain are completely equivalent.
You can do many operations in either domain with the same result. You can always switch between frequency domain and time domain. Since some operations are trivial in a certain domain, it is often useful to first switch to the desirable domain, do the trivial operation, and then switch back to the original domain (if necessary).
Switching between frequency and time domain is done using a FT (Fourier Transformation). Programmatically one often uses the special case of buffers which contain a power of two number of samples and the FFT algorithm (Fast Fourier Transformation).
The other interesting property is the convolution theorem: The FT translates between multiplication of functions in one domain and convolution of functions on the other domain.
Now what has this all to do with your low pass filter?
Your suggested low pass filter 0-80Hz, is a rectangular function in the frequency domain. You want to multiply that to your input in the frequency domain. This means letting all the frequency parts below 80Hz through and setting all others to zero.
Now you could do all that in the frequency domain, that would be easy, but for efficiency reasons you want to do that in the time domain to avoid the back and forth FFT. (In your case you just want to have the energy which you can also calculate in the frequency domain in the same way as you do it now (sum of squares).)
To do your low pass filter in the time domain, instead of FT-multiply-FT, you can also convolute with FT(rectangular function). The FT(rectangular function) is the ideal low pass filter: The sinc() function.
sinc(x) := sin(pi*x) / pi*x
This sinc(x) is the impulse response of your rectangular function. This concrete impulse response is infinite, which is impractical. This would mean you would need to calculate the convolution of your input with an infinite number of values.
What you want is a filter with a finite impulse response: FIR. This will cause errors to your filter, concretely that you will not see all frequencies < 80Hz with the same weight and that you will also see some frequencies over 80Hz in your energy.
This compromise is unavoidable.
BTW: When you use the FFT approach, where you can apply your perfect rectangular function without any error, you will also suffer from this error indirectly when windowing your input signal before doing the FFT. (Windowing means cutting out pieces (windows) of your input to do the FFT on.) This will have the same negative effects to your output and requires the same compromises as your filtering function and result.
You probably want a FIR filter of some kind as a low pass filter. And the strange numbers you see in the code from others will most likely be the coefficients of such an FIR filter.
The problem is that there is no 'optimal' compromise, because the compromise depends very much on how you define 'error' in the filter. Some people must avoid by any means frequency parts over 82 Hz (in your example) and so they need a very steep filter edge. This usually causes big artifacts near the 80Hz boundary, which one needs to accept then. Other people are fine with some energy coming from frequencies up to say 120Hz and stay below 10% above 120Hz, to reduce the artifacts near the 80Hz boundary (a softer low pass filter).
The whole topic is very well covered here: https://ccrma.stanford.edu/~jos/sasp/FIR_Digital_Filter_Design.html
Or if you want to start at the very beginning: https://ccrma.stanford.edu/~jos/sasp/sasp.html
Also take a look at the Wikipedia pages of FIR filter and sinc.
The above is not sufficient to design and implement your own filter, I admit. But it should give you enough background and pointers to get started.
And don't get put off by the sometimes strange mathematics.
Idea: One way you can visualize how well your filter works is to do a FFT after you applied your filter and look at the spectrum. It would be very hard to tell whether the filter works OK by looking at the RMS value only. Your iPad has more than enough processing power to do this.
(I just saw that there is also http://dsp.stackexchange.com for signal processing.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With