[英]Playing a stereo audio buffer from memory with AVAudioEngine
我正在尝试从我的 iOS 应用程序中的内存(而不是文件)播放立体声音频缓冲区,但是当我尝试将 AVAudioPlayerNode 'playerNode' 附加到 AVAudioEngine 'audioEngine' 时,我的应用程序崩溃了。 我得到的错误代码如下:
Thread 1: Exception: "required condition is false: _outputFormat.channelCount == buffer.format.channelCount"
我不知道这是否由于我声明 AVAudioEngine、AVAudioPlayerNode 的方式,如果我生成的缓冲区有问题,或者我是否错误地附加了节点(或其他东西!)。 我有一种感觉,这与我如何创建新缓冲区有关。 我正在尝试从两个单独的“单声道”数组制作立体声缓冲区,也许它的格式不正确。
我已经声明了audioEngine:AVAudioEngine! 和 playerNode: AVAudioPlayerNode! 全球:
var audioEngine: AVAudioEngine!
var playerNode: AVAudioPlayerNode!
然后我加载一个我的应用程序将要处理的单声道源音频文件(该文件中的数据不会被播放,它将被加载到一个数组中,经过处理,然后加载到一个新的缓冲区中):
// Read audio file
let audioFileFormat = audioFile.processingFormat
let frameCount = UInt32(audioFile.length)
let audioBuffer = AVAudioPCMBuffer(pcmFormat: audioFileFormat, frameCapacity: frameCount)!
// Read audio data into buffer
do {
try audioFile.read(into: audioBuffer)
} catch let error {
print(error.localizedDescription)
}
// Convert buffer to array of floats
let input: [Float] = Array(UnsafeBufferPointer(start: audioBuffer.floatChannelData![0], count: Int(audioBuffer.frameLength)))
然后将该数组发送到卷积函数两次,每次返回一个新数组。 这是因为单声道源文件需要成为立体声音频缓冲区:
maxSignalLength = input.count + 256
let leftAudioArray: [Float] = convolve(inputAudio: input, impulse: normalisedLeftImpulse)
let rightAudioArray: [Float] = convolve(inputAudio: input, impulse: normalisedRightImpulse)
maxSignalLength 变量当前是输入信号的长度 + 正在与之卷积的脉冲响应(normalisedImpulseResponse)的长度,目前为 256。这将在某个时候成为一个合适的变量。
然后我声明并加载新的缓冲区及其格式,我感觉错误就在这里,因为这将是播放的缓冲区:
let bufferFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: hrtfSampleRate, channels: 2, interleaved: false)!
let outputBuffer = AVAudioPCMBuffer(pcmFormat: bufferFormat, frameCapacity: AVAudioFrameCount(maxSignalLength))!
请注意,我不是在创建交错缓冲区,而是按如下方式将立体声音频数据加载到缓冲区中(我认为这也可能是错误的):
for ch in 0 ..< 2 {
for i in 0 ..< maxSignalLength {
var val: Float!
if ch == 0 { // Left
val = leftAudioArray[i]
// Limit
if val > 1 {
val = 1
}
if val < -1 {
val = -1
}
} else if ch == 1 { // Right
val = rightAudioArray[i]
// Limit
if val < 1 {
val = 1
}
if val < -1 {
val = -1
}
}
outputBuffer.floatChannelData![ch][i] = val
}
}
音频也仅限于 -1 和 1 之间的值。
然后我终于来(试图)将缓冲区加载到音频节点,将音频节点附加到音频引擎,启动音频引擎然后播放节点。
let frameCapacity = AVAudioFramePosition(outputBuffer.frameCapacity)
let frameLength = outputBuffer.frameLength
playerNode.scheduleBuffer(outputBuffer, at: nil, options: AVAudioPlayerNodeBufferOptions.interrupts, completionHandler: nil)
playerNode.prepare(withFrameCount: frameLength)
let time = AVAudioTime(sampleTime: frameCapacity, atRate: hrtfSampleRate)
audioEngine.attach(playerNode)
audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: outputBuffer.format)
audioEngine.prepare()
do {
try audioEngine.start()
} catch let error {
print(error.localizedDescription)
}
playerNode.play(at: time)
我在运行时遇到的错误是:
AVAEInternal.h:76 required condition is false: [AVAudioPlayerNode.mm:712:ScheduleBuffer: (_outputFormat.channelCount == buffer.format.channelCount)]
它不显示发生此错误的行。 我已经坚持了一段时间,并尝试了很多不同的事情,但似乎没有关于从内存中播放音频而不是从我能找到的带有 AVAudioEngine 的文件中播放音频的非常明确的信息。 任何帮助将不胜感激。
谢谢!
编辑 #1:更好的标题
编辑#2:更新 - 我已经找到了为什么我收到错误。 这似乎是由在将 playerNode 附加到 audioEngine 之前设置它引起的。 交换订单阻止程序崩溃并抛出错误:
let frameCapacity = AVAudioFramePosition(outputBuffer.frameCapacity)
let frameLength = outputBuffer.frameLength
audioEngine.attach(playerNode)
audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: outputBuffer.format)
audioEngine.prepare()
playerNode.scheduleBuffer(outputBuffer, at: nil, options: AVAudioPlayerNodeBufferOptions.interrupts, completionHandler: nil)
playerNode.prepare(withFrameCount: frameLength)
let time = AVAudioTime(sampleTime: frameCapacity, atRate: hrtfSampleRate)
do {
try audioEngine.start()
} catch let error {
print(error.localizedDescription)
}
playerNode.play(at: time)
然而,我没有任何声音。 使用与输入信号相同的方法创建 outputBuffer 的浮点数组,并用断点查看其内容后,它似乎是空的,因此我也必须错误地将数据存储到 outputBuffer。
您可能错误地创建和填充缓冲区。 尝试这样做:
let fileURL = Bundle.main.url(forResource: "my_file", withExtension: "aiff")!
let file = try! AVAudioFile(forReading: fileURL)
let buffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, frameCapacity: UInt32(file.length))!
try! file.read(into: buffer)
我已经解决了这个问题!
我尝试了很多解决方案,最终完全重写了我的应用程序的音频引擎部分,现在我在 ViewController 类中声明了 AVAudioEngine 和 AVAudioPlayerNode,如下所示:
class ViewController: UIViewController {
var audioEngine: AVAudioEngine = AVAudioEngine()
var playerNode: AVAudioPlayerNode = AVAudioPlayerNode()
...
我仍然不清楚是将这些全局声明还是在 iOS 中声明为类变量更好,但是我可以确认我的应用程序正在播放音频,这些在 ViewController 类中声明。 我确实知道它们不应该在函数中声明,因为它们会在函数超出范围时消失并停止播放。
但是,在我将AVAudioPCMBuffer.frameLength设置为 frameCapacity之前,我仍然没有获得任何音频输出。
我在网上找到的关于从浮动数组创建新的 AVAudioPCMBuffer 的信息很少,但这似乎是我需要做的使 outputBuffer 可播放的缺失步骤。 在我设置之前,它默认为 0。
AVAudioFormat 类声明中不需要 frameLength 成员。 但它很重要,我的缓冲区在我手动设置它之前是不可播放的,并且在类实例声明之后:
let bufferFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: hrtfSampleRate, channels: 2, interleaved: false)!
let frameCapacity = UInt32(audioFile.length)
guard let outputBuffer = AVAudioPCMBuffer(pcmFormat: bufferFormat, frameCapacity: frameCapacity) else {
fatalError("Could not create output buffer.")
}
outputBuffer.frameLength = frameCapacity // Important!
这花了很长时间才找到,希望这会在将来对其他人有所帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.