diff --git a/CHANGELOG.md b/CHANGELOG.md index 11409b3..7910616 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ # 1.1.8 (pre-release) - Added `.md` (Markdown) file extension support by @brianxiadong in PR #75 +- Added new option `Configure silence between chapters` that lets users configure the silence between chapters, mentioned by @lfperez1982 in #79 - Improved the markdown logic to better handle various markdown structures and edge cases. -- Fixed `No Qt platform plugin could be initialized` error, mentioned in #59 by @sunrainxyz +- Fixed `No Qt platform plugin could be initialized` error, mentioned by @sunrainxyz in #59 +- Improvements in code and documentation. # 1.1.7 - Added MPS GPU acceleration support for Silicon Mac, mentioned in https://github.com/denizsafak/abogen/issues/32#issuecomment-3155902040 by @jefro108. **Please read the [Mac](https://github.com/denizsafak/abogen?tab=readme-ov-file#mac) section in the documentation again, as it requires additional configuration.** diff --git a/README.md b/README.md index 2a70a49..11c3a82 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ Here’s Abogen in action: in this demo, it processes ∼3,000 characters of tex |---------|-------------| | **Theme** | Change the application's theme using `System`, `Light`, or `Dark` options. | | **Configure max words per subtitle** | Configures the maximum number of words per subtitle entry. | +| **Configure silence between chapters** | Configures the duration of silence between chapters (in seconds). | | **Configure max lines in log window** | Configures the maximum number of lines to display in the log window. | | **Separate chapters audio format** | Configures the audio format for separate chapters as `wav`, `flac`, `mp3`, or `opus`. | | **Create desktop shortcut** | Creates a shortcut on your desktop for easy access. | diff --git a/abogen/conversion.py b/abogen/conversion.py index c09d997..6a4f721 100644 --- a/abogen/conversion.py +++ b/abogen/conversion.py @@ -176,6 +176,7 @@ class ConversionThread(QThread): self.waiting_for_user_input = False self.use_gpu = use_gpu # Store the GPU setting self.max_subtitle_words = 50 # Default value, will be overridden from GUI + self.silence_duration = 2.0 # Default value, will be overridden from GUI def _stream_audio_in_chunks( self, segments, process_func, progress_prefix="Processing" @@ -286,6 +287,12 @@ class ConversionThread(QThread): f"- Separate chapters format: {separate_format}" ) + # If merge_at_end is True, display the silence duration + if getattr(self, "merge_chapters_at_end", True): + self.log_updated.emit( + f"- Silence between chapters: {self.silence_duration} seconds" + ) + if self.save_option == "Choose output folder": self.log_updated.emit( f"- Output folder: {self.output_folder or os.getcwd()}" @@ -899,6 +906,22 @@ class ConversionThread(QThread): # Update progress more frequently (after each result) self.progress_updated.emit(percent, etr_str) + # Add silence between chapters for merged output (except after the last chapter) + if merge_chapters_at_end and chapter_idx < total_chapters: + silence_samples = int(self.silence_duration * 24000) # Silence duration at 24,000 Hz + silence_audio = self.np.zeros(silence_samples, dtype="float32") + silence_bytes = silence_audio.tobytes() + + if merged_out_file: + merged_out_file.write(silence_audio) + elif ffmpeg_proc: + ffmpeg_proc.stdin.write(silence_bytes) + + # Update timing for the silence + current_time += self.silence_duration + if chapter_out_file or chapter_ffmpeg_proc: + chapter_current_time += self.silence_duration + # Set chapter end time after processing if merge_chapters_at_end: chapter_time["end"] = current_time diff --git a/abogen/gui.py b/abogen/gui.py index 7c7ea97..607f98d 100644 --- a/abogen/gui.py +++ b/abogen/gui.py @@ -689,6 +689,9 @@ class abogen(QWidget): self.max_subtitle_words = self.config.get( "max_subtitle_words", 50 ) # Default max words per subtitle + self.silence_duration = self.config.get( + "silence_duration", 2.0 + ) # Default silence duration self.selected_format = self.config.get("selected_format", "wav") self.separate_chapters_format = self.config.get( "separate_chapters_format", "wav" @@ -1862,6 +1865,8 @@ class abogen(QWidget): self.conversion_thread.file_size_str = file_size_str # Pass max_subtitle_words from config self.conversion_thread.max_subtitle_words = self.max_subtitle_words + # Pass silence_duration from config + self.conversion_thread.silence_duration = self.silence_duration # Pass replace_single_newlines setting self.conversion_thread.replace_single_newlines = ( self.replace_single_newlines @@ -2815,6 +2820,11 @@ class abogen(QWidget): max_words_action.triggered.connect(self.set_max_subtitle_words) menu.addAction(max_words_action) + # Add silence between chapters option + silence_action = QAction("Configure silence between chapters", self) + silence_action.triggered.connect(self.set_silence_between_chapters) + menu.addAction(silence_action) + max_lines_action = QAction("Configure max lines in log window", self) max_lines_action.triggered.connect(self.set_max_log_lines) menu.addAction(max_lines_action) @@ -3471,6 +3481,40 @@ Categories=AudioVideo;Audio;Utility; f"Maximum words per subtitle set to {value}.", ) + def set_silence_between_chapters(self): + """Open a dialog to set the silence duration between chapters""" + from PyQt5.QtWidgets import QInputDialog, QDialog + + current_value = self.config.get("silence_duration", 2.0) + + dlg = QInputDialog(self) + dlg.setWindowTitle("Silence Duration (seconds)") + dlg.setLabelText( + "Enter the duration of silence\nbetween chapters (in seconds):" + ) + dlg.setInputMode(QInputDialog.DoubleInput) + dlg.setDoubleDecimals(1) + dlg.setDoubleMinimum(0.0) + dlg.setDoubleMaximum(60.0) + dlg.setDoubleValue(current_value) + dlg.setDoubleStep(0.1) # <-- set step to 0.1 + + if dlg.exec_() == QDialog.Accepted: + value = dlg.doubleValue() + # Round to one decimal to avoid floating-point representation noise + value = round(value, 1) + + # Save the new value + self.silence_duration = value + self.config["silence_duration"] = value + save_config(self.config) + + # Show confirmation (format with one decimal) + QMessageBox.information( + self, + "Setting Saved", + f"Silence duration between chapters set to {value:.1f} seconds.", + ) def set_separate_chapters_format(self, fmt): """Set the format for separate chapters audio files.""" self.separate_chapters_format = fmt