Mixxx: mappings en XML y JavaScript
Mixxx separa el mapping en dos capas: un XML que traduce mensajes y un script JavaScript que resuelve lo complejo. Así se combinan y esto es lo que solo puede hacer el script.
Mixxx es el caso más didáctico de todo este bloque, y no porque sea el más fácil, sino por lo contrario: es el único que te enseña las dos capas por separado. En otros programas el mapping ocurre dentro de una interfaz; aquí ocurre en dos ficheros que puedes leer, copiar y modificar.
Eso lo convierte en la mejor manera de entender qué es realmente un mapping por dentro. Lo que aprendas aquí se traslada mentalmente a cualquier otro software.
Qué es el controller scripting de Mixxx
Mixxx ofrece lo que llama Controller Scripting: permite mapear controles de hardware a funciones JavaScript, de modo que un mapping pueda gestionar comportamientos complejos, no solo traducciones directas.
Desde Mixxx 2.4 ese JavaScript se ejecuta en un QJSEngine y puede usar el conjunto de características de ES7, salvo los módulos ES6. Es un detalle relevante si vas a escribir código moderno: puedes usar let, const y plantillas de cadena, pero no import.
XML y JavaScript: dos capas, no una alternativa
El error mental más común es pensar que puedes elegir «hacerlo en XML» o «hacerlo en JavaScript». No funciona así: son dos capas del mismo mapping y normalmente trabajan juntas.
La propia documentación de formatos lo dice sin rodeos: los mappings se escriben en XML y JavaScript, se pueden hacer mappings solo con XML, pero la mayoría de controladores necesitará algo de JavaScript para un mapping completo.
El XML como capa de traducción
El fichero XML es el traductor. Le dice a Mixxx cómo convertir los mensajes que llegan del controlador en comandos que el software entiende. Cada mensaje se declara como un <control> con su grupo, su status, su número de control y sus opciones.
El JavaScript como capa de comportamiento
El script entra cuando la respuesta a un mensaje no es siempre la misma. Si la acción depende de si otro botón está pulsado, del estado real del reproductor o de en qué deck estás, eso no cabe en una tabla de traducción: hace falta código.
El contrato entre el XML y el script
Aquí está la parte que hay que entender para no perderse, porque es literalmente la unión entre los dos ficheros.
scriptfiles y functionprefix
Para que Mixxx cargue tu script, se declara dentro del XML:
<scriptfiles>
<file filename="Fabricante-modelo-scripts.js" functionprefix="MiControlador"/>
</scriptfiles>
El atributo functionprefix es la clave: indica el nombre del objeto JavaScript que contiene los métodos init y shutdown, que Mixxx llama al abrir y cerrar el controlador. Puedes declarar varios ficheros, pero cada uno debe especificar su functionprefix.
Existe además un fichero de funciones comunes, common-controller-scripts.js, que siempre se carga y contiene utilidades compartidas por todos los controladores.
init y shutdown
En el script declaras el objeto y le das esos dos métodos:
var MiControlador = {};
MiControlador.init = function (id, debugging) {
// se ejecuta al abrir el controlador
};
MiControlador.shutdown = function () {
// se ejecuta al cerrar
};
Pueden estar vacíos, pero son muy útiles para dejar el equipo en un estado conocido: por ejemplo, apagar los LEDs al cerrar. El parámetro debugging llega a true si has arrancado con el modo de depuración de controladores.
Un aviso de estilo que da la documentación y que evita fallos difíciles de encontrar: en lugar de usar variables globales, define propiedades de tu objeto de controlador, para no chocar con nombres de otros scripts cargados.
Script-Binding y la firma de la función
Para enlazar un mensaje con una función, se pone su nombre completo en la etiqueta <key> y se añade <Script-Binding/> en <options>:
<control>
<group>[Master]</group>
<key>MiControlador.faderTempo</key>
<status>0xB0</status>
<midino>0x04</midino>
<options>
<Script-Binding/>
</options>
</control>
Cuando Mixxx recibe un mensaje cuyos dos primeros bytes coinciden con <status> y <midino>, llama a esa función. Y aquí viene el detalle que hay que memorizar: la firma es siempre la misma.
MiControlador.faderTempo = function (channel, control, value, status, group) {
// ...
};
Los parámetros son, en orden: canal MIDI, número de control o nota, valor, byte de estado y el grupo de Mixxx. Si no usas alguno, la convención es prefijarlo con guion bajo (_channel) para dejar claro que se ignora.
La trampa del nombre reservado
La documentación incluye un aviso que conviene leer dos veces: no llames incomingData a una función de entrada MIDI normal. Es un nombre reservado que Mixxx usa internamente, y si lo usas, tu función recibirá parámetros equivocados y no funcionará. Usa cualquier otro nombre descriptivo.
Qué se resuelve en XML y qué exige JavaScript
La tabla resume la decisión práctica. La regla de fondo: si la respuesta a un mensaje siempre es la misma, cabe en XML; si depende de algo que cambia, hace falta script.
| Tarea | Capa | Por qué |
|---|---|---|
| Asignar un botón a una función | XML | Traducción directa de un mensaje a un comando. |
| Encender un LED siempre igual | XML | No hay lógica: solo un valor fijo que se envía. |
| Que el LED refleje el estado real | JavaScript | Requiere leer el estado del software y decidir. |
| Capas SHIFT y modificadores | JavaScript | La respuesta depende de un estado que cambia. |
| Soft-takeover | JavaScript | Solo funciona si el control se manipula desde el script. |
| Un controlador de 2 decks, usado como 4 | JavaScript | Hay que redirigir los controles a otros grupos sobre la marcha. |
La regla general: si la respuesta a un mensaje siempre es la misma, cabe en XML. Si depende de algo que cambia, hace falta script.
Los mensajes que vas a manejar
Mensajes cortos de 3 bytes
La mayoría de los mensajes MIDI son de tres bytes. A tu función llegarán en este orden: canal (0x00 para el canal 1 y 0x0F para el 16), número de control o nota, valor, byte de estado y grupo.
Un ejemplo concreto que aparece en la documentación y que ahorra muchos errores: los botones suelen enviar 0x7F al pulsar y 0x00 al soltar, así que tu función se llama en los dos casos. Si quieres que solo actúe al pulsar, hay que comprobar el valor.
Mensajes SysEx
Los mensajes de sistema exclusivo son distintos. En el XML, su byte de estado debe ser 0xF0 y no hace falta <midino>. La función recibe un array de bytes y su longitud. Y un matiz importante: si el controlador puede enviar varios SysEx distintos, una sola función es responsable de distinguir cuál ha llegado y actuar en consecuencia.
Cómo se controlan los LEDs
Para iluminar LEDs se envía MIDI de vuelta al controlador. Hay dos funciones, una para mensajes cortos y otra para SysEx.
La convención general es tranquilizadora: los botones suelen controlar su LED enviando un mensaje corto con los dos primeros bytes iguales que cuando el botón se pulsa. Si el botón envía 0x91, 0x11, 0x7F al pulsarse, enviar ese mismo mensaje enciende el LED, y enviarlo con 0x00 lo apaga. Si el LED tiene varios colores, normalmente el color lo decide el tercer byte.
Y hay una recomendación de diseño que separa un mapping bueno de uno frágil: no envíes los mensajes de LED directamente desde la función de entrada. Cambia el estado de un control de Mixxx y envía el MIDI desde una función de callback que reaccione a ese cambio. Así el estado del controlador siempre coincide con lo que Mixxx está haciendo de verdad, aunque el usuario use el teclado, el ratón u otro controlador. Es la diferencia entre un mapping que funciona solo si usas los botones y uno que funciona siempre.
Depurar el mapping sin reiniciar
Dos ventajas de trabajar en Mixxx que conviene aprovechar desde el principio:
- El script se recarga al guardar. No hace falta reiniciar la aplicación, lo que hace el ciclo de prueba muy rápido.
- Hay modo de depuración de controladores, que registra todos los mensajes MIDI entrantes y salientes, y desde Mixxx 2.4 puedes usar
console.log.
Ese modo de depuración es también la respuesta a la pregunta «¿qué mensaje envía este botón?»: si el fabricante no documenta los códigos, se interceptan con herramientas como MIDI-OX en Windows o MIDI Monitor en macOS, o directamente con esta opción.
La trampa de soft-takeover en Mixxx
Mixxx tiene soft-takeover, y su definición es la más precisa de todo el bloque: mientras está activo en un parámetro, manipular el control físico no tiene ningún efecto hasta que su posición se acerca a la del software, momento en el que toma el control y funciona con normalidad. Se activa y desactiva cuando quieras, y opera por control de forma independiente.
Pero hay una limitación que hay que conocer antes de perder una tarde: solo funciona en controles que se manipulan desde el script con setValue o setParameter. Si el control está mapeado directamente en el XML, no le afecta.
Y un detalle fino para capas: si cambias la funcionalidad de un control absoluto (uno con topes, no un encoder infinito) que tiene soft-takeover activo, hay que avisar a Mixxx de qué mando físico estás manipulando cada vez que cambias esa funcionalidad. Si no lo haces, al volver a la función anterior el valor salta de golpe. Para eso existe una función específica de «ignora el siguiente valor».
Es un caso perfecto de por qué el soft-takeover y las capas van siempre de la mano, y por eso tienen su propia guía.
Errores frecuentes
- Intentarlo todo en XML. Funciona hasta que necesitas que algo dependa de un estado, y ahí te bloqueas.
- Llamar
incomingDataa una función de entrada. Nombre reservado: no funcionará. - Olvidar el functionprefix o no definir
inityshutdown. - No tomar todos los parámetros en la firma de la función.
- Enviar los LEDs desde la función de entrada en lugar de desde un callback, con lo que la luz se desincroniza.
- Esperar soft-takeover en un control mapeado en XML. No aplica.
- Usar variables globales en el script y chocar con otros mappings cargados.
Siguiente paso
Ya tienes el modelo de las dos capas, que es el más transferible de todo el bloque. Con él entendido, el siguiente paso lógico es el problema que más gente sufre sin saber que tiene nombre y que ya ha aparecido dos veces en esta guía: el soft-takeover, los saltos de parámetro y cómo evitarlos.
Preguntas frecuentes
¿Puedo hacer un mapping de Mixxx sin programar?
Depende del equipo. Un controlador sencillo se puede mapear solo con XML, pero la propia documentación avisa de que la mayoría necesitará algo de JavaScript para un mapping completo.
¿Qué es el functionprefix del XML?
El nombre del objeto JavaScript que Mixxx buscará al cargar el script. Ese objeto debe definir métodos init y shutdown, que Mixxx llama al abrir y cerrar el controlador.
¿Cómo sabe Mixxx qué función llamar para cada mensaje MIDI?
Con la etiqueta Script-Binding dentro del bloque options del control en el XML. El nombre completo de la función va en la etiqueta key, y Mixxx la llama cuando llega un mensaje que coincide con status y midino.
¿Por qué no debo llamar incomingData a mi función de entrada?
Porque es un nombre reservado que Mixxx usa internamente. Si lo usas para una función normal de entrada, recibirá parámetros equivocados y no funcionará. Usa cualquier otro nombre descriptivo.
¿Tengo que reiniciar Mixxx cada vez que cambio el script?
No. Cada vez que guardas el archivo, Mixxx lo recarga al momento. Es una de las cosas que hace muy rápido el ciclo de prueba.
¿Funciona soft-takeover en un control mapeado desde el XML?
No. Soft-takeover solo afecta a controles que se manipulan desde el script con setValue o setParameter. Si el control está mapeado directamente en el XML, no le afecta.
Continúa aprendiendo
Si esto te suena a chino, empieza por aquí
Relacionadas
Fuentes
- Mixxx Wiki — MIDI Scripting (Controller Scripting)
Fuente principal. Explica que Mixxx ofrece Controller Scripting, que permite mapear controles de hardware a funciones JavaScript para gestionar comportamientos complejos, y que desde Mixxx 2.4 los mappings se ejecutan en un QJSEngine con el conjunto de características de ES7 salvo módulos ES6. Documenta el contrato XML-script: el bloque scriptfiles con file y functionprefix, los métodos init y shutdown, la etiqueta Script-Binding, la firma de las funciones de entrada (channel, control, value, status, group), los mensajes cortos de 3 bytes y la función para SysEx (data, length), la advertencia de no llamar incomingData a un manejador de entrada, engine.makeConnection para callbacks de salida, midi.sendShortMsg y midi.sendSysexMsg, la recarga inmediata del script al guardar, las capas SHIFT y el truco para convertir un controlador de 2 decks en uno de 4. Incluye la definición de soft-takeover y su limitación: solo funciona en controles manipulados con setValue o setParameter, no en los mapeados desde el XML.
- Mixxx Wiki — MIDI controller mapping file format
Define el formato del fichero XML: es el que le dice a Mixxx cómo traducir los mensajes MIDI del controlador a comandos que Mixxx entiende, y documenta que la vía más fácil de crear un mapping nuevo es el asistente MIDI Learning Wizard.
- Mixxx User Manual 2.4 — Advanced Topics
Documenta el mismo modelo desde el manual de usuario: se añade soporte para dispositivos creando un fichero de mapping que traduce los mensajes MIDI o HID del controlador a comandos del software.
- MIDI 1.0 Control Change Messages (Data Bytes)
Contexto de los bytes que aparecen en el XML: los mensajes de control van de 0 a 127 y los control numbers 120-127 están reservados para Channel Mode Messages.
Última revisión técnica: 29/09/2026. Si detectas un error, indícalo para corregirlo.