Nahrávání zvuků (MFC a C++)

Jelikož o minulý článek o přehrávání wave souborů byl celkem velký zájem, tak jsem se rozhodl napsat k němu pokračování. Jedná se o nahrávání zvuků pomocí funkcí waveIn a následné ukládání do wave souborů. Ukládání zde však nebudu nijak významně popisovat, ale použiji třídu CWaveFile (vlastní tvorba), která se pro tento případ velice hodí.

Inicializace nahrávání

Nejprve si opět přilinkujeme knihovnu winmm.lib a vložíme hlavičkový soubor mmsystem.h , ve které jsou uloženy definice všech těchto funkcí.

K vlastní inicializaci slouží několik funkcí. Jako první se použije waveInOpen() , která nám zpřístupní manipulátor HWAVEIN zvukového vstupu. Potom nastavíme ve struktuře WAVEHDR formát zvukových dat, která chceme nahrávat, a buffer, do kterého chceme data ukládat. Následně zavoláme funkci waveInPrepareHeader , která nechystá buffer pro přijetí zvukových dat. Nyní bližší popis těchto funkcí:

MMRESULT waveInOpen(
  LPHWAVEIN phwi,     	       // Handle pro nahrávání zvuku
  UINT uDeviceID,              // Identifikátor zařízení
  LPWAVEFORMATEX pwfx,         // Formát dat pro záznam
  DWORD dwCallback,            // Adresa CALLBACK funkce
  DWORD dwCallbackInstance,    // Nepoužito
  DWORD fdwOpen                // Určuje, co je v parametru dwCallback
);

//příklad na funkci waveInOpen
waveInOpen(&m_hWaveIn,WAVE_MAPPER,&m_WaveFormat,NULL,NULL,NULL);
MMRESULT waveInPrepareHeader(
  HWAVEOUT hwo,            // Handle vstupního zař.
  LPWAVEHDR pwh,           // Adresa hlavičky, ve které jsou data
  UINT cbwh                // velikost hlavičky pwh
);

//příklad na funkci waveInPrepareHeader
waveInPrepareHeader(m_hWaveIn,&m_WaveHdr,sizeof(m_hWaveHdr));

Když už máme správně nastavené zařízení pro záznam, zbývá už jenom spustit nahrávání. K tomu slouží následující funkce.

Spuštění nahrávání

Před vlastním spuštění nahrávání však ještě musíme buffer, který máme připravený, dodat nahrávacímu zařízení, aby do něj mohl umisťovat zvuková data. K tomu slouží funkce waveInAddBuffer . To, že byl buffer naplněn, se stejně jako v případě nahrávání dozvíme pomocí členu dwFlags struktury WAVEHDR.

MMRESULT waveInAddBuffer(
  HWAVEIN hwi,       //Handle výst. zař.
  LPWAVEHDR pwh,     //Adresa struktury WAVEHDR
  UINT cbwh          //Velikot struktury WAVEHDR
);
//příklad na volání
waveInAddBuffer(m_hWaveIn,&m_WaveHdr,sizeof(m_WaveHdr));

Jakmile máme buffer umístěný ve frontě, stačí zavolat funkci waveInStart , která má pouze jeden parametr a to je identifikátor vstupního zařízení. Když se potom buffer naplní, musíme zařízení znovu uvolnit a to pomocí funkcí waveInStop() a waveInReset(), jejichž použití je stejné jako v případě přehrávání zvuků.

Souhrnný příklad

Nyní dáme všechny tyto funkce dohromady a vytvoříme jednoduchou aplikaci na nahrávání zvuků a jejich ukládání do souboru.

void CMujDialog::OnRecordFile()
{
  BYTE* buffer=new BYTE[1000000];
  HWAVEIN win;
  CWaveFile file;
  file.BuildFormat(2,22050,16);

  ::waveInOpen(&win,WAVE_MAPPER,&file.GetFormat(),NULL,NULL,NULL);
  WAVEHDR hdr;
  ::ZeroMemory(&hdr,sizeof(hdr));
  hdr.dwBufferLength=1000000;
  hdr.lpData=(LPSTR)buffer;
  ::waveInPrepareHeader(win,&hdr,sizeof(hdr));
  ::waveInAddBuffer(win,&hdr,sizeof(hdr));
  ::waveInStart(win);
  MSG message;
  while(!(hdr.dwFlags & WHDR_DONE))
  {
    while (::PeekMessage(&message,NULL,0,0,PM_REMOVE)) //čekací smyčka
    {
      ::TranslateMessage(&message);
      ::DispatchMessage(&message);
    }
  }
  ::waveInStop(win);
  ::waveInUnprepareHeader(win,&hdr,sizeof(hdr));
  ::waveInReset(win);
  
  file.SetLength(1000000);
  file.SetBuffer(buffer);
  file.Save("C:\\zvuk.wav");
  MessageBox("Hotovo!");
}

A jak to to tedy všechno pracuje? Nejdříve se vytvoří zvukový buffer o velikosti 1MB a nedeklarují se potřebné proměnné. Jelikož funkci waveInOpen musíme předat formát zvukových dat, využijeme třídy CWaveFile, která ho pomocí funkce BuildFormat sestaví a potom ho vrátí pomocí funkce GetFormat(). Po otevření zařízení naplníme strukturu WAVEHDR daty a začneme nahrávat. Následuje čekací smyčka a po ní uvolnění vstupního zařízení. Nakonec opět použijeme třídu CWaveFile a to k uložení souboru. Buffer mazat nemusíme, protože třída jej vymaže sama ve svém destruktoru.

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

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