From 9f61c6e79368085cd512ff61570b75926c126fe6 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Sun, 11 Jan 2026 13:14:05 +0300 Subject: Fix (tts): interrupt --- extra/tts.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'extra') diff --git a/extra/tts.go b/extra/tts.go index 96c8859..3103757 100644 --- a/extra/tts.go +++ b/extra/tts.go @@ -30,6 +30,7 @@ var ( TTSFlushChan = make(chan bool, 1) TTSDoneChan = make(chan bool, 1) // endsWithPunctuation = regexp.MustCompile(`[;.!?]$`) + threeOrMoreDashesRE = regexp.MustCompile(`-{3,}`) ) // cleanText removes markdown and special characters that are not suitable for TTS @@ -43,20 +44,16 @@ func cleanText(text string) string { text = strings.ReplaceAll(text, "[", "") // Link brackets text = strings.ReplaceAll(text, "]", "") // Link brackets text = strings.ReplaceAll(text, "!", "") // Exclamation marks (if not punctuation) - // Remove HTML tags using regex htmlTagRegex := regexp.MustCompile(`<[^>]*>`) text = htmlTagRegex.ReplaceAllString(text, "") - // Split text into lines to handle table separators lines := strings.Split(text, "\n") var filteredLines []string - for _, line := range lines { // Check if the line looks like a table separator (e.g., |----|, |===|, | - - - |) // A table separator typically contains only |, -, =, and spaces isTableSeparator := regexp.MustCompile(`^\s*\|\s*[-=\s]+\|\s*$`).MatchString(strings.TrimSpace(line)) - if !isTableSeparator { // If it's not a table separator, remove vertical bars but keep the content processedLine := strings.ReplaceAll(line, "|", "") @@ -64,8 +61,8 @@ func cleanText(text string) string { } // If it is a table separator, skip it (don't add to filteredLines) } - text = strings.Join(filteredLines, "\n") + text = threeOrMoreDashesRE.ReplaceAllString(text, "") text = strings.TrimSpace(text) // Remove leading/trailing whitespace return text } @@ -89,6 +86,7 @@ type KokoroOrator struct { currentStream *beep.Ctrl // Added for playback control currentDone chan bool textBuffer strings.Builder + interrupt bool // textBuffer bytes.Buffer } @@ -99,6 +97,7 @@ type GoogleTranslateOrator struct { currentStream *beep.Ctrl currentDone chan bool textBuffer strings.Builder + interrupt bool } func (o *KokoroOrator) stoproutine() { @@ -110,7 +109,9 @@ func (o *KokoroOrator) stoproutine() { for len(TTSTextChan) > 0 { <-TTSTextChan } + o.textBuffer.Reset() o.currentDone <- true + o.interrupt = true } } @@ -121,6 +122,7 @@ func (o *KokoroOrator) readroutine() { for { select { case chunk := <-TTSTextChan: + o.interrupt = false // sentenceBuf.WriteString(chunk) // text := sentenceBuf.String() _, err := o.textBuffer.WriteString(chunk) @@ -175,6 +177,9 @@ func (o *KokoroOrator) readroutine() { o.logger.Debug("calling Speak with remainder", "rem", remaining) sentencesRem := tokenizer.Tokenize(remaining) for _, rs := range sentencesRem { // to avoid dumping large volume of text + if o.interrupt { + break + } if err := o.Speak(rs.Text); err != nil { o.logger.Error("tts failed", "sentence", rs, "error", err) } @@ -307,11 +312,13 @@ func (o *GoogleTranslateOrator) stoproutine() { <-TTSDoneChan o.logger.Debug("orator got done signal") o.Stop() - o.currentDone <- true // drain the channel for len(TTSTextChan) > 0 { <-TTSTextChan } + o.textBuffer.Reset() + o.currentDone <- true + o.interrupt = true } } @@ -320,6 +327,7 @@ func (o *GoogleTranslateOrator) readroutine() { for { select { case chunk := <-TTSTextChan: + o.interrupt = false _, err := o.textBuffer.WriteString(chunk) if err != nil { o.logger.Warn("failed to write to stringbuilder", "error", err) @@ -371,6 +379,9 @@ func (o *GoogleTranslateOrator) readroutine() { o.logger.Debug("calling Speak with remainder", "rem", remaining) sentencesRem := tokenizer.Tokenize(remaining) for _, rs := range sentencesRem { // to avoid dumping large volume of text + if o.interrupt { + break + } if err := o.Speak(rs.Text); err != nil { o.logger.Error("tts failed", "sentence", rs.Text, "error", err) } -- cgit v1.2.3