首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >completionHandler of AVAudioPlayerNode.scheduleFile()被过早调用

completionHandler of AVAudioPlayerNode.scheduleFile()被过早调用
EN

Stack Overflow用户
提问于 2015-04-03 06:26:27
回答 7查看 6.2K关注 0票数 8

我正在尝试在AVAudioEngine 8中使用新的iOS。

看起来,在声音文件播放完之前,completionHandler of player.scheduleFile()被称为

我使用的是长度为5s的声音文件-- println()-Message大约在声音结束前1秒左右出现。

我是做错了什么,还是误解了completionHandler的概念?

谢谢!

以下是一些代码:

代码语言:javascript
运行
复制
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()
    }
}
EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2015-04-14 14:32:54

回到AVAudioEngine 8天的iOS文档一定是错误的。同时,作为一种解决办法,我注意到如果您使用scheduleBuffer:atTime:options:completionHandler:,则会按预期的方式触发回调(在播放结束后)。

示例代码:

代码语言:javascript
运行
复制
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!");
    });
}];
票数 6
EN

Stack Overflow用户

发布于 2015-04-06 19:35:53

我也看到了同样的行为。

从我的实验中,我相信回调是在缓冲区/段/文件被“调度”后调用的,而不是在播放结束时调用的。

尽管文档明确声明:“在缓冲区完全播放或播放机停止后调用。可能为零。”

所以我认为要么是错误的文档,要么是错误的文档。不知道哪个

票数 9
EN

Stack Overflow用户

发布于 2016-03-15 02:03:38

使用AVAudioTime,您可以随时计算音频回放完成的未来时间。当前行为非常有用,因为它支持在当前缓冲区/段/文件结束之前从回调中调度额外的缓冲区/段/文件,从而避免音频播放中的空白。这允许您创建一个简单的循环播放器,而不需要做大量的工作。下面是一个例子:

代码语言:javascript
运行
复制
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,如下所示:

代码语言:javascript
运行
复制
let looping = loopWholeFile(file, player)
sleep(1000)
looping.value = false
player.stop()
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29427253

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档