mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
Add preview button to voice mixer
This commit is contained in:
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
- Added `Insert chapter marker` button in text editor to insert chapter markers at the current cursor position.
|
- 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 `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.
|
- Fixed m4b chapter generation opens CMD window in Windows.
|
||||||
+6
-7
@@ -290,8 +290,8 @@ class InputBox(QLabel):
|
|||||||
)
|
)
|
||||||
win.selected_book_path = file_path
|
win.selected_book_path = file_path
|
||||||
win.open_book_file(
|
win.open_book_file(
|
||||||
file_path
|
file_path # This will handle the dialog and setting file info
|
||||||
) # This will handle the dialog and setting file info
|
)
|
||||||
event.acceptProposedAction()
|
event.acceptProposedAction()
|
||||||
else:
|
else:
|
||||||
self.set_error("Please drop a .txt, .epub, or .pdf file.")
|
self.set_error("Please drop a .txt, .epub, or .pdf file.")
|
||||||
@@ -328,7 +328,7 @@ class TextboxDialog(QDialog):
|
|||||||
self.setWindowFlags(
|
self.setWindowFlags(
|
||||||
Qt.Window | Qt.WindowCloseButtonHint | Qt.WindowMaximizeButtonHint
|
Qt.Window | Qt.WindowCloseButtonHint | Qt.WindowMaximizeButtonHint
|
||||||
)
|
)
|
||||||
self.resize(600, 400)
|
self.resize(700, 500)
|
||||||
|
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
@@ -452,7 +452,7 @@ class TextboxDialog(QDialog):
|
|||||||
def insert_chapter_marker(self):
|
def insert_chapter_marker(self):
|
||||||
# Insert a fixed chapter marker without prompting
|
# Insert a fixed chapter marker without prompting
|
||||||
cursor = self.text_edit.textCursor()
|
cursor = self.text_edit.textCursor()
|
||||||
cursor.insertText("<<CHAPTER_MARKER:Chapter Title>>")
|
cursor.insertText("<<CHAPTER_MARKER:Title>>")
|
||||||
self.text_edit.setTextCursor(cursor)
|
self.text_edit.setTextCursor(cursor)
|
||||||
self.update_char_count()
|
self.update_char_count()
|
||||||
|
|
||||||
@@ -1500,14 +1500,14 @@ class abogen(QWidget):
|
|||||||
# Build voice formula string
|
# Build voice formula string
|
||||||
components = [f"{name}*{weight}" for name, weight in self.mixed_voice_state]
|
components = [f"{name}*{weight}" for name, weight in self.mixed_voice_state]
|
||||||
voice = " + ".join(filter(None, components))
|
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:
|
if self.selected_profile_name:
|
||||||
from voice_profiles import load_profiles
|
from voice_profiles import load_profiles
|
||||||
|
|
||||||
entry = load_profiles().get(self.selected_profile_name, {})
|
entry = load_profiles().get(self.selected_profile_name, {})
|
||||||
lang = entry.get("language")
|
lang = entry.get("language")
|
||||||
else:
|
else:
|
||||||
lang = None
|
lang = self.selected_lang
|
||||||
if not lang and self.mixed_voice_state:
|
if not lang and self.mixed_voice_state:
|
||||||
lang = (
|
lang = (
|
||||||
self.mixed_voice_state[0][0][0]
|
self.mixed_voice_state[0][0][0]
|
||||||
@@ -2138,4 +2138,3 @@ class abogen(QWidget):
|
|||||||
"Setting Saved",
|
"Setting Saved",
|
||||||
f"Maximum words per subtitle set to {value}.",
|
f"Maximum words per subtitle set to {value}.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ VOICE_MIXER_WIDTH = 100
|
|||||||
SLIDER_WIDTH = 32
|
SLIDER_WIDTH = 32
|
||||||
MIN_WINDOW_WIDTH = 600
|
MIN_WINDOW_WIDTH = 600
|
||||||
MIN_WINDOW_HEIGHT = 400
|
MIN_WINDOW_HEIGHT = 400
|
||||||
INITIAL_WINDOW_WIDTH = 1000
|
INITIAL_WINDOW_WIDTH = 1200
|
||||||
INITIAL_WINDOW_HEIGHT = 500
|
INITIAL_WINDOW_HEIGHT = 500
|
||||||
|
|
||||||
# Language options for the language selector loaded from constants
|
# Language options for the language selector loaded from constants
|
||||||
@@ -413,6 +413,11 @@ class VoiceFormulaDialog(QDialog):
|
|||||||
self.language_combo.setCurrentIndex(idx)
|
self.language_combo.setCurrentIndex(idx)
|
||||||
self.language_combo.currentIndexChanged.connect(self.mark_profile_modified)
|
self.language_combo.currentIndexChanged.connect(self.mark_profile_modified)
|
||||||
header_row.addWidget(self.language_combo)
|
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)
|
mixer_layout.addLayout(header_row)
|
||||||
|
|
||||||
# Error message
|
# Error message
|
||||||
@@ -708,6 +713,8 @@ class VoiceFormulaDialog(QDialog):
|
|||||||
]
|
]
|
||||||
|
|
||||||
total = sum(w for _, w in selected)
|
total = sum(w for _, w in selected)
|
||||||
|
# disable Preview if no voices selected
|
||||||
|
self.btn_preview_mix.setEnabled(total > 0)
|
||||||
|
|
||||||
if total > 0:
|
if total > 0:
|
||||||
self.error_label.hide()
|
self.error_label.hide()
|
||||||
@@ -1338,3 +1345,37 @@ class VoiceFormulaDialog(QDialog):
|
|||||||
else:
|
else:
|
||||||
item.setBackground(QColor("white"))
|
item.setBackground(QColor("white"))
|
||||||
self.update_profile_save_buttons()
|
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()
|
||||||
|
|||||||
Reference in New Issue
Block a user