我在应用程序的后台播放音频非常困难。该应用程序是一个倒计时和播放铃声的计时器,一切都使用计时器工作。由于你无法在后台运行超过3分钟的计时器,我需要以另一种方式播放铃声。
用户可以选择铃铛并设置播放铃声的时间(例如,立即播放铃声,5分钟后播放铃声,每10分钟重复一次铃声等)。
到目前为止,我已尝试使用DispatchQueue.main使用通知,如果用户没有暂停计时器,这将正常工作。如果他们重新进入应用程序并暂停,我似乎无法取消此队列或暂停它。
接下来我尝试使用AVAudioEngine,并创建了一组节点。这些将在应用程序处于前台时播放,但似乎在后台停止时停止。此外,当我暂停引擎并稍后恢复时,它不会正确暂停序列。它会把钟声压得一个接一个地打或者根本不打。
如果有人对如何解决我的问题有任何想法,那就太棒了。从技术上讲,我可以尝试从引擎中删除所有内容,并在用户暂停/恢复的暂停时间重新创建它,但这似乎相当昂贵。它也没有解决后台音频停止的问题。我有所需的背景模式'App播放音频或使用Airplay播放音频/视频',并且还在功能的后台模式下进行检查。
以下是我尝试设置音频引擎的示例。 registerAndPlaySound方法被多次调用以创建节点链(或者这样做不正确?)。这段代码有点混乱,因为我一直在努力尝试让它发挥作用。
func setupSounds{
if (attached){
engine.detach(player)
}
engine.attach(player)
attached = true
let mixer = engine.mainMixerNode
engine.connect(player, to: mixer, format: mixer.outputFormat(forBus: 0))
var bell = ""
do {
try engine.start()
} catch {
return
}
if (currentSession.bellObject?.startBell != nil){
bell = (currentSession.bellObject?.startBell)!
guard let url = Bundle.main.url(forResource: bell, withExtension: "mp3") else {
return
}
registerAndPlaySound(url: url, delay: warmUpTime)
}
}
func registerAndPlaySound(url: URL, delay: Double) {
do {
let file = try AVAudioFile(forReading: url)
let format = file.processingFormat
let capacity = file.length
let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(capacity))
do {
try file.read(into: buffer)
}catch {
return
}
let sampleRate = buffer.format.sampleRate
let sampleTime = sampleRate*delay
let futureTime = AVAudioTime(sampleTime: AVAudioFramePosition(sampleTime), atRate: sampleRate)
player.scheduleBuffer(buffer, at: futureTime, options: AVAudioPlayerNodeBufferOptions(rawValue: 0), completionHandler: nil)
player.play()
} catch {
return
}
}