┌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
|
|
Poslednφ Φßst. NapφÜeme funkci CreateOggCodecObject pro poskytovßnφ ukazatel∙ na rozhranφ kterß poskytujek knihovna OggCodec kterou jsme napsali v p°edchozφch Φßstech. Ve druhΘ
Φßsti se pokusφme p°ehrßt testovacφ soubor v projektu Tester.
Obsah:
Ve t°etφ Φßsti jste si jist∞ vÜimli funkce CreateOggCodecObject. Pou₧ili jsme ji k zφskßnφ ukazatele na rozhranφ IOggFile.
Nynφ se jφ podφvßme pod kapotu. Op∞t se jednß o funkci kterß u₧ byla probφrßna v kurzu Direct X. ZaΦneme hlaviΦkov²m souborem s definicφ. Manager.h:
#pragma once
enum OGGCODEC_API OGGIID
{
OGGIID_ISoundManager,
OGGIID_IOggFile
};
OGGCODEC_API HRESULT CreateOggCodecObject(OGGIID IntrfaceID,void** ppv);
Jeden z nejkratÜφch soubor∙ celΘho °eÜenφ :) Nejd°φve definujeme v²Φtov² datov² typ s konstantami pro r∙znß rozhranφ
je₧ poskytuje naÜe knihovna OggCodec. V naÜem p°φpad∞ je to jen IOggFile a ISoundManager. DalÜφ je prototyp funkce CreateOggCodecObject.
Je exportovanß z knihovny stejn∞ jako zmφn∞nß rozhranφ. Prvnφm parametrem je konstanta urΦujφcφ rozhranφ na kterΘ chceme zφskat ukazatel.
Druh² parametr je ukazatel na prom∞nnou do kterΘ se mß ulo₧it ukazatel na rozhranφ. (P°φÜernß v∞ta :)). Te∩ implementace, soubor Manager.cpp:
#include "StdAfx.h"
#include "Common.h"
#include "Interfaces.h"
#include "OggFile.h"
#include "SoundManager.h"
#include "Manager.h"
OGGCODEC_API HRESULT CreateOggCodecObject(OGGIID InterfaceID,void** ppv)
{
static ISoundManager* g_SoundManager = NULL;
switch (InterfaceID)
{
case OGGIID_ISoundManager:
if (!g_SoundManager)
*ppv = g_SoundManager = new CSoundManager;
else
*ppv = g_SoundManager;
((ISoundManager*)(*ppv))->AddRef();
break;
case OGGIID_IOggFile:
*ppv = new COggFile;
((IOggFile*)(*ppv))->AddRef();
break;
default:
return E_NOINTERFACE;
}
return S_OK;
}
Ve funkci se nachßzφ statickß prom∞nnß typu ukazatel na ISoundManager. To je jen kv∙li tomu, aby v₧dy kdy₧
si za₧ßdßme o ukazatel na rozhranφ ISoundManager byl vrßcen stejn² ukazatel. Tj. t°φda CSoundManager bude vytvo°ena jen p°i prvnφm volßnφ. Proto₧e
je CSoundManager potomek ISoundManager, m∙₧eme vrßtit ukazatel na ISoundManager, kterΘ poskytuje jen metody. P°i po₧adavku na rozhranφ IOggFile se
vytvß°φ v₧dy novß t°φda COggFile a je vrßcen ukazatel na IOggFile. Pro ob∞ rozhranφ se p°ed nßvratem z funkce volß funkce AddRef, kterß zv²Üφ poΦet odkaz∙ na t°φdu.
Snad jsem to moc nezamotal. Pro zdßrnΘ pochopenφ rozhranφ je nutnΘ znßt d∞diΦnost. Probφrala se v kurzu C++ od 13. dφlu.
Obsah
Zb²vß nßm snad u₧ jen pou₧φt vytvo°enß rozhranφ v projektu Tester a p°ehrßt si n∞jak² ten soubor. V²vojovΘ prost°edφ za nßs vytvo°ilo zßkladnφ oeknnφ aplikaci pro
Windows. Nemusφme se tedy zab²vat vytvß°enφm ok²nka, ale jen dopsßnφm naÜeho k≤du a modifikacφ toho co vygeneroval pr∙vodce. N∞kterΘ Φßsti nßm toi₧ nevyhovujφ.
Nejd°φve se podφvjeme na hlaviΦkovΘ soubory vlo₧enΘ do souboru Tester.cpp
#include "stdafx.h"
#include "Tester.h"
#include "../oggcodec/common.h"
#include "../oggcodec/interfaces.h"
#include "../oggcodec/manager.h"
Soubory stdafx.h a Tester.h za nßs vlo₧ilo ji₧ v²vojovΘ prost°edφ. Zb²vajφcφ t°i musφme vlo₧it my. V soubor Common.h jsou spoleΦnΘ definicie kterΘ vy₧aduje veÜker² k≤d projektu.
Intrfaces.h obsahuje definice rozhranφ a Manager.h obsahuje definici funkce CreateOggCodecObject. Jedna malß ·prava kterß nßs Φekß je vymazßnφ prom∞nnΘ HWND hWnd z t∞la funkce
InitInstance. Vyma₧te jen definici. Pot°ebujeme mφt tuto prom∞nnou globßlnφ a ne aby si ji syslila jedna funkce pro sebe. Nadefinujte ji tedy jako globßlnφ prom∞nnou na zaΦßtku souboru p°ed implemtacφ
vÜech funkcφ a za vlo₧en²mi hlaviΦkov²mi soubory. Kdy₧ u₧ jsme u t∞ch globßlnφch prom∞nn²ch, definujte jeÜt∞ jednu typu ukazatel na ISoundManager (ISoundManager* gSoundManager = NULL; ).
Te∩ se podφvßme na funkci _tWinMain, kterß obsahuje hlavnφ programovou smyΦku.
int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
{
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_TESTER, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_TESTER);
HRESULT hRet = CreateOggCodecObject(OGGIID_ISoundManager,(void**)&gSoundManager);
if (FAILED(hRet))
return hRet;
hRet = gSoundManager->CreateDirectSoundSystem(hWnd);
if (FAILED(hRet))
return hRet;
hRet = gSoundManager->LoadOgg(0,"test.ogg");
IOggFile* gOgg = gSoundManager->GetOggFile(0);
if (gOgg)
{
FILE* fstream = fopen("ogg_info.txt","w");
fprintf(fstream,"Info about file: test.ogg\n");
fprintf(fstream,"File Vendor: %s\n",gOgg->GetOggFileVendor());
fprintf(fstream,"File Version: %d\n",gOgg->GetOggFileVersion());
fprintf(fstream,"File Playing Time: %g\n",gOgg->GetOggFilePlayingTime());
fprintf(fstream,"File PCM Size: %ld\n",gOgg->GetOggFilePCMSize());
for (UINT i = 0; i < gOgg->GetCountOfOggFileComments(); i++)
fprintf(fstream,"Comment %d, %s\n",i,gOgg->GetOggFileComment(i));
fclose(fstream);
}
gOgg->ConvertOggToWav("test.ogg","test.wav");
hRet = gSoundManager->PlayOgg(0);
// Main message loop:
while(true)
{
// Look for messages, if none are found then
// update the state and display it
if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
{
if( 0 == GetMessage(&msg, NULL, 0, 0 ) )
{
// WM_QUIT was posted, so exit
SAFE_RELEASE(gSoundManager);
return (int)msg.wParam;
}
// Translate and dispatch the message
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
hRet = gSoundManager->CheckAllOggIfNeedReFillBuffer();
}
}
SAFE_RELEASE(gSoundManager);
return (int) msg.wParam;
}
K≤d vyznaΦen² tuΦn∞ je ten kter² jsem p°idal nebo upravil. Upravenß je hlavnφ programovß smyΦka (cyklus while). Zbytek je k≤d generovan²
p°i vytvß°enφ projektu. ZaΦneme tφm ₧e po₧ßdßme funkci CreateOggCodecObject o ukazatel na ISoundManager stejn∞ jako jsme to d∞lali v p°φpad∞ rozhranφ IOggFile. Jen prvnφ parametr je
OGGIID_ISoundManager. Mßme ukazatel na rozhranφ ISoundManager, ale jeÜt∞ ne₧ zaΦneme volat funkce pro prßci s ogg, m∞li bychom inicializovat DirectSound. Zavolßme funkci
CreateDirectSoundSystem rozhranφ ISoundManager. Zadßvßme jen prvnφ parametr, handle okna (globßlnφ prom∞nnß hWnd). Druh² parametr, m≤d spoluprßce je nepovinn² (mß v²chozφ hodnotu DSSCL_PRIORITY).
Pokud se to poda°φ, m∙₧eme se pokusit naΦφst ogg soubor. Jß v tomto p°φkladu pou₧φvßm pevn² nßzev test.ogg, ale nenφ problΘm brßt jmΘno souboru nap°φklad z p°φkazovΘ °ßdky.
Otev°eme soubor test.ogg pomocφ funkce LoadOgg. M∞li bychom v₧dy kontrolovat vrßcenou hodnotu, zda nedoÜlo k chyb∞. Zde to nemßm jen z toho d∙vodu ₧e soubor je zadßn na pevno a p°i testovßnφ
k ₧ßdnΘ chyb∞ nedoÜlo. Pokud ale budete d∞lat univerzßlnφ p°ehrßvaΦ, v₧dy kontrolujte vrßcenou hodnotu. Platφ to dvojnßsob hlavn∞ p°i programovßnφ s DirectX. Tedy tam jsem to pocφtil nejvφc.
V∞tÜina k≤du je jen kontrola vrßcen²ch hodnot a p°edchßzenφ chybßm. Dßle se v k≤du ptßme na ukazatel rozhranφ IOggFile prßv∞ otev°enΘho test.ogg. Nßsledn∞ toto rozhranφ pou₧φvßme
k p°φstupu k funkcφm kterΘ rozhranφ ISoundManager neposkytuje a pou₧φvßme je k v²pisu zßkladnφch informacφ o souboru do souboru ogg_info.txt. PotΘ testujeme funkci ConvertOggToWav.
O jejφ funkΦnosti by vßs m∞l p°esv∞dΦit dek≤dovan² soubor na disku. Poslednφm p°φkazem p°ed hlavnφ programovou smyΦkou je spuÜt∞nφ p°ehrßvßnφ pomocφ funkce PlayOgg.
V hlavnφ programovΘ smyΦce se k≤d v∞nuje hlavn∞ zpracovßnφ zprßv a pokud zrovna ₧ßdnΘ nejsou (netahßte oknem z mφsta na mφsto nebo na n∞j neklikßme jako divφ) volß funkci
CheckAllOggIfNeedReFillBuffer aby bylo zajiÜt∞no p°ehrßvßnφ souboru. Pokud dojde k ukonΦenφ smyΦky, tj. aplikace je ukonΦena, nesmφme zapomenout uvolnit rozhranφ pomocφ makra SAFE_RELEASE.
Poslednφ ·pravy jsou ve funkci WndProc:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_ACTIVATEAPP:
//zjisteni zda je aplikace aktivni, pokud je, prijde zprava WM_ACTIVATE
IsAppActive = (bool)wParam;
if (gSoundManager)
gSoundManager->HandleAppActiveStateChanges(IsAppActive);
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
gSoundManager->StopAllOgg();
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Zde jsem p°idal jen reakci na zprßvu WM_ACTIVATEAPP a zastavenφ p°ehrßvßnφ vÜech soubor∙ (StopAllOgg) p°i zprßv∞ WM_DESTROY (aplikace je ukonΦovßna).
Pokud obdr₧φme zprßvu WM_ACTIVATEAPP, zavolßme funkci HandleAppActiveStateChanges kterß pozastavφ nebo op∞t spustφ p°ehrßvßnφ soubor∙. To je z toho d∙bodu, ₧e kdy₧
ztratφme fokus, nemusφ se stihnout volat funkce pro obnovenφ zvukovΘho bufferu v dostateΦn²ch intervalech. Mohly by vznikat mezery, slyÜeli bychom n∞jakou Φßst vφcekrßt ne₧ by doÜlo
k dalÜφmu obnovenφ. To je vÜe. Pokud nynφ sestavφte celΘ °eÜenφ ... co₧ se vßm nepoda°φ, ne ₧e by bylo n∞co Üpatn∞, jen z principu, p°eklepy, tu zapomenete vlo₧it hlaviΦkov² soubor atd.
M∞ se snad jeÜt∞ nikdy nepoda°ilo sestavit p°φklad z n∞jakΘho chytrΘho v²ukovΘho kurzu napoprvΘ :) A₧ se vßm to poda°φ, m∞l by se soubor p°ehrßvat korektn∞. (Ale nevφm, n∞kterΘ moje programy
majφ tendenci fungovat jen m∞ a jinde mi d∞lat ostudu :(). Ale...
Obsah
Bohu₧el to nenφ p°eklep ani aprφl, ve t°φd∞ COggFile mßme chybu, a o docela vß₧nou. Ale jak °φkajφ n∞kte°φ z m²ch vyuΦujφcφch na vysokΘ Ükole, ka₧dß chyba kterou ud∞lßm, je Φist∞ ·myslnß aby jste se z nφ
pouΦili a abych v∞d∞l zda dßvßte pozor a vÜimnete si jφ. Ne te∩ vß₧ne, samoz°ejm∞ ₧e to nebyla ·myslnß chyba. NaÜel jsem ji kdy₧ jsem dola∩oval ukßzkov² projekt pro tento kurz, ale °ekl jsem
si ₧e mo₧nß bude lepÜφ ne₧ opravovat druhou lekci ji tam nechat a vrßtit se k nφ a₧ tady, abychom se z nφ mohli pouΦit a vid∞li ₧e nikdo nenφ neomyln². Doufßm ₧e to byla jedna z poslednφch. Jde o to, ₧e mßme Üpatn∞ naprogramovanou obnovu bufferu. P°esn∞ji °eΦeno, zapom∞l jsem
hlφdat stav kdy je dek≤dovßn cel² soubor a p°ehrßvßnφ jeÜt∞ neskonΦilo. Takov² stav nastane v₧dy p°ed koncem p°ehrßvßnφ ogg souboru. Soubor bude v₧dy dek≤dovßn d°φv ne₧ bude cel² p°ehrßn. Kdy₧ je ale dek≤dovßn, nemß smysl
u₧ volat funkci WriteDataToBuffer pro zapsßnφ nov²ch dat, co₧ souΦasnß t°φda ned∞lß. Dφky tomu dochßzelo k tomu, ₧e funkce WriteDataToBuffer zaΦala vracet v prom∞nnΘ dwDataDelta velikΘ hodnoty. Tj. v prom∞nnΘ kterß udßvß
kolik z dwBytes po₧adovan²ch bajt∙ nebylo dek≤dovßno. P°i krokovßnφ jsem pak m∞l hodnotu dwDataDelta = dwBytes - 1. Vzdßlenost kurzor∙ zaΦala stßle r∙st a funkce WriteDataToBuffer se zaΦala volat p°i ka₧dΘm volßnφ funkce
CheckIfNeedReFillBuffer se stßle v∞tÜφ vzdßlenostφ kurzor∙. Vzdßlenost kurzor∙ jak si jist∞ takΘ vzpomφnßte urΦuje kolik bajt∙ chceme zapsat do zvukovΘho bufferu, jak velkou pam∞t alokujeme p°i mezizßpisu do pam∞ti.
Netrvalo dlouho a doÜlo k chyb∞ v programu proto₧e vzdßlenost kurzor∙ vzrostla natolik, ₧e u₧ nebylo mo₧nΘ alokovat tolik pam∞ti. Prost∞ doÜlo k chybnΘ alokaci a program spadl. D°φv jsem si toho nevÜiml, proto₧e ve svΘ p∙vodnφ t°φd∞ pou₧φvßm mφsto DataBuffer = new char[dwBytes] vlastnφ
t°φdu pro prßci s °et∞zcov²mi, bajtov²mi poli, kterß to n∞jak vst°ebala. ╪eÜenφ nenφ a₧ tak slo₧itΘ, ale bude si vy₧adovat p°idßnφ ΦlenskΘ prom∞nnΘ
m_dwWriteProgress do t°φdy COggFile. Tato prom∞nnß bude hlφdat pr∙b∞h zapisovßnφ, dek≤dovßnφ ogg souboru. V konstruktoru t°φdy nastavte tuto prom∞nnou na nula. Dßle ve funkci WriteDataToBuffer napiÜte m_dwWriteProgress += lSize; za dek≤dovacφ cyklus while s
funkcφ ov_read. Poslednφ jsou ·pravy ve funkci CheckIfNeedReFillBuffer. Zm∞ny a p°φdavky jsou op∞t tuΦn∞:
//**********************************************************************************************************
//Fce pro kontrolu nutnosti zapisu novych dat do bufferu
//**********************************************************************************************************
HRESULT COggFile::CheckIfNeedReFillBuffer()
{
HRESULT hRet = S_FALSE;
bool bRet = false;
DWORD dwCurrPlayPos = 0;
DWORD dwDataDelta = 0;
DWORD dwCursorsDistance = 0;
if (m_StreamedBuffer->IsSoundPlaying() || m_bStatus == OGGSTATUS_PAUSED)
{
//ziskani aktualni pozice hraciho kurzoru
if(FAILED(hRet = m_StreamedBuffer->GetBuffer()->GetCurrentPosition(&dwCurrPlayPos,NULL)))
return hRet;
if ((m_dwPlayProgress + dwCurrPlayPos) >= m_dwPCMSize) //tady to se zpracovava pokud dojde k prehrani celeho souboru
{
hRet = m_StreamedBuffer->Stop(); //pokud dojde k prehrani celeho souboru, zastavime prehravani bufferu
hRet = m_StreamedBuffer->Reset(); //a resetujeme buffer a prislusne promenne
m_dwNextWriteOffset = 0; //zapis bude zacinat opet na nule
m_dwPlayProgress = 0; //stejne tak pocet prehranzch bytu je nula
m_dwLastPlayPos = 0; //posledni pozice hraciho kurzoru taktez
dwCursorsDistance = 0; //vzdalenost hraciho a zapisovaciho kurzoru taktez nulova
dwCurrPlayPos = 0;
m_dwWriteProgress = 0;
ov_raw_seek(&m_OggVorbisFile,0); //presun na zacatek v ogg souboru
WriteDataToBuffer(0,m_dwBufferSize,NULL); //naplneni bufferu daty z ogg souboru od zacatku, kdyby byla pozdeji spustena fce play
//tak at to tam je
if (!m_iRepeatCount) //pokud je pocet opakovani 0
{
//nastavime stav zastaveno
m_bStatus = OGGSTATUS_STOPPED;
return S_FALSE; //vratime false jako indikaci ze nedoslo k obnoveni dat v bufferu
}
if (m_iRepeatCount != OGGPLAY_REPEATINFINITE) //pokud pocet opakovani neni nula a neni ani roven konstante pro
m_iRepeatCount--; //nekonecno opakovani, snizime pocet opakovani
hRet = m_StreamedBuffer->Play(0,DSBPLAY_LOOPING); //zacneme znovu prehravat buffer, pac se este neprehral tolikrat jak je nastaveno
return hRet;
}
//pokud je posledni ulozena pozice hraciho kurzoru vetsi nez pozice kurzoru aktualni, znamena to ze
//buffer dosahl konce a preskocil zpet na zacatek, podle toho musime pocitat vzdalenost kurzoru rozdilne
if (m_dwLastPlayPos > dwCurrPlayPos)
//hraci kurzor presel zpet na zacatek, musime vypocitat kolik bytu zbylo na konci bufferu neaktualizovanzch od posledni aktualizace
//a pricist k nim pocet bytu co uz byly prehrany znova od zacatku a tak ziskame vzdalenost kurzoru
//vzdalenost mezi hracim a zapisovacim kurzorem
dwCursorsDistance = (m_dwBufferSize - m_dwNextWriteOffset) + dwCurrPlayPos;
else
//hraci kurzor je pred koncem, zapisovaci za nim, vzdalenost je rozdil jejich pozic
//abychom nepocitali i bajt na kterem hraci kurzor zrovna je
dwCursorsDistance = dwCurrPlayPos - m_dwNextWriteOffset;
//ted se mrknem jestli vzdalenost kurzoru presahla pocet bajtu po kterych se ma buffer obnovovat
if (dwCursorsDistance >= m_dwNotifySize) //a pokud ano, zapiseme do bufferu tolik bytu jako je vzdalenost kurzoru
{
if (m_dwWriteProgress < m_dwPCMSize)
{
bRet = WriteDataToBuffer(m_dwNextWriteOffset,dwCursorsDistance - 1,&dwDataDelta); //hraciho kurzoru, tedy mela by, muze se stat
//vzdalenost mezi kurzory bude licha a v tom pripade nam fce ov_read ktera dekoduje ogg soubor
m_dwNextWriteOffset += dwCursorsDistance - 1 - dwDataDelta; //vrati o nekolik bytu min, dekoduje totiz vzdy sudy pocet a ten pocet bajtu
//musime snizit pozici dalsiho zapisovani, aby nevznikla jakasi dira, viz dwDataDelta
if (m_dwNextWriteOffset >= m_dwBufferSize) //tady se hlida situace kdy je psaci kurzor skoro na konci a jeho aktualizaci o pocet zapanzch bajtu by doslo k prekroceni bufferu
m_dwNextWriteOffset -= m_dwBufferSize; //odecteme proto velikost bufferu a psaci kurzor se dostane na spravnou pozici od zacatku bufferu
}
if (m_dwLastPlayPos > dwCurrPlayPos)
m_dwPlayProgress += m_dwBufferSize;
m_dwLastPlayPos = dwCurrPlayPos; //aktualizace posledni pozice hraciho kurzoru
}
}
return hRet;
}
Tak doufßm ₧e to byla poslednφ zßva₧nß chyba v programu. N∞jakΘ menÜφ se najdou v₧dy. Doufßm ₧e se mi poda°ilo aspo≥ troÜku srozumiteln∞ vysv∞tlit jak to funguje a ₧e vßm to n∞co dalo. Pokud budete mφt n∞co na srdci (n∞co ohledn∞ kurzu, nejsem psychologickß poradna :)), m∙j email a icq
pod hlavnφm menu nemajφ jen copyright funkci :) Klidn∞ piÜte a ptejte se. Rady na zlepÜenφ takΘ rßd p°ijmu. Kritiku..., no budu se s tφm muset n∞jak srovnat. Pokud budu v∞d∞t, rßd s p°φpadn²mi problΘmy pom∙₧u. Sna₧te se ale dßt co nejvφc informacφ. Na dotaz typu: nefunguje mi to! Co s tφm ud∞lßÜ? toho asi moc nevymyslφm :)
Obsah
|