feat(audio): Load sounds from JSON

This commit is contained in:
Cody Bromley
2025-01-10 21:04:10 -06:00
parent 75ab2b644c
commit e81cdec556
6 changed files with 145 additions and 94 deletions
+39 -16
View File
@@ -85,23 +85,46 @@ class AudioManager: ObservableObject {
}
}
private func loadSounds() {
sounds = [
Sound(title: "Rain", systemIconName: "cloud.rain", fileName: "rain"),
Sound(title: "Storm", systemIconName: "cloud.bolt.rain", fileName: "storm"),
Sound(title: "Wind", systemIconName: "wind", fileName: "wind"),
Sound(title: "Waves", systemIconName: "water.waves", fileName: "waves"),
Sound(title: "Stream", systemIconName: "humidity", fileName: "stream"),
Sound(title: "Birds", systemIconName: "bird", fileName: "birds"),
Sound(title: "Summer Night", systemIconName: "moon.stars.fill", fileName: "summer-night"),
Sound(title: "Train", systemIconName: "tram.fill", fileName: "train"),
Sound(title: "Boat", systemIconName: "sailboat.fill", fileName: "boat"),
Sound(title: "City", systemIconName: "building.2", fileName: "city"),
Sound(title: "Coffee Shop", systemIconName: "cup.and.saucer.fill", fileName: "coffee-shop"),
Sound(title: "Fireplace", systemIconName: "fireplace", fileName: "fireplace"),
Sound(title: "Pink Noise", systemIconName: "waveform.path", fileName: "pink-noise"),
Sound(title: "White Noise", systemIconName: "waveform", fileName: "white-noise"),
]
print("🎵 AudioManager: Loading sounds from JSON")
let bundlePath = Bundle.main.bundlePath
print("📦 Bundle path: \(bundlePath)")
if let resourcePath = Bundle.main.resourcePath {
print("📂 Resource path: \(resourcePath)")
do {
let resources = try FileManager.default.contentsOfDirectory(atPath: resourcePath)
print("📑 Resources in bundle: \(resources)")
} catch {
print("❌ Error listing resources: \(error)")
}
}
guard let url = Bundle.main.url(forResource: "sounds", withExtension: "json") else {
print("❌ AudioManager: sounds.json file not found in Resources folder")
ErrorReporter.shared.report(AudioError.fileNotFound)
return
}
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let soundsContainer = try decoder.decode(SoundsContainer.self, from: data)
self.sounds = soundsContainer.sounds
.sorted(by: { $0.defaultOrder < $1.defaultOrder })
.map { soundData in
Sound(
title: soundData.title,
systemIconName: soundData.systemIconName,
fileName: soundData.fileName
)
}
} catch {
print("❌ AudioManager: Failed to parse sounds.json: \(error)")
ErrorReporter.shared.report(error)
}
}
private func setupMediaControls() {
print("🎵 AudioManager: Setting up media controls")
// Remove all previous handlers
+45
View File
@@ -0,0 +1,45 @@
//
// SoundCreditsManager.swift
// Blankie
//
// Created by Cody Bromley on 1/10/25.
//
import SwiftUI
class SoundCreditsManager: ObservableObject {
static let shared = SoundCreditsManager()
@Published private(set) var credits: [SoundCredit] = []
@Published private(set) var loadError: Error?
private init() {
loadCredits()
}
private func loadCredits() {
guard let url = Bundle.main.url(forResource: "sounds", withExtension: "json") else {
print("Error: sounds.json not found in bundle")
return
}
do {
let data = try Data(contentsOf: url)
let container = try JSONDecoder().decode(SoundsContainer.self, from: data)
DispatchQueue.main.async {
self.credits = container.sounds.map { sound in
SoundCredit(
name: sound.title,
author: sound.author,
license: License(rawValue: sound.license.lowercased()) ?? .cc0,
editor: sound.editor,
soundUrl: URL(string: sound.soundUrl)
)
}
}
} catch {
print("Error loading sounds.json: \(error)")
loadError = error
}
}
}
+36
View File
@@ -0,0 +1,36 @@
//
// License.swift
// Blankie
//
// Created by Cody Bromley on 1/10/25.
//
import Foundation
enum License: String {
case cc0 = "cc0"
case ccBy = "ccby"
case ccBySa = "ccbysa"
case ccBy3 = "ccby3"
case publicDomain = "publicdomain"
var linkText: String {
switch self {
case .cc0: return "CC0"
case .ccBy: return "CC BY"
case .ccBySa: return "CC BY-SA"
case .ccBy3: return "CC BY 3.0"
case .publicDomain: return "Public Domain"
}
}
var url: URL? {
switch self {
case .cc0: return URL(string: "https://creativecommons.org/publicdomain/zero/1.0/")
case .ccBy: return URL(string: "https://creativecommons.org/licenses/by/4.0/")
case .ccBySa: return URL(string: "https://creativecommons.org/licenses/by-sa/4.0/")
case .ccBy3: return URL(string: "https://creativecommons.org/licenses/by/3.0/")
case .publicDomain: return URL(string: "https://wiki.creativecommons.org/wiki/Public_domain")
}
}
}
-77
View File
@@ -20,80 +20,3 @@ struct SoundCredit {
return text
}
}
enum License {
case cc0
case ccBy
case ccBySa
case ccBy3
case publicDomain
var linkText: String {
switch self {
case .cc0: return "CC0"
case .ccBy: return "CC BY"
case .ccBySa: return "CC BY-SA"
case .ccBy3: return "CC BY 3.0"
case .publicDomain: return "Public Domain"
}
}
var url: URL? {
switch self {
case .cc0: return URL(string: "https://creativecommons.org/publicdomain/zero/1.0/")
case .ccBy: return URL(string: "https://creativecommons.org/licenses/by/4.0/")
case .ccBySa: return URL(string: "https://creativecommons.org/licenses/by-sa/4.0/")
case .ccBy3: return URL(string: "https://creativecommons.org/licenses/by/3.0/")
case .publicDomain: return URL(string: "https://wiki.creativecommons.org/wiki/Public_domain")
}
}
}
// Sound credits data
let soundCredits: [SoundCredit] = [
SoundCredit(
name: "Birds", author: "kvgarlic", license: .cc0, editor: "Porrumentzio",
soundUrl: URL(string: "https://freesound.org/people/kvgarlic/sounds/156826/")),
SoundCredit(
name: "Boat", author: "Falcet", license: .cc0, editor: "Porrumentzio",
soundUrl: URL(string: "https://freesound.org/people/Falcet/sounds/439365/")),
SoundCredit(
name: "City", author: "gezortenplotz", license: .ccBy, editor: "Porrumentzio",
soundUrl: URL(string: "https://freesound.org/people/gezortenplotz/sounds/44796/")),
SoundCredit(
name: "Coffee Shop", author: "stephan", license: .publicDomain, editor: nil,
soundUrl: URL(string: "https://soundbible.com/1664-Restaurant-Ambiance.html")),
SoundCredit(
name: "Fireplace", author: "ezwa", license: .publicDomain, editor: nil,
soundUrl: URL(string: "https://soundbible.com/1543-Fireplace.html")),
SoundCredit(
name: "Pink noise", author: "Omegatron", license: .ccBySa, editor: nil,
soundUrl: URL(string: "https://es.wikipedia.org/wiki/Archivo:Pink_noise.ogg")),
SoundCredit(
name: "Rain", author: "alex36917", license: .ccBy, editor: "Porrumentzio",
soundUrl: URL(string: "https://freesound.org/people/alex36917/sounds/524605/")),
SoundCredit(
name: "Summer night", author: "Lisa Redfern", license: .publicDomain, editor: nil,
soundUrl: URL(string: "https://soundbible.com/2083-Crickets-Chirping-At-Night.html")),
SoundCredit(
name: "Storm", author: "Digifish music", license: .ccBy, editor: "Porrumentzio",
soundUrl: URL(string: "https://freesound.org/people/digifishmusic/sounds/41739/")),
SoundCredit(
name: "Stream", author: "gluckose", license: .cc0, editor: nil,
soundUrl: URL(string: "https://freesound.org/people/gluckose/sounds/333987/")),
SoundCredit(
name: "Train", author: "SDLx", license: .ccBy3, editor: nil,
soundUrl: URL(string: "https://freesound.org/people/SDLx/sounds/259988/")),
SoundCredit(
name: "Waves", author: "Luftrum", license: .ccBy, editor: "Porrumentzio",
soundUrl: URL(string: "https://freesound.org/people/Luftrum/sounds/48412/")),
SoundCredit(
name: "White noise", author: "Jorge Stolfi", license: .ccBySa, editor: nil,
soundUrl: URL(
string:
"https://commons.wikimedia.org/w/index.php?title=File%3AWhite-noise-sound-20sec-mono-44100Hz.ogg"
)),
SoundCredit(
name: "Wind", author: "felix.blume", license: .cc0, editor: "Porrumentzio",
soundUrl: URL(string: "https://freesound.org/people/felix.blume/sounds/217506/")),
]
+23
View File
@@ -0,0 +1,23 @@
//
// SoundData.swift
// Blankie
//
// Created by Cody Bromley on 1/10/25.
//
struct SoundData: Codable {
let defaultOrder: Int
let title: String
let systemIconName: String
let fileName: String
let author: String
let authorUrl: String?
let license: String
let editor: String?
let editorUrl: String?
let soundUrl: String
}
struct SoundsContainer: Codable {
let sounds: [SoundData]
}
+2 -1
View File
@@ -8,6 +8,7 @@
import SwiftUI
struct AboutView: View {
@ObservedObject private var creditsManager = SoundCreditsManager.shared
@Environment(\.dismiss) private var dismiss
private let appVersion =
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0"
@@ -129,7 +130,7 @@ struct AboutView: View {
.font(.system(size: 13, weight: .bold))
VStack(alignment: .leading, spacing: 4) {
ForEach(soundCredits, id: \.name) { credit in
ForEach(creditsManager.credits, id: \.name) { credit in
CreditRow(credit: credit)
}
}