Add rate-limited parameter updates and message type system
- Rate limit parameter updates to 100ms minimum interval - Send immediate updates if rate limit allows, otherwise queue - Process pending updates during beat handling - All knob changes (CC30-37) now trigger parameter updates - Add message type field: 'b' for beats, 'u' for updates - Optimize message type to single letters to save packet space - Prevents ESP-NOW network flooding during rapid knob adjustments - All packets stay under 230-byte limit with automatic splitting
This commit is contained in:
45
src/midi.py
45
src/midi.py
@@ -59,6 +59,12 @@ class MidiHandler:
|
||||
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
|
||||
self.pending_param_update: bool = False
|
||||
|
||||
# Sequential pulse pattern state
|
||||
self.sequential_pulse_enabled: bool = False
|
||||
self.sequential_pulse_step: int = 0
|
||||
@@ -79,6 +85,7 @@ class MidiHandler:
|
||||
# Create minimal payload - only send pattern
|
||||
payload = {
|
||||
"d": { # Defaults - only pattern
|
||||
"t": "b", # Message type: beat
|
||||
"pt": "p", # pulse
|
||||
}
|
||||
}
|
||||
@@ -100,6 +107,7 @@ class MidiHandler:
|
||||
# Create minimal payload - only send what changes
|
||||
payload = {
|
||||
"d": { # Defaults - only pattern and n1/n2
|
||||
"t": "b", # Message type: beat
|
||||
"pt": "a", # alternating
|
||||
"n1": self.n1,
|
||||
"n2": self.n2,
|
||||
@@ -126,6 +134,7 @@ class MidiHandler:
|
||||
# Calculate packet size for full parameters
|
||||
full_payload = {
|
||||
"d": {
|
||||
"t": "u", # Message type: update
|
||||
"pt": PATTERN_NAMES.get(self.current_pattern, self.current_pattern),
|
||||
"dl": self.delay,
|
||||
"cl": [self._current_color_rgb()],
|
||||
@@ -144,6 +153,7 @@ class MidiHandler:
|
||||
# Packet 1: Pattern and timing parameters
|
||||
payload1 = {
|
||||
"d": {
|
||||
"t": "u", # Message type: update
|
||||
"pt": PATTERN_NAMES.get(self.current_pattern, self.current_pattern),
|
||||
"dl": self.delay,
|
||||
"br": self.brightness,
|
||||
@@ -155,6 +165,7 @@ class MidiHandler:
|
||||
# Packet 2: Color and pattern parameters
|
||||
payload2 = {
|
||||
"d": {
|
||||
"t": "u", # Message type: update
|
||||
"cl": [self._current_color_rgb()],
|
||||
"n1": self.n1,
|
||||
"n2": self.n2,
|
||||
@@ -176,10 +187,26 @@ class MidiHandler:
|
||||
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):
|
||||
"""Request a parameter update with rate limiting"""
|
||||
import time
|
||||
current_time = time.time()
|
||||
|
||||
if current_time - self.last_param_update >= self.param_update_interval:
|
||||
# Can send immediately
|
||||
self.last_param_update = current_time
|
||||
await self._send_full_parameters()
|
||||
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)")
|
||||
|
||||
async def _send_normal_pattern(self):
|
||||
"""Send normal pattern to all bars - minimal payload for beats"""
|
||||
payload = {
|
||||
"d": { # Defaults - only pattern
|
||||
"t": "b", # Message type: beat
|
||||
"pt": PATTERN_NAMES.get(self.current_pattern, self.current_pattern),
|
||||
}
|
||||
}
|
||||
@@ -233,6 +260,16 @@ class MidiHandler:
|
||||
if self.beat_index % 8 == 0:
|
||||
await self._send_full_parameters()
|
||||
|
||||
# Check for pending parameter updates (rate limited)
|
||||
if self.pending_param_update:
|
||||
import time
|
||||
current_time = time.time()
|
||||
if current_time - self.last_param_update >= self.param_update_interval:
|
||||
self.last_param_update = current_time
|
||||
self.pending_param_update = False
|
||||
await self._send_full_parameters()
|
||||
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
|
||||
await self._handle_sequential_pulse()
|
||||
@@ -369,9 +406,11 @@ class MidiHandler:
|
||||
case 36:
|
||||
self.n3 = max(1, msg.value) # Update n3 step rate
|
||||
logging.info(f"n3 set to {self.n3} by MIDI controller (CC36)")
|
||||
await self._request_param_update()
|
||||
case 37:
|
||||
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 27:
|
||||
if msg.value == 127:
|
||||
self.beat_sending_enabled = True
|
||||
@@ -387,24 +426,30 @@ class MidiHandler:
|
||||
# Map 0-127 to 0-100 brightness scale
|
||||
self.brightness = round((msg.value / 127) * 100)
|
||||
logging.info(f"Brightness set to {self.brightness} by MIDI controller (CC33)")
|
||||
await self._request_param_update()
|
||||
case 30:
|
||||
# Red 0-127 -> 0-255
|
||||
self.color_r = round((msg.value / 127) * 255)
|
||||
logging.info(f"Red set to {self.color_r}")
|
||||
await self._request_param_update()
|
||||
case 31:
|
||||
# Green 0-127 -> 0-255
|
||||
self.color_g = round((msg.value / 127) * 255)
|
||||
logging.info(f"Green set to {self.color_g}")
|
||||
await self._request_param_update()
|
||||
case 32:
|
||||
# Blue 0-127 -> 0-255
|
||||
self.color_b = round((msg.value / 127) * 255)
|
||||
logging.info(f"Blue set to {self.color_b}")
|
||||
await self._request_param_update()
|
||||
case 34:
|
||||
self.n1 = int(msg.value)
|
||||
logging.info(f"n1 set to {self.n1} by MIDI controller (CC34)")
|
||||
await self._request_param_update()
|
||||
case 35:
|
||||
self.n2 = int(msg.value)
|
||||
logging.info(f"n2 set to {self.n2} by MIDI controller (CC35)")
|
||||
await self._request_param_update()
|
||||
|
||||
await asyncio.sleep(0.001) # Important: Yield control to asyncio event loop
|
||||
|
||||
|
Reference in New Issue
Block a user