iOS - 检测 Blow into Mic 并转换结果! (swift)
iOS - Detect Blow into Mic and convert the results! (swift)
我需要在 swift 中开发一个 iOS 应用程序,用于检测用户对麦克风的打击。这必须是一场挑战游戏,两名玩家必须依次向 iPhone 麦克风吹气。分贝值应以米或公里为单位进行测量和转换,以便我确定优胜者。 "blows further" (player1: 50km, player2: 70km) 获胜的玩家。
这是一个可能的实现吗?
我在 swift 中有此代码,但我不知道如何继续:
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);
}
}
提前致谢!
关闭。你有几个问题。您的选择器调用使应用程序崩溃,因为您没有传递参数并且 levelTimerCallback()
需要一个参数。
averagePowerPerChannel
似乎给了我一个更实时的计量,所以我用它而不是 peakPowerPerChannel
此外,您需要设置一个音频会话。我不太确定所有这些数学是关于什么的,所以我只是在这里摆脱它。我在下面粘贴了整个视图控制器以进行基本的麦克风检测。
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.sharedInstance()
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.
}
}
我把 Andrew 的回答转换成了 Swift 4:
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.sharedInstance()
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.
}
}
我需要在 swift 中开发一个 iOS 应用程序,用于检测用户对麦克风的打击。这必须是一场挑战游戏,两名玩家必须依次向 iPhone 麦克风吹气。分贝值应以米或公里为单位进行测量和转换,以便我确定优胜者。 "blows further" (player1: 50km, player2: 70km) 获胜的玩家。
这是一个可能的实现吗?
我在 swift 中有此代码,但我不知道如何继续:
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);
}
}
提前致谢!
关闭。你有几个问题。您的选择器调用使应用程序崩溃,因为您没有传递参数并且 levelTimerCallback()
需要一个参数。
averagePowerPerChannel
似乎给了我一个更实时的计量,所以我用它而不是 peakPowerPerChannel
此外,您需要设置一个音频会话。我不太确定所有这些数学是关于什么的,所以我只是在这里摆脱它。我在下面粘贴了整个视图控制器以进行基本的麦克风检测。
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.sharedInstance()
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.
}
}
我把 Andrew 的回答转换成了 Swift 4:
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.sharedInstance()
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.
}
}