""" Simulator Display GUI Simplified visual representation showing only channel volumes. """ from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QLabel, QFrame, QPushButton, QSlider, QGroupBox) from PyQt5.QtCore import Qt, QTimer, pyqtSlot from PyQt5.QtGui import QPainter, QColor, QBrush, QPen, QFont class SynthWidget(QFrame): """Individual synth display widget - simplified to show only volume""" def __init__(self, channel: int): super().__init__() self.channel = channel self.channel_volume_midi = 100 # 0-127 MIDI CC7 value self.setFrameStyle(QFrame.Box) self.setFixedSize(100, 80) self.setStyleSheet(""" QFrame { border: 2px solid #404040; border-radius: 6px; background-color: #2a2a2a; } """) def set_channel_volume(self, volume_midi: int): """Set channel volume (0-127 MIDI CC7) - this is what controls brightness""" self.channel_volume_midi = max(0, min(127, volume_midi)) self.update() # Trigger repaint def paintEvent(self, event): """Custom paint for volume-based lighting""" super().paintEvent(event) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # Calculate brightness based on MIDI volume (0-127) brightness_intensity = int((self.channel_volume_midi / 127.0) * 255) # Create color based on channel (different hues) hue = (self.channel - 1) * 360 / 16 # Distribute hues across spectrum color = QColor.fromHsv(int(hue), 200, brightness_intensity + 20) # Draw volume-based lighting effect if self.channel_volume_midi > 6: # Only show if volume is above ~5% (6/127) # Draw volume glow glow_rect = self.rect().adjusted(8, 8, -8, -8) painter.setBrush(QBrush(color)) painter.setPen(QPen(color.lighter(120), 1)) painter.drawRoundedRect(glow_rect, 4, 4) # Draw channel info painter.setPen(QPen(QColor(255, 255, 255), 1)) painter.setFont(QFont("Arial", 11, QFont.Bold)) # Channel number painter.drawText(10, 20, f"Ch {self.channel}") # Volume level (MIDI CC7 value) painter.setFont(QFont("Arial", 9)) painter.drawText(10, 65, f"Vol: {self.channel_volume_midi}") class SimulatorDisplay(QWidget): """Simplified simulator display widget""" def __init__(self, simulator, channel_manager): super().__init__() self.simulator = simulator self.channel_manager = channel_manager self.synth_widgets = {} self.current_synth_count = 8 self.setup_ui() self.connect_signals() def setup_ui(self): """Set up the user interface""" layout = QVBoxLayout(self) # Title title = QLabel("Synth Array - Channel Volume Display") title.setStyleSheet("font-size: 14px; font-weight: bold; color: #ffffff; margin: 5px;") title.setAlignment(Qt.AlignCenter) layout.addWidget(title) # Synth display grid self.synth_grid_widget = QWidget() self.synth_grid_layout = QGridLayout(self.synth_grid_widget) self.synth_grid_layout.setSpacing(5) layout.addWidget(self.synth_grid_widget) # Initialize with default synth count self.create_synth_widgets(self.current_synth_count) def connect_signals(self): """Connect signals""" # Channel manager signals if hasattr(self.channel_manager, 'volume_changed'): self.channel_manager.volume_changed.connect(self.on_channel_volume_changed) def create_synth_widgets(self, count: int): """Create synth widgets for display""" # Clear existing widgets for widget in self.synth_widgets.values(): widget.deleteLater() self.synth_widgets.clear() # Clear layout while self.synth_grid_layout.count(): child = self.synth_grid_layout.takeAt(0) if child.widget(): child.widget().deleteLater() # Create new widgets cols = 4 # 4 synths per row for i in range(count): channel = i + 1 widget = SynthWidget(channel) row = i // cols col = i % cols self.synth_grid_layout.addWidget(widget, row, col) self.synth_widgets[channel] = widget self.current_synth_count = count @pyqtSlot(int) def set_synth_count(self, count: int): """Set number of synths to display""" if count != self.current_synth_count: self.create_synth_widgets(count) @pyqtSlot(int, float) def on_channel_volume_changed(self, channel: int, volume: float): """Handle channel volume change""" if channel in self.synth_widgets: self.synth_widgets[channel].set_channel_volume(volume) @pyqtSlot(int, int, int, float) def on_note_played(self, channel: int, note: int, velocity: int, duration: float): """Handle note played - we don't need special handling for this in volume mode""" # In the simplified version, we only care about volume, not individual notes pass @pyqtSlot(int, int) def on_midi_volume_changed(self, channel: int, volume: int): """Handle MIDI volume change (CC7) - channel 1-16, volume 0-127""" if channel in self.synth_widgets: self.synth_widgets[channel].set_channel_volume(volume) @pyqtSlot(int, float, float) @pyqtSlot(dict) def update_lighting(self, lighting_data: dict): """Update lighting based on channel volume from simulator engine""" for channel, brightness in lighting_data.items(): if channel in self.synth_widgets: # Convert brightness (0-1) to MIDI (0-127) for display consistency midi_volume = int(brightness * 127) self.synth_widgets[channel].set_channel_volume(midi_volume) def update_channel_volumes_from_output_manager(self, output_manager): """Update all channel volumes from output manager""" for channel in range(1, self.current_synth_count + 1): volume = output_manager.get_channel_volume(channel) / 127.0 # Convert from MIDI to 0-1 if channel in self.synth_widgets: self.synth_widgets[channel].set_channel_volume(volume)