AVAudioEngine是最适合做这个的。根据苹果文档:
- 如果需要播放和录制单个音轨,请使用AVAudioPlayer和AVAudioRecorder。
- 如果需要进行更复杂的音频处理,请使用AVAudioEngine。 AVAudioEngine包括AVAudioInputNode和AVAudioOutputNode,用于音频输入和输出。您还可以使用AVAudioNode对象将处理和混合效果与音频混合在一起。
我坦白地告诉你:AVAudioEngine是一个非常棘手的API,文档模糊,错误信息也很少有帮助,并且几乎没有在线代码示例演示超过最基本任务的内容。但是,如果你花时间克服学习曲线,你可以相对容易地做出一些神奇的事情。
我已经构建了一个简单的“playground”视图控制器,演示了麦克风和音频文件采样同时工作的情况:
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()
}
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()
}
}
音频样本通过installTap的完成处理程序提供给您,该处理程序在实时传递经过点击节点(麦克风或音频文件播放器)的音频时不断调用。 您可以通过索引我在每个块中创建的sampleData指针来访问单个样本。