fix: update TTS voice mapping to only use valid Edge TTS voices

Previously the voice map referenced 10+ non-existent Edge TTS voice names
(xiaohan, xiaochen, xiaoshuang, etc.), causing NoAudioReceived errors and
fallback to browser default TTS. Now only maps to the 6 actually available
Chinese voices (xiaoxiao, xiaoyi, yunxi, yunyang, yunjian, yunxia).
Added voice speed adjustments and fixed --rate argument format for Windows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 17:06:51 +08:00
parent fbf8dbbe86
commit 3483c6b3be
7 changed files with 307 additions and 138 deletions

View File

@@ -15,6 +15,7 @@ import com.tiangong.aiagent.domain.model.Message
import com.tiangong.aiagent.domain.model.SseEvent
import com.tiangong.aiagent.util.AudioPlayer
import com.tiangong.aiagent.util.AudioRecorder
import com.tiangong.aiagent.util.MarkdownRenderer
import com.tiangong.aiagent.util.OfflineQueueManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
@@ -728,8 +729,10 @@ class ChatViewModel @Inject constructor(
fun speakText(text: String) {
if (text.isBlank()) return
val plainText = MarkdownRenderer.stripMarkdownForTTS(text)
if (plainText.isBlank()) return
viewModelScope.launch {
chatRepository.synthesizeSpeech(text)
chatRepository.synthesizeSpeech(plainText)
.onSuccess { audioUrl -> audioPlayer.play(audioUrl) }
.onFailure { Log.w("ChatViewModel", "TTS failed", it) }
}

View File

@@ -28,6 +28,29 @@ import io.noties.markwon.linkify.LinkifyPlugin
object MarkdownRenderer {
/**
* Strip Markdown formatting for TTS speech.
* Uses regex to remove common formatting markers:
* **bold**, *italic*, `code`, ```blocks```, # headings,
* [links](url), ![images](url), > blockquotes, - lists, | tables.
*/
fun stripMarkdownForTTS(text: String): String {
if (text.isBlank()) return ""
return text
.replace(Regex("```[\\s\\S]*?```"), "") // fenced code blocks
.replace(Regex("`([^`]*)`"), "$1") // inline code
.replace(Regex("!\\[.*?]\\(.*?\\)"), "") // images
.replace(Regex("\\[([^]]*)]\\(.*?\\)"), "$1") // links -> text
.replace(Regex("(?m)^#{1,6}\\s+"), "") // heading markers
.replace(Regex("(\\*{1,3}|_{1,3})(.*?)\\1"), "$2") // bold/italic
.replace(Regex("(?m)^[*-]\\s+"), "") // list bullets
.replace(Regex("(?m)^>\\s*"), "") // blockquote
.replace(Regex("(?m)^---+$"), "") // horizontal rules
.replace("|", " ") // table pipes
.replace(Regex("\\n{3,}"), "\n\n") // collapse excessive newlines
.trim()
}
private var markwonInstance: Markwon? = null
private fun getMarkwon(context: Context): Markwon {