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.
126 lines
3.4 KiB
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)
|
|
}
|
|
}
|
|
|
|
}
|
|
|