Úvod
Instalace Ogg SDK
Torie k DirectSound
Část 1
Část 2
Část 3
Část 4
Funkce knihovny vorbisfile.dll
Odkazy
Ke stažení
R.Henys@seznam.cz
ICQ: 159629842
|
|
V této části vytvoříme projekty se kterými budeme dále pracovat a budeme se též věnovat implementaci
zvukového bufferu.
Obsah:
Vytvoříme si dva nové projekty. Oba budou typu Win32. Bude se jednat o jednu dynamickou knihovnu (dll) a spustitelný
soubor (exe). V knihovně bude implementována samotná třída a spustitelná aplikace bude sloužit jako testovací program.
Já jsem si pro knihovnu zvolil název OggCodec a pro aplikaci Tester.
Otevřete vývojové prostředí a vytvořte nový projekt typu Win32 Project, zadejte název projektu jaký je vám nejbližší a zaškrtněte volbu
Create directory for Solution (zobrazí se po stisku tlačítka More). Klepněte na Ok. Zobrazí se průvodce. Na kartě Application Settings
vyberte přepínač Dll a vygenerujte základní soubory kliknutím na Finish. Teď přidáme druhý projekt přes menu File->Add Project->New Project....
Dále postupujeme stejně jako při vytváření prvního projektu, jen vybereme přepínač Windows Application.

Obsah
Dále je třeba provést ještě nějaká nastavení než budeme moci začít. Klepněte na první projekt pravým myšítkem a z kontextového menu
vyberte Properties. Ve stromové struktuře v levé části okna vyberte Configuration Properties/General a upravte vlastnost
Output Directory na ..\Debug u Debug konfigurace projektu, respektive na ..\Release u Release konfigurace projektu. Takto učiňte i pro
druhý projekt.

Dále u projektu OggCodec (dynamická knihovna) přejděte do záložky Linker a do řádku Input vložte, zapište následující
knihovny: vorbisfile_static.lib ogg.lib vorbis.lib dxerr9.lib dsound.lib winmm.lib strmiids.lib dxguid.lib. Zbývá už jen nastavit
závislost projektů, respektive aplikace Tester na knihovně OggCodec. Stačí navštívit dialog Dependencies pomocí nabídky Project->Project Dependencies....
V menu vybereme projekt Tester a zaškrtneme políčko u OggCodec v seznamu. Pokud nyní řešení přeložíte (Build->Build Solution), dostane se vám chybového hlášení při sestavování ve smyslu že
nelze nálezt soubor OggCodec.lib. To je způsobeno tím, že jste z projektu ještě neexportovali žádnou funkci, třídu ani rozhraní.
Obsah
Jelikož od tohoto odstavce už vlastně začínáme s celým projektem, doporučil bych vám hned na začátku vytvořit si v projektu OggCodec nový
hlavičkový soubor Common.h, který bude sloužit definicím které budou společné pro více souborů. Navíc se tak vyhnete problémům s vícenásobným
vložením hlavičkového souboru a chybovým hlášením a v neposlední řadě je to víc přejhlednější, aspoň podle mého názoru, než vkládat takovou definici do každého
cpp souboru nebo hlavičkového souboru. Do souboru vložte, napište tyto řádky:
Soubor Common.h:
#include <dmusici.h>
#include <vector>
#include <dsound.h>
#include <dxerr9.h>
#include "vorbisfile.h"
#include "codec.h"
#include <math.h>
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
#define SAFE_DELETE_ARRAY(p) { if(p) {delete [] (p); (p) = NULL; } }
#define OGGCODEC_API __declspec(dllexport)
Do souboru jsou vloženy hlavičkové soubory nezbytně nutné pro běh našeho "kodeku", jako je
dsound.h. Hlavičkové soubory z Ogg SDK: vorbisfile.h, codec.h. Makra SAFE_RELEASE, SAFE_DELETE a SAFE_DELETE_ARRAY která budeme často
používat. První makro slouží k uvolnění rozhraní z paměti, druhé ke smazání dynamicky vytvořeného objektu a třetí ke smazání dynamicky
vytvořeného pole. OGGCODEC_API použijeme u definic funkcí, tříd nebo rozhraní které budeme chtít z knihovny exportovat abychom je mohli
použít v dalších programech.
Nyní už se můžeme pustit do třídy CSoundBuffer :)
Metody třídy CSoundBuffer
Create |
Vytvoří rozhraní reprezentující zvukový buffer. |
RestoreBuffer |
Obnoví buffer při jeho ztrátě. |
Reset |
Nastaví pozici hracího kurzoru v bufferu na 0. |
Play |
Spustí přehrávání bufferu |
Stop |
Zastaví přehrávání bufferu. |
SetFrequency |
Nastaví frekvenci přehrávaných zvukových dat. |
SetPan |
Nastavení vyváženosti levého a pravého kanálu. |
SetVolume |
Nastaví hlasitost bufferu. |
IsSoundPlaying |
Zjistí zda buffer data přehrává nebo ne. |
Implementace bufferu je velice podobná té kterou je možné najít v příkladech SDK k DirectX, konkrétně u DirectSound,
ale tato třída je tak jednoduchá, že se u ní snad ani mnoho nového vymyslet nedá. Jedná se vlastně jen o volání metod
rozhraní IDirectSoundBuffer pomocí našich vlastních metod, které navíc jen ošetřují možné chybové stavy.
Vložte do projektu OggCodec novou třídu CSoundBuffer a zapište implementaci třídy. Podívejme se na soubor SoundBuffer.h:
#pragma once
//********************************************************************************************
//Trida zapouzdrujici fce pro praci se zvukovymi buffery
//********************************************************************************************
class CSoundBuffer
{
protected:
LPDIRECTSOUNDBUFFER m_DSSoundBuffer; //ukazatel na rozhrani IDirectSoundBuffer
DWORD m_dwBufferSize; //velikost bufferu v bajtech
DWORD m_dwCreationFlags; //volby ktere jsem pouzili pri vytvareni bufferu
public:
virtual HRESULT Create(LPDIRECTSOUND lpDSound,DSBUFFERDESC* dsBufferDesc);
HRESULT RestoreBuffer(bool* bWasRestored);
HRESULT Play(DWORD dwPriority = 0,DWORD dwFlags = 0);
HRESULT Stop();
HRESULT Reset();
HRESULT SetFrequency(long lFrequency);
HRESULT SetPan(long lPan);
HRESULT SetVolume(long lVolume);
bool IsSoundPlaying();
LPDIRECTSOUNDBUFFER GetBuffer() {return m_DSSoundBuffer;}
CSoundBuffer(void);
~CSoundBuffer(void);
};
Nyní se můžeme podbobněji podívat na jednotlivé funkce. První bude konstruktor a destruktor, kde se provádí jen inicializace a úklid
a myslím že to není třeba podbrobněji rozebírat. Soubor SoundBuffer.cpp:
#include "StdAfx.h"
#include "Common.h"
#include "soundbuffer.h"
//********************************************************************************************
//Konstruktor
//********************************************************************************************
CSoundBuffer::CSoundBuffer(void)
{
m_DSSoundBuffer = NULL; //neplatny ukazatel na rozhrani, neni jeste vytvoreno
m_dwBufferSize = 0; //vychozi hodnoty promennych
m_dwCreationFlags = 0;
}
//********************************************************************************************
//Destruktor
//********************************************************************************************
CSoundBuffer::~CSoundBuffer(void)
{
SAFE_RELEASE(m_DSSoundBuffer); //uvolneni rozhrani z pameti
}
Další si pobereme funkci Create
//*************************************************************************************************************
//Fce pro vytvoreni rozhrani pro zvukovy buffer
//*************************************************************************************************************
HRESULT CSoundBuffer::Create(LPDIRECTSOUND lpDSound,DSBUFFERDESC* dsBufferDesc)
{
HRESULT hRet = DS_OK;
if (FAILED(hRet = lpDSound->CreateSoundBuffer(dsBufferDesc,&m_DSSoundBuffer,NULL))) //pokud vytvareni rozhrani selze
return hRet; //vratime chybu dal
m_dwBufferSize = dsBufferDesc->dwBufferBytes; //ulozeni velikosti bufferu
m_dwCreationFlags = dsBufferDesc->dwFlags; //ulozeni příznaků
return hRet; //vraceni vysledku, sem uz by se melo dostat jen S_OK
}
Funkce očekává že jí předáme ukazatel na rozhraní IDirectSound a strukturu obsahující popis bufferu.
Voláním funkce CreateSoundBuffer zmíněného rozhraní vytvoříme rozhraní které bude reprezentovat zvukový buffer. Předáme jí popis
bufferu, do druhého argumentu se uloží ukazatel na nově vytvořené rozhraní a poslední argument souvisí s technologií COM a je vždy NULL.
Pokud funkce vrátí chybnou hodnotu, nepokračuje se dál. V opačném případě si jen uložíme velikost bufferu a příznaky.
//*************************************************************************************************************
//Fce pro obnoveni bufferu
//*************************************************************************************************************
HRESULT CSoundBuffer::RestoreBuffer(bool *bWasRestored)
{
HRESULT hRet;
if (!m_DSSoundBuffer) //kontrola jestli je rozhrani vubec vytvorene
return CO_E_NOTINITIALIZED;
if (bWasRestored) //kontrola ukazatele
*bWasRestored = false;
DWORD dwStatus;
if (FAILED(hRet = m_DSSoundBuffer->GetStatus(&dwStatus))) //pokud se nam nepodari ziskat stav bufferu
return hRet; //tak rovnou koncime vracenim chyby
if (dwStatus & DSBSTATUS_BUFFERLOST) //pokud byl buffer ztracen
{
do //budeme se v cyklu snazit ho obnovit
{ //dokud se to nepovede
hRet = m_DSSoundBuffer->Restore();
if (hRet == DSERR_BUFFERLOST)
Sleep(10);
}
while ((hRet = m_DSSoundBuffer->Restore()) == DSERR_BUFFERLOST);
if (bWasRestored) //ulozime do uzivatelske promenne ze buffer
*bWasRestored = true; //byl obnoven
return S_OK;
}
return S_FALSE;
}
Funkce nejdřív kontroluje ukazatel na rozhraní bufferu a posléze ukazatel který byl zadán jako parametr. Na místo kam ukazuje bWasRestored se
v případě že je ukazatel platný uloží zda byl buffer obnoven nebo ne. Bude-li tato hodnota po vykonání funkce nabývat false, nemusí to znamenat že fuknce selhala, ale
také že nebylo třeba buffer obnovit. Pokud je potřeba buffer obnovit, provádí se to v cyklu, který trvá tak dlouho, dokud funkce Restore rozhraní
IDirectSoundBuffer nevrátí jinou hodnotu než DSBSTATUS_BUFFERLOST. Pokud je buffer ztracen, je možné že ho nebudeme moci obnovit právě v době volání
této funkce, ale až později. Třeba proto že aplikace nemá fokus.
//*************************************************************************************************************
//Fce spusti prehravani bufferu
//*************************************************************************************************************
HRESULT CSoundBuffer::Play(DWORD dwPriority,DWORD dwFlags)
{
if (!m_DSSoundBuffer) //kontrola platnosti ukazatele na rozhrani
return CO_E_NOTINITIALIZED;
return m_DSSoundBuffer->Play(0,dwPriority,dwFlags);
}
Funkce Play je jednoduchá. Kontroluje jen platnost ukazatele na rozhraní, abychom se neodkazovali na neplatné místo v paměti a nezpůsobili tak krach programu
a pak už jen volá metodu Play rozhraní IDirectSoundBuffer a předává jí argumenty které dostala. Jak si můžete všimnout v hlavičkovém souboru třídy CSoundBuffer, jsou
pro oba parametry zadány výchozí hodnoty, funkci proto můžete volat bez, s jedním nebo s oběma parametry. První parametr je priorita a pokud je nula a budete mít takové buffery
třeba dva, výsledný efekt bude ten, že budou hrát oba najednou. Pokud bude u jednoho priorita 1, počká se až dohraje buffer s prioritou 0 a pak se spustí buffer s prioritou 1.
//*************************************************************************************************************
//Fce nastavi frekvenci
//*************************************************************************************************************
HRESULT CSoundBuffer::SetFrequency(long lFrequency)
{
if (!m_DSSoundBuffer)
return CO_E_NOTINITIALIZED;
if (lFrequency != -1 && m_dwCreationFlags & DSBCAPS_CTRLFREQUENCY)
return m_DSSoundBuffer->SetFrequency(lFrequency);
return S_FALSE;
}
//*************************************************************************************************************
//Fce nastavi hlasitost
//*************************************************************************************************************
HRESULT CSoundBuffer::SetVolume(long lVolume)
{
if (!m_DSSoundBuffer)
return CO_E_NOTINITIALIZED;
if (m_dwCreationFlags & DSBCAPS_CTRLVOLUME)
return m_DSSoundBuffer->SetVolume(lVolume);
return S_FALSE;
}
//*************************************************************************************************************
//Fce pro nastaveni vyvazenosti leveho a preveho kanalu
//*************************************************************************************************************
HRESULT CSoundBuffer::SetPan(long lPan)
{
if (!m_DSSoundBuffer)
return CO_E_NOTINITIALIZED;
if (m_dwCreationFlags & DSBCAPS_CTRLPAN)
return m_DSSoundBuffer->SetPan(lPan);
return S_FALSE;
}
//*************************************************************************************************************
//Fce zastavi prehravani bufferu
//*************************************************************************************************************
HRESULT CSoundBuffer::Stop()
{
if (!m_DSSoundBuffer)
return CO_E_NOTINITIALIZED;
return m_DSSoundBuffer->Stop();
}
//*************************************************************************************************************
//Fce nastavi pozici hraciho kurzoru v bufferu na nula
//*************************************************************************************************************
HRESULT CSoundBuffer::Reset()
{
if (!m_DSSoundBuffer)
return CO_E_NOTINITIALIZED;
return m_DSSoundBuffer->SetCurrentPosition(0);
}
První tři funkce manipulují s různými parametry které jsou zvukovým datům vlastní :) Jde o nastavení frekvence (SetFrequency), vyváženosti levého
a pravého kanálu (SetPan) a hlasitosti (SetVolume. Všechny tyto funkce kontrolují ukazatel na rozhraní a pak také příznaky s jakými byl buffer vytvořen. Pokud
není příznak pro danou vlastnost nastaven, znamená to že ji u befferu nemůžeme řídit, měnit, jen ji číst. Pokud ano, zavolá se jen příslušná funkce rozhraní IDirectSoundBuffer a předá se jí zadaný parametr.
Další dvě funkce jsou Stop a Reset. Zase se jen kontroluje ukazatel na rozhraní. Funkce Reset funguje tak, že nastaví pozici hracího kurzoru na 0, dalo by se jí také říkat Rewind.
Poslední funkcí třídy CSoundBuffer je IsSoundPlaying:
//*************************************************************************************************************
//Fce rekne jestli je buffer prehravan ci ne
//*************************************************************************************************************
bool CSoundBuffer::IsSoundPlaying()
{
if (!m_DSSoundBuffer)
return false;
DWORD dwStatus;
m_DSSoundBuffer->GetStatus(&dwStatus);
return (dwStatus & DSBSTATUS_PLAYING) ? true : false;
}
Funkce je opět jednoduchá. Zda je buffer přehráván zjistíme jednoduše tak, že se ho zeptáme na stav pomocí fuknce GetStatus.
Pokud je buffer přehráván vrátí se nám DSBSTATUS_PLAYING a podle toho vrátíme buď true nebo false
To je ke třídě CSoundBuffer vše. Pokud řešení přeložíte (Build->Build Solution), oběví se vám opět
chyba o nenalezení lib souboru knihovny OggCodec. Otevřete soubor SoundBuffer.h a definici třídy upravte takto:
class OGGCODEC_API CSoundBuffer
Přidáním konstanty ze souboru Common.h jste vývojovému prostředí řekli, že chcete třídu exportovat a soubor lib bude automaticky
vytvořen. Pokud nyní řešení zkusíte přeložit znovu, neměla by se objevit již žádná chyba. Upravte definici zpět na původní, protože
tuto třídu exportovat nebudeme. Ale pokud chcete, můžete ji samozřejmě takto ponechat. Na další dění to nemá vliv :)
Můžete se pustit do další části :)
Obsah
|