From f2dcdabf29b0fbe2b82c5f54fbb4e8ed2f2b7012 Mon Sep 17 00:00:00 2001 From: jimmy Date: Fri, 19 Sep 2025 00:02:51 +1200 Subject: [PATCH] Fix indentation errors and reduce debug output - Comment out all debug logging statements to reduce console noise - Fix empty if/else blocks by adding pass statements - Remove beat logging, TCP server logging, and MIDI debug messages - Keep only essential info, warning, and error messages - Revert radiate delay separation back to using main delay parameter --- src/bar_config.py | 2 +- src/main.py | 93 ++++++++++++++++++++++++----- src/midi.py | 149 +++++++++++++++++++++++++++++++++------------- 3 files changed, 186 insertions(+), 58 deletions(-) diff --git a/src/bar_config.py b/src/bar_config.py index fd1fcb6..a6a987c 100644 --- a/src/bar_config.py +++ b/src/bar_config.py @@ -25,7 +25,7 @@ DEFAULT_BAR_SETTINGS = { "pattern": "pulse", "delay": 100, "colors": [(0, 255, 0)], # Default green - "brightness": 100, + "brightness": 10, "num_leds": 200, "n1": 10, "n2": 10, diff --git a/src/main.py b/src/main.py index 602f701..03e01e8 100644 --- a/src/main.py +++ b/src/main.py @@ -36,7 +36,9 @@ class App: # --- MIDI Handler --- MIDI_PORT_INDEX = 1 # Adjust as needed WEBSOCKET_SERVER_URI = "ws://192.168.4.1:80/ws" + print(f"Initializing MIDI handler with port index {MIDI_PORT_INDEX}") self.midi_handler = MidiHandler(MIDI_PORT_INDEX, WEBSOCKET_SERVER_URI) + print("MIDI handler initialized") self.midi_task: asyncio.Task | None = None # Start MIDI in background self.root.after(0, async_handler(self.start_midi)) @@ -61,12 +63,12 @@ class App: # Row1: n1 (left), n2 (right) # Row2: B (left), Brightness (right) # Row3: R (left), G (right) - dials_frame = ttk.LabelFrame(controls_frame, text="Dials (CC)") + dials_frame = ttk.LabelFrame(controls_frame, text="Dials (CC30-37)") dials_frame.pack(side="left", padx=12) for c in range(2): - dials_frame.grid_columnconfigure(c, minsize=180) + dials_frame.grid_columnconfigure(c, minsize=140) for rr in range(4): - dials_frame.grid_rowconfigure(rr, minsize=100) + dials_frame.grid_rowconfigure(rr, minsize=70) self.dials_boxes: list[tk.Label] = [] # Create with placeholders so they are visible before first update @@ -87,19 +89,63 @@ class App: text=placeholders.get((r, c), "-"), bg=bg_color, fg=fg_color, - font=("Arial", 16), + font=("Arial", 14), padx=6, pady=6, borderwidth=2, relief="ridge", - width=20, - height=5, + width=14, + height=4, anchor="center", justify="center", ) lbl.grid(row=r, column=c, padx=6, pady=6, sticky="nsew") self.dials_boxes.append(lbl) + # Additional knobs box: 4 rows by 2 columns (CC38-45) + # Row0: K1 (left), K2 (right) + # Row1: K3 (left), K4 (right) + # Row2: K5 (left), K6 (right) + # Row3: K7 (left), K8 (right) + knobs_frame = ttk.LabelFrame(controls_frame, text="Knobs (CC38-45)") + knobs_frame.pack(side="left", padx=12) + for c in range(2): + knobs_frame.grid_columnconfigure(c, minsize=140) + for rr in range(4): + knobs_frame.grid_rowconfigure(rr, minsize=70) + + self.knobs_boxes: list[tk.Label] = [] + # Create with placeholders so they are visible before first update + knob_placeholders = { + (0, 0): "CC44\n-", + (0, 1): "CC45\n-", + (1, 0): "Rad n1\n-", + (1, 1): "Rad delay\n-", + (2, 0): "Alt n1\n-", + (2, 1): "Alt n2\n-", + (3, 0): "Pulse n1\n-", + (3, 1): "Pulse n2\n-", + } + for r in range(4): + for c in range(2): + lbl = tk.Label( + knobs_frame, + text=knob_placeholders.get((r, c), "-"), + bg=bg_color, + fg=fg_color, + font=("Arial", 14), + padx=6, + pady=6, + borderwidth=2, + relief="ridge", + width=14, + height=4, + anchor="center", + justify="center", + ) + lbl.grid(row=r, column=c, padx=6, pady=6, sticky="nsew") + self.knobs_boxes.append(lbl) + # Buttons bank (single) buttons_frame = ttk.Frame(controls_frame) buttons_frame.pack(side="left", padx=12) @@ -107,9 +153,9 @@ class App: buttons1_frame = ttk.LabelFrame(buttons_frame, text="Buttons (notes 36-51)") buttons1_frame.pack(side="top", pady=8) for c in range(4): - buttons1_frame.grid_columnconfigure(c, minsize=110) + buttons1_frame.grid_columnconfigure(c, minsize=140) for rr in range(1, 5): - buttons1_frame.grid_rowconfigure(rr, minsize=110) + buttons1_frame.grid_rowconfigure(rr, minsize=70) self.button1_cells: list[tk.Label] = [] for r in range(4): for c in range(4): @@ -118,13 +164,13 @@ class App: text="", bg=bg_color, fg=fg_color, - font=("Arial", 16), - padx=4, - pady=4, + font=("Arial", 14), + padx=6, + pady=6, borderwidth=2, relief="ridge", - width=12, - height=6, + width=14, + height=4, anchor="center", justify="center", ) @@ -193,6 +239,17 @@ class App: if idx < len(self.dials_boxes): self.dials_boxes[idx].config(text=f"{label}\n{value}") + # Update additional knobs (CC38-45) + knob_values = [ + ("CC44", getattr(self.midi_handler, 'knob7', '-')), ("CC45", getattr(self.midi_handler, 'knob8', '-')), + ("Rad n1", getattr(self.midi_handler, 'n1', '-')), ("Rad delay", getattr(self.midi_handler, 'delay', '-')), + ("Alt n1", getattr(self.midi_handler, 'n1', '-')), ("Alt n2", getattr(self.midi_handler, 'n2', '-')), + ("Pulse n1", getattr(self.midi_handler, 'n1', '-')), ("Pulse n2", getattr(self.midi_handler, 'n2', '-')), + ] + for idx, (label, value) in enumerate(knob_values): + if idx < len(self.knobs_boxes): + self.knobs_boxes[idx].config(text=f"{label}\n{value}") + # Update buttons bank mappings and selection (single bank) # Pattern icons for nicer appearance icon_for = { @@ -207,8 +264,14 @@ class App: "-": "", } bank1_patterns = [ - "pulse", "flicker", "alternating", "n chase", - "rainbow", "radiate", "sequential\npulse", "alternating\nphase", + # Pulse patterns (row 1) + "pulse", "sequential\npulse", + # Alternating patterns (row 2) + "alternating", "alternating\nphase", + # Chase/movement patterns (row 3) + "n chase", "rainbow", + # Effect patterns (row 4) + "flicker", "radiate", "-", "-", "-", "-", "-", "-", "-", "-", ] diff --git a/src/midi.py b/src/midi.py index 811e380..6d3d62b 100644 --- a/src/midi.py +++ b/src/midi.py @@ -54,12 +54,20 @@ class MidiHandler: # Raw CC-driven parameters (0-127) self.n1 = 10 self.n2 = 10 - self.n3 = 1 + # Additional knobs (CC38-45) + self.knob1 = 0 + self.knob2 = 0 + self.knob3 = 0 + self.knob4 = 0 + self.knob5 = 0 + self.knob6 = 0 + self.knob7 = 0 + self.knob8 = 0 # Current state for GUI display self.current_bpm: float | None = None self.current_pattern: str = "" self.beat_index: int = 0 - + # Rate limiting for parameter updates self.last_param_update: float = 0.0 self.param_update_interval: float = 0.1 # 100ms minimum between updates @@ -82,11 +90,11 @@ class MidiHandler: # Calculate which bar should pulse based on beat (1 beat per bar) bar_index = self.beat_index % 4 # 0-3, cycles every 4 beats - # Create minimal payload - only send pattern + # Create minimal payload - defaults to off payload = { - "d": { # Defaults - only pattern + "d": { # Defaults - off for all bars "t": "b", # Message type: beat - "pt": "p", # pulse + "pt": "o", # off } } @@ -97,34 +105,35 @@ class MidiHandler: payload[left_bar] = {"pt": "p"} # pulse payload[right_bar] = {"pt": "p"} # pulse - logging.debug(f"[Sequential Pulse] Beat {self.beat_index}, pulsing bars {left_bar} and {right_bar}") + # logging.debug(f"[Sequential Pulse] Beat {self.beat_index}, pulsing bars {left_bar} and {right_bar}") await self.ws_client.send_data(payload) async def _handle_alternating_phase(self): - """Handle alternating pattern with phase offset: every second bar swaps n1 and n2""" + """Handle alternating pattern with phase offset: every second bar uses different step""" from bar_config import LED_BAR_NAMES - # Create minimal payload - only send what changes + # Create minimal payload - same n1/n2 for all bars payload = { - "d": { # Defaults - only pattern and n1/n2 + "d": { # Defaults - pattern and n1/n2 "t": "b", # Message type: beat "pt": "a", # alternating "n1": self.n1, "n2": self.n2, + "s": self.beat_index % 2, # Default step for in-phase bars } } - # Set n1/n2 swap for every second bar (bars 101, 103, 105, 107) + # Set step offset for every second bar (bars 101, 103, 105, 107) swap_bars = ["101", "103", "105", "107"] for bar_name in LED_BAR_NAMES: if bar_name in swap_bars: - # Swap n1 and n2 for out-of-phase bars - payload[bar_name] = {"n1": self.n2, "n2": self.n1} + # Send step offset for out-of-phase bars + payload[bar_name] = {"s": (self.beat_index + 1) % 2} else: # In-phase bars use defaults (no override needed) payload[bar_name] = {} - logging.debug(f"[Alternating Phase] Beat {self.beat_index}, n1/n2 swap for bars {swap_bars}") + # logging.debug(f"[Alternating Phase] Beat {self.beat_index}, step offset for bars {swap_bars}") await self.ws_client.send_data(payload) async def _send_full_parameters(self): @@ -142,6 +151,7 @@ class MidiHandler: "n1": self.n1, "n2": self.n2, "n3": self.n3, + "s": self.beat_index % 2, # Keep step small (0 or 1) for alternating patterns } } @@ -170,12 +180,13 @@ class MidiHandler: "n1": self.n1, "n2": self.n2, "n3": self.n3, + "s": self.beat_index % 2, # Keep step small (0 or 1) for alternating patterns } } for bar_name in LED_BAR_NAMES: payload2[bar_name] = {} - logging.debug(f"[Full Params] Sending in 2 packets due to size ({payload_size} bytes)") + # logging.debug(f"[Full Params] Sending in 2 packets due to size ({payload_size} bytes)") await self.ws_client.send_data(payload1) await asyncio.sleep(0.01) # Small delay between packets await self.ws_client.send_data(payload2) @@ -184,7 +195,7 @@ class MidiHandler: for bar_name in LED_BAR_NAMES: full_payload[bar_name] = {} - logging.debug(f"[Full Params] Sending single packet ({payload_size} bytes)") + # logging.debug(f"[Full Params] Sending single packet ({payload_size} bytes)") await self.ws_client.send_data(full_payload) async def _request_param_update(self): @@ -196,26 +207,39 @@ class MidiHandler: # Can send immediately self.last_param_update = current_time await self._send_full_parameters() - logging.debug("[Rate Limit] Parameter update sent immediately") + # logging.debug("[Rate Limit] Parameter update sent immediately") else: # Rate limited - mark as pending self.pending_param_update = True - logging.debug("[Rate Limit] Parameter update queued (rate limited)") + # logging.debug("[Rate Limit] Parameter update queued (rate limited)") async def _send_normal_pattern(self): - """Send normal pattern to all bars - minimal payload for beats""" + """Send normal pattern to all bars - include required parameters""" + # Patterns that need specific parameters + patterns_needing_params = ["alternating", "flicker", "n_chase", "rainbow", "radiate"] + payload = { - "d": { # Defaults - only pattern + "d": { # Defaults "t": "b", # Message type: beat "pt": PATTERN_NAMES.get(self.current_pattern, self.current_pattern), } } + # Add required parameters for patterns that need them + if self.current_pattern in patterns_needing_params: + payload["d"].update({ + "n1": self.n1, + "n2": self.n2, + "n3": self.n3, + "dl": self.delay, + "s": self.beat_index % 2, # Keep step small (0 or 1) for alternating patterns + }) + # Add empty entries for each bar (they'll use defaults) for bar_name in LED_BAR_NAMES: payload[bar_name] = {} - logging.debug(f"[Beat] Triggering '{self.current_pattern}' for {len(LED_BAR_NAMES)} bars using defaults") + # logging.debug(f"[Beat] Triggering '{self.current_pattern}' for {len(LED_BAR_NAMES)} bars") await self.ws_client.send_data(payload) async def _send_reset_to_sound(self): @@ -243,7 +267,7 @@ class MidiHandler: break message = data.decode().strip() - logging.debug(f"[MidiHandler - TCP Server] Received from {addr}: {message}") # Changed to debug + # logging.debug(f"[MidiHandler - TCP Server] Received from {addr}: {message}") # Changed to debug if self.beat_sending_enabled: try: @@ -252,7 +276,7 @@ class MidiHandler: self.current_bpm = bpm_value # On each beat, trigger currently selected pattern(s) if not self.current_pattern: - logging.debug("[Beat] No pattern selected yet; ignoring beat") + pass # No pattern selected yet; ignoring beat else: self.beat_index = (self.beat_index + 1) % 1000000 @@ -268,7 +292,7 @@ class MidiHandler: self.last_param_update = current_time self.pending_param_update = False await self._send_full_parameters() - logging.debug("[Rate Limit] Pending parameter update sent") + # logging.debug("[Rate Limit] Pending parameter update sent") if self.current_pattern == "sequential_pulse": # Sequential pulse pattern: each bar pulses for 1 beat, then next bar, mirrored @@ -284,7 +308,7 @@ class MidiHandler: except Exception as e: logging.error(f"[MidiHandler - TCP Server] Error processing received message from {addr}: {e}") # Changed to error else: - logging.debug(f"[MidiHandler - TCP Server] Beat received from {addr} but sending to WebSocket is disabled: {message}") # Changed to debug + pass # Beat sending disabled except asyncio.CancelledError: logging.info(f"[MidiHandler - TCP Server] Client handler for {addr} cancelled.") # Changed to info @@ -317,6 +341,9 @@ class MidiHandler: elif msg.control == 37: self.delay = msg.value * 4 logging.info(f"[Init] Delay set to {self.delay} ms from CC37") + elif msg.control == 39: + self.delay = msg.value * 4 + logging.info(f"[Init] Delay set to {self.delay} ms from CC39") elif msg.control == 33: self.brightness = round((msg.value / 127) * 100) logging.info(f"[Init] Brightness set to {self.brightness} from CC33") @@ -370,36 +397,35 @@ class MidiHandler: while True: msg = port.receive(block=False) # Non-blocking read if msg: - logging.debug(msg) # Changed to debug + # logging.debug(msg) # Changed to debug match msg.type: case 'note_on': - logging.debug(f" Note ON: Note={msg.note}, Velocity={msg.velocity}, Channel={msg.channel}") # Changed to debug + # logging.debug(f" Note ON: Note={msg.note}, Velocity={msg.velocity}, Channel={msg.channel}") # Changed to debug # Bank1 patterns starting at MIDI note 36 - pattern_bindings: list[tuple[str, dict]] = [ - ("pulse", {"n1": 120, "n2": 120}), - ("flicker", {}), - ("alternating", {"n1": 6, "n2": 6}), - ("n_chase", {"n1": 5, "n2": 5}), - ("rainbow", {}), - ("radiate", {"n1": 8}), - ("sequential_pulse", {}), - ("alternating_phase", {}), + pattern_bindings: list[str] = [ + # Pulse patterns (row 1) + "pulse", + "sequential_pulse", + # Alternating patterns (row 2) + "alternating", + "alternating_phase", + # Chase/movement patterns (row 3) + "n_chase", + "rainbow", + # Effect patterns (row 4) + "flicker", + "radiate", ] idx = msg.note - 36 if 0 <= idx < len(pattern_bindings): - pattern_name, extra = pattern_bindings[idx] + pattern_name = pattern_bindings[idx] self.current_pattern = pattern_name - # Apply any immediate param tweaks from binding to local state - if "n1" in extra: - self.n1 = extra["n1"] - if "n2" in extra: - self.n2 = extra["n2"] logging.info(f"[Select] Pattern selected via note {msg.note}: {self.current_pattern} (n1={self.n1}, n2={self.n2})") # Send full parameters when pattern changes await self._send_full_parameters() else: - logging.debug(f"Note {msg.note} not bound to patterns") + pass # Note not bound to patterns case 'control_change': match msg.control: @@ -411,6 +437,38 @@ class MidiHandler: self.delay = msg.value * 4 # Update instance delay logging.info(f"Delay set to {self.delay} ms by MIDI controller (CC37)") await self._request_param_update() + case 38: + self.n1 = msg.value # pulse n1 for pulse patterns + logging.info(f"Pulse n1 set to {self.n1} by MIDI controller (CC38)") + await self._request_param_update() + case 39: + self.n2 = msg.value # pulse n2 for pulse patterns + logging.info(f"Pulse n2 set to {self.n2} by MIDI controller (CC39)") + await self._request_param_update() + case 40: + self.n1 = msg.value # n1 for alternating patterns + logging.info(f"Alternating n1 set to {self.n1} by MIDI controller (CC40)") + await self._request_param_update() + case 41: + self.n2 = msg.value # n2 for alternating patterns + logging.info(f"Alternating n2 set to {self.n2} by MIDI controller (CC41)") + await self._request_param_update() + case 42: + self.n1 = msg.value # radiate n1 for radiate patterns + logging.info(f"Radiate n1 set to {self.n1} by MIDI controller (CC42)") + await self._request_param_update() + case 43: + self.delay = msg.value * 4 # delay for radiate patterns + logging.info(f"Delay set to {self.delay} ms by MIDI controller (CC43)") + await self._request_param_update() + case 44: + self.knob7 = msg.value + logging.info(f"Knob7 set to {self.knob7} by MIDI controller (CC44)") + await self._request_param_update() + case 45: + self.knob8 = msg.value + logging.info(f"Knob8 set to {self.knob8} by MIDI controller (CC45)") + await self._request_param_update() case 27: if msg.value == 127: self.beat_sending_enabled = True @@ -464,6 +522,10 @@ class MidiHandler: try: await self.ws_client.connect() logging.info(f"[MidiHandler] WebSocket client connected to {self.ws_client.uri}") # Changed to info + + # List available MIDI ports for debugging + print(f"Available MIDI input ports: {mido.get_input_names()}") + print(f"Trying to open MIDI port index {self.midi_port_index}") await asyncio.gather( self._midi_listener(), @@ -471,6 +533,9 @@ class MidiHandler: ) except mido.PortsError as e: logging.error(f"[MidiHandler] Error opening MIDI port: {e}") # Changed to error + print(f"MIDI Port Error: {e}") + print(f"Available MIDI ports: {mido.get_input_names()}") + print("Please check your MIDI device connection and port index") except asyncio.CancelledError: logging.info("[MidiHandler] Tasks cancelled due to program shutdown.") # Changed to info except KeyboardInterrupt: