Restore sound play after quitting

main
Laurent 3 years ago
parent 604e167faa
commit 2a09dc6593
  1. 2
      LeCountdown.xcodeproj/project.pbxproj
  2. 47
      LeCountdown/Conductor.swift
  3. 1
      LeCountdown/LeCountdownApp.swift
  4. 2
      LeCountdown/Model/Model+SharedExtensions.swift
  5. 45
      LeCountdown/Sound/DelaySoundPlayer.swift

@ -155,7 +155,6 @@
C4E5D66A29B73FC6008E7465 /* TimerShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D66929B73FC6008E7465 /* TimerShortcuts.swift */; };
C4E5D66D29B753D7008E7465 /* AppShortcuts.strings in Resources */ = {isa = PBXBuildFile; fileRef = C4E5D66F29B753D7008E7465 /* AppShortcuts.strings */; };
C4E5D67429B88734008E7465 /* DelaySoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */; };
C4E5D67729B88BB5008E7465 /* DelaySoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */; };
C4E5D67829B88BB5008E7465 /* DelaySoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */; };
C4E5D67A29B8C5A1008E7465 /* VolumeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67929B8C5A1008E7465 /* VolumeView.swift */; };
C4E5D67C29B8D4A5008E7465 /* Low_Tom_Disto_Earth.wav in Resources */ = {isa = PBXBuildFile; fileRef = C4E5D67B29B8D4A5008E7465 /* Low_Tom_Disto_Earth.wav */; };
@ -1129,7 +1128,6 @@
C4BA2B4A299FCE0C00CB4FBA /* IntervalGroup+CoreDataProperties.swift in Sources */,
C4BA2B4D299FCE0C00CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */,
C4F8B194298AC288005C86A5 /* Record+CoreDataProperties.swift in Sources */,
C4E5D67729B88BB5008E7465 /* DelaySoundPlayer.swift in Sources */,
C438C8152982BD9000BF3EF9 /* IntentDataProvider.swift in Sources */,
C473C31929A926F50056B38A /* LaunchWidget.intentdefinition in Sources */,
C4F8B15B29892D40005C86A5 /* SoundPlayer.swift in Sources */,

