parent
a500ecb6cb
commit
3f60675775
@ -0,0 +1,21 @@ |
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> |
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22A400" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier=""> |
||||
<entity name="Activity" representedClassName="Activity" syncable="YES" codeGenerationType="class"> |
||||
<attribute name="name" attributeType="String" defaultValueString=""/> |
||||
<relationship name="countdowns" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Countdown" inverseName="activity" inverseEntity="Countdown"/> |
||||
<relationship name="records" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Record" inverseName="activity" inverseEntity="Record"/> |
||||
</entity> |
||||
<entity name="Countdown" representedClassName="Countdown" syncable="YES" codeGenerationType="class"> |
||||
<attribute name="duration" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/> |
||||
<attribute name="image" optional="YES" attributeType="String"/> |
||||
<attribute name="order" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> |
||||
<attribute name="repeats" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/> |
||||
<attribute name="sound" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> |
||||
<relationship name="activity" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Activity" inverseName="countdowns" inverseEntity="Activity"/> |
||||
</entity> |
||||
<entity name="Record" representedClassName="Record" syncable="YES" codeGenerationType="class"> |
||||
<attribute name="end" attributeType="Date" defaultDateTimeInterval="696425400" usesScalarValueType="NO"/> |
||||
<attribute name="start" attributeType="Date" defaultDateTimeInterval="696425400" usesScalarValueType="NO"/> |
||||
<relationship name="activity" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Activity" inverseName="records" inverseEntity="Activity"/> |
||||
</entity> |
||||
</model> |
||||
@ -0,0 +1,38 @@ |
||||
// |
||||
// Sound.swift |
||||
// LeCountdown |
||||
// |
||||
// Created by Laurent Morvillier on 30/01/2023. |
||||
// |
||||
|
||||
import Foundation |
||||
|
||||
struct SoundFile { |
||||
var filename: String |
||||
var fileExtension: String |
||||
|
||||
var url: URL? { |
||||
return Bundle.main.url(forResource: self.filename, withExtension: self.fileExtension) |
||||
} |
||||
} |
||||
|
||||
// Sound id are stored thus case order should not be changed |
||||
enum Sound : Int, CaseIterable, Identifiable { |
||||
|
||||
var id: Int { return self.rawValue } |
||||
|
||||
case trainhorn // default |
||||
|
||||
var localizedString: String { |
||||
switch self { |
||||
case .trainhorn: return NSLocalizedString("Train horn", comment: "") |
||||
} |
||||
} |
||||
|
||||
var soundFile: SoundFile { |
||||
switch self { |
||||
case .trainhorn: return SoundFile(filename: "train_horn", fileExtension: "mp3") |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,44 @@ |
||||
// |
||||
// SoundPlayer.swift |
||||
// LeCountdown |
||||
// |
||||
// Created by Laurent Morvillier on 30/01/2023. |
||||
// |
||||
|
||||
import Foundation |
||||
import AVFoundation |
||||
|
||||
enum SoundPlayerError : Error { |
||||
case missingResourceError(file: SoundFile) |
||||
} |
||||
|
||||
class SoundPlayer { |
||||
|
||||
fileprivate var _player: AVAudioPlayer? |
||||
|
||||
func playSound(soundFile: SoundFile, repeats: Bool) throws { |
||||
guard let url = soundFile.url else { |
||||
throw SoundPlayerError.missingResourceError(file: soundFile) |
||||
} |
||||
|
||||
_player = try AVAudioPlayer(contentsOf: url) |
||||
_player?.prepareToPlay() |
||||
let loopCount = repeats ? Int.max : 0 |
||||
_player?.numberOfLoops = loopCount |
||||
_player?.volume = 1.0 |
||||
|
||||
do { |
||||
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [.allowBluetooth, .defaultToSpeaker]) |
||||
} catch { |
||||
print("audioSession error = \(error)") |
||||
} |
||||
|
||||
_player?.play() |
||||
|
||||
} |
||||
|
||||
func stop() { |
||||
self._player?.stop() |
||||
} |
||||
|
||||
} |
||||
Binary file not shown.
Loading…
Reference in new issue