From 1d33622c13462d3e5685cdba8f94387eee4bf4cd Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Mon, 6 May 2024 16:30:02 +0100 Subject: [PATCH] WIP: time to vocals --- app/music.py | 11 +- app/musicmuster.py | 52 +++--- app/ui/main_window.ui | 337 ++++++++++++++++++++++++++++++++------- app/ui/main_window_ui.py | 111 ++++++++++--- 4 files changed, 407 insertions(+), 104 deletions(-) diff --git a/app/music.py b/app/music.py index 18b6197..439107c 100644 --- a/app/music.py +++ b/app/music.py @@ -58,10 +58,11 @@ class Music: Manage the playing of music tracks """ - def __init__(self) -> None: + def __init__(self, name) -> None: self.VLC = vlc.Instance() - self.VLC.set_user_agent = (Config.VLC_MAIN_PLAYER_NAME, Config.VLC_MAIN_PLAYER_NAME) + self.VLC.set_user_agent(name, name) self.player = None + self.name = name self.max_volume = Config.VLC_VOLUME_DEFAULT self.start_dt: Optional[dt.datetime] = None @@ -73,7 +74,7 @@ class Music: to hold up the UI during the fade. """ - log.info("Music.stop()") + log.info(f"Music[{self.name}].stop()") if not self.player: return @@ -137,7 +138,7 @@ class Music: Log and return if path not found. """ - log.info(f"Music.play({path=}, {position=}") + log.info(f"Music[{self.name}].play({path=}, {position=}") if file_is_unreadable(path): log.error(f"play({path}): path not readable") @@ -181,7 +182,7 @@ class Music: def stop(self) -> float: """Immediately stop playing""" - log.info("Music.stop()") + log.info(f"Music[{self.name}].stop()") if not self.player: return 0.0 diff --git a/app/musicmuster.py b/app/musicmuster.py index c26aefc..6e7560d 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -47,7 +47,7 @@ from PyQt6.QtWidgets import ( ) # Third party imports -from pygame import mixer +# from pygame import mixer import pipeclient from sqlalchemy.exc import IntegrityError from sqlalchemy.orm.session import Session @@ -226,10 +226,12 @@ class Window(QMainWindow, Ui_MainWindow): self.setupUi(self) self.timer10: QTimer = QTimer() + self.timer100: QTimer = QTimer() self.timer500: QTimer = QTimer() self.timer1000: QTimer = QTimer() - self.music: music.Music = music.Music() + self.music: music.Music = music.Music(name=Config.VLC_MAIN_PLAYER_NAME) + self.preview_player: music.Music = music.Music(name=Config.VLC_PREVIEW_PLAYER_NAME) self.playing: bool = False self.set_main_window_size() @@ -239,11 +241,6 @@ class Window(QMainWindow, Ui_MainWindow): self.txtSearch.setHidden(True) self.statusbar.addWidget(self.txtSearch) self.hide_played_tracks = False - try: - mixer.init() - except Exception: - helpers.show_warning(self, "Fatal error", "Cannot initialise sound device") - sys.exit(1) self.widgetFadeVolume.hideAxis("bottom") self.widgetFadeVolume.hideAxis("left") @@ -267,6 +264,7 @@ class Window(QMainWindow, Ui_MainWindow): self.disable_selection_timing = False self.clock_counter = 0 self.timer10.start(10) + self.timer100.start(100) self.timer500.start(500) self.timer1000.start(1000) self.signals = MusicMusterSignals() @@ -306,7 +304,7 @@ class Window(QMainWindow, Ui_MainWindow): btn.path = cart.path btn.player = self.music.VLC.media_player_new(cart.path) if btn.player: - btn.player.audio_set_volume(Config.VOLUME_VLC_DEFAULT) + btn.player.audio_set_volume(Config.VLC_VOLUME_DEFAULT) if cart.enabled: btn.setEnabled(True) btn.pgb.setVisible(True) @@ -420,7 +418,7 @@ class Window(QMainWindow, Ui_MainWindow): btn.setEnabled(True) # Setting to position 0 doesn't seem to work btn.player = self.music.VLC.media_player_new(btn.path) - btn.player.audio_set_volume(Config.VOLUME_VLC_DEFAULT) + btn.player.audio_set_volume(Config.VLC_VOLUME_DEFAULT) colour = Config.COLOUR_CART_READY btn.setStyleSheet("background-color: " + colour + ";\n") btn.pgb.setValue(0) @@ -598,6 +596,7 @@ class Window(QMainWindow, Ui_MainWindow): self.timer10.timeout.connect(self.tick_10ms) self.timer500.timeout.connect(self.tick_500ms) + self.timer100.timeout.connect(self.tick_100ms) self.timer1000.timeout.connect(self.tick_1000ms) def create_playlist( @@ -717,7 +716,7 @@ class Window(QMainWindow, Ui_MainWindow): if self.btnDrop3db.isChecked(): self.music.set_volume(Config.VLC_VOLUME_DROP3db, set_default=False) else: - self.music.set_volume(Config.VOLUME_VLC_DEFAULT, set_default=False) + self.music.set_volume(Config.VLC_VOLUME_DEFAULT, set_default=False) def enable_escape(self, enabled: bool) -> None: """ @@ -1237,9 +1236,7 @@ class Window(QMainWindow, Ui_MainWindow): def preview(self) -> None: """ - Preview selected or next track. We use a different mechanism to - normal track playing so that the user can route the output audio - differently (eg, to headphones). + Preview selected or next track. """ if self.btnPreview.isChecked(): @@ -1251,11 +1248,10 @@ class Window(QMainWindow, Ui_MainWindow): if not track_path: self.btnPreview.setChecked(False) return - mixer.music.load(track_path) - mixer.music.play() + self.preview_player.play(path=track_path) else: - mixer.music.stop() + self.preview_player.stop() def rename_playlist(self) -> None: """ @@ -1675,14 +1671,32 @@ class Window(QMainWindow, Ui_MainWindow): # Update carts # self.cart_tick() + def tick_100ms(self) -> None: + """ + Called every 100ms + """ + + # 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}") + def tick_1000ms(self) -> None: """ Called every 1000ms """ - # Ensure preview button is reset if preview finishes playing - self.btnPreview.setChecked(mixer.music.get_busy()) - # Only update play clocks once a second so that their updates # are synchronised (otherwise it looks odd) diff --git a/app/ui/main_window.ui b/app/ui/main_window.ui index 88863b2..e2e4302 100644 --- a/app/ui/main_window.ui +++ b/app/ui/main_window.ui @@ -229,10 +229,16 @@ padding-left: 8px; + + + 0 + 131 + + 230 - 16777215 + 131 @@ -241,13 +247,13 @@ padding-left: 8px; QFrame::Raised - - + + 208 - 109 + 0 @@ -263,6 +269,27 @@ padding-left: 8px; + + + + + FreeSans + 18 + 50 + false + + + + color: black; + + + 00:00 / 00:00 + + + Qt::AlignCenter + + + @@ -435,24 +462,175 @@ padding-left: 8px; - - - - FreeSans - 18 - 50 - false - + + + + 132 + 46 + - - color: black; + + + 132 + 46 + - - 00:00 / 00:00 - - - Qt::AlignCenter + + + + + + 0 + 0 + 44 + 23 + + + + + 44 + 23 + + + + + 44 + 23 + + + + << + + + + + + 44 + 0 + 44 + 23 + + + + + 44 + 23 + + + + + 44 + 23 + + + + o + + + true + + + + + + 88 + 0 + 44 + 23 + + + + + 44 + 23 + + + + + 44 + 23 + + + + >> + + + + + + 0 + 23 + 44 + 23 + + + + + 44 + 23 + + + + + 44 + 23 + + + + < + + + + + + 44 + 23 + 44 + 23 + + + + + 44 + 23 + + + + + 44 + 23 + + + + * + + + + + + 88 + 23 + 44 + 23 + + + + + 44 + 23 + + + + + 44 + 23 + + + + > + + @@ -526,6 +704,55 @@ padding-left: 8px; + + + + + 152 + 112 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Intro + + + Qt::AlignCenter + + + + + + + + FreeSans + 40 + 50 + false + + + + 00:00 + + + Qt::AlignCenter + + + + + + @@ -592,46 +819,36 @@ padding-left: 8px; QFrame::Raised - - - - 10 - 10 - 45 - 24 - - - - Silent - - - Qt::AlignCenter - - - - - - 10 - 48 - 132 - 54 - - - - - FreeSans - 40 - 50 - false - - - - 00:00 - - - Qt::AlignCenter - - + + + + + Silent + + + Qt::AlignCenter + + + + + + + + FreeSans + 40 + 50 + false + + + + 00:00 + + + Qt::AlignCenter + + + + @@ -661,7 +878,7 @@ padding-left: 8px; 151 - 16777215 + 112 diff --git a/app/ui/main_window_ui.py b/app/ui/main_window_ui.py index 34f45e1..d9b39f0 100644 --- a/app/ui/main_window_ui.py +++ b/app/ui/main_window_ui.py @@ -132,20 +132,32 @@ class Ui_MainWindow(object): self.verticalLayout.addWidget(self.hdrNextTrack) self.horizontalLayout_3.addLayout(self.verticalLayout) self.frame_2 = QtWidgets.QFrame(parent=self.centralwidget) - self.frame_2.setMaximumSize(QtCore.QSize(230, 16777215)) + self.frame_2.setMinimumSize(QtCore.QSize(0, 131)) + self.frame_2.setMaximumSize(QtCore.QSize(230, 131)) self.frame_2.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) self.frame_2.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) self.frame_2.setObjectName("frame_2") - self.gridLayout_2 = QtWidgets.QGridLayout(self.frame_2) - self.gridLayout_2.setObjectName("gridLayout_2") + self.verticalLayout_10 = QtWidgets.QVBoxLayout(self.frame_2) + self.verticalLayout_10.setObjectName("verticalLayout_10") self.lblTOD = QtWidgets.QLabel(parent=self.frame_2) - self.lblTOD.setMinimumSize(QtCore.QSize(208, 109)) + self.lblTOD.setMinimumSize(QtCore.QSize(208, 0)) font = QtGui.QFont() font.setPointSize(35) self.lblTOD.setFont(font) self.lblTOD.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.lblTOD.setObjectName("lblTOD") - self.gridLayout_2.addWidget(self.lblTOD, 0, 0, 1, 1) + self.verticalLayout_10.addWidget(self.lblTOD) + self.label_elapsed_timer = QtWidgets.QLabel(parent=self.frame_2) + font = QtGui.QFont() + font.setFamily("FreeSans") + font.setPointSize(18) + font.setBold(False) + font.setWeight(50) + self.label_elapsed_timer.setFont(font) + self.label_elapsed_timer.setStyleSheet("color: black;") + self.label_elapsed_timer.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.label_elapsed_timer.setObjectName("label_elapsed_timer") + self.verticalLayout_10.addWidget(self.label_elapsed_timer) self.horizontalLayout_3.addWidget(self.frame_2) self.gridLayout_4.addLayout(self.horizontalLayout_3, 0, 0, 1, 1) self.frame_4 = QtWidgets.QFrame(parent=self.centralwidget) @@ -211,17 +223,43 @@ class Ui_MainWindow(object): self.btnPreview.setCheckable(True) self.btnPreview.setObjectName("btnPreview") self.verticalLayout_4.addWidget(self.btnPreview) - self.label_elapsed_timer = QtWidgets.QLabel(parent=self.FadeStopInfoFrame) - font = QtGui.QFont() - font.setFamily("FreeSans") - font.setPointSize(18) - font.setBold(False) - font.setWeight(50) - self.label_elapsed_timer.setFont(font) - self.label_elapsed_timer.setStyleSheet("color: black;") - self.label_elapsed_timer.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) - self.label_elapsed_timer.setObjectName("label_elapsed_timer") - self.verticalLayout_4.addWidget(self.label_elapsed_timer) + self.groupBoxIntroControls = QtWidgets.QGroupBox(parent=self.FadeStopInfoFrame) + self.groupBoxIntroControls.setMinimumSize(QtCore.QSize(132, 46)) + 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.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.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)) @@ -244,6 +282,29 @@ class Ui_MainWindow(object): 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("") + self.frame_intro.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) + self.frame_intro.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) + self.frame_intro.setObjectName("frame_intro") + self.verticalLayout_9 = QtWidgets.QVBoxLayout(self.frame_intro) + self.verticalLayout_9.setObjectName("verticalLayout_9") + self.label_7 = QtWidgets.QLabel(parent=self.frame_intro) + self.label_7.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.label_7.setObjectName("label_7") + self.verticalLayout_9.addWidget(self.label_7) + self.label_intro_timer = QtWidgets.QLabel(parent=self.frame_intro) + font = QtGui.QFont() + font.setFamily("FreeSans") + font.setPointSize(40) + font.setBold(False) + font.setWeight(50) + self.label_intro_timer.setFont(font) + self.label_intro_timer.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.label_intro_timer.setObjectName("label_intro_timer") + self.verticalLayout_9.addWidget(self.label_intro_timer) + self.horizontalLayout.addWidget(self.frame_intro) self.frame_fade = QtWidgets.QFrame(parent=self.InfoFooterFrame) self.frame_fade.setMinimumSize(QtCore.QSize(152, 112)) self.frame_fade.setStyleSheet("") @@ -273,12 +334,13 @@ class Ui_MainWindow(object): self.frame_silent.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) self.frame_silent.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) self.frame_silent.setObjectName("frame_silent") + self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.frame_silent) + self.verticalLayout_7.setObjectName("verticalLayout_7") self.label_5 = QtWidgets.QLabel(parent=self.frame_silent) - self.label_5.setGeometry(QtCore.QRect(10, 10, 45, 24)) self.label_5.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.label_5.setObjectName("label_5") + self.verticalLayout_7.addWidget(self.label_5) self.label_silent_timer = QtWidgets.QLabel(parent=self.frame_silent) - self.label_silent_timer.setGeometry(QtCore.QRect(10, 48, 132, 54)) font = QtGui.QFont() font.setFamily("FreeSans") font.setPointSize(40) @@ -287,6 +349,7 @@ class Ui_MainWindow(object): self.label_silent_timer.setFont(font) self.label_silent_timer.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.label_silent_timer.setObjectName("label_silent_timer") + self.verticalLayout_7.addWidget(self.label_silent_timer) self.horizontalLayout.addWidget(self.frame_silent) self.widgetFadeVolume = PlotWidget(parent=self.InfoFooterFrame) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) @@ -299,7 +362,7 @@ class Ui_MainWindow(object): self.horizontalLayout.addWidget(self.widgetFadeVolume) self.frame = QtWidgets.QFrame(parent=self.InfoFooterFrame) self.frame.setMinimumSize(QtCore.QSize(151, 0)) - self.frame.setMaximumSize(QtCore.QSize(151, 16777215)) + self.frame.setMaximumSize(QtCore.QSize(151, 112)) self.frame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) self.frame.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) self.frame.setObjectName("frame") @@ -517,10 +580,18 @@ class Ui_MainWindow(object): self.current_track_2.setText(_translate("MainWindow", "Current track:")) self.next_track_2.setText(_translate("MainWindow", "Next track:")) self.lblTOD.setText(_translate("MainWindow", "00:00:00")) - self.btnPreview.setText(_translate("MainWindow", " Preview")) self.label_elapsed_timer.setText(_translate("MainWindow", "00:00 / 00:00")) + self.btnPreview.setText(_translate("MainWindow", " Preview")) + self.btnIntoStart.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.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"))