Práce s MIDI rozhraním (MFC a C++)

Pokud jste někdy měli v úmyslu používat PC jako hudební nástroj nebo jste PC propojovali s elektronickým hudebním nástrojem (např. klávesami), určitě jste se s MIDI rozhraním setkali. Jedná se o zařízení, které z dat, která mu zasíláme, což jsou pouze informace o notách, jejich hlasitostech, nástroji, který má hrát, atd., vytvoří zvukový výstup, který se potom přehrává. Nejznámější jsou asi MIDI soubory, které tuto techniku také využívají, ale těmi se nyní zabývat nebudeme, protože jsou poměrně složité. Dnes si ukážeme, jak inicializovat MIDI zařízení a přehrávat jednoduché noty. Nezapomeňte si také do projektu vložit hlavičkový soubor mmsystem.h a knihovnu winmm.lib.

Výpis MIDI zařízení

První věc, kterou musíme udělat, je zjistit, zda je v PC nějaké MIDI zařízení dostupné. K tomu slouží funkce midiOutGetNumDevs, která předává počet zařízení dostupných v systému. Toto číslo však nezahrnuje výchozí zařízení MIDI_MAPPER.

Další věc, kterou musíme udělat, je zjištění informací o jednotlivých zařízeních (jméno, ID). K tomu slouží funkce midiOutGetDevCaps, které se předá pořadové číslo zařízení a ona vrátí informace ve struktuře MIDIOUTCAPS. Zde je popis funkce s příkladem výpisu jmen všech zařízení:

MMRESULT midiOutGetDevCaps(
  UINT uDeviceID,               //ID zařízení     
  LPMIDIOUTCAPS lpMidiOutCaps,  //Struktura pro informace
  UINT cbMidiOutCaps            //Velikost této struktury
);

//Příklad

//Zjistit počet zařízení
int NumDevices=midiOutGetNumDevs();

MIDIOUTCAPS moc;
int Index;

//Zjistit info o MIDI_MAPPER
midiOutGetDevCaps(MIDIMAPPER,&moc,sizeof(moc));
MessageBox(moc.szPname);

//Zjistit info o ostatních
for(int i=0;i<NumDevices;i++)
{
  midiOutGetDevCaps(i,&moc,sizeof(moc));
  MessageBox(moc.szPname);
}

Otevření MIDI zařízení

Nyní si tedy vybereme zařízení, které chceme otevřít, a jeho ID předáme funkci midiOutOpen, která inicializuje proměnnou typu HMIDIOUT, kterou budeme používat v dalších funkcích. Funkce vrátí při chybě nenulovou hodnotu.

UINT midiOutOpen(
  LPHMIDIOUT lphmo,               //Adresa proměnné HMIDIOUT
  UINT uDeviceID,                 //ID zařízení
  DWORD dwCallback,          	//Adresa callback funkce
  DWORD dwCallbackInstance,  	//Data pro callback funkci
  DWORD dwFlags              	//Typ callback funkce
);

//Příklad na volání
//Incicializuje výchozí zařízení MIDI_MAPPER
HMIDIOUT hMidiOut;
if(midiOutOpen(&hMidiOut,MIDI_MAPPER,0,0,0))
{
  MessageBox("Nepodařilo se otevřít MIDI zařízení!","Chyba",MB_ICONERROR);
  return;
}

Uzavření MIDI zařízení

Po ukončení práce s MIDI bychom měli MIDI zařízení uvolnit a to pomocí funkce midiOutClose, které předáte jeden parametr a to proměnnou typu HMIDIOUT, kterou jste použili ve funkci pro otevření.

MIDI zprávy

Je několik způsobů, jak předávat MIDI data do zařízení, my použijeme ten, při kterém se posílají krátké čtyřbajtové zprávy, které se skládají z typu akce, kanálu pro akci a dvou bajtů pro data. Pro odeslání této zprávy slouží funkce midiOutShortMsg, my si však definujeme další funkci, která už bude sama skládat jednotlivé parametry:

//Naše nová funkce
MidiOutMessage(int Status, int Channel, int Data1, int Data2)
{
  DWORD All=Status | Channel | (Data1<<8) | (Data2<<16);	//Složit data
  return midiOutShortMsg(hMidiOut,All);                     //Odeslat
}

Nastavení nástroje

Pro každý kanál (je jich celkem 16) bychom měli na začátku nastavit nástroj, kterým se noty budou přehrávat. Seznam těchto nástrojů zde nebudu vypisovat (je jich celkem 128), ale dá se to najít v každém progamu editujícím MIDI soubory. Tento nástroj nastavíme tak, že jako Akci (Status) předáme hexa hodnotu 0xC0, dále předáme kanál a jako první datový byte nastavíme číslo nástroje, který chceme vybrat.

//Naše funkce pro nastavení nástroje
DWORD MidiOutSetPatch(int Channel, int Voice)
{
  return MidiOutMessage(0x0C0,Channel,Voice,0);
}

Přehrávání not

Přehrávání not probíhá v MIDI tak, že nejprve odešleme zprávu, aby se nota začala přehrávat a když ji chceme umlčet, pošleme další zprávu o vypnutí noty. Těmto funkcím opět předáváme číslo kanálu a dále číslo noty, kterou chceme spustit a potom hlasitost v rozsahu 0 až 128.

//Zapnutí noty
DWORD MidiOutNoteOn(int Channel, int Octave, int Note, int Vel)
{
  return MidiOutMessage(0x90,Channel,12*Octave+Note,Vel);
}

//Vypnutí noty
DWORD MidiOutNoteOff(int Channel, int Octave, int Note, int Vel)
{
  return MidiOutMessage(0x80,Channel,12*Octave+Note,Vel);
}

Celkový příklad

Na konec je tu ukázka, jak postupovat, když chcete přehrát notu C5 na nástroji Klavír (číslo nástroje má 0):

//Incicializuje zařízení
HMIDIOUT hMidiOut;
if(midiOutOpen(&hMidiOut,MIDI_MAPPER,0,0,0))
{
  MessageBox("Nepodařilo se otevřít MIDI zařízení!","Chyba",MB_ICONERROR);
  return;
}

//Nastavení nástroje
MidiOutSetPatch(0,0);         //Kanál 0, nástroj klavír

//Zapnutí noty
MidiOutNoteOn(0,5,0,128);     //Kanál 0, Oktáva 5, Nota 0
//Čekání 1 sek.
Sleep(1000);
//Vypnutí noty
MidiOutNoteOff(0,5,0,128);

//Deinicializace zařízení
midiOutClose(hMidiOut);

Stáhnout zdrojový kód (28 kb)

UPOZORNĚNÍ: Jedná se o archiv článků z let 2003 - 2005, uvedené technologie či postupy již mohou být neaktuální.