Skip to content

Commit 6d85d81

Browse files
committed
Change the TTS voice
1 parent 6fdc0a5 commit 6d85d81

File tree

8 files changed

+43
-12
lines changed

8 files changed

+43
-12
lines changed

Sources/Navigator/TTS/AVTTSEngine.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,19 @@ public class AVTTSEngine: NSObject, TTSEngine, AVSpeechSynthesizerDelegate, Logg
7373
avUtterance.pitchMultiplier = Float(avPitchRange.valueForPercentage(config.pitch))
7474
avUtterance.preUtteranceDelay = utterance.delay
7575
avUtterance.postUtteranceDelay = config.delay
76-
avUtterance.voice = AVSpeechSynthesisVoice(language: utterance.language ?? config.defaultLanguage)
76+
avUtterance.voice = voice(for: utterance)
7777
return avUtterance
7878
}
7979

80+
private func voice(for utterance: TTSUtterance) -> AVSpeechSynthesisVoice? {
81+
let language = utterance.language ?? config.defaultLanguage
82+
if let voice = config.voice, voice.language == language {
83+
return AVSpeechSynthesisVoice(identifier: voice.identifier)
84+
} else {
85+
return AVSpeechSynthesisVoice(language: language)
86+
}
87+
}
88+
8089
public func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
8190
guard let utterance = (utterance as? AVUtterance)?.utterance else {
8291
return

Sources/Navigator/TTS/TTSController.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ public class TTSController: Loggable, TTSEngineDelegate {
7474
)
7575
}
7676

77-
if let language = publication.metadata.languages.first {
78-
engine.config.defaultLanguage = Language(code: .bcp47(language))
77+
if let language = publication.metadata.language {
78+
engine.config.defaultLanguage = language
7979
}
8080
engine.delegate = self
8181
}
@@ -260,7 +260,7 @@ public class TTSController: Loggable, TTSEngineDelegate {
260260
return TTSUtterance(
261261
text: text,
262262
locator: locator,
263-
language: language,
263+
language: language.takeIf { $0 != publication.metadata.language },
264264
delay: delay
265265
)
266266
}

Sources/Navigator/TTS/TTSEngine.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright 2021 Readium Foundation. All rights reserved.
2+
// Copyright 2022 Readium Foundation. All rights reserved.
33
// Use of this source code is governed by the BSD-style license
44
// available in the top-level LICENSE file of the project.
55
//
@@ -23,7 +23,9 @@ public protocol TTSEngineDelegate: AnyObject {
2323
}
2424

2525
public struct TTSConfiguration {
26-
public var defaultLanguage: Language
26+
public var defaultLanguage: Language {
27+
didSet { voice = nil }
28+
}
2729
public var rate: Double
2830
public var pitch: Double
2931
public var voice: TTSVoice?
@@ -44,12 +46,12 @@ public struct TTSConfiguration {
4446
}
4547
}
4648

47-
public struct TTSVoice {
48-
public enum Gender {
49+
public struct TTSVoice: Hashable {
50+
public enum Gender: Hashable {
4951
case female, male, unspecified
5052
}
5153

52-
public enum Quality {
54+
public enum Quality: Hashable {
5355
case low, medium, high
5456
}
5557

Sources/Shared/Publication/Metadata.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ public struct Metadata: Hashable, Loggable, WarningLogger {
3030

3131
public let modified: Date?
3232
public let published: Date?
33-
// FIXME: Use `Language` instead of raw String
3433
public let languages: [String] // BCP 47 tag
34+
// Main language of the publication.
35+
public let language: Language?
3536
public let sortAs: String?
3637
public let subjects: [Subject]
3738
public let authors: [Contributor]
@@ -104,6 +105,7 @@ public struct Metadata: Hashable, Loggable, WarningLogger {
104105
self.modified = modified
105106
self.published = published
106107
self.languages = languages
108+
self.language = languages.first.map { Language(code: .bcp47($0)) }
107109
self.sortAs = sortAs
108110
self.subjects = subjects
109111
self.authors = authors
@@ -152,6 +154,7 @@ public struct Metadata: Hashable, Loggable, WarningLogger {
152154
self.modified = parseDate(json.pop("modified"))
153155
self.published = parseDate(json.pop("published"))
154156
self.languages = parseArray(json.pop("language"), allowingSingle: true)
157+
self.language = languages.first.map { Language(code: .bcp47($0)) }
155158
self.sortAs = json.pop("sortAs") as? String
156159
self.subjects = [Subject](json: json.pop("subject"), warnings: warnings)
157160
self.authors = [Contributor](json: json.pop("author"), warnings: warnings, normalizeHREF: normalizeHREF)

Sources/Shared/Publication/Services/Search/StringSearchService.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class _StringSearchService: _SearchService {
2525
return { context in
2626
_StringSearchService(
2727
publication: context.publication,
28-
language: context.manifest.metadata.languages.first.map { Language(code: .bcp47($0)) },
28+
language: context.manifest.metadata.language,
2929
snippetLength: snippetLength,
3030
searchAlgorithm: searchAlgorithm,
3131
extractorFactory: extractorFactory

Sources/Shared/Toolkit/Language.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,10 @@ public struct Language: Hashable {
3939
public init(locale: Locale) {
4040
self.init(code: .bcp47(locale.identifier))
4141
}
42+
}
43+
44+
extension Language: CustomStringConvertible {
45+
public var description: String {
46+
code.bcp47
47+
}
4248
}

TestApp/Sources/Reader/Common/TTS/TTSView.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright 2021 Readium Foundation. All rights reserved.
2+
// Copyright 2022 Readium Foundation. All rights reserved.
33
// Use of this source code is governed by the BSD-style license
44
// available in the top-level LICENSE file of the project.
55
//
@@ -89,6 +89,13 @@ struct TTSSettings: View {
8989
choices: viewModel.availableLanguages,
9090
choiceLabel: { $0.localizedName }
9191
)
92+
93+
ConfigPicker(
94+
caption: "Voice",
95+
for: \.voice,
96+
choices: viewModel.availableVoices(for: viewModel.config.defaultLanguage),
97+
choiceLabel: { $0?.name ?? "Default" }
98+
)
9299
}
93100
}
94101
.listStyle(.insetGrouped)

TestApp/Sources/Reader/Common/TTS/TTSViewModel.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ final class TTSViewModel: ObservableObject, Loggable {
5858

5959
var defaultConfig: TTSConfiguration { tts.defaultConfig }
6060

61+
func availableVoices(for language: Language) -> [TTSVoice?] {
62+
[nil] + tts.availableVoices.filter { $0.language == language }
63+
}
64+
6165
lazy var availableLanguages: [Language] =
6266
tts.availableVoices
6367
.map { $0.language }

0 commit comments

Comments
 (0)