iOS: detecta el golpe en el micrófono y convierte los resultados. (rápido)

Necesito desarrollar una aplicación iOS en swift que detecte un golpe en el micrófono de un usuario. Esto tiene que ser un desafío-juego donde dos jugadores tienen que soplar en el micrófono del iPhone uno tras otro. Los valores de decibelios se deben medir y convertir en un metro o kilómetro para que pueda determinar un ganador. El jugador que "sopla más" (jugador1: 50km, jugador2: 70km) gana.

¿Es esta una posible implementación?

Tengo este código rápido y no sé cómo proceder:

import Foundation import UIKit import AVFoundation import CoreAudio class ViewController: UIViewController { // @IBOutlet weak var mainImage: UIImageView! var recorder: AVAudioRecorder! var levelTimer = NSTimer() var lowPassResults: Double = 0.0 override func viewDidLoad() { super.viewDidLoad() let url = NSURL.fileURLWithPath("dev/null") //numbers are automatically wrapped into NSNumber objects, so I simplified that to [NSString : NSNumber] var settings : [NSString : NSNumber] = [AVSampleRateKey: 44100.0, AVFormatIDKey: kAudioFormatAppleLossless, AVNumberOfChannelsKey: 1, AVEncoderAudioQualityKey: AVAudioQuality.Max.rawValue] var error: NSError? // mainImage?.image = UIImage(named: "flyForReal.png"); recorder = AVAudioRecorder(URL:url, settings:settings, error:&error) if((recorder) != nil){ recorder.prepareToRecord() recorder.meteringEnabled = true recorder.record() levelTimer = NSTimer.scheduledTimerWithTimeInterval(0.05, target: self, selector: Selector("levelTimerCallback"), userInfo: nil, repeats: true) } else{ NSLog("%@", "Error"); } } func levelTimerCallback(timer:NSTimer) { recorder.updateMeters() let ALPHA: Double = 0.05 var peakPowerForChannel = pow(Double(10), (0.05 * Double(recorder.peakPowerForChannel(0)))) lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults; if(lowPassResults > 0.95){ NSLog("@Mic blow detected"); } NSLog("@Average input: %f Peak input: %f Low pass results: %f", recorder.averagePowerForChannel(0), recorder.peakPowerForChannel(0), lowPassResults); } } 

Gracias adelante!

Cerca. Tienes un par de problemas. Su llamada selectora bloquea la aplicación porque no está pasando un argumento y levelTimerCallback() espera uno.

averagePowerPerChannel parece darme una medición más en time real, así que utilicé eso en lugar de peakPowerPerChannel

Además, debe configurar una session de audio. No estaba realmente seguro de qué se trataba todo eso de las matemáticas, así que me deshice de él aquí. He pegado todo el controller de vista a continuación para la detección básica de micrófonos.

 import Foundation import UIKit import AVFoundation import CoreAudio class ViewController: UIViewController { var recorder: AVAudioRecorder! var levelTimer = NSTimer() var lowPassResults: Double = 0.0 override func viewDidLoad() { super.viewDidLoad() //make an AudioSession, set it to PlayAndRecord and make it active var audioSession:AVAudioSession = AVAudioSession.shanetworkingInstance() audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, error: nil) audioSession.setActive(true, error: nil) //set up the URL for the audio file var documents: AnyObject = NSSearchPathForDirectoriesInDomains( NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] var str = documents.stringByAppendingPathComponent("recordTest.caf") var url = NSURL.fileURLWithPath(str as String) // make a dictionary to hold the recording settings so we can instantiate our AVAudioRecorder var recordSettings: [NSObject : AnyObject] = [AVFormatIDKey:kAudioFormatAppleIMA4, AVSampleRateKey:44100.0, AVNumberOfChannelsKey:2,AVEncoderBitRateKey:12800, AVLinearPCMBitDepthKey:16, AVEncoderAudioQualityKey:AVAudioQuality.Max.rawValue ] //declare a variable to store the returned error if we have a problem instantiating our AVAudioRecorder var error: NSError? //Instantiate an AVAudioRecorder recorder = AVAudioRecorder(URL:url, settings: recordSettings, error: &error) //If there's an error, print that shit - otherwise, run prepareToRecord and meteringEnabled to turn on metering (must be run in that order) if let e = error { println(e.localizedDescription) } else { recorder.prepareToRecord() recorder.meteringEnabled = true //start recording recorder.record() //instantiate a timer to be called with whatever frequency we want to grab metering values self.levelTimer = NSTimer.scheduledTimerWithTimeInterval(0.02, target: self, selector: Selector("levelTimerCallback"), userInfo: nil, repeats: true) } } //This selector/function is called every time our timer (levelTime) fires func levelTimerCallback() { //we have to update meters before we can get the metering values recorder.updateMeters() //print to the console if we are beyond a threshold value. Here I've used -7 if recorder.averagePowerForChannel(0) > -7 { print("Dis be da level I'm hearin' you in dat mic ") println(recorder.averagePowerForChannel(0)) println("Do the thing I want, mofo") } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } 

¡Convertí la respuesta de Andrew a Swift 4 y funciona genial! ¡Gracias!

 import Foundation import UIKit import AVFoundation import CoreAudio class ViewController: UIViewController { var recorder: AVAudioRecorder! var levelTimer = Timer() let LEVEL_THRESHOLD: Float = -10.0 override func viewDidLoad() { super.viewDidLoad() let documents = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0]) let url = documents.appendingPathComponent("record.caf") let recordSettings: [String: Any] = [ AVFormatIDKey: kAudioFormatAppleIMA4, AVSampleRateKey: 44100.0, AVNumberOfChannelsKey: 2, AVEncoderBitRateKey: 12800, AVLinearPCMBitDepthKey: 16, AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue ] let audioSession = AVAudioSession.shanetworkingInstance() do { try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord) try audioSession.setActive(true) try recorder = AVAudioRecorder(url:url, settings: recordSettings) } catch { return } recorder.prepareToRecord() recorder.isMeteringEnabled = true recorder.record() levelTimer = Timer.scheduledTimer(timeInterval: 0.02, target: self, selector: #selector(levelTimerCallback), userInfo: nil, repeats: true) } @objc func levelTimerCallback() { recorder.updateMeters() let level = recorder.averagePower(forChannel: 0) let isLoud = level > LEVEL_THRESHOLD // do whatever you want with isLoud } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }