Jak si popovídat se svým PC DirectSoundCapture

Záznam zvuku v DirectX Rozhraní DirectX od firmy Microsoft znáte určitě všichni velice dobře z různých her, kterým ...


Záznam zvuku v DirectX
Rozhraní DirectX od firmy Microsoft znáte určitě všichni velice dobře z různých
her, kterým právě toto řešení umožňuje omámit vás záplavou barev a zvuků.
DirectX ale v žádném případě není určeno jen pro hry, dá se často použít i k
věcem nečekaným, jako je tomu v případě DirectSoundCapture. A právě touto částí
se budeme zabývat v následujících odstavcích, které jsou určeny jak těm, co
přímo pod DirectX pracují, tak těm ostatním, kteří by rádi zjistili, co se
vlastně za tím tajemným slovem DirectX skrývá.
DirectSoundCapture (dále jen DSCapture) je komponenta DirectX sloužící k
záznamu zvuku. Tato komponenta je méně známá, protože se nachází poněkud ve
stínu daleko významnějšího DirectSound sloužícího k přehrávání zvuku.
Záznam zvuku je činnost, při které přenášíme zvuk z vnějšího zdroje do
počítačové paměti. Takto vznikají tzv. samply, obvykle osmi nebo
šestnáctibitové vzorky zvuku. Každý sampl má určitou vzorkovací frekvenci,
jejíž výše je přímo úměrná kvalitě zvuku. Známá je zejména frekvence 44 100 Hz,
kterou používá standard CD Audio.
Inicializace DSCapture
Použití DSCapture je poměrně jednoduché. Za předpokladu, že máte překladač C++
a potřebné knihovny DirectX 5 nebo novější (DSound.h, DSound.lib a DXGUID.lib),
můžete začít. Nejprve je nutné inicializovat objekt DSCapture.
#include <DSound.h>
LPDIRECTSOUNDCAPTURE lpdsc; //pointer na objekt
//DSCapture
//InitCapture vrací příznak chyby
int InitCapture() { if FAILED(DirectSoundCaptureCreate(0,&lpdsc,0)) return 1;
return 0;
}
Po skončení práce je samozřejmě třeba objekt řádně uvolnit. To si ale ukážeme
až později.
Nastavení formátu zvuku
Jak již bylo naznačeno, zvuk se zaznamenává v určitém formátu. Na výběr máte
mono nebo stereo, 8bit nebo 16bit a to na některé z frekvencí 11 025 Hz, 22 050
Hz a 44 100 Hz. Mnohé novější zvukové karty umožňují záznam i na jiných
frekvencích (např. 32 000 Hz nebo 48 000 Hz) nebo úplně libovolné frekvence.
Následující kód doplňte na konec naší funkce InitCapture.
DSCCAPS dsccaps;
//formát zvuku CD Audio
WAVEFORMATEX wfx={WAVE_FORMAT_PCM,2,44100,176400,4,16,0};
//zjistíme možnosti zvukové karty
dsccaps.dwSize = sizeof(DSCCAPS);
if FAILED(lpdsc->GetCaps(&dsccaps)) return 1; //chyba
//když zvuková karta neumí CD kvalitu, použijeme //8bitový zvuk
if ((dsccaps.dwFormats & WAVE_FORMAT_4S16)==0) { wfx.nChannels = 1; //mono
wfx.nSamplesPerSec = 11025; wfx.nAvgBytesPerSec = 11025; wfx.nBlockAlign = 1;
wfx.wBitsPerSample = 8;
}
Ve skutečnosti asi použijete rafinovanější algoritmus na nalezení optimálního
formátu. Většina zvukových karet naštěstí CD kvalitu podporuje, stejně jako
frekvence 11 050 Hz a 22 050 Hz. Výjimkou je SoundBlaster Pro a starší karty,
které se dnes již naštěstí moc nepoužívají.
Nyní tedy máme nastavený formát zvuku ve struktuře wfx. Zbývá ho tedy aktivovat.
DSCBUFFERDESC dscbDesc;
LPDIRECTSOUNDCAPTUREBUFFER lpdscb;
dscbDesc.dwSize = sizeof(DSCBUFFERDESC);
dscbDesc.dwFlags = 0;
//vytvoříme buffer na 1 sekundu zvuku
dscbDesc.dwBufferBytes = wfx.nAvgBytesPerSec;
dscbDesc.dwReserved = 0;
dscbDesc.lpwfxFormat = &wfx;
if FAILED(lpdsc->CreateCaptureBuffer(&dscbDesc,&lpdscb, 0)) return 1; //chyba
Signály
Důležitým prvkem při záznamu zvuku jsou signály, kterými nám počítač oznamuje
naplnění bufferu. Obvykle pracujeme tak, že si necháváme oznamovat naplnění
vždy poloviny zvukového bufferu. Záznam pak pokračuje do druhé poloviny bufferu
a my zatím první polovinu zpracujeme. Po naplnění druhé poloviny bufferu
dostaneme další signál a záznam pokračuje opět v první polovině bufferu. Tak to
funguje stále dokola. Buffer zvuku tak používáme vlastně dva poloviční, které
jsou cyklicky zřetězené podobně, jak to ukazuje obrázek.
HANDLE rghEvent[2];
DSBPOSITIONNOTIFY rgdscbpn[2];
LPDIRECTSOUNDNOTIFY lpdsNotify;
//vytvoříme 2 signály
for(int i=0;i<2;i++) { rghEvent[i] = CreateEvent(0,0,0,0); if(!rghEvent[i])
return 1; //chyba
}
//nastavíme signály na začátek a polovinu bufferu
rgdscbpn[0].dwOffset = dscbDesc.dwBufferBytes/2;
rgdscbpn[0].hEventNotify = rghEvent[0];
rgdscbpn[1].dwOffset = 0;
rgdscbpn[1].hEventNotify = rghEvent[1];
//vyžádáme si Notify interface
if FAILED(lpdscb->QueryInterface(IID_IDirectSoundNotify,(void**)&lpdsNotify))
return 1; //chyba
//nastavíme notification positions
if FAILED(lpdsNotify->SetNotificationPositions(2,rgdscb pn))
{ lpdsNotify-> Release(); return 1; //chyba
}
return 0; //konec funkce InitCapture
Čekání na signály
Jakmile máme nastaveny signály, zbývá jen dořešit jejich ošetření. Na signály
můžete čekat jednoduše funkcí WaitForMultipleObjects. Ovšem praxe je poněkud
složitější, protože tato systémová funkce je blokující. Ukážeme si tedy
jednotlivé případy, které mohou nastat.
1. Konzolová aplikace
Konzolová aplikace nemá frontu zpráv, takže můžete bez obav použít zmíněnou
funkci WaitForMultipleObjects.
//budeme čekat nekonečně dlouho
DWORD num=WaitForMultipleObjects(2,rghEvent,0,INFINITE);
//num bude číslo naplněného bloku 0 nebo 1
num-=WAIT_OBJECT_0;
2. GDI aplikace
V GDI (tedy oknové) aplikaci musíte obsluhovat frontu zpráv, takže nemůžete
jednoduše volat blokující funkce WaitForMultipleObjects. Existují však dvě
možná řešení. Pokud se nebojíte použít multithreading, můžete signály zvukové
karty obsluhovat v samostatném vláknu (thread). V tom případě postupujete jako
v případě konzolové aplikace.
Pokud však chcete zůstat u jednoho vlákna, musíte přepsat aplikační smyčku
zpráv (message queue).
int done=0;
while(!done) { //čekáme na signál nebo zprávu co přijde dříve DWORD
num=MsgWaitForMultipleObjects(NUMCAPTUREEVENTS,
rghEvent,0,INFINITE,QS_ALLINPUT); //num bude číslo naplněného bloku 0 nebo 1
num-=WAIT_OBJECT_0; if(num<2) { /* můžete zpracovat signál */ } else if(num==2)
{ //zpracujeme všechny zprávy ve frontě zpráv
while(PeekMessage(&msg,0,0,0,PM_REMOVE)) { if(msg.message==WM_QUIT) done=1;
//konec aplikace else { TranslateMessage(&msg); DispatchMessage(&msg); } } }
}
Zpracování zaznamenaného bloku
Signál přichází vždy při naplnění jednoho ze dvou bloků. Zaznamenaný zvuk
můžete po uzamknutí zpracovat libovolným způsobem.
LPVOID p,p2;
DWORD bytes,b2;
lpdscb->Lock(num*dscbDesc.dwBufferBytes/2,
dscbDesc.dwBufferBytes/2,&p,&bytes,&p2,&b2,0);
//blok je připraven na adrese p o délce bytes bajtů
lpdscb->Unlock(p,bytes,p2,b2);
Začátek a ukončení záznamu
Nyní již máte připraveny všechny náležitosti a můžete začít se záznamem. K tomu
slouží dvě jednoduché metody capture bufferu.
lpdscb->Start(DSCBSTART_LOOPING); //začátek záznamu
lpdscb->Stop(); //konec záznamu
Před ukončením aplikace
Na závěr ještě musíte řádně uvolnit všechny objekty DirectX, které jste
používali.
void CloseCapture() { if(lpdsNotify) lpdsNotify->Release(),lpdsNotify=0;
if(lpdscb) lpdscb->Release(),lpdscb=0; if(lpdsc) lpdsc->Release(),lpdsc=0;
}

0 1691 / alsn









Komentáře
K tomuto článku není připojena žádná diskuze, nebo byla zakázána.