From b47b1ca49a0a48965f7ce9e92ef13f5b7f84892d Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 7 Nov 2022 18:43:59 +0800 Subject: [PATCH] Add samples for voices --- .../TextToSpeechVoiceSelectionView.swift | 57 ++++++++++++------- .../AudioSession/AudioController.swift | 26 ++++++++- .../Services/AudioSession/Voices.swift | 2 +- 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/TextToSpeechVoiceSelectionView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/TextToSpeechVoiceSelectionView.swift index deddc99dd..8c0acf295 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/TextToSpeechVoiceSelectionView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/TextToSpeechVoiceSelectionView.swift @@ -9,6 +9,8 @@ let language: VoiceLanguage let showLanguageChanger: Bool + @State var playbackSample: String? = nil + init(forLanguage: VoiceLanguage, showLanguageChanger: Bool) { self.language = forLanguage self.showLanguageChanger = showLanguageChanger @@ -45,27 +47,7 @@ ForEach(language.categories, id: \.self) { category in Section(category.rawValue) { ForEach(audioController.voiceList?.filter { $0.category == category } ?? [], id: \.key.self) { voice in - HStack { - Button(action: { - audioController.setPreferredVoice(voice.key, forLanguage: language.key) - audioController.currentVoice = voice.key - }, label: { - HStack { - Text(voice.name) - Spacer() - - if voice.selected { - if audioController.isPlaying, audioController.isLoading { - ProgressView() - } else { - Image(systemName: "checkmark") - } - } - } - .contentShape(Rectangle()) - }) - .buttonStyle(PlainButtonStyle()) - } + voiceRow(for: voice) } } } @@ -83,6 +65,39 @@ func voiceRow(for voice: VoiceItem) -> some View { HStack { + Button(action: { + if audioController.isPlayingSample(voice: voice.key) { + playbackSample = nil + audioController.stopVoiceSample() + } else { + playbackSample = voice.key + audioController.playVoiceSample(voice: voice.key) + Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { timer in + let playing = audioController.isPlayingSample(voice: voice.key) + if playing { + playbackSample = voice.key + } else if !playing { + // If the playback sample is something else, its taken ownership + // of the value so we just ignore it and shut down our timer. + if playbackSample == voice.key { + playbackSample = nil + } + timer.invalidate() + } + } + } + }, label: { + if playbackSample == voice.key { + Image(systemName: "stop.circle") + .font(.appTitleTwo) + .padding(.trailing, 16) + } else { + Image(systemName: "play.circle") + .font(.appTitleTwo) + .padding(.trailing, 16) + } + }) + Button(action: { audioController.setPreferredVoice(voice.key, forLanguage: language.key) audioController.currentVoice = voice.key diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift index ec0726c62..c8f994403 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift @@ -246,6 +246,8 @@ var durations: [Double]? var lastReadUpdate = 0.0 + var samplePlayer: AVAudioPlayer? + public init(dataService: DataService) { self.dataService = dataService @@ -631,9 +633,13 @@ public func playVoiceSample(voice: String) { do { - if let url = Bundle.main.url(forResource: "tts-voice-sample-\(voice)", withExtension: "mp3") { - let player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue) - player.play() + pause() + + if let url = Bundle(url: UtilsPackage.bundleURL)?.url(forResource: voice, withExtension: "mp3") { + samplePlayer = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue) + if !(samplePlayer?.play() ?? false) { + throw BasicError.message(messageText: "Unable to playback audio") + } } else { NSNotification.operationFailed(message: "Error playing voice sample.") } @@ -643,6 +649,20 @@ } } + public func isPlayingSample(voice: String) -> Bool { + if let samplePlayer = self.samplePlayer, let url = Bundle(url: UtilsPackage.bundleURL)?.url(forResource: voice, withExtension: "mp3") { + return samplePlayer.url == url && samplePlayer.isPlaying + } + return false + } + + public func stopVoiceSample() { + if let samplePlayer = self.samplePlayer { + samplePlayer.stop() + self.samplePlayer = nil + } + } + private func updateDurations(oldPlayback: Double, newPlayback: Double) { if let oldDurations = durations { durations = oldDurations.map { $0 * oldPlayback / newPlayback } diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/Voices.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/Voices.swift index a314d2b88..dc892573d 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/Voices.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/Voices.swift @@ -87,7 +87,7 @@ public enum Voices { ] public static let UltraPairs = [ - VoicePair(firstKey: "Larry", secondKey: "susan", firstName: "Larry", secondName: "Susan", language: "en-US", category: .enUS), + VoicePair(firstKey: "Larry", secondKey: "Susan", firstName: "Larry", secondName: "Susan", language: "en-US", category: .enUS), VoicePair(firstKey: "Jordan", secondKey: "William", firstName: "Jordan", secondName: "William", language: "en-US", category: .enUS), VoicePair(firstKey: "Adrian", secondKey: "Anthony", firstName: "Adrian", secondName: "Anthony", language: "en-US", category: .enUS),