Overhaul: 5-task implementation

- Remove TURN decks (3/4), clean up MIDI, layout
- Single cart player with 3-item autoload queue + continue toggle
- Event logging (SessionLogger), mic tracking, large mic timer widget
- LUFS metering moved from external ports to internal master mix
- Master output WAV recording with top-bar REC toggle
- Music mix output (ch 3-8 only, no mics)
- Preview bar for playlist auditioning
- Merged show log with [SONG][CART][TALK] prefixes
- Live CSV writer for OBS song display (~/live_songs.csv)
- Misc fixes: preview race condition, datetime import for recording
This commit is contained in:
2026-07-17 20:44:15 +10:00
parent da8240a9e2
commit 0277089b2d
13 changed files with 1015 additions and 326 deletions
+66 -6
View File
@@ -410,8 +410,8 @@ class LUFSExpandedSection(QFrame):
self.engine = engine
self._master = engine.mixer['master']
self.setFixedWidth(200)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
self.setMinimumWidth(160)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.setStyleSheet("""
LUFSExpandedSection {
background-color: #181a1e;
@@ -484,6 +484,70 @@ class LUFSExpandedSection(QFrame):
pass
class MicTimerWidget(QFrame):
def __init__(self, engine, parent=None):
super().__init__(parent)
self.engine = engine
self.setMinimumWidth(120)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.setStyleSheet("""
MicTimerWidget {
background-color: #181a1e;
border: 1px solid #282a30;
border-radius: 6px;
}
""")
root = QVBoxLayout(self)
root.setContentsMargins(8, 12, 8, 12)
root.setSpacing(4)
header = QLabel("MIC ON TIME")
header.setFont(QFont("Segoe UI", 9, QFont.Bold))
header.setAlignment(Qt.AlignCenter)
header.setStyleSheet("color: #556670; background: transparent; border: none;")
root.addWidget(header)
self.duration_label = QLabel("00:00")
self.duration_label.setFont(QFont("Courier New", 28, QFont.Bold))
self.duration_label.setAlignment(Qt.AlignCenter)
self.duration_label.setStyleSheet("color: #00cc66; background: transparent; border: none;")
root.addWidget(self.duration_label, stretch=1)
self.status_label = QLabel("MIC OFF")
self.status_label.setFont(QFont("Segoe UI", 10, QFont.Bold))
self.status_label.setAlignment(Qt.AlignCenter)
self.status_label.setStyleSheet("color: #444; background: transparent; border: none;")
root.addWidget(self.status_label)
self.timer = QTimer(self)
self.timer.setInterval(100)
self.timer.timeout.connect(self._refresh)
self.timer.start()
def _refresh(self):
dur = self.engine.get_mic_duration()
channels = self.engine.mixer['channels']
any_on = any(ch.mic_on for ch in channels[:2])
if any_on:
mins = int(dur // 60)
secs = int(dur % 60)
self.duration_label.setText(f"{mins:02d}:{secs:02d}")
self.duration_label.setStyleSheet("color: #00ff41; background: transparent; border: none;")
self.status_label.setText("MIC LIVE")
self.status_label.setStyleSheet("color: #ff4444; font-weight: bold; background: transparent; border: none;")
else:
self.duration_label.setText("00:00")
self.duration_label.setStyleSheet("color: #333; background: transparent; border: none;")
self.status_label.setText("MIC OFF")
self.status_label.setStyleSheet("color: #444; background: transparent; border: none;")
def apply_theme(self, theme: dict):
pass
class MixerWidget(QFrame):
def __init__(self, engine, parent=None):
super().__init__(parent)
@@ -548,9 +612,6 @@ class MixerWidget(QFrame):
self.master_pre = MasterPreSection(self.engine)
channels_layout.addWidget(self.master_pre)
self.lufs_section = LUFSExpandedSection(self.engine)
channels_layout.addWidget(self.lufs_section)
scroll.setWidget(scroll_content)
root.addWidget(scroll)
@@ -558,7 +619,6 @@ class MixerWidget(QFrame):
for strip in self._strips:
strip.apply_theme(theme)
self.master_pre.apply_theme(theme)
self.lufs_section.apply_theme(theme)
def refresh_strips(self):
for strip in self._strips: