Alan_s Alan_s - 1 year ago 59
iOS Question

How can I get my iPhone to listen for sound frequencies above a certain threshold?

I'm interested in getting my iOS app to turn on the microphone and only listen for frequencies above 17000 hz. If it hears something in that range, I'd like the app to call a method.

I was able to find a repository that detects frequency:

And here is a post breaking down FFT:

Get Hz frequency from audio stream on iPhone

Using these examples, I've been able to get the phone to react to the strongest frequency it hears, but I'm more interested in just reacting to the above 17000 hz frequencies.

Answer Source

The fact that I wrote that code helps me answering this question but the answer probably only applies to this code.

You can easily limit the frequencies you listen to just by trimming that output array to a piece that contains only the range you need.

In details: To be simple - array[0..255] contains your audio in frequency domain. For example you sample rate was 44100 when you did FFT. Then maximum frequency you can encode is 22050. (Nyquist theorem).

That is array[0] contains value for 22050/256=86.13 Hz. Array[1] contains value for 86.13*2 = 172.26 Hz, array[2] contains value for 86.13*3 = 258.39 Hz. And so on. Your full range is distributed across those 256 values. (and yes, precision suffers)

So if you only need to listen to some range, let's say above 17000Hz, you just take a piece of that array and ignore the rest. In this case you take 17000/86.13=197 to 255 subarray and you have it. Only 17000-22050 range.

In my repo you modify strongestFrequencyHZ function like that:

static Float32 strongestFrequencyHZ(Float32 *buffer, FFTHelperRef *fftHelper, UInt32 frameSize, Float32 *freqValue) {
Float32 *fftData = computeFFT(fftHelper, buffer, frameSize);
fftData[0] = 0.0;
unsigned long length = frameSize/2.0;
Float32 max = 0;
unsigned long maxIndex = 0;

Float32 freqLimit = 17000; //HZ

Float32 freqsPerIndex = NyquistMaxFreq/length;
unsigned long lowestLimitIndex = (unsigned long) freqLimit/freqsPerIndex;

unsigned long newLen = length-lowestLimitIndex;
Float32 *newData = fftData+lowestLimitIndex; //address arithmetic
max = vectorMaxValueACC32_index(newData, newLen, 1, &maxIndex);
if (freqValue!=NULL) { *freqValue = max; }
Float32 HZ = frequencyHerzValue(lowestLimitIndex+maxIndex, length, NyquistMaxFreq);
return HZ;


I did some address arithmetic in there so it looks kind of complicated. You can just take that fftData array and do the regular stuff.

Other things to keep in mind:

Finding strongest freq. is easy. You just find maximum in that array. That's it. But in you case you need to monitor the range and find when it went from regular weak noise to some strong signal. In other words when stuff peaks, and this is not so trivial, but possible. You can probably just set some limit above which the signal becomes detected, although this not the best option.

I would rather be optimistic about this cause in real life you can't see much noise at 18000Hz around you. The only thing a can remember of are some old TVs that produce that high pitched sound when they're on.