AudioPlaybackKit/Sources/AudioPlaybackKit/Models/SoundConfiguration.swift
Matt Bruce df8e097a0f moved from TheNoiseClock
Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
2026-02-18 14:49:47 -06:00

156 lines
5.5 KiB
Swift

//
// SoundConfiguration.swift
// AudioPlaybackKit
//
// Created by Matt Bruce on 9/8/25.
//
import Foundation
/// Configuration model for sound system loaded from JSON
public struct SoundConfiguration: Codable {
public let sounds: [Sound]
public let settings: AudioSettings
public init(sounds: [Sound], settings: AudioSettings) {
self.sounds = sounds
self.settings = settings
}
}
/// Simple struct for loading just the sounds array from category JSON files
public struct SoundsOnly: Codable {
public let sounds: [Sound]
public init(sounds: [Sound]) {
self.sounds = sounds
}
}
/// Audio settings configuration
public struct AudioSettings: Codable {
public let defaultVolume: Float
public let defaultLoopCount: Int
public let preloadSounds: Bool
public let preloadStrategy: String // "all", "category", "none"
public let audioSessionCategory: String
public let audioSessionMode: String
public let audioSessionOptions: [String]
public init(defaultVolume: Float, defaultLoopCount: Int, preloadSounds: Bool, preloadStrategy: String, audioSessionCategory: String, audioSessionMode: String, audioSessionOptions: [String]) {
self.defaultVolume = defaultVolume
self.defaultLoopCount = defaultLoopCount
self.preloadSounds = preloadSounds
self.preloadStrategy = preloadStrategy
self.audioSessionCategory = audioSessionCategory
self.audioSessionMode = audioSessionMode
self.audioSessionOptions = audioSessionOptions
}
}
/// Service for loading and managing sound configuration
public class SoundConfigurationService {
public static let shared = SoundConfigurationService()
private var configuration: SoundConfiguration?
private init() {}
/// Load audio settings from SoundsSettings.json
private func loadAudioSettings(from bundle: Bundle = .main) -> AudioSettings {
guard let url = bundle.url(forResource: "SoundsSettings", withExtension: "json") else {
print("⚠️ Warning: SoundsSettings.json not found, using default settings")
return AudioSettings(
defaultVolume: 0.8,
defaultLoopCount: -1,
preloadSounds: true,
preloadStrategy: "category",
audioSessionCategory: "playback",
audioSessionMode: "default",
audioSessionOptions: ["mixWithOthers"]
)
}
do {
let data = try Data(contentsOf: url)
let settings = try JSONDecoder().decode(AudioSettings.self, from: data)
print("✅ Loaded audio settings from SoundsSettings.json")
return settings
} catch {
print("⚠️ Warning: Error loading audio settings, using defaults: \(error)")
return AudioSettings(
defaultVolume: 0.8,
defaultLoopCount: -1,
preloadSounds: true,
preloadStrategy: "category",
audioSessionCategory: "playback",
audioSessionMode: "default",
audioSessionOptions: ["mixWithOthers"]
)
}
}
/// Load sound configuration from multiple category-specific JSON files
public func loadConfigurationFromBundles(from bundle: Bundle = .main) -> SoundConfiguration {
// Include AlarmSounds bundle for alarm sound preview functionality
let bundleNames = ["Colored", "Nature", "Mechanical", "Ambient", "AlarmSounds"]
var allSounds: [Sound] = []
for bundleName in bundleNames {
guard let bundleURL = bundle.url(forResource: bundleName, withExtension: "bundle"),
let categoryBundle = Bundle(url: bundleURL),
let url = categoryBundle.url(forResource: "sounds", withExtension: "json") else {
print("⚠️ Warning: Could not find sounds.json in \(bundleName).bundle")
continue
}
do {
let data = try Data(contentsOf: url)
let soundsOnly = try JSONDecoder().decode(SoundsOnly.self, from: data)
allSounds.append(contentsOf: soundsOnly.sounds)
print("✅ Loaded \(soundsOnly.sounds.count) sounds from \(bundleName).bundle")
} catch {
print("⚠️ Warning: Error loading sounds from \(bundleName).bundle: \(error)")
}
}
// Load settings from separate file
let settings = loadAudioSettings(from: bundle)
let config = SoundConfiguration(sounds: allSounds, settings: settings)
self.configuration = config
print("✅ Loaded combined sound configuration with \(allSounds.count) sounds from \(bundleNames.count) bundles")
return config
}
/// Get current configuration
public func getConfiguration() -> SoundConfiguration {
if configuration == nil {
return loadConfigurationFromBundles()
}
return configuration!
}
/// Get all available sounds
public func getAvailableSounds() -> [Sound] {
return getConfiguration().sounds
}
/// Get sounds by category
public func getSoundsByCategory(_ categoryId: String) -> [Sound] {
return getConfiguration().sounds
.filter { $0.category == categoryId }
}
/// Get audio settings
public func getAudioSettings() -> AudioSettings {
return getConfiguration().settings
}
}