From 67b35d1dfb19ecab6285ec65f928d943aecdca57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deniz=20=C5=9Eafak?= Date: Sun, 4 May 2025 15:10:08 +0300 Subject: [PATCH] Add preview button to voice mixer --- CHANGELOG.md | 3 ++- abogen/gui.py | 13 ++++++----- abogen/voice_formula_gui.py | 43 ++++++++++++++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55b641e..cb51620 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ - Added `Insert chapter marker` button in text editor to insert chapter markers at the current cursor position. +- Added `Preview` button in voice mixer to preview the voice mix with the selected settings. - Fixed `f-string: unmatched '['` error in Voice preview, mentioned in #14 -- Fixed the content before first chapter not being included in the output (added handling for initial content). +- Fixed the issue with the content before first chapter not being included in the output. - Fixed m4b chapter generation opens CMD window in Windows. \ No newline at end of file diff --git a/abogen/gui.py b/abogen/gui.py index 544def1..f66d66e 100644 --- a/abogen/gui.py +++ b/abogen/gui.py @@ -290,8 +290,8 @@ class InputBox(QLabel): ) win.selected_book_path = file_path win.open_book_file( - file_path - ) # This will handle the dialog and setting file info + file_path # This will handle the dialog and setting file info + ) event.acceptProposedAction() else: self.set_error("Please drop a .txt, .epub, or .pdf file.") @@ -328,7 +328,7 @@ class TextboxDialog(QDialog): self.setWindowFlags( Qt.Window | Qt.WindowCloseButtonHint | Qt.WindowMaximizeButtonHint ) - self.resize(600, 400) + self.resize(700, 500) layout = QVBoxLayout(self) @@ -452,7 +452,7 @@ class TextboxDialog(QDialog): def insert_chapter_marker(self): # Insert a fixed chapter marker without prompting cursor = self.text_edit.textCursor() - cursor.insertText("<>") + cursor.insertText("<>") self.text_edit.setTextCursor(cursor) self.update_char_count() @@ -1500,14 +1500,14 @@ class abogen(QWidget): # Build voice formula string components = [f"{name}*{weight}" for name, weight in self.mixed_voice_state] voice = " + ".join(filter(None, components)) - # determine language: use profile setting if available, else first voice code + # determine language: use profile setting, else explicit mixer selection, else fallback to first voice code if self.selected_profile_name: from voice_profiles import load_profiles entry = load_profiles().get(self.selected_profile_name, {}) lang = entry.get("language") else: - lang = None + lang = self.selected_lang if not lang and self.mixed_voice_state: lang = ( self.mixed_voice_state[0][0][0] @@ -2138,4 +2138,3 @@ class abogen(QWidget): "Setting Saved", f"Maximum words per subtitle set to {value}.", ) - diff --git a/abogen/voice_formula_gui.py b/abogen/voice_formula_gui.py index 2060f0c..6c105dd 100644 --- a/abogen/voice_formula_gui.py +++ b/abogen/voice_formula_gui.py @@ -47,7 +47,7 @@ VOICE_MIXER_WIDTH = 100 SLIDER_WIDTH = 32 MIN_WINDOW_WIDTH = 600 MIN_WINDOW_HEIGHT = 400 -INITIAL_WINDOW_WIDTH = 1000 +INITIAL_WINDOW_WIDTH = 1200 INITIAL_WINDOW_HEIGHT = 500 # Language options for the language selector loaded from constants @@ -413,6 +413,11 @@ class VoiceFormulaDialog(QDialog): self.language_combo.setCurrentIndex(idx) self.language_combo.currentIndexChanged.connect(self.mark_profile_modified) header_row.addWidget(self.language_combo) + # Preview current voice mix using main window's preview + self.btn_preview_mix = QPushButton("Preview", self) + self.btn_preview_mix.setToolTip("Preview current voice mix") + self.btn_preview_mix.clicked.connect(self.preview_current_mix) + header_row.addWidget(self.btn_preview_mix) mixer_layout.addLayout(header_row) # Error message @@ -708,6 +713,8 @@ class VoiceFormulaDialog(QDialog): ] total = sum(w for _, w in selected) + # disable Preview if no voices selected + self.btn_preview_mix.setEnabled(total > 0) if total > 0: self.error_label.hide() @@ -1338,3 +1345,37 @@ class VoiceFormulaDialog(QDialog): else: item.setBackground(QColor("white")) self.update_profile_save_buttons() + + def preview_current_mix(self): + # Disable preview until playback completes + self.btn_preview_mix.setEnabled(False) + self.btn_preview_mix.setText("Loading...") + parent = self.parent() + if parent and hasattr(parent, "preview_voice"): + # Apply mixed voices and selected language + parent.mixed_voice_state = self.get_selected_voices() + parent.selected_profile_name = None + lang = self.language_combo.currentData() + parent.selected_lang = lang + parent.subtitle_combo.setEnabled( + lang in SUPPORTED_LANGUAGES_FOR_SUBTITLE_GENERATION + ) + # Reset start flag and trigger preview + self._started = False + parent.preview_voice() + # Poll preview_playing: wait for start then end + self._preview_poll_timer = QTimer(self) + self._preview_poll_timer.timeout.connect(self._check_preview_done) + self._preview_poll_timer.start(200) + + def _check_preview_done(self): + parent = self.parent() + if parent and hasattr(parent, "preview_playing"): + # Mark when playback starts + if parent.preview_playing: + self._started = True + # Once started and then stopped, re-enable + elif getattr(self, "_started", False): + self.btn_preview_mix.setEnabled(True) + self.btn_preview_mix.setText("Preview") + self._preview_poll_timer.stop()