@ -20,15 +20,13 @@ fileprivate enum Const: String {
case confirmationSound = "PVP_Stab_Oneshot_Bleep_Em.wav"
}
typealias TimerID = String
class Conductor: ObservableObject {
static let maestro: Conductor = Conductor()
@Published var soundPlayer: SoundPlayer? = nil
var delayedSoundPlayers: [TimerID : DelaySoundPlayer] = [:]
fileprivate var _delayedSoundPlayers: [TimerID : DelaySoundPlayer] = [:]
@UserDefault(PreferenceKey.countdowns.rawValue, defaultValue: [:]) static var savedCountdowns: [String : DateInterval]
@UserDefault(PreferenceKey.stopwatches.rawValue, defaultValue: [:]) static var savedStopwatches: [String : Date]
@ -62,8 +60,13 @@ class Conductor: ObservableObject {
}
func removeLiveTimer(id: String) {
Logger.log("removeLiveTimer")
self.liveTimers.removeAll(where: { $0.id == id })
self.cancelledCountdowns.removeAll(where: { $0 == id })
if let soundPlayer = self._delayedSoundPlayers[id] {
soundPlayer.stop()
}
}
fileprivate func _buildLiveTimers() {
@ -162,17 +165,15 @@ class Conductor: ObservableObject {
do {
let date = Date(timeIntervalSinceNow: countdown.duration)
self.startCountdown(date, countdown: countdown)
let soundFile = try SoundFile(fullName: countdown.soundName)
let soundPlayer = DelaySoundPlayer(soundFile: soundFile)
self.delayedSoundPlayers[countdown.stringId] = soundPlayer
let soundPlayer = try DelaySoundPlayer(timerID: countdown.stringId, soundFile: soundFile)
self._delayedSoundPlayers[countdown.stringId] = soundPlayer
try soundPlayer.start(in: countdown.duration,
repeatCount: Int(countdown.repeatCount))
self.startCountdown(date, countdown: countdown)
if Preferences.playConfirmationSound {
self._playSound(Const.confirmationSound.rawValue)
}
@ -215,6 +216,28 @@ class Conductor: ObservableObject {
}
}
func restore() {
for (countdownId, interval) in self.currentCountdowns {
if !self._delayedSoundPlayers.contains(where: { $0.key == countdownId }) {
let context = PersistenceController.shared.container.viewContext
if let countdown = context.object(stringId: countdownId) as? Countdown {
do {
let soundFile = try SoundFile(fullName: countdown.soundName)
let soundPlayer = try DelaySoundPlayer(timerID: countdownId, soundFile: soundFile)
self._delayedSoundPlayers[countdown.stringId] = soundPlayer
try soundPlayer.restore(for: interval.end, repeatCount: Int(countdown.repeatCount))
} catch {
Logger.error(error)
}
}
}
}
}
// MARK: - Cleanup
func cleanup() {
@ -273,9 +296,9 @@ class Conductor: ObservableObject {
}
func cancelSoundPlayer(id: TimerID) {
if let soundPlayer = self.delayedSoundPlayers[id] {
if let soundPlayer = self._delayedSoundPlayers[id] {
soundPlayer.stop()
self.delayedSoundPlayers.removeValue(forKey: id)
self._delayedSoundPlayers.removeValue(forKey: id)
}
}

@ -72,6 +72,7 @@ struct LeCountdownApp: App {
case .active:
Logger.log("onChange(of: scenePhase) active")
Logger.log(Conductor.maestro.currentCountdowns.count)
Conductor.maestro.restore()
Conductor.maestro.cleanup()
default:
break

@ -7,6 +7,8 @@
import Foundation
typealias TimerID = String
extension AbstractTimer {
var displayName: String {

@ -10,44 +10,53 @@ import AVFoundation
@objc class DelaySoundPlayer: NSObject, AVAudioPlayerDelegate {
fileprivate var _player: AVAudioPlayer?
fileprivate var _player: AVAudioPlayer
private var soundFile: SoundFile
fileprivate var _timerID: TimerID
init(soundFile: SoundFile) {
self.soundFile = soundFile
init(timerID: TimerID, soundFile: SoundFile) throws {
self._timerID = timerID
guard let url = soundFile.url else {
throw SoundPlayerError.missingResourceError(file: soundFile)
}
self._player = try AVAudioPlayer(contentsOf: url)
}
func restore(for playDate: Date, repeatCount: Int) throws {
let timeLeft = playDate.timeIntervalSinceNow
try self._play(in: timeLeft, repeatCount: repeatCount)
}
func start(in duration: TimeInterval, repeatCount: Int) throws {
try self._play(in: duration, repeatCount: repeatCount)
}
fileprivate func _play(in duration: TimeInterval, repeatCount: Int) throws {
guard let url = self.soundFile.url else {
throw SoundPlayerError.missingResourceError(file: self.soundFile)
}
let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playback, options: .duckOthers)
try audioSession.setActive(true)
let player = try AVAudioPlayer(contentsOf: url)
player.prepareToPlay()
player.volume = 1.0
player.delegate = self
player.numberOfLoops = repeatCount
self._player.prepareToPlay()
self._player.volume = 1.0
self._player.delegate = self
self._player.numberOfLoops = repeatCount
self._player = player
self._player.play(atTime: self._player.deviceCurrentTime + duration)
player.play(atTime: player.deviceCurrentTime + duration)
}
func stop() {
self._player?.stop()
self._player.stop()
}
// MARK: - Delegate
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
self.stop()
Logger.log("audioPlayerDidFinishPlaying: successfully = \(flag)")
Conductor.maestro.cancelSoundPlayer(id: self._timerID)
}
}

Loading…
Cancel
Save