Módulo 6.3: El SDK de C de Max: Anatomía de un Objeto Externo Nativo
Llegamos a la capa más profunda y fundamental de toda la plataforma: el Cycling ‘74 Max C SDK.
Todos los objetos que usamos habitualmente en Max ([cycle~], [metro], [biquad~], [dict]) no son magia negra: son librerías dinámicas (.mxe64 en Windows, .mxo bundle en macOS) compiladas en C o C++ que implementan una interfaz binaria de aplicación (ABI) estricta contra el motor central de Max.
Aprender a programar objetos externos (externals) en C te da control total sobre la memoria, vectorización SIMD nativa, bindings directos con hardware, GPU y bibliotecas científicas en C/C++ o Rust.
1. El Ciclo de Vida de un External en C
Un objeto de Max en C sigue una arquitectura clásica orientada a objetos en C puro mediante structs opacos y tablas de despacho:
graph TD
A["Max inicia / Carga DLL"] --> B["ext_main(void *r)"]
B -->|Registra Clase| C["class_new('mi_objeto', ...)"]
C -->|Asocia Métodos| D["class_addmethod()"]
D -->|Instanciación en Patcher| E["mi_objeto_new()"]
E --> F["Ciclo de Vida / Mensajes: bang(), int(), float()"]
F -->|Destrucción del objeto| G["mi_objeto_free()"]
1.1. Los Cuatro Elementos del Código Fuente C
- Estructura del Objeto (
t_object): Todo external debe comenzar obligatoriamente con el struct baset_object(para objetos de control) ot_pxobject(para objetos de audio MSP). Esto garantiza la herencia binaria por puntero:typedef struct _mi_objeto {t_object ob; // Cabecera obligatoria de Max (debe ser el primer campo)long valor_interno; // Estado interno del objetovoid *salida_bang; // Puntero opaco al outletvoid *salida_datos;} t_mi_objeto; - El Punto de Entrada:
ext_main: Es la función exportada que Max busca cuando encuentra la caja en el patcher. Inicializa la clase en el registro global:void *mi_objeto_class;void ext_main(void *r) {t_class *c;c = class_new("mi_objeto", (method)mi_objeto_new, (method)mi_objeto_free,(long)sizeof(t_mi_objeto), 0L, A_GIMME, 0);// Enlazamos mensajes a funciones de Cclass_addmethod(c, (method)mi_objeto_bang, "bang", 0);class_addmethod(c, (method)mi_objeto_int, "int", A_LONG, 0);class_register(CLASS_BOX, c);mi_objeto_class = c;} - Constructor
mi_objeto_newy Destructormi_objeto_free:object_alloc(mi_objeto_class)aloca la memoria inicial.- Se crean los outlets usando
outlet_new()obangout(). - En el destructor, se libera cualquier memoria dinámica alocada con
sysmem_freeptr().
2. Tipos de Argumentos de la API de Max (A_LONG, A_FLOAT, A_GIMME)
Max provee un sistema de tipado dinámico para la firma de métodos:
A_LONG: Entero de 32 o 64 bits (long).A_FLOAT: Punto flotante (double).A_SYM: Puntero a símbolo único interning de Max (t_symbol *).A_GIMME: Permite recibir una lista arbitraria de argumentos como una tupla(short argc, t_atom *argv). Unt_atomes una unión etiquetada que representa un entero, flotante, símbolo u objeto.
3. Código Fuente Educativo: mi_objeto_externo.c
En esta lección proveemos el archivo de referencia completo mi_objeto_externo.c listo para ser compilado con CMake y el SDK oficial, ilustrando:
- Manejo correcto de
A_GIMMEy átomos. - Comprobación de tipos en runtime con
atom_gettype(). - Thread-safety elemental: evitar llamar funciones de interfaz gráfica desde el Scheduler thread.