Záznam zvuku pomocí MCI v C++

V předchozích dvou článcích jsme se zabývali přehráváním multimediálních souborů pomocí MCI (Media Control Interface). V tomto článku si ukážeme, jak lze MCI využít pro záznam zvuku do formátu WAV.
Použijeme funkci mciSendCommand.

MCIERROR mciSendCommand(
  MCIDEVICEID IDDevice,
  UINT uMsg,
  DWORD fdwCommand,
  DWORD dwParam
);

Pro použití této funkce v projektu je potřeba přidat (pokud to ještě nemáte) hlavičkový soubor <mmsystem.h> a knihovnu winmm.lib.

S touto funkcí jsme se již setkali, když jsme si ukázali, jak programově ovládat CD mechaniku. Tato funkce je totiž velmi univerzální a vhodným nastavením parametrů lze její pomocí realizovat nejrůznější multimediální funkce.

Jako parametr dwParam v případě záznamu zvuku budeme zadávat adresy některých MCI struktur. Jako první použijeme strukturu MCI_OPEN_PARMS

typedef struct {
    DWORD        dwCallback;
    MCIDEVICEID  wDeviceID;
    LPCSTR      lpstrDeviceType;
    LPCSTR      lpstrElementName;
    LPCSTR      lpstrAlias;
} MCI_OPEN_PARMS;

V nejjednodušším případě stačí jako parametr lpstrDeviceType zadat text „waveaudio“, ostatní parametry „vynulovat“ a zavolat poprvé funkci mciSendCommand:

mciOpen.dwCallback = 0;
mciOpen.lpstrAlias = NULL;
mciOpen.lpstrDeviceType = TEXT("waveaudio");
mciOpen.lpstrElementName = TEXT("");
mciOpen.wDeviceID = 0;
dwResult = mciSendCommand(0, MCI_OPEN,
MCI_WAIT | MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
(DWORD)(LPMCI_OPEN_PARMS)&mciOpen);
if ( dwResult != 0 )
{
ErrorMessage(dwResult);
return;
}

Vlastní chybová funkce, která z kódu chyby vypíše textový popis chyby, může vypadat následovně:

void CMainFrame::ErrorMessage(DWORD dwError)
{
TCHAR chText[1024];
mciGetErrorString(dwError, chText, sizeof(chText) / sizeof(TCHAR));
MessageBox(chText, "Chyba při záznamu", MB_ICONERROR);
}

Možná se divíte, proč při otevírání zařízení nemusíme zadat žádný identifikátor (prvek wDeviceID). Je tomu tak proto, že takto zavolaná funkce nám právě tento identifikátor vrátí, takže dalším krokem bude:

wDeviceID = mciOpen.wDeviceID;

Dalším krokem bude další volání funkce mciSendCommand, tentokrát s použitím struktury

typedef struct {
    DWORD dwCallback;
    DWORD dwFrom;
    DWORD dwTo;
} MCI_RECORD_PARMS;

Prvek dwCallback určuje handle okna, které bude dostávat notifikační zprávy o změně či dokončení příslušné operace. To je výhodné, zejména pokud specifikujeme také parametry dwFrom a dwTo, které, jak již názvy napovídají, určují „pozice“ začátku a konce operace záznamu. V našem případě je zatím necháme nulové, budeme tedy nahrávat zvuk neustále až do „ručního“ zastavení.

wDeviceID = mciOpen.wDeviceID;
mciRecord.dwCallback = (DWORD)m_hWnd;
mciRecord.dwFrom = 0;
mciRecord.dwTo = 0;
mciSendCommand(wDeviceID, MCI_RECORD, MCI_NOTIFY,
(DWORD)(LPMCI_RECORD_PARMS)&mciRecord);
m_RecordingNow = TRUE;

Jak je vidět, opět jde o použití funkce mciSendCommand, tentokrát s parametrem MCI_RECORD.

Zmínil jsem se o „ručním“ zastavení. Když si vytvoříme třeba handler stlačení tlačítka na dialogu, bude funkce zastavení záznamu zvuku vypadat třeba takto:

