mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
added voice formulas for mixed text
This commit is contained in:
@@ -9,6 +9,7 @@ from PyQt5.QtWidgets import QCheckBox, QVBoxLayout, QDialog, QLabel, QDialogButt
|
||||
import soundfile as sf
|
||||
from utils import clean_text
|
||||
from constants import PROGRAM_NAME, LANGUAGE_DESCRIPTIONS, SAMPLE_VOICE_TEXTS
|
||||
from voice_formulas import get_new_voice
|
||||
|
||||
|
||||
def get_sample_voice_text(lang_code):
|
||||
@@ -346,9 +347,12 @@ class ConversionThread(QThread):
|
||||
|
||||
# Set split_pattern to \n+ which will split on one or more newlines
|
||||
split_pattern = r"\n+"
|
||||
|
||||
loaded_voice = get_new_voice(tts, self.voice, self.use_gpu)
|
||||
|
||||
for result in tts(
|
||||
chapter_text,
|
||||
voice=self.voice,
|
||||
voice=loaded_voice,
|
||||
speed=self.speed,
|
||||
split_pattern=split_pattern,
|
||||
):
|
||||
|
||||
+12
-2
@@ -567,6 +567,13 @@ class abogen(QWidget):
|
||||
'"a" => American English\n"b" => British English\n"e" => Spanish\n"f" => French\n"h" => Hindi\n"i" => Italian\n"j" => Japanese\n"p" => Brazilian Portuguese\n"z" => Mandarin Chinese\nThe second character represents the gender:\n"m" => Male\n"f" => Female'
|
||||
)
|
||||
voice_row.addWidget(self.voice_combo)
|
||||
|
||||
# voice formula - text box for now, add a dialog later
|
||||
self.voice_formula = QTextEdit(self)
|
||||
self.voice_formula.setAcceptRichText(False)
|
||||
self.voice_formula.setPlaceholderText("Enter voice formula here...")
|
||||
self.voice_formula.setText('0.242 * am_michael + 0.758 * bf_isabella')
|
||||
voice_row.addWidget(self.voice_formula)
|
||||
|
||||
# Play/Stop icons
|
||||
def make_icon(color, shape):
|
||||
@@ -1037,12 +1044,15 @@ class abogen(QWidget):
|
||||
if not self.subtitle_combo.isEnabled()
|
||||
else self.subtitle_mode
|
||||
)
|
||||
|
||||
|
||||
voice_formula = self.voice_formula.toPlainText()
|
||||
|
||||
self.conversion_thread = ConversionThread(
|
||||
self.selected_file,
|
||||
self.selected_lang,
|
||||
speed,
|
||||
self.selected_voice,
|
||||
#self.selected_voice,
|
||||
voice_formula,
|
||||
self.save_option,
|
||||
self.selected_output_folder,
|
||||
subtitle_mode=actual_subtitle_mode,
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
from constants import VOICES_INTERNAL
|
||||
|
||||
# Calls parsing and loads the voice to gpu or cpu
|
||||
def get_new_voice(pipeline, formula, use_gpu):
|
||||
try:
|
||||
weighted_voice = parse_voice_formula(pipeline, formula)
|
||||
device = "cuda" if use_gpu else "cpu"
|
||||
return weighted_voice.to(device)
|
||||
except Exception as e:
|
||||
raise ValueError(f"Failed to create voice: {str(e)}")
|
||||
|
||||
# Parse the formula and get the combined voice tensor
|
||||
def parse_voice_formula(pipeline, formula):
|
||||
"""Parse the voice formula string and return the combined voice tensor."""
|
||||
if not formula.strip():
|
||||
raise ValueError("Empty voice formula")
|
||||
|
||||
# Initialize the weighted sum
|
||||
weighted_sum = None
|
||||
|
||||
# Split the formula into terms
|
||||
terms = formula.split('+')
|
||||
|
||||
for term in terms:
|
||||
# Parse each term (format: "0.333 * voice_name")
|
||||
weight, voice_name = term.strip().split('*')
|
||||
weight = float(weight.strip())
|
||||
voice_name = voice_name.strip()
|
||||
|
||||
# Get the voice tensor
|
||||
# use VOICES_INTERNAL
|
||||
if voice_name not in VOICES_INTERNAL:
|
||||
raise ValueError(f"Unknown voice: {voice_name}")
|
||||
|
||||
voice_tensor = pipeline.load_single_voice(voice_name)
|
||||
|
||||
# Add to weighted sum
|
||||
if weighted_sum is None:
|
||||
weighted_sum = weight * voice_tensor
|
||||
else:
|
||||
weighted_sum += weight * voice_tensor
|
||||
|
||||
return weighted_sum
|
||||
Reference in New Issue
Block a user