Something went wrong. Try again.
daemon connecting Claude Code sessions to the JamJum JP-mini via usb or bluetooth
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101//! JamJum JP-Mini wire protocol, reverse-engineered from KuSuite 5.3 (see jpmini.py).//!//! Packet: 78 59 <cmd> <len: 3 bytes LE> <payload> <checksum = !sum(payload)>//! SysEx: F0 <packet as LSB-first 7-bit bitstream> F7
pub const SLOTS: usize = 16;const PAYLOAD_SLOTS: usize = 18;const CMD_PAD_RGB: u8 = 0x51;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]pub struct Rgb(pub u8, pub u8, pub u8);
impl Rgb { pub const OFF: Rgb = Rgb(0, 0, 0);}
pub type Grid = [Rgb; SLOTS];
fn packet(cmd: u8, payload: &[u8]) -> Vec<u8> { let n = payload.len() as u32; let mut p = vec![0x78, 0x59, cmd, n as u8, (n >> 8) as u8, (n >> 16) as u8]; p.extend_from_slice(payload); p.push(!payload.iter().fold(0u8, |a, &b| a.wrapping_add(b))); p}
fn pack7(data: &[u8]) -> Vec<u8> { let mut out = Vec::with_capacity(data.len() * 8 / 7 + 1); let (mut acc, mut bits) = (0u32, 0u32); for &b in data { acc |= u32::from(b) << bits; bits += 8; while bits >= 7 { out.push((acc & 0x7F) as u8); acc >>= 7; bits -= 7; } } if bits > 0 { out.push((acc & 0x7F) as u8); } out}
/// Complete SysEx message setting all 16 pads of the bank currently displayed.pub fn pad_colors_sysex(grid: &Grid) -> Vec<u8> { let mut payload = Vec::with_capacity(1 + 3 * PAYLOAD_SLOTS); payload.push(0xFF); for &Rgb(r, g, b) in grid { payload.extend_from_slice(&[g, r, b]); // the device expects G, R, B } payload.resize(1 + 3 * PAYLOAD_SLOTS, 0); let mut msg = vec![0xF0]; msg.extend(pack7(&packet(CMD_PAD_RGB, &payload))); msg.push(0xF7); msg}
#[cfg(test)]mod tests { use super::*;
fn hex(s: &str) -> Vec<u8> { s.split_whitespace() .map(|b| u8::from_str_radix(b, 16).unwrap()) .collect() }
#[test] fn pack7_matches_reference() { assert_eq!( pack7(&[0x78, 0x59, 0xFF, 0x00, 0x80]), vec![0x78, 0x32, 0x7D, 0x07, 0x00, 0x10] ); }
#[test] fn all_off_matches_reference() { let expected = hex( "F0 78 32 45 3A 03 00 40 7F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F7", ); assert_eq!(pad_colors_sysex(&[Rgb::OFF; SLOTS]), expected); }
#[test] fn mixed_grid_matches_reference() { let mut grid = [Rgb::OFF; SLOTS]; grid[0] = Rgb(0, 0, 255); grid[1] = Rgb(40, 40, 40); grid[15] = Rgb(255, 100, 0); let expected = hex( "F0 78 32 45 3A 03 00 40 7F 00 00 7C 47 02 05 0A 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ 00 00 00 00 20 76 1F 00 00 00 00 00 00 00 40 09 F7", ); assert_eq!(pad_colors_sysex(&grid), expected); }}