通过 iOS (Swift) 中的 AVAudioPlayerNode 播放缓冲区时出现可听故障 *在模拟器中工作,但不在设备上
Posted
技术标签:
【中文标题】通过 iOS (Swift) 中的 AVAudioPlayerNode 播放缓冲区时出现可听故障 *在模拟器中工作,但不在设备上【英文标题】:Audible glitches on buffer playback via AVAudioPlayerNode in iOS (Swift) *working in simulator, but not on device 【发布时间】:2015-03-10 18:39:42 【问题描述】:当使用 AVAudioPlayerNode 安排一个短缓冲区以在触摸事件(“Touch Up Inside”)时立即播放时,我注意到在测试时播放时会出现可听见的故障/伪影。 ios 模拟器中的音频完全没有故障,但是当我在实际的 iOS 设备上运行该应用程序时,播放时会出现声音失真。可听失真随机发生(触发的声音有时听起来很棒,而其他时候听起来失真)
我尝试过使用不同的音频文件、文件格式,并使用 prepareWithFrameCount 方法准备播放缓冲区,但不幸的是结果始终相同,我一直想知道可能出了什么问题..
为了清晰和简单起见,我将代码简化为全局变量。任何帮助或见解将不胜感激。这是我第一次尝试开发 iOS 应用,也是我在 Stack Overflow 上发布的第一个问题。
let filePath = NSBundle.mainBundle().pathForResource("BD_withSilence", ofType: "caf")!
let fileURL: NSURL = NSURL(fileURLWithPath: filePath)!
var error: NSError?
let file = AVAudioFile(forReading: fileURL, error: &error)
let fileFormat = file.processingFormat
let frameCount = UInt32(file.length)
let buffer = AVAudioPCMBuffer(PCMFormat: fileFormat, frameCapacity: frameCount)
let audioEngine = AVAudioEngine()
let playerNode = AVAudioPlayerNode()
func startEngine()
var error: NSError?
file.readIntoBuffer(buffer, error: &error)
audioEngine.attachNode(playerNode)
audioEngine.connect(playerNode, to: audioEngine.mainMixerNode, format: buffer.format)
audioEngine.prepare()
func start()
var error: NSError?
audioEngine.startAndReturnError(&error)
start()
startEngine()
let frameCapacity = AVAudioFramePosition(buffer.frameCapacity)
let frameLength = buffer.frameLength
let sampleRate: Double = 44100.0
func play()
func scheduleBuffer()
playerNode.scheduleBuffer(buffer, atTime: nil, options: AVAudioPlayerNodeBufferOptions.Interrupts, completionHandler: nil)
playerNode.prepareWithFrameCount(frameLength)
if playerNode.playing == false
scheduleBuffer()
let time = AVAudioTime(sampleTime: frameCapacity, atRate: sampleRate)
playerNode.playAtTime(time)
else
scheduleBuffer()
// triggered by a "Touch Up Inside" event on a UIButton in my ViewController
@IBAction func triggerPlay(sender: AnyObject)
play()
更新:
好的,我想我已经确定了失真的来源:节点的音量在输出时太大并导致削波。通过在我的 startEngine 函数中添加这两行,失真不再发生:
playerNode.volume = 0.8
audioEngine.mainMixerNode.volume = 0.8
但是,我仍然不知道为什么我需要降低输出 - 我的音频文件本身没有剪辑。我猜这可能是 AVAudioPlayerNodeBufferOptions.Interrupts 实现方式的结果。当一个缓冲区中断另一个缓冲区时,是否会因中断而导致输出音量增加,从而导致输出削波?我仍在寻找关于为什么会发生这种情况的可靠理解。如果有人愿意/能够对此提供任何澄清,那就太好了!
【问题讨论】:
我遇到了同样的问题。在模拟器中效果很好,但在设备(iPhone 6+)上它会随机播放失真的声音。一旦启动,我必须退出整个应用程序。我试过你改变音量的想法,但仍然有问题。想知道是否有其他解决方案,或者这是一个 iOS 错误 嗨,阿莫斯,是的,我意识到这并没有完全解决事后的问题——它似乎只是让它不那么引人注目/不太可能......所以,也许是一个错误。我已经离开了这个项目,但很快就会再次挖掘。如果我找到解决方案,会在这里通知您。或许在此期间将其引起苹果公司的注意将是一个好主意。 GL! 这也可能是因为您的引擎上有太多节点。你需要分离一些 您对此有什么解决方案吗? 【参考方案1】:不确定这是否是您在 2015 年遇到的问题,可能与 @suthar 在 2018 年遇到的问题相同。
我遇到了一个非常相似的问题,这是由于设备上的 sampleRate 与模拟器不同。在 macOS 上为 44100,在 iOS 设备(后期型号)上为 48000。
因此,当您在 48000 设备上使用 44100 个样本填充缓冲区时,您将获得 3900 个静音样本。播放时听起来不像是静音,听起来像是小故障。
在连接我的 playerNode 以及创建我的 pcmBuffer 时,我使用了 mainMixer 格式。不要在代码中的任何地方引用 48000 或 44100。
audioEngine.attach( playerNode)
audioEngine.connect( playerNode, to:mixerNode, format:mixerNode.outputFormat(forBus:0))
let pcmBuffer = AVAudioPCMBuffer( pcmFormat:SynthEngine.shared.audioEngine.mainMixerNode.outputFormat( forBus:0),
frameCapacity:AVAudioFrameCount(bufferSize))
【讨论】:
以上是关于通过 iOS (Swift) 中的 AVAudioPlayerNode 播放缓冲区时出现可听故障 *在模拟器中工作,但不在设备上的主要内容,如果未能解决你的问题,请参考以下文章
iPhone App:不恢复 AVAudio Player 中的音频会话
自定义 TableViewController 声音在 Swift 3 中播放和崩溃