En C se puede usar la librería multimedia para programar mediante mensajes básicos, en verdad es tan sencillo el protocolo que no se necesita de una librería especial que te lo dé mascado, aunque imagino que las habrá, pero así te enteras mejor de cómo funciona todo y tienes mayor control.
Desde luego necesitas conocer la especificación midi, porque cada mensaje lleva un comando, normalmente será un valor hexadecimal 0xB0, donde el valor más bajo es el canal, luego el número de comando, y el valor, así que si por ejemplo tendrás una función que mande mensajes como esta.
//---------------------------------------------------------------------------
// MENSAJES MIDI
//---------------------------------------------------------------------------
void EnviarEventoMidi(HMIDIOUT out, BYTE bStatus, BYTE bData1, BYTE bData2)
{
union { DWORD dwData;BYTE bData[4]; } u;
u.bData[0] = bStatus; // MIDI status byte
u.bData[1] = bData1; // first MIDI data byte
u.bData[2] = bData2; // second MIDI data byte
u.bData[3] = 0;
midiOutShortMsg(out, u.dwData);
}
[ Imagen no disponible ]
Segun la tabla para el volumen es el comando 7, y el parámetro para ajustarlo a la mitad sería 63 (la mitad de 128 - 1)
EnviarEventoMidi(puertoOut,(BYTE)(0xB0+canal), 7, 63);
las pistas no tendrán volumen sino el canal, y cada canal puede estar asignado a una pista o varias.
otro mensaje, por ejemplo para tocar una nota (para liberarla hará falta otro comando con su número correspondiente).
EnviarEventoMidi(puertoOut,(BYTE)(0x90+canal), nota, 0x64);
Eso para los mensajes simples donde se le pasa un canal, un comando, y un parámetro, pero luego puede haber mensajes más complejos que necesiten pasar más datos, ahí entran los RPN y los NRPN, te paso las funciones.
//---------------------------------------------------------------------------
// Mensaje NRPN
//---------------------------------------------------------------------------
void mensaje_NRPN(HMIDIOUT out, BYTE CanalSelec, BYTE msb, BYTE lsb, DATASB data)
{
EnviarEventoMidi(out,(BYTE)(0xB0+CanalSelec),99,msb);
EnviarEventoMidi(out,(BYTE)(0xB0+CanalSelec),98,lsb);
EnviarEventoMidi(out,(BYTE)(0xB0+CanalSelec),6,data.msb);
EnviarEventoMidi(out,(BYTE)(0xB0+CanalSelec),38,data.msb);
}
//---------------------------------------------------------------------------
// Mensaje RPN
//---------------------------------------------------------------------------
void mensaje_RPN(HMIDIOUT out, BYTE CanalSelec, BYTE msb, BYTE lsb, DATASB data)
{
EnviarEventoMidi(out,(BYTE)(0xB0+CanalSelec),101,msb);
EnviarEventoMidi(out,(BYTE)(0xB0+CanalSelec),100,lsb);
EnviarEventoMidi(out,(BYTE)(0xB0+CanalSelec),6,data.msb);
EnviarEventoMidi(out,(BYTE)(0xB0+CanalSelec),38,data.lsb);
}
Pero de momento empezaría con los mensajes simples, elegir instrumento para el canal, fijar volumen, panorama, tocar notas, y para cada cosa crear funciones que contengan sus mensajes, por ejemplo para elegir programa y banco, crear las funciones correspondiente con los dos mensajes.
//---------------------------------------------------------------------------
// Cambio de Programa y banco
//---------------------------------------------------------------------------
void SeleccionarInstrumento(BYTE canal, BYTE programa, BYTE banco)
{
EnviarEventoMidi(puertoOut,(BYTE)(0xB0+canal),0,0);
EnviarEventoMidi(puertoOut,(BYTE)(0xB0+canal),32,banco);
}
Lo difícil y pesado es lo del piano roll y todo el tema gráfico y de la interfaz. Eso ya depende del entorno que uses.
(igual puede haber un fallo en el código que te he puesto, lo he picado de por ahí que lo tenía, que sepas que es a modo de orientación y para que puedas arrancar con algo).