100 lines
2.3 KiB
Go
100 lines
2.3 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package app
|
|
|
|
import (
|
|
"strings"
|
|
"unicode"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/config"
|
|
)
|
|
|
|
// Apply pronunciation choices to the final text handed to the speech backend.
|
|
// Python Cthulhu splits lower-to-upper case boundaries and numeric words.
|
|
func readingSpeechText(text string, settings config.ReadingSettings) string {
|
|
if settings.SpeakMultiCaseStringsAsWords {
|
|
var result strings.Builder
|
|
var previous rune
|
|
for _, current := range text {
|
|
if unicode.IsLower(previous) && unicode.IsUpper(current) {
|
|
result.WriteByte(' ')
|
|
}
|
|
result.WriteRune(current)
|
|
previous = current
|
|
}
|
|
text = result.String()
|
|
}
|
|
if !settings.SpeakNumbersAsDigits {
|
|
return text
|
|
}
|
|
var result strings.Builder
|
|
var word []rune
|
|
flush := func() {
|
|
if len(word) == 0 {
|
|
return
|
|
}
|
|
allNumbers := true
|
|
for _, value := range word {
|
|
if !unicode.IsNumber(value) {
|
|
allNumbers = false
|
|
break
|
|
}
|
|
}
|
|
for index, value := range word {
|
|
if allNumbers && index != 0 {
|
|
result.WriteByte(' ')
|
|
}
|
|
result.WriteRune(value)
|
|
}
|
|
word = word[:0]
|
|
}
|
|
for _, value := range text {
|
|
if unicode.IsLetter(value) || unicode.IsNumber(value) || value == '_' {
|
|
word = append(word, value)
|
|
} else {
|
|
flush()
|
|
result.WriteRune(value)
|
|
}
|
|
}
|
|
flush()
|
|
return result.String()
|
|
}
|
|
|
|
// Replace complete words once, without feeding replacements back into the
|
|
// dictionary. This keeps punctuation and spacing from the accessible text.
|
|
func pronunciationSpeechText(text string, entries []config.PronunciationEntry) string {
|
|
if len(entries) == 0 {
|
|
return text
|
|
}
|
|
dictionary := make(map[string]string, len(entries))
|
|
for _, entry := range entries {
|
|
dictionary[strings.ToLower(entry.Word)] = entry.Replacement
|
|
}
|
|
var output strings.Builder
|
|
var word []rune
|
|
flush := func() {
|
|
if len(word) == 0 {
|
|
return
|
|
}
|
|
original := string(word)
|
|
if replacement, ok := dictionary[strings.ToLower(original)]; ok {
|
|
output.WriteString(replacement)
|
|
} else {
|
|
output.WriteString(original)
|
|
}
|
|
word = word[:0]
|
|
}
|
|
for _, current := range text {
|
|
if unicode.IsLetter(current) || unicode.IsNumber(current) || current == '_' {
|
|
word = append(word, current)
|
|
} else {
|
|
flush()
|
|
output.WriteRune(current)
|
|
}
|
|
}
|
|
flush()
|
|
return output.String()
|
|
}
|