我的应用程序是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")
}
答案 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()
}
}