void CUkazkaDlg::StopRecording()
{
MCI_GENERIC_PARMS mciGeneric;
if ( !m_RecordingNow )
return;
mciGeneric.dwCallback = 0;
mciSendCommand (wDeviceID, MCI_STOP, MCI_WAIT,
(DWORD)(LPMCI_GENERIC_PARMS)&mciGeneric);
mciSave.dwCallback = 0;
mciSave.lpfilename = TEXT(chFileName);
mciSendCommand(wDeviceID, MCI_SAVE, MCI_WAIT | MCI_SAVE_FILE,
(DWORD)(LPMCI_SAVE_PARMS)&mciSave);
mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT,
(DWORD)(LPMCI_GENERIC_PARMS)&mciGeneric);
m_RecordingNow = FALSE;
}

Nejprve jsme tedy použili (jak jinak) opět funkci mciSendCommand s parametrem MCI_STOP a dále jsem uložili zaznamenaný zvuk do souboru (typu .wav) na disk, zavoláním funkce mciSendCommand s parametrem MCI_SAVE a vyplněnou strukturou

typedef struct {
    DWORD  dwCallback;
    LPCSTR lpfilename;
} MCI_SAVE_PARMS;

kde lpfilename je samozřejmě jméno požadovaného souboru a dwCallback může být opět handle okna, které obdrží zprávu o dokončené operaci. V našem případě jej zatím nepoužíváme.

Jak jistě víte, v systému Windows existuje většinou několik cest k témuž cíli a ani při programování MCI zařízení tomu není jinak. Ukažme si ještě trochu jiný způsob přehrávání záznamů, než jsme používali v předchozích dvou dílech tohoto seriálu. Nyní, když jsme si ukázali „univerzální“ funkci mciSendCommand, nebude asi žádným překvapením, že touto funkcí můžeme záznam také přehrávat (a samozřejmě zastavit přehrávání).

Pro spuštění přehrávání použijeme opět dvojí volání této funkce, přičemž tím prvním již známým způsobem otevřeme zařízení a poté použijeme strukturu MCI_PLAY_PARMS:

typedef struct {
    DWORD dwCallback;
    DWORD dwFrom;
    DWORD dwTo;
} MCI_PLAY_PARMS;

význam jejichž parametrů je jistě již zřejmý, a parametr MCI_PLAY:

void CUkazkaDlg::OnPlay()
{
mciOpen.dwCallback = 0;
mciOpen.wDeviceID = 0;
mciOpen.lpstrDeviceType = NULL;
mciOpen.lpstrElementName = chFileName;
mciOpen.lpstrAlias = NULL;
dwResult = mciSendCommand(0, MCI_OPEN,
MCI_WAIT | MCI_OPEN_ELEMENT,
(DWORD)(LPMCI_OPEN_PARMS)&mciOpen);
if ( dwResult != 0 )
ErrorMessage(dwResult);
wDeviceID = mciOpen.wDeviceID;
mciPlay.dwCallback = (DWORD)m_hWnd;
mciPlay.dwFrom = 0;
mciPlay.dwTo = 0;
mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY,
(DWORD)(LPMCI_PLAY_PARMS)&mciPlay);
m_PlayingNow = TRUE;
}

Nakonec obdobným způsobem můžeme „ručně“ zastavit přehrávání souboru

void CUkazkaDlg::StopPlaying()
{
mciGeneric.dwCallback = 0;
mciSendCommand(wDeviceID, MCI_STOP, MCI_WAIT,
(DWORD)(LPMCI_GENERIC_PARMS)&mciGeneric);
mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT,
(DWORD)(LPMCI_GENERIC_PARMS)&mciGeneric);
m_PlayingNow = FALSE;
}

Nyní si ukažme, jak lze v proceduře okna zachytávat události o změně stavu přehrávání. Okno, jehož handle jsme určili při spuštění příslušné akce, dostane zprávu MM_MCINOTIFY

MM_MCINOTIFY
wParam = (WPARAM) wFlags
lParam = (LONG) lDevID

