如何解决使用AVAudioEngine
我正在尝试从iOS应用程序中的内存(而不是文件)中播放 stereo 音频缓冲区,但是当我尝试将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!
然后我加载一个我的应用程序要处理的单源音频文件(该文件中的数据将不会播放,将被加载到数组中进行处理和然后加载到 new 缓冲区中):
// 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变量当前为输入信号的长度+与之卷积的脉冲响应(normalizedImpulseResponse)的长度,目前为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之前设置了playerNode引起的。交换订单可防止程序崩溃并引发错误:
let frameCapacity = AVAudioFramePosition(outputBuffer.frameCapacity)
let frameLength = outputBuffer.frameLength
audioEngine.attach(playerNode)
audioEngine.connect(playerNode,format: outputBuffer.format)
audioEngine.prepare()
playerNode.scheduleBuffer(outputBuffer,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类中声明的这些变量播放音频。我确实知道不应在函数中将它们声明为they will disappear and stop playing when the function goes out of scope.
但是,直到将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!
这花了很长时间才发现,希望这对以后的人有帮助。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。