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
|
import soundfile as sf
|
||||||
from utils import clean_text
|
from utils import clean_text
|
||||||
from constants import PROGRAM_NAME, LANGUAGE_DESCRIPTIONS, SAMPLE_VOICE_TEXTS
|
from constants import PROGRAM_NAME, LANGUAGE_DESCRIPTIONS, SAMPLE_VOICE_TEXTS
|
||||||
|
from voice_formulas import get_new_voice
|
||||||
|
|
||||||
|
|
||||||
def get_sample_voice_text(lang_code):
|
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
|
# Set split_pattern to \n+ which will split on one or more newlines
|
||||||
split_pattern = r"\n+"
|
split_pattern = r"\n+"
|
||||||
|
|
||||||
|
loaded_voice = get_new_voice(tts, self.voice, self.use_gpu)
|
||||||
|
|
||||||
for result in tts(
|
for result in tts(
|
||||||
chapter_text,
|
chapter_text,
|
||||||
voice=self.voice,
|
voice=loaded_voice,
|
||||||
speed=self.speed,
|
speed=self.speed,
|
||||||
split_pattern=split_pattern,
|
split_pattern=split_pattern,
|
||||||
):
|
):
|
||||||
|
|||||||
+11
-1
@@ -568,6 +568,13 @@ class abogen(QWidget):
|
|||||||
)
|
)
|
||||||
voice_row.addWidget(self.voice_combo)
|
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
|
# Play/Stop icons
|
||||||
def make_icon(color, shape):
|
def make_icon(color, shape):
|
||||||
pix = QPixmap(20, 20)
|
pix = QPixmap(20, 20)
|
||||||
@@ -1038,11 +1045,14 @@ class abogen(QWidget):
|
|||||||
else self.subtitle_mode
|
else self.subtitle_mode
|
||||||
)
|
)
|
||||||
|
|
||||||
|
voice_formula = self.voice_formula.toPlainText()
|
||||||
|
|
||||||
self.conversion_thread = ConversionThread(
|
self.conversion_thread = ConversionThread(
|
||||||
self.selected_file,
|
self.selected_file,
|
||||||
self.selected_lang,
|
self.selected_lang,
|
||||||
speed,
|
speed,
|
||||||
self.selected_voice,
|
#self.selected_voice,
|
||||||
|
voice_formula,
|
||||||
self.save_option,
|
self.save_option,
|
||||||
self.selected_output_folder,
|
self.selected_output_folder,
|
||||||
subtitle_mode=actual_subtitle_mode,
|
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