如何使用Swift在iOS中捕获音频样本?

hun*_*ley 14 audio signal-processing fft ios swift

我在网上找到了许多用于在iOS中处理音频的例子,但是大多数都已经过时了,并不适用于我想要完成的任务.这是我的项目:

我需要从两个来源捕获音频样本 - 麦克风输入和存储的音频文件.我需要对这些样本执行FFT以产生整个剪辑的"指纹",以及应用一些额外的过滤器.最终目标是构建一种类似于Shazam等的歌曲识别软件.

在iOS 8中捕获单个音频样本以执行快速傅里叶变换的最佳方法是什么?我想最终会有大量的这些,但我怀疑它可能不会像那样.其次,如何使用Accelerate框架处理音频?它似乎是在iOS中对音频执行复杂分析的最有效方式.

我在网上看到的所有例子都使用旧版本的iOS和Objective-C,但我无法将它们成功转换为Swift.iOS 8是否为这类东西提供了一些新的框架?

Swi*_*ect 8

迅速

在iOS录制:

  • 创建和维护一个实例AVAudioRecorder,如var audioRecorder: AVAudioRecorder? = nil
  • 使用AVAudioRecorderURL 初始化您以存储样本和一些记录设置

录制会话顺序:

  1. 调用 prepareToRecord()
  2. 调用 record()
  3. 调用 stop()

完整的Swift/AVAudioRecorder示例

在录制方法的核心,您可以:

func record() {
    self.prepareToRecord()
    if let recorder = self.audioRecorder {
        recorder.record()
    }
}
Run Code Online (Sandbox Code Playgroud)

要准备录制(流式传输到a file),您可以:

func prepareToRecord() {
    var error: NSError?
    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! NSString
    let soundFileURL: NSURL? = NSURL.fileURLWithPath("\(documentsPath)/recording.caf")

    self.audioRecorder = AVAudioRecorder(URL: soundFileURL, settings: recordSettings as [NSObject : AnyObject], error: &error)
    if let recorder = self.audioRecorder {
        recorder.prepareToRecord()
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,要停止录制,请使用:

func stopRecording() {
    if let recorder = self.audioRecorder {
        recorder.stop()
    }
}
Run Code Online (Sandbox Code Playgroud)

以上示例也需要import AVFoundation和一些recordSettings,留给您选择.一个例子recordSettings可能如下所示:

let recordSettings = [
    AVFormatIDKey: kAudioFormatAppleLossless,
    AVEncoderAudioQualityKey : AVAudioQuality.Max.rawValue,
    AVEncoderBitRateKey : 320000,
    AVNumberOfChannelsKey: 2,
    AVSampleRateKey : 44100.0
]
Run Code Online (Sandbox Code Playgroud)

这样做,你已经完成了.


您可能还想查看此Stack Overflow应答,其中包括一个演示项目.

  • 此信息很有用,但如何从录制中提取单个音频样本?我需要原始数据 - 最好是Floats数组,我可以在其上执行分析.同样的问题适用于已在磁盘上的文件. (2认同)

Won*_*ray 7

AVAudioEngine是实现此目的的方法。从苹果的文档:

  • 要播放和录制单个轨道,请使用AVAudioPlayer和AVAudioRecorder。
  • 对于更复杂的音频处理,请使用AVAudioEngine。AVAudioEngine包括用于音频输入和输出的AVAudioInputNode和AVAudioOutputNode。您还可以使用AVAudioNode对象来处理效果并将其混合到音频中

我将直接与您联系:AVAudioEngine是一个极其挑剔的API,具有模糊的文档,很少有用的错误消息,并且几乎没有在线代码示例能够演示最基本的任务。但是,如果您花时间克服较小的学习曲线,则可以真正相对轻松地完成一些神奇的事情。

我构建了一个简单的“游乐场”视图控制器,该控制器演示了麦克风和音频文件采样协同工作的情况:

import UIKit

class AudioEnginePlaygroundViewController: UIViewController {
    private var audioEngine: AVAudioEngine!
    private var mic: AVAudioInputNode!
    private var micTapped = false
    override func viewDidLoad() {
        super.viewDidLoad()
        configureAudioSession()
        audioEngine = AVAudioEngine()
        mic = audioEngine.inputNode!
    }

    static func getController() -> AudioEnginePlaygroundViewController {
        let me = AudioEnginePlaygroundViewController(nibName: "AudioEnginePlaygroundViewController", bundle: nil)
        return me
    }

    @IBAction func toggleMicTap(_ sender: Any) {
        if micTapped {
            mic.removeTap(onBus: 0)
            micTapped = false
            return
        }

        let micFormat = mic.inputFormat(forBus: 0)
        mic.installTap(onBus: 0, bufferSize: 2048, format: micFormat) { (buffer, when) in
            let sampleData = UnsafeBufferPointer(start: buffer.floatChannelData![0], count: Int(buffer.frameLength))
        }
        micTapped = true
        startEngine()
    }

    @IBAction func playAudioFile(_ sender: Any) {
        stopAudioPlayback()
        let playerNode = AVAudioPlayerNode()

        let audioUrl = Bundle.main.url(forResource: "test_audio", withExtension: "wav")!
        let audioFile = readableAudioFileFrom(url: audioUrl)
        audioEngine.attach(playerNode)
        audioEngine.connect(playerNode, to: audioEngine.outputNode, format: audioFile.processingFormat)
        startEngine()

        playerNode.scheduleFile(audioFile, at: nil) {
            playerNode .removeTap(onBus: 0)
        }
        playerNode.installTap(onBus: 0, bufferSize: 4096, format: playerNode.outputFormat(forBus: 0)) { (buffer, when) in
            let sampleData = UnsafeBufferPointer(start: buffer.floatChannelData![0], count: Int(buffer.frameLength))
        }
        playerNode.play()
    }

    // MARK: Internal Methods

    private func configureAudioSession() {
        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, with: [.mixWithOthers, .defaultToSpeaker])
            try AVAudioSession.sharedInstance().setActive(true)
        } catch { }
    }

    private func readableAudioFileFrom(url: URL) -> AVAudioFile {
        var audioFile: AVAudioFile!
        do {
            try audioFile = AVAudioFile(forReading: url)
        } catch { }
        return audioFile
    }

    private func startEngine() {
        guard !audioEngine.isRunning else {
            return
        }

        do {
            try audioEngine.start()
        } catch { }
    }

    private func stopAudioPlayback() {
        audioEngine.stop()
        audioEngine.reset()
    }
}
Run Code Online (Sandbox Code Playgroud)

音频样本是通过installTap的完成处理程序提供给您的,当音频实时通过轻敲的节点(麦克风或音频文件播放器)时,会连续调用该示例。您可以通过索引我在每个块中创建的sampleData指针来访问单个样本。

  • 这就是OP正在寻找的。感谢您在问题提出多年后添加此答案。 (3认同)