AudioKit的AKAudioFile是否总是一次读取整个音频文件?

时间:2017-12-16 22:08:58

标签: swift audiokit

我的应用程序是macOS的音频播放器,使用AVAudioEngine框架用Swift 3编写。

现在,我正在尝试利用AudioKit框架,而不是直接使用AVAudioEngine,因为它看起来像一个经过良好测试和信任的框架(AudioKit 3.7.1(不是4.x),因为我仍在使用Swift进行编码3)

在我目前的音频播放器实现中(没有AudioKit),我一次读取小块音频数据(5到15秒之间),原因很明显 - 在播放或在轨道内寻找时立即开始播放,以及保持内存占用空间相当小。并且,我始终在内存中保留一个额外的“向前看”缓冲区,以避免播放中的空白。

所以,如果我切换到使用AudioKit,我希望它能够做同样的事情 - 理想情况下,我可以指定一个缓冲区大小来使用,它将读取给定大小的音频数据块。但是,它似乎总是一次读取整个文件。对于需要响应并保持合理内存占用的音频播放器应用程序而言,这是不可行的。

假设我播放3小时有声读物MP3 ...将整个文件读入内存需要5到10秒钟。因此,用户必须等待很长时间才能开始播放。并且内存使用量进入GB范围(即整个音频文件被加载到内存中)!这对我的应用程序(或任何音频播放器应用程序)是不可接受的。

P.S。我查看了AudioKit源代码,找不到替代的AKAudioFile初始化程序,AKAudioPlayer中的设置,或者其他能完成我想要的音频文件/播放器类。

我错过了一些完全明显的东西吗?我是AudioKit的初学者。我认为这是用于音频播放/合成的最流行的“高级”MacOS框架。我无法想象它的开发者会错过这个根本的东西吗?或者我完全被我的期望误导了?

以下是我的播放器代码的精髓:

do {
    let akFile = try AKAudioFile(forReading: track.file)
    akPlayer = try AKAudioPlayer(file: akFile)

    AudioKit.output = akPlayer
    AudioKit.start()
    akPlayer.start()

} catch {
    print("Problem")
}

3 个答案:

答案 0 :(得分:1)

我建议尝试在开发分支上发生新的AKPlayer。 AKAudioPlayer的日子已经屈指可数了。您可以向AKPlayer发送常规AVAudioFile,这应该可以解决您的问题。

答案 1 :(得分:1)

问题是AKAudioPlayer(不是AKAudioFile)只播放来自Ram的音频。要做的事情是Aure说并使用AKPlayer。这当前仅在开发分支中,但默认情况下将从磁盘流式传输。

答案 2 :(得分:1)

Audiokit正在使用AVAudioPlayerNode,它可以直接从磁盘或缓冲区播放文件。您可以使用自己的课程扩展Audiokit。在这个例子中,我想通过无缝循环从磁盘播放文件。

import Foundation
import AudioKit

open class Xplayer: AKNode {

    var url : URL? = nil

    let playerNode = AVAudioPlayerNode()
    private var mixer = AVAudioMixerNode()
    var inFile : AKAudioFile

    var isLooping : Bool = true


    public init(audioFile: AKAudioFile)
    {
        self.inFile = audioFile
                AudioKit.engine.attach(playerNode)
                AudioKit.engine.attach(mixer)
                AudioKit.engine.connect(playerNode, to: mixer)
                super.init(avAudioNode: mixer, attach: false)
    }


        func Prepare()
    {

        let frames = inFile.length

        var offsetFrame: Int64 = 0

        self.isLooping = true

        let sampleRate = playerNode.outputFormat(forBus: 0).sampleRate
        var segmentTime : AVAudioFramePosition = 0
        var segmentCompletion : AVAudioNodeCompletionHandler!
        segmentCompletion = {
            if (self.isLooping == true) {    // Need to set false before stopping sound else delays start
            segmentTime += frames - offsetFrame
            self.playerNode.scheduleFile(self.inFile, at: AVAudioTime(sampleTime: segmentTime, atRate: sampleRate), completionHandler: segmentCompletion)
            offsetFrame = 0
            }
        }


        offsetFrame = 0

        let frameCount = frames - offsetFrame

        playerNode.scheduleSegment(inFile,
                                   startingFrame: AVAudioFramePosition(offsetFrame),
                                   frameCount: AVAudioFrameCount(frameCount),
                                   at: nil,
            completionHandler: segmentCompletion)

    }

    func Play()
    {
    Prepare()
    playerNode.play()

    }

    func Stop()
    {
        isLooping = false
        playerNode.stop()
    }


    func Change_sound(audioFile: AKAudioFile)
    {
        Stop()
        self.inFile = audioFile
        Play()

    }

}