iOS - 检测麦克风吹气并转换结果!(Swift)

12

我需要使用Swift开发一个iOS应用程序,可以通过用户对麦克风吹气的检测来实现。这将是一个挑战游戏,其中两名玩家必须依次向iPhone麦克风吹气。声音分贝值应该被测量并转换为米或千米,以便我可以确定胜者。“吹得更远”的玩家(玩家1:50千米,玩家2:70千米)获胜。

这个实现方式是否可行?

我已经有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);
 }
}

提前感谢!

2个回答

16

我将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.
    }


}

使用这段代码,我在尝试 recorder = AVAudioRecorder(url:url, settings: recordSettings) 时遇到了 Thread 1: EXC_BAD_ACCESS (code=1, address=0x0) 错误。你知道为什么吗? - Jeff
@Jeff 在 Info.plist 中添加权限字符串“NSMicrophoneUsageDescription”:https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/requesting_authorization_for_media_capture_on_ios - Vitaliy A

13

关闭。你有一些问题。因为你没有传递参数,所以你的选择器调用会导致应用程序崩溃,而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.
}


}

有没有一种方法可以在不激活.record()的情况下实现这一点?我有一个USB Apogee麦克风,在开始时会出现爆炸声,因为预放大器仅在调用record()后才打开,有没有一种在录音之前激活麦克风的方法? - DaWiseguy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接