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"))