Reputation: 417
My main goal: find the frequency of the noises being pulled in through AVAudioRecorder. I have followed this:
http://www.ehow.com/how_12224909_detect-blow-mic-xcode.html
I have read up on many questions on SO asking how to detect frequency. The majority of those answers say, "Use FFT!" and then the question ask-ers say, "Oh, great!".
My question is, how do you get from here:
- (void)levelTimerCallback {
[recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
if (lowPassResults > sensitivitySlider.value) {
NSLog(@"Sound detected");
//What goes here so I can spit out a frequency?
}
}
Somehow magically use FFT... (I will use accelerate.h),
And wind up with "The frequency = 450.3"?
If somebody could show me the actual code that I would use to
That would be greatly appreciated.
Thanks in advance.
Upvotes: 4
Views: 1904
Reputation: 417
Well, it turns out that something CAN "go there". Instead of using Accelerate, I bought a book on Fourier Analysis on Amazon and used it to build my own FFT. Which spits out not a single frequency but the levels of each of many frequencies, which is basically what I wanted.
Here's my FFT-computing class:
class FFTComputer: NSObject {
class func integerBitReverse(_ input:Int,binaryDigits:Int) -> Int {
return integerForReversedBooleans(booleansForInt(input, binaryDigits: binaryDigits))
}
class func integerForReversedBooleans(_ booleans:[Bool]) -> Int {
var integer = 0
var digit = booleans.count - 1
while digit >= 0 {
if booleans[digit] == true {
integer += Int(pow(Double(2), Double(digit)))
}
digit -= 1
}
return integer
}
class func Pnumber(_ k:Int,placesToMove:Int, gamma:Int) -> Int {
var booleans = booleansForInt(k, binaryDigits: gamma)
for _ in 0 ..< placesToMove {
booleans.removeLast()
booleans.insert(false, at: 0)
}
return integerForReversedBooleans(booleans)
}
class func booleansForInt(_ input:Int,binaryDigits:Int) -> [Bool] {
var booleans = [Bool]()
var remainingInput = input
var digit = binaryDigits - 1
while digit >= 0 {
let potential = Int(pow(Double(2), Double(digit)))
if potential > remainingInput {
booleans.append(false)
} else {
booleans.append(true)
remainingInput -= potential
}
digit += -1
}
return booleans
}
class func fftOfTwoRealFunctions(_ realX1:[CGFloat], realX2:[CGFloat], gamma:Int) -> (([CGFloat],[CGFloat]),([CGFloat],[CGFloat])) {
let theFFT = fft(realX1, imaginaryXin: realX2, gamma: gamma)
var R = theFFT.0
var I = theFFT.1
let N = Int(pow(2.0, Double(gamma)))
var realOut1 = [CGFloat]()
var imagOut1 = [CGFloat]()
var realOut2 = [CGFloat]()
var imagOut2 = [CGFloat]()
for n in 0..<N {
var Rback:CGFloat
var Iback:CGFloat
if n == 0 {
Rback = R[0]
Iback = I[0]
} else {
Rback = R[N-n]
Iback = I[N-n]
}
realOut1.append(CGFloat(R[n]/2 + Rback/2))
realOut2.append(CGFloat(I[n]/2 + Iback/2))
imagOut1.append(CGFloat(I[n]/2 - Iback/2))
imagOut2.append(-CGFloat(R[n]/2 - Rback/2))
}
return ((realOut1,imagOut1),(realOut2,imagOut2))
}
class func fft(_ realXin:[CGFloat], imaginaryXin:[CGFloat], gamma:Int) -> ([CGFloat],[CGFloat]) {
var realX = realXin
var imaginaryX = imaginaryXin
let N = Int(pow(2.0, Double(gamma)))
var N2 = N/2
var NU1 = gamma - 1 // Always equals (gamma - l)
var realWP:Double = 1
var imaginaryWP:Double = 0
var redoPCounter = 0
func redoP(_ k:Int, places:Int) {
let P = Pnumber(k, placesToMove:places, gamma: gamma)
let inside = (-2*Double.pi*Double(P))/Double(N)
realWP = cos(inside)
imaginaryWP = sin(inside)
}
var l = 1
while l <= gamma {
var k = 0
var I = 1
while k < N - 1 {
if redoPCounter == N2 {
redoP(k,places: NU1)
redoPCounter = 0
}
redoPCounter += 1
// Swift.print(realX.count,imaginaryX.count,k+N2)
let realT1 = (realWP*Double(realX[k + N2]))-(imaginaryWP*Double(imaginaryX[k + N2]))
let imaginaryT1 = (realWP*Double(imaginaryX[k + N2]))+(imaginaryWP*Double(realX[k + N2]))
realX[k+N2] = realX[k] - CGFloat(realT1)
imaginaryX[k+N2] = imaginaryX[k] - CGFloat(imaginaryT1)
realX[k] = realX[k] + CGFloat(realT1)
imaginaryX[k] = imaginaryX[k] + CGFloat(imaginaryT1)
k += 1
if I == N2 {
k += N2
I = 1
} else {
I += 1
}
}
N2 = N2/2
NU1 = NU1 - 1
redoPCounter = 0
realWP = 1
imaginaryWP = 0
l += 1
}
for k in 0 ..< N - 1 {
let i = integerBitReverse(k, binaryDigits:gamma)
if i > k {
let placeholderReal = realX[k]
let placeholderImaginary = imaginaryX[k]
realX[k] = realX[i]
imaginaryX[k] = imaginaryX[i]
realX[i] = placeholderReal
imaginaryX[i] = placeholderImaginary
}
}
return (realX,imaginaryX)
}
class func magnitudeAndPhasePresentations(_ realX:[CGFloat], imaginaryX:[CGFloat]) -> ([CGFloat],[CGFloat]) {
var magnitudes = [CGFloat]()
var phases = [CGFloat]()
var lastMagnitude:CGFloat = 0
var lastPhase:CGFloat = 0
for n in 0 ..< realX.count {
let real = realX[n]
let imaginary = imaginaryX[n]
if real != 0 {
lastMagnitude = sqrt(pow(real, 2)+pow(imaginary, 2))
lastPhase = atan(imaginary/real)
}
magnitudes.append(lastMagnitude)
phases.append(lastPhase)
}
return (magnitudes,phases)
}
class func magnitudePresentation(_ realX:[CGFloat], imaginaryX:[CGFloat]) -> [CGFloat] {
var magnitudes = [CGFloat]()
var lastMagnitude:CGFloat = 0
for n in 0 ..< realX.count {
let real = realX[n]
let imaginary = imaginaryX[n]
if real != 0 {
lastMagnitude = sqrt(pow(real, 2)+pow(imaginary, 2))
}
magnitudes.append(lastMagnitude)
}
return magnitudes
}
}
And to get the audio, I used Novocaine: https://github.com/alexbw/novocaine
I would recommend reading a bit about the Fourier Transform, but it really doesn't have to be that difficult to plug data from Novocaine (the mic) into an FFTComputer and get back some frequencies.
(2 to the gamma is the count of realXin. I could have just computed gamma, so if you want to change that, go ahead. Just turn the Novocaine data into an array of CGFloats, put that in realXin, put an empty array of the same size in imagXin, and enter the right gamma. Then, maybe graph the output to see the frequencies.)
Upvotes: 1
Reputation: 70693
Nothing "goes there", as the AVRecorder API does not plug into the Accelerate framework. Instead, you have to use a completely different API, the Audio Queue or RemoteIO Audio Unit API, to capture audio input, a completely different code arrangement, such as waiting for callbacks to get your data, buffer size management to get data arrays of the appropriate size to feed an FFT, then know enough DSP to post-process the FFT results for the particular kind of frequency measure for which you are looking.
Upvotes: 4