ve které wParam určuje, z jakého důvodu došlo k příslušné události. Může nabývat následujících hodnot:

  • MCI_NOTIFY_ABORTED – zařízení obdrželo příkaz, který přerušil právě probíhající a ještě nedokončenou operaci.
  • MCI_NOTIFY_FAILURE – během provádění příkazu nastala na zařízení chyba
  • MCI_NOTIFY_SUCCESSFULL – operace byla úspěšně dokončena
  • MCI_NOTIFY_SUPERSEDED – zařízení obdrželo jiný příkaz s nastaveným příznakem notifikace, který změnil aktuální podmínky notifikace.
V našem případě budeme zachytávat zprávu MM_MCINOTOFY a testovat pouze hodnotu MCI_NOTIFY_SUCCESSFUL. Ukažme si, jak zachytit ukončení (a to jak „ruční“ nebo ukončení dosažením konce souboru) přehrávání a záznamu:

LRESULT CUkazkaDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
switch ( message )
{
case MM_MCINOTIFY:
switch (wParam)
{
case MCI_NOTIFY_SUCCESSFUL:
if ( m_PlayingNow )
MessageBox("Přehrávání zastaveno");
if ( m_RecordingNow )
MessageBox("Záznam zastaven");
break;
}
break;
}
return CDialog::WindowProc(message, wParam, lParam);
}

Zde si můžete stáhnout ukázkový projekt: mci_recorder.zip (33 kB)

Váš názor Další článek: EMI nabídne předplatné hudby

Témata článku: Software, Windows, Programování, Zvuk, Nejjednodušší případ, WFLA, Struktura, Message, Code, Dokončené dílo, Notifikace, Zavolání, Záznam


Určitě si přečtěte

Nejlepší příslušenství k počítači. Tipy na osvědčené klávesnice, tiskárny, routery…

Nejlepší příslušenství k počítači. Tipy na osvědčené klávesnice, tiskárny, routery…

** Tipy na užitečné příslušenství k počítačům ** Poradíme, s jakými produkty neuděláte chybu ** Některé z věcí mohou být dobrými vánočními dárky

David Polesný, Stanislav Janů | 20

David PolesnýStanislav Janů
Příslušenství
Micro:bit V2: Tuto destičku plnou čipů dokáže naprogramovat i vaše babička

Micro:bit V2: Tuto destičku plnou čipů dokáže naprogramovat i vaše babička

** Chcete se teď hned naučit programovat čipy? ** Nechcete nic instalovat a číst zdlouhavé manuály? ** Naprogramujeme si Micro:bit, který zahraje Tichou noc

Jakub Čížek | 33

Jakub Čížek
Pojďme programovat elektronikuProgramování pro děti
Co je to UWB? Nová technologie zastoupí Wi-Fi, Bluetooth i NFC a slibuje velké věci

Co je to UWB? Nová technologie zastoupí Wi-Fi, Bluetooth i NFC a slibuje velké věci

** V nových mobilech se začíná objevovat tajemná zkratka UWB ** Jde o další technologii, jak navzájem propojit různá zařízení ** Oproti Wi-Fi a Bluetooth má řadu výhod

Lukáš Václavík | 36

Lukáš Václavík
UWBIoTTechnologie
Google chystá funkci, která z chytrého Gmailu udělá hloupý Gmail
Lukáš Václavík
SoukromíGmailGoogle
Velký den pro Apple: Uvedl tři nové Macy s vlastním procesorem M1
Lukáš Václavík
PočítačeApple
Jak v prohlížeči vypnout oznámení zasílaná webovými stránkami

Jak v prohlížeči vypnout oznámení zasílaná webovými stránkami

** Obtěžují vás neustálé dotazy webů, zda chcete zobrazovat oznámení? ** Můžete je zakázat, a to jak kompletně, tak i pro jednotlivé stránky ** Připravili jsme návody pro Chrome, Firefox, Edge a Operu

Karel Kilián | 11

Karel Kilián
Jak na InternetTipyProhlížeče
Uživatelé hlásí problémy s jednou z listopadových záplat pro Windows 10
Karel Kilián
Windows UpdateAktualizaceWindows 10

Aktuální číslo časopisu Computer

Jak prodloužit výdrž notebooku

Velké testy: gamepady a inkoustové tiskárny

Důkladný test Sony Playstation 5