115 lines
3.8 KiB
Python
115 lines
3.8 KiB
Python
# lufs_meter_widget.py
|
|||
|
|
|
||
|
|
from PySide6.QtWidgets import QFrame, QVBoxLayout, QLabel
|
||
|
|
from PySide6.QtCore import Qt, QTimer, QRect
|
||
|
|
from PySide6.QtGui import QFont, QPainter, QColor, QLinearGradient
|
||
|
|
|
||
|
|
|
||
|
|
class LUFSMeterWidget(QFrame):
|
||
|
|
"""
|
||
|
|
Vertical LUFS meter for broadcast monitoring.
|
||
|
|
EBU R128 standard: Target -23 LUFS ±1 LU (Loudness Units).
|
||
|
|
"""
|
||
|
|
|
||
|
|
def __init__(self, engine, parent=None):
|
||
|
|
super().__init__(parent)
|
||
|
|
self.engine = engine
|
||
|
|
|
||
|
|
self.setFixedWidth(80)
|
||
|
|
self.setFrameStyle(QFrame.Box | QFrame.Raised)
|
||
|
|
self.setStyleSheet("""
|
||
|
|
QFrame {
|
||
|
|
background-color: #0d0d0d;
|
||
|
|
border: 2px solid #444444;
|
||
|
|
border-radius: 6px;
|
||
|
|
}
|
||
|
|
""")
|
||
|
|
|
||
|
|
self._build_ui()
|
||
|
|
|
||
|
|
self.timer = QTimer(self)
|
||
|
|
self.timer.setInterval(100)
|
||
|
|
self.timer.timeout.connect(self.update)
|
||
|
|
self.timer.start()
|
||
|
|
|
||
|
|
def _build_ui(self):
|
||
|
|
layout = QVBoxLayout(self)
|
||
|
|
layout.setContentsMargins(6, 8, 6, 8)
|
||
|
|
|
||
|
|
header = QLabel("LUFS")
|
||
|
|
header.setFont(QFont("Arial", 10, QFont.Bold))
|
||
|
|
header.setStyleSheet("color: #888888; background: transparent; border: none;")
|
||
|
|
header.setAlignment(Qt.AlignCenter)
|
||
|
|
layout.addWidget(header)
|
||
|
|
|
||
|
|
# Meter canvas
|
||
|
|
self.meter_canvas = QFrame()
|
||
|
|
self.meter_canvas.setStyleSheet("""
|
||
|
|
QFrame {
|
||
|
|
background-color: #000000;
|
||
|
|
border: 1px solid #333333;
|
||
|
|
border-radius: 3px;
|
||
|
|
}
|
||
|
|
""")
|
||
|
|
layout.addWidget(self.meter_canvas, stretch=1)
|
||
|
|
|
||
|
|
# Numeric readout
|
||
|
|
self.readout = QLabel("-70.0")
|
||
|
|
self.readout.setFont(QFont("Courier New", 12, QFont.Bold))
|
||
|
|
self.readout.setStyleSheet("color: #00ff41; background: transparent; border: none;")
|
||
|
|
self.readout.setAlignment(Qt.AlignCenter)
|
||
|
|
layout.addWidget(self.readout)
|
||
|
|
|
||
|
|
def paintEvent(self, event):
|
||
|
|
super().paintEvent(event)
|
||
|
|
|
||
|
|
lufs = self.engine.lufs_current
|
||
|
|
self.readout.setText(f"{lufs:.1f}")
|
||
|
|
|
||
|
|
painter = QPainter(self.meter_canvas)
|
||
|
|
rect = self.meter_canvas.rect().adjusted(2, 2, -2, -2)
|
||
|
|
|
||
|
|
# LUFS scale: -70 (bottom) to 0 (top)
|
||
|
|
# Target zone: -24 to -22 (±1 LU around -23)
|
||
|
|
lufs_range = 70.0 # -70 to 0
|
||
|
|
|
||
|
|
if lufs < -70:
|
||
|
|
lufs = -70
|
||
|
|
if lufs > 0:
|
||
|
|
lufs = 0
|
||
|
|
|
||
|
|
fill_pct = (lufs + 70) / lufs_range
|
||
|
|
fill_h = int(rect.height() * fill_pct)
|
||
|
|
|
||
|
|
# Draw gradient meter
|
||
|
|
gradient = QLinearGradient(0, rect.height(), 0, 0)
|
||
|
|
|
||
|
|
# Color zones (broadcast standard)
|
||
|
|
if lufs < -30:
|
||
|
|
gradient.setColorAt(0, QColor("#003300")) # Very quiet - dark green
|
||
|
|
gradient.setColorAt(1, QColor("#005500"))
|
||
|
|
elif lufs < -24:
|
||
|
|
gradient.setColorAt(0, QColor("#00aa00")) # Quiet - green
|
||
|
|
gradient.setColorAt(1, QColor("#00dd00"))
|
||
|
|
elif lufs < -22:
|
||
|
|
gradient.setColorAt(0, QColor("#00ff00")) # Target zone - bright green
|
||
|
|
gradient.setColorAt(1, QColor("#00ff00"))
|
||
|
|
elif lufs < -9:
|
||
|
|
gradient.setColorAt(0, QColor("#ffcc00")) # Getting hot - yellow
|
||
|
|
gradient.setColorAt(1, QColor("#ffaa00"))
|
||
|
|
else:
|
||
|
|
gradient.setColorAt(0, QColor("#ff0000")) # Too hot - red
|
||
|
|
gradient.setColorAt(1, QColor("#ff0000"))
|
||
|
|
|
||
|
|
painter.fillRect(
|
||
|
|
rect.x(),
|
||
|
|
rect.y() + rect.height() - fill_h,
|
||
|
|
rect.width(),
|
||
|
|
fill_h,
|
||
|
|
gradient
|
||
|
|
)
|
||
|
|
|
||
|
|
# Draw target line at -23 LUFS
|
||
|
|
target_y = rect.y() + int(rect.height() * (1.0 - ((-23 + 70) / lufs_range)))
|
||
|
|
painter.setPen(QColor("#ffffff"))
|
||
|
|
painter.drawLine(rect.x(), target_y, rect.x() + rect.width(), target_y)
|