Saltar al contenido
Kira

Ingeniería inversa · Web · Audio

JamJum JP-1 Studio: ingeniería inversa de un controlador MIDI

Decodifiqué el protocolo de configuración de un controlador MIDI sin documentación y construí una app web propia para editarlo: pads, knobs, botones, color RGB y efectos.

Año
2026
Estado
Validado en hardware
Rol
Ingeniería inversa y desarrollo · proyecto personal
Repositorio
Repositorio en preparación

01 · Problema

El JamJum JP-1 (16 pads, 8 knobs, 6 botones de transporte, USB y Bluetooth) solo se configura con una app oficial limitada. No hay documentación del protocolo y el microcontrolador Holtek está bloqueado: su firmware no se puede leer.

02 · Enfoque

Capturé el tráfico USB-MIDI con USBPcap y Wireshark, comparé volcados bit a bit y probé hipótesis de checksum con fuerza bruta y RANSAC hasta reproducir, byte por byte, cada mensaje de la app oficial. Con eso escribí JamJum Studio, una app en Web MIDI que corre en Chrome.

03 · Resultado

Protocolo decodificado: la app lee el volcado completo de cada preset al conectar y escribe pads, knobs, botones de transporte y color RGB por pad. Los cuatro checksums están resueltos y verificados byte a byte contra la app oficial (en color, 384 de 384 bloques). Suma efectos RGB dinámicos, entre ellos uno reactivo de «ondas» al golpear los pads.

04 · Stack

  • Web MIDI API
  • JavaScript
  • USBPcap
  • Wireshark / tshark
  • Python
  • Node.js (pruebas)

El protocolo en una línea

Todo mensaje de configuración viaja como SysEx propietario: F0 78 32 <comando de 3 bytes> … F7. El contenido no son bytes «normales»: es un flujo de bits de 7 bits, LSB primero (de cada byte se toman sus 7 bits bajos). Por eso los campos no caen en fronteras de byte y hubo que mapearlos bit a bit.

Comando Dirección Qué hace
45 00 00 00 40 7F PC → JP-1 Abre la sesión de edición (sin esto, el aparato ignora las escrituras)
1D 0B … PC → JP-1 Selecciona preset y dispara su volcado
4D 0A 04 … PC → JP-1 Escribe el banco de pads activo
51 4A 02 … PC → JP-1 Escribe el banco de knobs activo
49 02 03 … PC → JP-1 Escribe los botones de transporte
45 3A 03 … PC → JP-1 Color RGB de los 16 pads
21 03 00 00 40 7F PC → JP-1 Guarda en flash

El checksum que no era CRC

Cada bloque de escritura lleva un checksum. La primera hipótesis —un CRC lineal— resultó falsa: venía de una prueba de solvencia con muy pocas ecuaciones. Los cuatro checksums son sumas modulares de la misma familia:

checksums.py
def checksum_color(canales: list[int], indice_pad: int) -> tuple[int, int]:
"""canales: los 48 valores de 8 bits (16 pads × G, R, B)."""
s = sum(canales) + indice_pad # la pieza que faltaba: sumar el índice
chk = (2720 - 32 * s) % 8192
return chk & 0x7F, (chk >> 7) & 0x7F # bytes 64 y 65
def checksum_pads(pads: list[dict]) -> tuple[int, int]:
s = sum(p["canal"] + 16 * p["tipo"] + p["nota"] + p["min"] + p["max"] for p in pads)
c = (510 - 2 * s) % 512
return c & 0x7F, (c >> 7) & 0x7F # bytes 76 y 77

El de botones es (8175 − 32·Σ) mod 8192 y el de knobs (15456 − 32·Σ) mod 16384. Para el color, el modelo «uniforme» encajaba en las primeras capturas por puro alias; lo resolvió una captura nueva con índices y brillos variados, más una búsqueda del tipo (K + w·Σ) mod M sobre varias definiciones de Σ.

Lo que se ve en la app

  • Auto-carga al conectar: deduce el preset y el banco activos del handshake y decodifica el volcado (incluido el color de los 3 bancos, que vive a partir del bit 2517).
  • Editor por pad: tipo (nota, CC, programa), canal, rango de velocidad y color, con vista previa en vivo.
  • Escritura como la app oficial: un solo bloque por cambio y guardado explícito en flash.
  • Efectos RGB: arcoíris, persecución, pulso, degradado, reactivo a golpes y «ondas» (anillos que se expanden desde el pad golpeado), con parámetros por efecto.

Lo que me llevo

  1. Una hipótesis no vale hasta verificarla en el hardware. Los modelos que encajaban en la computadora fallaron en el aparato hasta que la validación fue byte a byte.
  2. Hay que probar la capa que usa la persona. Al principio validé el motor desde la consola y no la interfaz; la interfaz tenía otro error.
  3. Las capturas bien diseñadas ahorran pruebas. Una sola captura con datos variados resolvió lo que decenas de pruebas al azar no.