From 83479f9840f230c0c0dbee083b75735090ae9322 Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Mon, 9 Mar 2026 20:30:56 -0500 Subject: initial 1.0 release --- DayByDay/SpeechSynthesizer.swift | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 DayByDay/SpeechSynthesizer.swift (limited to 'DayByDay/SpeechSynthesizer.swift') diff --git a/DayByDay/SpeechSynthesizer.swift b/DayByDay/SpeechSynthesizer.swift new file mode 100644 index 0000000..cac5caf --- /dev/null +++ b/DayByDay/SpeechSynthesizer.swift @@ -0,0 +1,52 @@ +// +// SpeechSynthesizer.swift +// DayByDay +// + +import AVFoundation + +/// Wrapper around AVSpeechSynthesizer that selects the highest-quality +/// en-US neural voice available on the device. Falls back gracefully if +/// premium/enhanced voices haven't been downloaded yet. +final class SpeechSynthesizer { + static let shared = SpeechSynthesizer() + + private let synthesizer = AVSpeechSynthesizer() + private let voice: AVSpeechSynthesisVoice? + + private init() { + self.voice = Self.bestAvailableVoice() + } + + func speak(_ text: String) { + if synthesizer.isSpeaking { + synthesizer.stopSpeaking(at: .immediate) + } + + let utterance = AVSpeechUtterance(string: text) + utterance.voice = voice + utterance.rate = 0.4 + utterance.pitchMultiplier = 1.0 + utterance.preUtteranceDelay = 0 + synthesizer.speak(utterance) + } + + /// Picks the best en-US voice on the device. + /// Preference order: premium > enhanced > default quality. + /// Skips novelty voices so the child always hears a natural-sounding voice. + private static func bestAvailableVoice() -> AVSpeechSynthesisVoice? { + let candidates = AVSpeechSynthesisVoice.speechVoices().filter { voice in + voice.language.hasPrefix("en-US") + && !voice.voiceTraits.contains(.isNoveltyVoice) + } + + if let premium = candidates.first(where: { $0.quality == .premium }) { + return premium + } + if let enhanced = candidates.first(where: { $0.quality == .enhanced }) { + return enhanced + } + // Fall back to the best default-quality voice. + return candidates.first ?? AVSpeechSynthesisVoice(language: "en-US") + } +} -- cgit v1.2.3