我正在尝试在AVAudioEngine 8中使用新的iOS。
看起来,在声音文件播放完之前,completionHandler of player.scheduleFile()被称为。
我使用的是长度为5s的声音文件-- println()
-Message大约在声音结束前1秒左右出现。
我是做错了什么,还是误解了completionHandler的概念?
谢谢!
以下是一些代码:
class SoundHandler {
let engine:AVAudioEngine
let player:AVAudioPlayerNode
let mainMixer:AVAudioMixerNode
init() {
engine = AVAudioEngine()
player = AVAudioPlayerNode()
engine.attachNode(player)
mainMixer = engine.mainMixerNode
var error:NSError?
if !engine.startAndReturnError(&error) {
if let e = error {
println("error \(e.localizedDescription)")
}
}
engine.connect(player, to: mainMixer, format: mainMixer.outputFormatForBus(0))
}
func playSound() {
var soundUrl = NSBundle.mainBundle().URLForResource("Test", withExtension: "m4a")
var soundFile = AVAudioFile(forReading: soundUrl, error: nil)
player.scheduleFile(soundFile, atTime: nil, completionHandler: { println("Finished!") })
player.play()
}
}
发布于 2015-04-14 14:32:54
回到AVAudioEngine 8天的iOS文档一定是错误的。同时,作为一种解决办法,我注意到如果您使用scheduleBuffer:atTime:options:completionHandler:
,则会按预期的方式触发回调(在播放结束后)。
示例代码:
AVAudioFile *file = [[AVAudioFile alloc] initForReading:_fileURL commonFormat:AVAudioPCMFormatFloat32 interleaved:NO error:nil];
AVAudioPCMBuffer *buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:file.processingFormat frameCapacity:(AVAudioFrameCount)file.length];
[file readIntoBuffer:buffer error:&error];
[_player scheduleBuffer:buffer atTime:nil options:AVAudioPlayerNodeBufferInterrupts completionHandler:^{
// reminder: we're not on the main thread in here
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"done playing, as expected!");
});
}];
发布于 2015-04-06 19:35:53
我也看到了同样的行为。
从我的实验中,我相信回调是在缓冲区/段/文件被“调度”后调用的,而不是在播放结束时调用的。
尽管文档明确声明:“在缓冲区完全播放或播放机停止后调用。可能为零。”
所以我认为要么是错误的文档,要么是错误的文档。不知道哪个
发布于 2016-03-15 02:03:38
使用AVAudioTime,您可以随时计算音频回放完成的未来时间。当前行为非常有用,因为它支持在当前缓冲区/段/文件结束之前从回调中调度额外的缓冲区/段/文件,从而避免音频播放中的空白。这允许您创建一个简单的循环播放器,而不需要做大量的工作。下面是一个例子:
class Latch {
var value : Bool = true
}
func loopWholeFile(file : AVAudioFile, player : AVAudioPlayerNode) -> Latch {
let looping = Latch()
let frames = file.length
let sampleRate = file.processingFormat.sampleRate
var segmentTime : AVAudioFramePosition = 0
var segmentCompletion : AVAudioNodeCompletionHandler!
segmentCompletion = {
if looping.value {
segmentTime += frames
player.scheduleFile(file, atTime: AVAudioTime(sampleTime: segmentTime, atRate: sampleRate), completionHandler: segmentCompletion)
}
}
player.scheduleFile(file, atTime: AVAudioTime(sampleTime: segmentTime, atRate: sampleRate), completionHandler: segmentCompletion)
segmentCompletion()
player.play()
return looping
}
上面的代码在调用player.play()之前对整个文件进行了两次调度。当每个片段接近完成时,它将在未来安排另一个完整的文件,以避免播放中的空白。要停止循环,可以使用返回值Latch,如下所示:
let looping = loopWholeFile(file, player)
sleep(1000)
looping.value = false
player.stop()
https://stackoverflow.com/questions/29427253
复制相似问题