Tired and Poor and Old - Radio Panel v0.1.1
This commit is contained in:
+15
-53
@@ -2,19 +2,6 @@
|
||||
"""
|
||||
MIDI Engine for Radio Panel
|
||||
Numark DJ2Go controller support.
|
||||
|
||||
Confirmed mapping from hardware testing:
|
||||
------------------------------------------
|
||||
Play/Pause Deck 1 : Note 0x3B ch=0
|
||||
Play/Pause Deck 2 : Note 0x42 ch=0
|
||||
Cue Deck 1 : Note 0x33 ch=0
|
||||
Cue Deck 2 : Note 0x3C ch=0
|
||||
Jog Wheel Deck 1 : CC 0x19 ch=0
|
||||
Jog Wheel Deck 2 : CC 0x18 ch=0
|
||||
Cart 1 : Note 0x44 ch=0
|
||||
Cart 2 : Note 0x43 ch=0
|
||||
Cart 3 : Note 0x46 ch=0
|
||||
Cart 4 : Note 0x45 ch=0
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -33,7 +20,6 @@ NOTE_ON = 0x90
|
||||
NOTE_OFF = 0x80
|
||||
CC = 0xB0
|
||||
|
||||
# All controls on ch=0, differentiated by note/CC number only
|
||||
DECK1_PLAY = 0x3B
|
||||
DECK1_CUE = 0x33
|
||||
DECK1_JOG = 0x19
|
||||
@@ -42,22 +28,24 @@ DECK2_PLAY = 0x42
|
||||
DECK2_CUE = 0x3C
|
||||
DECK2_JOG = 0x18
|
||||
|
||||
# Cart buttons - confirmed from hardware test
|
||||
CART_NOTES = {
|
||||
0x44: 0, # Cart slot 1
|
||||
0x43: 1, # Cart slot 2
|
||||
0x46: 2, # Cart slot 3
|
||||
0x45: 3, # Cart slot 4
|
||||
0x44: 0,
|
||||
0x43: 1,
|
||||
0x46: 2,
|
||||
0x45: 3,
|
||||
}
|
||||
|
||||
JOG_SCRUB_SECONDS = 5.0
|
||||
JOG_DEAD_ZONE = 5 # Ignore movements below this threshold
|
||||
|
||||
|
||||
def _decode_relative_jog(value: int) -> int:
|
||||
"""Returns movement amount, or 0 if within dead zone."""
|
||||
if 1 <= value <= 63:
|
||||
return 1
|
||||
return value if value >= JOG_DEAD_ZONE else 0
|
||||
elif 65 <= value <= 127:
|
||||
return -1
|
||||
backward = 128 - value
|
||||
return -backward if backward >= JOG_DEAD_ZONE else 0
|
||||
return 0
|
||||
|
||||
|
||||
@@ -148,14 +136,6 @@ class MidiEngine(QObject):
|
||||
value = message[2] if len(message) > 2 else 0
|
||||
|
||||
status_type = status & 0xF0
|
||||
channel = status & 0x0F
|
||||
|
||||
logger.debug(
|
||||
f"MIDI IN status=0x{status:02X} "
|
||||
f"data=0x{data_byte:02X} "
|
||||
f"val={value} "
|
||||
f"type=0x{status_type:02X} ch={channel}"
|
||||
)
|
||||
|
||||
if status_type == NOTE_ON and value > 0:
|
||||
self._handle_note_on(data_byte)
|
||||
@@ -164,43 +144,25 @@ class MidiEngine(QObject):
|
||||
|
||||
def _handle_note_on(self, note: int):
|
||||
if note == DECK1_PLAY:
|
||||
logger.debug("DECK 1 PLAY/PAUSE")
|
||||
self.deck_play_pause.emit('deck1')
|
||||
|
||||
elif note == DECK1_CUE:
|
||||
logger.debug("DECK 1 CUE")
|
||||
self.deck_cue.emit('deck1')
|
||||
|
||||
elif note == DECK2_PLAY:
|
||||
logger.debug("DECK 2 PLAY/PAUSE")
|
||||
self.deck_play_pause.emit('deck2')
|
||||
|
||||
elif note == DECK2_CUE:
|
||||
logger.debug("DECK 2 CUE")
|
||||
self.deck_cue.emit('deck2')
|
||||
|
||||
elif note in CART_NOTES:
|
||||
slot = CART_NOTES[note]
|
||||
logger.debug(f"CART {slot + 1} TRIGGER")
|
||||
self.cart_trigger.emit(slot)
|
||||
|
||||
else:
|
||||
logger.debug(f"Unmapped note: 0x{note:02X}")
|
||||
self.cart_trigger.emit(CART_NOTES[note])
|
||||
|
||||
def _handle_cc(self, cc_num: int, value: int):
|
||||
direction = _decode_relative_jog(value)
|
||||
if direction == 0:
|
||||
movement = _decode_relative_jog(value)
|
||||
if movement == 0:
|
||||
return
|
||||
|
||||
delta = direction * JOG_SCRUB_SECONDS
|
||||
# Scale movement to seconds
|
||||
delta = (movement / 10.0) * JOG_SCRUB_SECONDS
|
||||
|
||||
if cc_num == DECK1_JOG:
|
||||
logger.debug(f"DECK 1 JOG delta={delta:+.1f}s")
|
||||
self.deck_jog.emit('deck1', delta)
|
||||
|
||||
elif cc_num == DECK2_JOG:
|
||||
logger.debug(f"DECK 2 JOG delta={delta:+.1f}s")
|
||||
self.deck_jog.emit('deck2', delta)
|
||||
|
||||
else:
|
||||
logger.debug(f"Unmapped CC: cc=0x{cc_num:02X} val={value}")
|
||||
self.deck_jog.emit('deck2', delta)
|
||||
Reference in New Issue
Block a user