From 85db3ec3eebf43e86a25da3d95ccb433a2c77f1c Mon Sep 17 00:00:00 2001 From: Rizwaan Salim Date: Tue, 11 Mar 2025 15:39:30 +0530 Subject: [PATCH] Added feature to play Sound from the video during preview playback --- app/processors/video_processor.py | 60 ++++++++- app/ui/core/MainWindow.ui | 82 +++++++++--- app/ui/core/main_window.py | 126 ++++++++++-------- app/ui/main_ui.py | 3 + .../widgets/actions/video_control_actions.py | 12 +- app/ui/widgets/settings_layout_data.py | 12 +- 6 files changed, 219 insertions(+), 76 deletions(-) diff --git a/app/processors/video_processor.py b/app/processors/video_processor.py index 9d4174da..f980b0b6 100644 --- a/app/processors/video_processor.py +++ b/app/processors/video_processor.py @@ -7,6 +7,7 @@ import os import gc from functools import partial +import psutil import cv2 import numpy @@ -49,6 +50,8 @@ def __init__(self, main_window: 'MainWindow', num_threads=2): self.virtcam: pyvirtualcam.Camera|None = None + self.ffplay_sound_sp = None + self.recording_sp: subprocess.Popen|None = None self.temp_file = '' #Used to calculate the total processing time @@ -120,7 +123,10 @@ def display_next_frame(self): if not self.recording: video_control_actions.update_widget_values_from_markers(self.main_window, self.next_frame_to_display) graphics_view_actions.update_graphics_view(self.main_window, pixmap, self.next_frame_to_display) - self.threads.pop(self.next_frame_to_display) + try: + self.threads.pop(self.next_frame_to_display) + except: + print("Thread not found in dict!") self.next_frame_to_display += 1 def display_next_webcam_frame(self): @@ -190,8 +196,12 @@ def process_video(self): else: fps = self.media_capture.get(cv2.CAP_PROP_FPS) + if self.main_window.liveSoundButton.isChecked(): + self.start_live_sound() + interval = 1000 / fps if fps > 0 else 30 interval = int(interval * 0.8) #Process 20% faster to offset the frame loading & processing time so the video will be played close to the original fps + print(f"Starting frame_read_timer with an interval of {interval} ms.") if self.recording: self.frame_read_timer.start() @@ -199,6 +209,7 @@ def process_video(self): else: self.frame_read_timer.start(interval) self.frame_display_timer.start() + self.gpu_memory_update_timer.start(5000) #Update GPU memory progressbar every 5 Seconds else: @@ -333,6 +344,7 @@ def stop_processing(self): self.frame_display_timer.stop() self.gpu_memory_update_timer.stop() self.join_and_clear_threads() + self.stop_live_sound() # print("Clearing Threads and Queues") @@ -441,4 +453,48 @@ def enable_virtualcam(self, backend=False): def disable_virtualcam(self): if self.virtcam: self.virtcam.close() - self.virtcam = None \ No newline at end of file + self.virtcam = None + + def start_live_sound(self): + # Start up audio if requested + seek_time = (self.next_frame_to_display)/self.fps + args = ["ffplay", + '-vn', + '-ss', str(seek_time), + '-nodisp', + '-stats', + '-loglevel', 'quiet', + '-sync', 'audio', + '-af', f'atempo={self.main_window.control["LiveSoundSpeedDecimalSlider"]}', + self.media_path] + + self.ffplay_sound_sp = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + def stop_live_sound(self): + if self.ffplay_sound_sp: + parent_pid = self.ffplay_sound_sp.pid + + try: + # Terminate any child processes spawned by ffplay + try: + parent_proc = psutil.Process(parent_pid) + children = parent_proc.children(recursive=True) + for child in children: + try: + child.kill() + except psutil.NoSuchProcess: + pass # The child process has already terminated + except psutil.NoSuchProcess: + pass # The parent process has already terminated + + # Terminate the parent process + self.ffplay_sound_sp.terminate() + try: + self.ffplay_sound_sp.wait(timeout=2) + except subprocess.TimeoutExpired: + self.ffplay_sound_sp.kill() + + except psutil.NoSuchProcess: + pass # The process no longer exists + + self.ffplay_sound_sp = None \ No newline at end of file diff --git a/app/ui/core/MainWindow.ui b/app/ui/core/MainWindow.ui index 568ff966..b3af02a4 100644 --- a/app/ui/core/MainWindow.ui +++ b/app/ui/core/MainWindow.ui @@ -134,6 +134,54 @@ + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::MinimumExpanding + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + [Experimental] Toggle Live Sound while the video is playing + + + + + + + :/media/media/audio_off.png:/media/media/audio_off.png + + + + 16 + 20 + + + + true + + + true + + + @@ -559,35 +607,35 @@ 120 - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAsNeeded - Save Embedding - - true + + Qt::ScrollBarPolicy::ScrollBarAlwaysOff - - QListView::IconMode + + Qt::ScrollBarPolicy::ScrollBarAsNeeded - - QListView::Static + + false - - true + + QListView::Movement::Static - QListView::Batched + QListView::LayoutMode::Batched 4 - - false + + QListView::ViewMode::IconMode + + + true + + + true diff --git a/app/ui/core/main_window.py b/app/ui/core/main_window.py index 136cf8ce..5ebd37c1 100644 --- a/app/ui/core/main_window.py +++ b/app/ui/core/main_window.py @@ -122,6 +122,18 @@ def setupUi(self, MainWindow): self.verticalLayoutMediaControls.addLayout(self.horizontalLayoutMediaSlider) self.horizontalLayoutMediaButtons = QHBoxLayout() self.horizontalLayoutMediaButtons.setObjectName(u"horizontalLayoutMediaButtons") + self.horizontalSpacer_7 = QSpacerItem(40, 20, QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Minimum) + self.horizontalLayoutMediaButtons.addItem(self.horizontalSpacer_7) + self.liveSoundButton = QPushButton(self.mediaLayout) + self.liveSoundButton.setObjectName(u"liveSoundButton") + self.liveSoundButton.setMinimumSize(QSize(0, 0)) + icon1 = QIcon() + icon1.addFile(u":/media/media/audio_off.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.liveSoundButton.setIcon(icon1) + self.liveSoundButton.setIconSize(QSize(16, 20)) + self.liveSoundButton.setCheckable(True) + self.liveSoundButton.setFlat(True) + self.horizontalLayoutMediaButtons.addWidget(self.liveSoundButton) self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Minimum) self.horizontalLayoutMediaButtons.addItem(self.horizontalSpacer) self.frameRewindButton = QPushButton(self.mediaLayout) @@ -132,18 +144,18 @@ def setupUi(self, MainWindow): sizePolicy2.setHeightForWidth(self.frameRewindButton.sizePolicy().hasHeightForWidth()) self.frameRewindButton.setSizePolicy(sizePolicy2) self.frameRewindButton.setMaximumSize(QSize(100, 16777215)) - icon1 = QIcon() - icon1.addFile(u":/media/media/previous_marker_off.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.frameRewindButton.setIcon(icon1) + icon2 = QIcon() + icon2.addFile(u":/media/media/previous_marker_off.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.frameRewindButton.setIcon(icon2) self.frameRewindButton.setFlat(True) self.horizontalLayoutMediaButtons.addWidget(self.frameRewindButton) self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum) self.horizontalLayoutMediaButtons.addItem(self.horizontalSpacer_3) self.buttonMediaRecord = QPushButton(self.mediaLayout) self.buttonMediaRecord.setObjectName(u"buttonMediaRecord") - icon2 = QIcon() - icon2.addFile(u":/media/media/rec_off.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.buttonMediaRecord.setIcon(icon2) + icon3 = QIcon() + icon3.addFile(u":/media/media/rec_off.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.buttonMediaRecord.setIcon(icon3) self.buttonMediaRecord.setCheckable(True) self.buttonMediaRecord.setFlat(True) self.horizontalLayoutMediaButtons.addWidget(self.buttonMediaRecord) @@ -157,9 +169,9 @@ def setupUi(self, MainWindow): sizePolicy3.setHeightForWidth(self.buttonMediaPlay.sizePolicy().hasHeightForWidth()) self.buttonMediaPlay.setSizePolicy(sizePolicy3) self.buttonMediaPlay.setMaximumSize(QSize(100, 16777215)) - icon3 = QIcon() - icon3.addFile(u":/media/media/play_off.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.buttonMediaPlay.setIcon(icon3) + icon4 = QIcon() + icon4.addFile(u":/media/media/play_off.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.buttonMediaPlay.setIcon(icon4) self.buttonMediaPlay.setCheckable(True) self.buttonMediaPlay.setFlat(True) self.horizontalLayoutMediaButtons.addWidget(self.buttonMediaPlay) @@ -170,46 +182,46 @@ def setupUi(self, MainWindow): sizePolicy2.setHeightForWidth(self.frameAdvanceButton.sizePolicy().hasHeightForWidth()) self.frameAdvanceButton.setSizePolicy(sizePolicy2) self.frameAdvanceButton.setMaximumSize(QSize(100, 16777215)) - icon4 = QIcon() - icon4.addFile(u":/media/media/next_marker_off.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.frameAdvanceButton.setIcon(icon4) + icon5 = QIcon() + icon5.addFile(u":/media/media/next_marker_off.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.frameAdvanceButton.setIcon(icon5) self.frameAdvanceButton.setFlat(True) self.horizontalLayoutMediaButtons.addWidget(self.frameAdvanceButton) self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum) self.horizontalLayoutMediaButtons.addItem(self.horizontalSpacer_2) self.addMarkerButton = QPushButton(self.mediaLayout) self.addMarkerButton.setObjectName(u"addMarkerButton") - icon5 = QIcon() - icon5.addFile(u":/media/media/add_marker_hover.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.addMarkerButton.setIcon(icon5) + icon6 = QIcon() + icon6.addFile(u":/media/media/add_marker_hover.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.addMarkerButton.setIcon(icon6) self.addMarkerButton.setFlat(True) self.horizontalLayoutMediaButtons.addWidget(self.addMarkerButton) self.removeMarkerButton = QPushButton(self.mediaLayout) self.removeMarkerButton.setObjectName(u"removeMarkerButton") - icon6 = QIcon() - icon6.addFile(u":/media/media/remove_marker_hover.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.removeMarkerButton.setIcon(icon6) + icon7 = QIcon() + icon7.addFile(u":/media/media/remove_marker_hover.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.removeMarkerButton.setIcon(icon7) self.removeMarkerButton.setFlat(True) self.horizontalLayoutMediaButtons.addWidget(self.removeMarkerButton) self.previousMarkerButton = QPushButton(self.mediaLayout) self.previousMarkerButton.setObjectName(u"previousMarkerButton") - icon7 = QIcon() - icon7.addFile(u":/media/media/previous_marker_hover.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.previousMarkerButton.setIcon(icon7) + icon8 = QIcon() + icon8.addFile(u":/media/media/previous_marker_hover.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.previousMarkerButton.setIcon(icon8) self.previousMarkerButton.setFlat(True) self.horizontalLayoutMediaButtons.addWidget(self.previousMarkerButton) self.nextMarkerButton = QPushButton(self.mediaLayout) self.nextMarkerButton.setObjectName(u"nextMarkerButton") - icon8 = QIcon() - icon8.addFile(u":/media/media/next_marker_hover.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.nextMarkerButton.setIcon(icon8) + icon9 = QIcon() + icon9.addFile(u":/media/media/next_marker_hover.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.nextMarkerButton.setIcon(icon9) self.nextMarkerButton.setFlat(True) self.horizontalLayoutMediaButtons.addWidget(self.nextMarkerButton) self.viewFullScreenButton = QPushButton(self.mediaLayout) self.viewFullScreenButton.setObjectName(u"viewFullScreenButton") - icon9 = QIcon() - icon9.addFile(u":/media/media/fullscreen.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.viewFullScreenButton.setIcon(icon9) + icon10 = QIcon() + icon10.addFile(u":/media/media/fullscreen.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.viewFullScreenButton.setIcon(icon10) self.viewFullScreenButton.setFlat(True) self.horizontalLayoutMediaButtons.addWidget(self.viewFullScreenButton) self.horizontalSpacer_5 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) @@ -274,15 +286,15 @@ def setupUi(self, MainWindow): self.inputEmbeddingsList.setSizePolicy(sizePolicy5) self.inputEmbeddingsList.setMinimumSize(QSize(320, 120)) self.inputEmbeddingsList.setMaximumSize(QSize(16777215, 120)) - self.inputEmbeddingsList.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - self.inputEmbeddingsList.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) - self.inputEmbeddingsList.setUniformItemSizes(True) - self.inputEmbeddingsList.setViewMode(QListView.IconMode) - self.inputEmbeddingsList.setMovement(QListView.Static) - self.inputEmbeddingsList.setWrapping(True) - self.inputEmbeddingsList.setLayoutMode(QListView.Batched) - self.inputEmbeddingsList.setSpacing(4) + self.inputEmbeddingsList.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.inputEmbeddingsList.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) self.inputEmbeddingsList.setAutoScroll(False) + self.inputEmbeddingsList.setMovement(QListView.Movement.Static) + self.inputEmbeddingsList.setLayoutMode(QListView.LayoutMode.Batched) + self.inputEmbeddingsList.setSpacing(4) + self.inputEmbeddingsList.setViewMode(QListView.ViewMode.IconMode) + self.inputEmbeddingsList.setUniformItemSizes(True) + self.inputEmbeddingsList.setProperty(u"wrapping", True) self.gridLayout_2.addWidget(self.inputEmbeddingsList, 1, 2, 1, 1) self.horizontalLayout_4 = QHBoxLayout() self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") @@ -298,23 +310,23 @@ def setupUi(self, MainWindow): self.horizontalLayout_3.addWidget(self.inputEmbeddingsSearchBox) self.openEmbeddingButton = QPushButton(self.facesPanelGroupBox) self.openEmbeddingButton.setObjectName(u"openEmbeddingButton") - icon10 = QIcon() - icon10.addFile(u":/media/media/open_file.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.openEmbeddingButton.setIcon(icon10) + icon11 = QIcon() + icon11.addFile(u":/media/media/open_file.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.openEmbeddingButton.setIcon(icon11) self.openEmbeddingButton.setFlat(True) self.horizontalLayout_3.addWidget(self.openEmbeddingButton) self.saveEmbeddingButton = QPushButton(self.facesPanelGroupBox) self.saveEmbeddingButton.setObjectName(u"saveEmbeddingButton") - icon11 = QIcon() - icon11.addFile(u":/media/media/save_file.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.saveEmbeddingButton.setIcon(icon11) + icon12 = QIcon() + icon12.addFile(u":/media/media/save_file.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.saveEmbeddingButton.setIcon(icon12) self.saveEmbeddingButton.setFlat(True) self.horizontalLayout_3.addWidget(self.saveEmbeddingButton) self.saveEmbeddingAsButton = QPushButton(self.facesPanelGroupBox) self.saveEmbeddingAsButton.setObjectName(u"saveEmbeddingAsButton") - icon12 = QIcon() - icon12.addFile(u":/media/media/save_file_as.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.saveEmbeddingAsButton.setIcon(icon12) + icon13 = QIcon() + icon13.addFile(u":/media/media/save_file_as.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.saveEmbeddingAsButton.setIcon(icon13) self.saveEmbeddingAsButton.setFlat(True) self.horizontalLayout_3.addWidget(self.saveEmbeddingAsButton) self.gridLayout_2.addLayout(self.horizontalLayout_3, 0, 2, 1, 1) @@ -358,7 +370,7 @@ def setupUi(self, MainWindow): sizePolicy7.setVerticalStretch(0) sizePolicy7.setHeightForWidth(self.buttonTargetVideosPath.sizePolicy().hasHeightForWidth()) self.buttonTargetVideosPath.setSizePolicy(sizePolicy7) - self.buttonTargetVideosPath.setIcon(icon10) + self.buttonTargetVideosPath.setIcon(icon11) self.buttonTargetVideosPath.setIconSize(QSize(18, 18)) self.buttonTargetVideosPath.setFlat(True) self.horizontalLayout_7.addWidget(self.buttonTargetVideosPath) @@ -371,23 +383,23 @@ def setupUi(self, MainWindow): self.horizontalLayout_9.addWidget(self.targetVideosSearchBox) self.filterImagesCheckBox = QCheckBox(self.dockWidgetContents) self.filterImagesCheckBox.setObjectName(u"filterImagesCheckBox") - icon13 = QIcon() - icon13.addFile(u":/media/media/image.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.filterImagesCheckBox.setIcon(icon13) + icon14 = QIcon() + icon14.addFile(u":/media/media/image.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.filterImagesCheckBox.setIcon(icon14) self.filterImagesCheckBox.setChecked(True) self.horizontalLayout_9.addWidget(self.filterImagesCheckBox) self.filterVideosCheckBox = QCheckBox(self.dockWidgetContents) self.filterVideosCheckBox.setObjectName(u"filterVideosCheckBox") - icon14 = QIcon() - icon14.addFile(u":/media/media/video.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.filterVideosCheckBox.setIcon(icon14) + icon15 = QIcon() + icon15.addFile(u":/media/media/video.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.filterVideosCheckBox.setIcon(icon15) self.filterVideosCheckBox.setChecked(True) self.horizontalLayout_9.addWidget(self.filterVideosCheckBox) self.filterWebcamsCheckBox = QCheckBox(self.dockWidgetContents) self.filterWebcamsCheckBox.setObjectName(u"filterWebcamsCheckBox") - icon15 = QIcon() - icon15.addFile(u":/media/media/webcam.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - self.filterWebcamsCheckBox.setIcon(icon15) + icon16 = QIcon() + icon16.addFile(u":/media/media/webcam.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) + self.filterWebcamsCheckBox.setIcon(icon16) self.filterWebcamsCheckBox.setChecked(False) self.horizontalLayout_9.addWidget(self.filterWebcamsCheckBox) self.vboxLayout.addLayout(self.horizontalLayout_9) @@ -409,7 +421,7 @@ def setupUi(self, MainWindow): self.buttonInputFacesPath.setObjectName(u"buttonInputFacesPath") sizePolicy7.setHeightForWidth(self.buttonInputFacesPath.sizePolicy().hasHeightForWidth()) self.buttonInputFacesPath.setSizePolicy(sizePolicy7) - self.buttonInputFacesPath.setIcon(icon10) + self.buttonInputFacesPath.setIcon(icon11) self.buttonInputFacesPath.setIconSize(QSize(18, 18)) self.buttonInputFacesPath.setFlat(True) self.horizontalLayout_8.addWidget(self.buttonInputFacesPath) @@ -567,6 +579,10 @@ def retranslateUi(self, MainWindow): #if QT_CONFIG(tooltip) self.videoSeekLineEdit.setToolTip(QCoreApplication.translate("MainWindow", u"Frame Number", None)) #endif // QT_CONFIG(tooltip) +#if QT_CONFIG(tooltip) + self.liveSoundButton.setToolTip(QCoreApplication.translate("MainWindow", u"[Experimental] Toggle Live Sound while the video is playing", None)) +#endif // QT_CONFIG(tooltip) + self.liveSoundButton.setText("") self.frameRewindButton.setText("") self.buttonMediaRecord.setText("") self.buttonMediaPlay.setText("") diff --git a/app/ui/main_ui.py b/app/ui/main_ui.py index 8aff4341..3148a7b5 100644 --- a/app/ui/main_ui.py +++ b/app/ui/main_ui.py @@ -149,6 +149,9 @@ def initialize_widgets(self): video_seek_line_edit_event_filter = videoSeekSliderLineEditEventFilter(self, self.videoSeekLineEdit) self.videoSeekLineEdit.installEventFilter(video_seek_line_edit_event_filter) + # Audio toggle + self.liveSoundButton.toggled.connect(partial(video_control_actions.toggle_live_sound, self)) + # Connect the Play/Stop button to the play_video method self.buttonMediaPlay.toggled.connect(partial(video_control_actions.play_video, self)) self.buttonMediaRecord.toggled.connect(partial(video_control_actions.record_video, self)) diff --git a/app/ui/widgets/actions/video_control_actions.py b/app/ui/widgets/actions/video_control_actions.py index b2026bce..f1b12d42 100644 --- a/app/ui/widgets/actions/video_control_actions.py +++ b/app/ui/widgets/actions/video_control_actions.py @@ -379,7 +379,6 @@ def reset_media_buttons(main_window: 'MainWindow'): set_play_button_icon(main_window) set_record_button_icon(main_window) - def set_play_button_icon(main_window: 'MainWindow'): if main_window.buttonMediaPlay.isChecked(): main_window.buttonMediaPlay.setIcon(QtGui.QIcon(":/media/media/play_on.png")) @@ -482,3 +481,14 @@ def save_current_frame_to_file(main_window: 'MainWindow'): else: common_widget_actions.create_and_show_messagebox(main_window, 'Invalid Frame', 'Cannot save the current frame!', parent_widget=main_window.saveImageButton) + +def toggle_live_sound(main_window: 'MainWindow', toggle_value: bool): + video_processor = main_window.video_processor + was_processing = video_processor.processing + + # If the video was playing, then stop and start it again to enable the audio + # Otherwise, just the toggle value so that the next time the play button is hit, it would automatically enable/disable the audio + # The play button is clicked twice in the below block to simulate the above mentioned behaviour. It should be changed into a set up in the next refactor + if was_processing: + main_window.buttonMediaPlay.click() + main_window.buttonMediaPlay.click() \ No newline at end of file diff --git a/app/ui/widgets/settings_layout_data.py b/app/ui/widgets/settings_layout_data.py index 520b54b9..9b076a8e 100644 --- a/app/ui/widgets/settings_layout_data.py +++ b/app/ui/widgets/settings_layout_data.py @@ -35,7 +35,7 @@ 'exec_function_args': [], }, }, - 'Video Settings': { + 'Video Playback Settings': { 'VideoPlaybackCustomFpsToggle': { 'level': 1, 'label': 'Set Custom Video Playback FPS', @@ -55,6 +55,16 @@ 'step': 1, 'help': 'Set the maximum FPS of the video when playing' }, + 'LiveSoundSpeedDecimalSlider': { + 'level': 1, + 'label': 'Audio Playback Speed', + 'min_value': '0.50', + 'max_value': '2.00', + 'default': '1.00', + 'step': 0.01, + 'decimals': 2, + 'help': '[Experimental] Set the playback speed of the audio, when Live Sound is enabled' + }, }, 'Auto Swap':{ 'AutoSwapToggle': {