You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
LeCountdown/LeCountdown/Sound/SoundPlayer.swift

126 lines
3.4 KiB

//
// SoundPlayer.swift
// LeCountdown
//
// Created by Laurent Morvillier on 30/01/2023.
//
import Foundation
import AVFoundation
struct SoundFile {
var filename: String
var fileExtension: String
init(fullName: String) throws {
let components = fullName.components(separatedBy: ".")
if components.count == 2 {
self.filename = components[0]
self.fileExtension = components[1]
} else {
throw SoundPlayerError.badFileName(name: fullName)
}
}
var url: URL? {
return Bundle.main.url(forResource: self.filename, withExtension: self.fileExtension)
}
}
enum SoundPlayerError : Error {
case missingResourceError(file: SoundFile)
case badFileName(name: String)
case playReturnedFalse
}
@objc class SoundPlayer: NSObject, AVAudioPlayerDelegate, ObservableObject {
fileprivate var _player: AVAudioPlayer?
fileprivate var _timer: Timer? = nil
@Published var currentFileName: String? = nil
func playSound(_ sound: Sound) throws {
try self._playSound(sound)
}
func playSound(_ sound: Sound, duration: TimeInterval) throws {
try self._playSound(sound, duration: duration)
}
fileprivate func _playSound(_ sound: Sound, duration: TimeInterval? = nil) throws {
try self.playOrPauseSound(sound.fileName, duration: duration)
}
func playOrPauseSound(_ file: String, duration: TimeInterval? = nil) throws {
if file == self.currentFileName {
if self._player?.isPlaying ?? false {
self._player?.stop()
self.currentFileName = nil
} else {
self._player?.play()
self.currentFileName = file
}
return
}
self.currentFileName = file
self._player?.stop()
let soundFile = try SoundFile(fullName: file)
if let duration {
try self.play(soundFile: soundFile, for: duration)
} else {
try self.playSound(soundFile: soundFile)
}
}
func play(soundFile: SoundFile, for duration: TimeInterval) throws {
try self.playSound(soundFile: soundFile)
self._timer = Timer(timeInterval: duration, repeats: false, block: { _ in
self._player?.stop()
})
}
func playSound(soundFile: SoundFile) throws {
guard let url = soundFile.url else {
throw SoundPlayerError.missingResourceError(file: soundFile)
}
let player = try AVAudioPlayer(contentsOf: url)
player.prepareToPlay()
player.volume = 1.0
player.delegate = self
self._player = player
player.play()
}
func stop() {
self._player?.stop()
self.currentFileName = nil
}
// func isSoundPlaying(_ sound: Sound) -> Bool {
// return sound.fileName == self.currentFileName && (self._player?.isPlaying ?? false)
// }
// MARK: - Delegate
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
self.currentFileName = nil
Conductor.maestro.deactivateAudioSessionIfPossible()
self.stop()
}
func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
if let error {
Logger.error(error)
}
}
}