Просмотр исходного кода

fix: 角色绑定的音色未生效

BuildSpeechPlanUseCase 只把 role 用于推断性别,从未查询过角色(男主/女主/
男配/女配)自身绑定的音色,导致书内绑定角色音色后仍退化为按性别兜底。补上
roleSubject() 与对应查询,主音色链与 fallback 链统一为角色专属绑定 > 角色
绑定 > 性别兜底。只识别已知角色标识,避免任意角色文本参与查询。

MainNavigatorReadAloudTest 未跟随 MainRouteCloudTtsEngines 改为带 bookUrl
的 data class,整个测试源集编译失败,上述缺陷因此一直未被测试发现。
5151561 3 месяцев назад
Родитель
Сommit
e57725ef91

+ 18 - 4
app/src/main/java/io/legado/app/domain/usecase/BuildSpeechPlanUseCase.kt

@@ -37,6 +37,11 @@ class BuildSpeechPlanUseCase(
             ?: narrator
 
         return segments.map { segment ->
+            val performance = segment.characterId?.let(characterPerformances::get)
+            // 角色(男主/女主/男配/女配)绑定的音色,优先级低于角色专属绑定、高于性别兜底
+            val roleVoice = performance?.roleSubject()?.let { subject ->
+                bindings.voice(subject, subject, voicesById)
+            }
             val primary = if (!useMultiSpeaker) {
                 // Multi-speaker disabled — every segment uses the default voice
                 defaultVoice
@@ -48,7 +53,6 @@ class BuildSpeechPlanUseCase(
                         voicesById,
                     )
                 }
-                val performance = segment.characterId?.let(characterPerformances::get)
                 val genderFallback = when (performance?.resolvedGender()) {
                     "male" -> bindings.voice(
                         BookVoiceBinding.SUBJECT_UNKNOWN_MALE,
@@ -65,7 +69,7 @@ class BuildSpeechPlanUseCase(
                 }
                 when (segment.roleType) {
                     SpeechRoleType.Character,
-                    SpeechRoleType.Thought -> characterVoice ?: genderFallback
+                    SpeechRoleType.Thought -> characterVoice ?: roleVoice ?: genderFallback
                         ?: unknown ?: narrator ?: defaultVoice
                     SpeechRoleType.Unknown -> unknown ?: narrator ?: defaultVoice
                     SpeechRoleType.Narrator -> narrator ?: defaultVoice
@@ -76,7 +80,7 @@ class BuildSpeechPlanUseCase(
             } else {
                 buildList {
                     if (segment.roleType != SpeechRoleType.Narrator) {
-                        val performance = segment.characterId?.let(characterPerformances::get)
+                        add(roleVoice)
                         add(
                             when (performance?.resolvedGender()) {
                                 "male" -> bindings.voice(
@@ -104,7 +108,7 @@ class BuildSpeechPlanUseCase(
                 segment = segment,
                 voice = primary,
                 fallbackVoices = fallbackVoices,
-                characterPerformance = segment.characterId?.let(characterPerformances::get),
+                characterPerformance = performance,
             )
         }
     }
@@ -115,6 +119,16 @@ class BuildSpeechPlanUseCase(
         voicesById: Map<String, ReadAloudVoice>,
     ): ReadAloudVoice? = get(subjectType to subjectId)?.voiceId?.let(voicesById::get)
 
+    /** 只有已知的角色标识才对应绑定主体,避免任意角色文本参与查询 */
+    private fun CharacterPerformanceProfile.roleSubject(): String? = when (role) {
+        BookVoiceBinding.SUBJECT_MALE_LEAD,
+        BookVoiceBinding.SUBJECT_FEMALE_LEAD,
+        BookVoiceBinding.SUBJECT_MALE_SUPPORTING,
+        BookVoiceBinding.SUBJECT_FEMALE_SUPPORTING -> role
+
+        else -> null
+    }
+
     private fun CharacterPerformanceProfile.resolvedGender(): String? = when (voiceGender) {
         "male", "female" -> voiceGender
         else -> when (role) {

+ 6 - 4
app/src/test/java/io/legado/app/ui/main/MainNavigatorReadAloudTest.kt

@@ -9,22 +9,24 @@ class MainNavigatorReadAloudTest {
     @Test
     fun `opens cloud TTS manager on top of reader`() {
         val reader = MainRouteReadBook(bookUrl = "book")
+        val cloudTts = MainRouteCloudTtsEngines(bookUrl = "book")
         val backStack = mutableListOf<NavKey>(MainRouteHome, reader)
 
-        MainNavigator.navigateToRoute(backStack, MainRouteCloudTtsEngines)
+        MainNavigator.navigateToRoute(backStack, cloudTts)
 
-        assertEquals(listOf(MainRouteHome, reader, MainRouteCloudTtsEngines), backStack)
+        assertEquals(listOf(MainRouteHome, reader, cloudTts), backStack)
     }
 
     @Test
     fun `resets to home before cloud TTS manager from unrelated route`() {
+        val cloudTts = MainRouteCloudTtsEngines()
         val backStack = mutableListOf<NavKey>(
             MainRouteHome,
             MainRouteSettings,
         )
 
-        MainNavigator.navigateToRoute(backStack, MainRouteCloudTtsEngines)
+        MainNavigator.navigateToRoute(backStack, cloudTts)
 
-        assertEquals(listOf(MainRouteHome, MainRouteCloudTtsEngines), backStack)
+        assertEquals(listOf(MainRouteHome, cloudTts), backStack)
     }
 }