From 543379db5412685216d9f21bf5b3b5b97ff7d781 Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Mon, 6 May 2024 17:11:54 +0100 Subject: [PATCH] WIP: time to vocals: preview +- working --- app/config.py | 2 + app/music.py | 105 ++++++++++++++++++++++++++++++++--- app/musicmuster.py | 79 +++++++------------------- app/ui/main_window.ui | 116 +++++++++++++++++++-------------------- app/ui/main_window_ui.py | 114 +++++++++++++++++++------------------- 5 files changed, 235 insertions(+), 181 deletions(-) diff --git a/app/config.py b/app/config.py index dcdedd4..59fb635 100644 --- a/app/config.py +++ b/app/config.py @@ -79,6 +79,8 @@ class Config(object): OBS_PASSWORD = "auster" OBS_PORT = 4455 PLAY_SETTLE = 500000 + PREVIEW_ADVANCE_MS = 5000 + PREVIEW_BACK_MS = 5000 REPLACE_FILES_DEFAULT_SOURCE = "/home/kae/music/Singles/tmp" RETURN_KEY_DEBOUNCE_MS = 500 ROOT = os.environ.get("ROOT") or "/home/kae/music" diff --git a/app/music.py b/app/music.py index 65c8b17..8a61def 100644 --- a/app/music.py +++ b/app/music.py @@ -1,18 +1,24 @@ +# Standard library imports +import datetime as dt import threading -import vlc # type: ignore - -from config import Config -from helpers import file_is_unreadable -from typing import Optional from time import sleep +from typing import Optional -from log import log - +# PyQt imports from PyQt6.QtCore import ( QRunnable, QThreadPool, ) +# Third party imports +import vlc # type: ignore + +# App imports +from config import Config +from helpers import file_is_unreadable +from log import log + + lock = threading.Lock() @@ -59,6 +65,25 @@ class Music: self.player = None self.name = name self.max_volume = Config.VLC_VOLUME_DEFAULT + self.start_dt: Optional[dt.datetime] = None + + def _adjust_by_ms(self, ms: int) -> None: + """Move player position by ms milliseconds""" + + if not self.player: + return + + elapsed_ms = self.get_playtime() + position = self.get_position() + if not position: + position = 0 + new_position = max(0, position + ((position * ms) / elapsed_ms)) + self.player.set_position(new_position) + # Adjus start time so elapsed time calculations are correct + if new_position == 0: + self.start_dt = dt.datetime.now() + else: + self.start_dt -= dt.timedelta(milliseconds=ms) def fade(self, fade_seconds: int = Config.FADEOUT_SECONDS) -> None: """ @@ -86,6 +111,21 @@ class Music: fader = FadeTrack(p, fade_seconds=fade_seconds) pool.start(fader) + def get_playtime(self) -> int: + """ + Return number of milliseconds current track has been playing or + zero if not playing. The vlc function get_time() only updates 3-4 + times a second; this function has much better resolution. + """ + + if self.player is None or self.start_dt is None: + return 0 + + now = dt.datetime.now() + elapsed_time = now - self.start_dt + elapsed_seconds = elapsed_time.seconds + (elapsed_time.microseconds / 1000000) + return int(elapsed_seconds * 1000) + def get_position(self) -> Optional[float]: """Return current position""" @@ -93,6 +133,41 @@ class Music: return None return self.player.get_position() + def is_playing(self) -> bool: + """ + Return True if we're playing + """ + + if not self.player: + return False + + # There is a discrete time between starting playing a track and + # player.is_playing() returning True, so assume playing if less + # than Config.PLAY_SETTLE microseconds have passed since + # starting play. + if self.start_dt and ( + self.player.is_playing() + or (dt.datetime.now() - self.start_dt) + < dt.timedelta(microseconds=Config.PLAY_SETTLE) + ): + return True + + return False + + def move_back(self, ms: int) -> None: + """ + Rewind player by ms milliseconds + """ + + self._adjust_by_ms(ms * -1) + + def move_forward(self, ms: int) -> None: + """ + Rewind player by ms milliseconds + """ + + self._adjust_by_ms(ms) + def play(self, path: str, position: Optional[float] = None) -> None: """ Start playing the track at path. @@ -110,9 +185,21 @@ class Music: self.player = media.player_new_from_media() if self.player: _ = self.player.play() - self.set_volume(self.max_volume) + self.start_dt = dt.datetime.now() if position: self.player.set_position(position) + # For as-yet unknown reasons. sometimes the volume gets + # reset to zero within 200mS or so of starting play. This + # only happened since moving to Debian 12, which uses + # Pipewire for sound (which may be irrelevant). + for _ in range(3): + if self.player: + volume = self.player.audio_get_volume() + if volume < Config.VLC_VOLUME_DEFAULT: + self.set_volume(Config.VLC_VOLUME_DEFAULT) + log.debug(f"Reset from {volume=}") + break + sleep(0.1) def set_volume(self, volume=None, set_default=True) -> None: """Set maximum volume used for player""" @@ -133,6 +220,8 @@ class Music: log.info(f"Music[{self.name}].stop()") + self.start_dt = None + if not self.player: return 0.0 diff --git a/app/musicmuster.py b/app/musicmuster.py index 4325ab1..042e224 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -581,7 +581,9 @@ class Window(QMainWindow, Ui_MainWindow): self.btnDrop3db.clicked.connect(self.drop3db) self.btnFade.clicked.connect(self.fade) self.btnHidePlayed.clicked.connect(self.hide_played) + self.btnPreviewBack.clicked.connect(self.preview_back) self.btnPreview.clicked.connect(self.preview) + self.btnPreviewFwd.clicked.connect(self.preview_fwd) self.btnStop.clicked.connect(self.stop) self.hdrCurrentTrack.clicked.connect(self.show_current) self.hdrNextTrack.clicked.connect(self.show_next) @@ -780,21 +782,6 @@ class Window(QMainWindow, Ui_MainWindow): self.stop_playing(fade=True) - def get_playtime(self) -> int: - """ - Return number of milliseconds current track has been playing or - zero if not playing. The vlc function get_time() only updates 3-4 - times a second; this function has much better resolution. - """ - - if track_sequence.now.track_id is None or track_sequence.now.start_time is None: - return 0 - - now = dt.datetime.now() - track_start = track_sequence.now.start_time - elapsed_seconds = (now - track_start).total_seconds() - return int(elapsed_seconds * 1000) - def hide_played(self): """Toggle hide played tracks""" @@ -1145,7 +1132,6 @@ class Window(QMainWindow, Ui_MainWindow): - Clear next track - Restore volume if -3dB active - Play (new) current track. - - Ensure 100% volume - Show closing volume graph - Notify model - Note that track is now playing @@ -1203,20 +1189,6 @@ class Window(QMainWindow, Ui_MainWindow): return self.music.play(track_sequence.now.path, position) - # Ensure 100% volume - # For as-yet unknown reasons. sometimes the volume gets - # reset to zero within 200mS or so of starting play. This - # only happened since moving to Debian 12, which uses - # Pipewire for sound (which may be irrelevant). - for _ in range(3): - if self.music.player: - volume = self.music.player.audio_get_volume() - if volume < Config.VLC_VOLUME_DEFAULT: - self.music.set_volume() - log.debug(f"Reset from {volume=}") - break - sleep(0.1) - # Show closing volume graph if track_sequence.now.fade_graph: track_sequence.now.fade_graph.plot() @@ -1266,6 +1238,17 @@ class Window(QMainWindow, Ui_MainWindow): else: self.preview_player.stop() + self.label_intro_timer.setText("0.0") + + def preview_back(self) -> None: + """Wind back preview file""" + + self.preview_player.move_back(Config.PREVIEW_BACK_MS) + + def preview_fwd(self) -> None: + """Advance preview file""" + + self.preview_player.move_forward(Config.PREVIEW_ADVANCE_MS) def rename_playlist(self) -> None: """ @@ -1680,20 +1663,12 @@ class Window(QMainWindow, Ui_MainWindow): """ # Ensure preview button is reset if preview finishes playing - if self.preview_player.player is not None and self.preview - and self.preview_player_start_time - and ( - self.preview_player.player.is_playing() - or (dt.datetime.now() - self.preview_player_start_time) - < dt.timedelta(microseconds=Config.PLAY_SETTLE) - ) - ) - self.btnPreview.setChecked(preview_playing) - if preview_playing and self.preview_player_start_time: - now = dt.datetime.now() - elapsed_time = now - self.preview_player_start_time - elapsed_seconds = elapsed_time.seconds + (elapsed_time.microseconds / 1000000) - self.label_intro_timer.setText(f"{elapsed_seconds:.1f}") + self.btnPreview.setChecked(self.preview_player.is_playing()) + + # Update preview timer + if self.preview_player.is_playing(): + playtime = self.preview_player.get_playtime() + self.label_intro_timer.setText(f"{playtime / 1000:.1f}") def tick_1000ms(self) -> None: """ @@ -1707,20 +1682,8 @@ class Window(QMainWindow, Ui_MainWindow): return # If track is playing, update track clocks time and colours - # There is a discrete time between starting playing a track and - # player.is_playing() returning True, so assume playing if less - # than Config.PLAY_SETTLE microseconds have passed since - # starting play. - if ( - self.music.player - and track_sequence.now.start_time - and ( - self.music.player.is_playing() - or (dt.datetime.now() - track_sequence.now.start_time) - < dt.timedelta(microseconds=Config.PLAY_SETTLE) - ) - ): - playtime = self.get_playtime() + if self.music.player and self.music.player.is_playing(): + playtime = self.player.get_playtime() time_to_fade = track_sequence.now.fade_at - playtime time_to_silence = track_sequence.now.silence_at - playtime diff --git a/app/ui/main_window.ui b/app/ui/main_window.ui index e2e4302..2833f15 100644 --- a/app/ui/main_window.ui +++ b/app/ui/main_window.ui @@ -478,7 +478,7 @@ padding-left: 8px; - + 0 @@ -531,7 +531,7 @@ padding-left: 8px; true - + 88 @@ -556,7 +556,7 @@ padding-left: 8px; >> - + 0 @@ -581,7 +581,7 @@ padding-left: 8px; < - + 44 @@ -606,7 +606,7 @@ padding-left: 8px; * - + 88 @@ -636,6 +636,55 @@ padding-left: 8px; + + + + + 152 + 112 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Intro + + + Qt::AlignCenter + + + + + + + + FreeSans + 40 + 50 + false + + + + 0:0 + + + Qt::AlignCenter + + + + + + @@ -704,55 +753,6 @@ padding-left: 8px; - - - - - 152 - 112 - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - Intro - - - Qt::AlignCenter - - - - - - - - FreeSans - 40 - 50 - false - - - - 00:00 - - - Qt::AlignCenter - - - - - - @@ -1029,7 +1029,7 @@ padding-left: 8px; - ../../../../.designer/backup/icon-play.png../../../../.designer/backup/icon-play.png + ../../../../../../.designer/backup/icon-play.png../../../../../../.designer/backup/icon-play.png &Play next @@ -1053,7 +1053,7 @@ padding-left: 8px; - ../../../../.designer/backup/icon_search_database.png../../../../.designer/backup/icon_search_database.png + ../../../../../../.designer/backup/icon_search_database.png../../../../../../.designer/backup/icon_search_database.png Insert &track... @@ -1065,7 +1065,7 @@ padding-left: 8px; - ../../../../.designer/backup/icon_open_file.png../../../../.designer/backup/icon_open_file.png + ../../../../../../.designer/backup/icon_open_file.png../../../../../../.designer/backup/icon_open_file.png Add &file @@ -1077,7 +1077,7 @@ padding-left: 8px; - ../../../../.designer/backup/icon-fade.png../../../../.designer/backup/icon-fade.png + ../../../../../../.designer/backup/icon-fade.png../../../../../../.designer/backup/icon-fade.png F&ade diff --git a/app/ui/main_window_ui.py b/app/ui/main_window_ui.py index d9b39f0..5921406 100644 --- a/app/ui/main_window_ui.py +++ b/app/ui/main_window_ui.py @@ -228,60 +228,39 @@ class Ui_MainWindow(object): self.groupBoxIntroControls.setMaximumSize(QtCore.QSize(132, 46)) self.groupBoxIntroControls.setTitle("") self.groupBoxIntroControls.setObjectName("groupBoxIntroControls") - self.btnIntoStart = QtWidgets.QPushButton(parent=self.groupBoxIntroControls) - self.btnIntoStart.setGeometry(QtCore.QRect(0, 0, 44, 23)) - self.btnIntoStart.setMinimumSize(QtCore.QSize(44, 23)) - self.btnIntoStart.setMaximumSize(QtCore.QSize(44, 23)) - self.btnIntoStart.setObjectName("btnIntoStart") + self.btnPreviewStart = QtWidgets.QPushButton(parent=self.groupBoxIntroControls) + self.btnPreviewStart.setGeometry(QtCore.QRect(0, 0, 44, 23)) + self.btnPreviewStart.setMinimumSize(QtCore.QSize(44, 23)) + self.btnPreviewStart.setMaximumSize(QtCore.QSize(44, 23)) + self.btnPreviewStart.setObjectName("btnPreviewStart") self.btnIntroArm = QtWidgets.QPushButton(parent=self.groupBoxIntroControls) self.btnIntroArm.setGeometry(QtCore.QRect(44, 0, 44, 23)) self.btnIntroArm.setMinimumSize(QtCore.QSize(44, 23)) self.btnIntroArm.setMaximumSize(QtCore.QSize(44, 23)) self.btnIntroArm.setCheckable(True) self.btnIntroArm.setObjectName("btnIntroArm") - self.btnIntoEnd = QtWidgets.QPushButton(parent=self.groupBoxIntroControls) - self.btnIntoEnd.setGeometry(QtCore.QRect(88, 0, 44, 23)) - self.btnIntoEnd.setMinimumSize(QtCore.QSize(44, 23)) - self.btnIntoEnd.setMaximumSize(QtCore.QSize(44, 23)) - self.btnIntoEnd.setObjectName("btnIntoEnd") - self.btnIntroBack = QtWidgets.QPushButton(parent=self.groupBoxIntroControls) - self.btnIntroBack.setGeometry(QtCore.QRect(0, 23, 44, 23)) - self.btnIntroBack.setMinimumSize(QtCore.QSize(44, 23)) - self.btnIntroBack.setMaximumSize(QtCore.QSize(44, 23)) - self.btnIntroBack.setObjectName("btnIntroBack") - self.btnIntroMark = QtWidgets.QPushButton(parent=self.groupBoxIntroControls) - self.btnIntroMark.setGeometry(QtCore.QRect(44, 23, 44, 23)) - self.btnIntroMark.setMinimumSize(QtCore.QSize(44, 23)) - self.btnIntroMark.setMaximumSize(QtCore.QSize(44, 23)) - self.btnIntroMark.setObjectName("btnIntroMark") - self.btnIntroFwd = QtWidgets.QPushButton(parent=self.groupBoxIntroControls) - self.btnIntroFwd.setGeometry(QtCore.QRect(88, 23, 44, 23)) - self.btnIntroFwd.setMinimumSize(QtCore.QSize(44, 23)) - self.btnIntroFwd.setMaximumSize(QtCore.QSize(44, 23)) - self.btnIntroFwd.setObjectName("btnIntroFwd") + self.btnPreviewEnd = QtWidgets.QPushButton(parent=self.groupBoxIntroControls) + self.btnPreviewEnd.setGeometry(QtCore.QRect(88, 0, 44, 23)) + self.btnPreviewEnd.setMinimumSize(QtCore.QSize(44, 23)) + self.btnPreviewEnd.setMaximumSize(QtCore.QSize(44, 23)) + self.btnPreviewEnd.setObjectName("btnPreviewEnd") + self.btnPreviewBack = QtWidgets.QPushButton(parent=self.groupBoxIntroControls) + self.btnPreviewBack.setGeometry(QtCore.QRect(0, 23, 44, 23)) + self.btnPreviewBack.setMinimumSize(QtCore.QSize(44, 23)) + self.btnPreviewBack.setMaximumSize(QtCore.QSize(44, 23)) + self.btnPreviewBack.setObjectName("btnPreviewBack") + self.btnPreviewMark = QtWidgets.QPushButton(parent=self.groupBoxIntroControls) + self.btnPreviewMark.setGeometry(QtCore.QRect(44, 23, 44, 23)) + self.btnPreviewMark.setMinimumSize(QtCore.QSize(44, 23)) + self.btnPreviewMark.setMaximumSize(QtCore.QSize(44, 23)) + self.btnPreviewMark.setObjectName("btnPreviewMark") + self.btnPreviewFwd = QtWidgets.QPushButton(parent=self.groupBoxIntroControls) + self.btnPreviewFwd.setGeometry(QtCore.QRect(88, 23, 44, 23)) + self.btnPreviewFwd.setMinimumSize(QtCore.QSize(44, 23)) + self.btnPreviewFwd.setMaximumSize(QtCore.QSize(44, 23)) + self.btnPreviewFwd.setObjectName("btnPreviewFwd") self.verticalLayout_4.addWidget(self.groupBoxIntroControls) self.horizontalLayout.addWidget(self.FadeStopInfoFrame) - self.frame_toggleplayed_3db = QtWidgets.QFrame(parent=self.InfoFooterFrame) - self.frame_toggleplayed_3db.setMinimumSize(QtCore.QSize(152, 112)) - self.frame_toggleplayed_3db.setMaximumSize(QtCore.QSize(184, 16777215)) - self.frame_toggleplayed_3db.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) - self.frame_toggleplayed_3db.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) - self.frame_toggleplayed_3db.setObjectName("frame_toggleplayed_3db") - self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.frame_toggleplayed_3db) - self.verticalLayout_6.setObjectName("verticalLayout_6") - self.btnDrop3db = QtWidgets.QPushButton(parent=self.frame_toggleplayed_3db) - self.btnDrop3db.setMinimumSize(QtCore.QSize(132, 41)) - self.btnDrop3db.setMaximumSize(QtCore.QSize(164, 16777215)) - self.btnDrop3db.setCheckable(True) - self.btnDrop3db.setObjectName("btnDrop3db") - self.verticalLayout_6.addWidget(self.btnDrop3db) - self.btnHidePlayed = QtWidgets.QPushButton(parent=self.frame_toggleplayed_3db) - self.btnHidePlayed.setMinimumSize(QtCore.QSize(132, 41)) - self.btnHidePlayed.setMaximumSize(QtCore.QSize(164, 16777215)) - self.btnHidePlayed.setCheckable(True) - self.btnHidePlayed.setObjectName("btnHidePlayed") - self.verticalLayout_6.addWidget(self.btnHidePlayed) - self.horizontalLayout.addWidget(self.frame_toggleplayed_3db) self.frame_intro = QtWidgets.QFrame(parent=self.InfoFooterFrame) self.frame_intro.setMinimumSize(QtCore.QSize(152, 112)) self.frame_intro.setStyleSheet("") @@ -305,6 +284,27 @@ class Ui_MainWindow(object): self.label_intro_timer.setObjectName("label_intro_timer") self.verticalLayout_9.addWidget(self.label_intro_timer) self.horizontalLayout.addWidget(self.frame_intro) + self.frame_toggleplayed_3db = QtWidgets.QFrame(parent=self.InfoFooterFrame) + self.frame_toggleplayed_3db.setMinimumSize(QtCore.QSize(152, 112)) + self.frame_toggleplayed_3db.setMaximumSize(QtCore.QSize(184, 16777215)) + self.frame_toggleplayed_3db.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) + self.frame_toggleplayed_3db.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) + self.frame_toggleplayed_3db.setObjectName("frame_toggleplayed_3db") + self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.frame_toggleplayed_3db) + self.verticalLayout_6.setObjectName("verticalLayout_6") + self.btnDrop3db = QtWidgets.QPushButton(parent=self.frame_toggleplayed_3db) + self.btnDrop3db.setMinimumSize(QtCore.QSize(132, 41)) + self.btnDrop3db.setMaximumSize(QtCore.QSize(164, 16777215)) + self.btnDrop3db.setCheckable(True) + self.btnDrop3db.setObjectName("btnDrop3db") + self.verticalLayout_6.addWidget(self.btnDrop3db) + self.btnHidePlayed = QtWidgets.QPushButton(parent=self.frame_toggleplayed_3db) + self.btnHidePlayed.setMinimumSize(QtCore.QSize(132, 41)) + self.btnHidePlayed.setMaximumSize(QtCore.QSize(164, 16777215)) + self.btnHidePlayed.setCheckable(True) + self.btnHidePlayed.setObjectName("btnHidePlayed") + self.verticalLayout_6.addWidget(self.btnHidePlayed) + self.horizontalLayout.addWidget(self.frame_toggleplayed_3db) self.frame_fade = QtWidgets.QFrame(parent=self.InfoFooterFrame) self.frame_fade.setMinimumSize(QtCore.QSize(152, 112)) self.frame_fade.setStyleSheet("") @@ -406,7 +406,7 @@ class Ui_MainWindow(object): MainWindow.setStatusBar(self.statusbar) self.actionPlay_next = QtGui.QAction(parent=MainWindow) icon4 = QtGui.QIcon() - icon4.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon-play.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon4.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-play.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.actionPlay_next.setIcon(icon4) self.actionPlay_next.setObjectName("actionPlay_next") self.actionSkipToNext = QtGui.QAction(parent=MainWindow) @@ -416,17 +416,17 @@ class Ui_MainWindow(object): self.actionSkipToNext.setObjectName("actionSkipToNext") self.actionInsertTrack = QtGui.QAction(parent=MainWindow) icon6 = QtGui.QIcon() - icon6.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_search_database.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon6.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon_search_database.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.actionInsertTrack.setIcon(icon6) self.actionInsertTrack.setObjectName("actionInsertTrack") self.actionAdd_file = QtGui.QAction(parent=MainWindow) icon7 = QtGui.QIcon() - icon7.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_open_file.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon7.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon_open_file.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.actionAdd_file.setIcon(icon7) self.actionAdd_file.setObjectName("actionAdd_file") self.actionFade = QtGui.QAction(parent=MainWindow) icon8 = QtGui.QIcon() - icon8.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon-fade.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) + icon8.addPixmap(QtGui.QPixmap("app/ui/../../../../../../.designer/backup/icon-fade.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.actionFade.setIcon(icon8) self.actionFade.setObjectName("actionFade") self.actionStop = QtGui.QAction(parent=MainWindow) @@ -582,16 +582,16 @@ class Ui_MainWindow(object): self.lblTOD.setText(_translate("MainWindow", "00:00:00")) self.label_elapsed_timer.setText(_translate("MainWindow", "00:00 / 00:00")) self.btnPreview.setText(_translate("MainWindow", " Preview")) - self.btnIntoStart.setText(_translate("MainWindow", "<<")) + self.btnPreviewStart.setText(_translate("MainWindow", "<<")) self.btnIntroArm.setText(_translate("MainWindow", "o")) - self.btnIntoEnd.setText(_translate("MainWindow", ">>")) - self.btnIntroBack.setText(_translate("MainWindow", "<")) - self.btnIntroMark.setText(_translate("MainWindow", "*")) - self.btnIntroFwd.setText(_translate("MainWindow", ">")) + self.btnPreviewEnd.setText(_translate("MainWindow", ">>")) + self.btnPreviewBack.setText(_translate("MainWindow", "<")) + self.btnPreviewMark.setText(_translate("MainWindow", "*")) + self.btnPreviewFwd.setText(_translate("MainWindow", ">")) + self.label_7.setText(_translate("MainWindow", "Intro")) + self.label_intro_timer.setText(_translate("MainWindow", "0:0")) self.btnDrop3db.setText(_translate("MainWindow", "-3dB to talk")) self.btnHidePlayed.setText(_translate("MainWindow", "Hide played")) - self.label_7.setText(_translate("MainWindow", "Intro")) - self.label_intro_timer.setText(_translate("MainWindow", "00:00")) self.label_4.setText(_translate("MainWindow", "Fade")) self.label_fade_timer.setText(_translate("MainWindow", "00:00")) self.label_5.setText(_translate("MainWindow", "Silent"))