14/06/2003
02/05/2010
Bazı şeyleri
işlemede çok titiz olmanın bize gerekmediği
düşünülüp birkaç tane çok basit çam devirdik. Bunun sebebi, sağlamlıktan ziyade daha kolay öğrenmek
için kodları basit
yazmaktı. Çabucak ileri
kısma geçmek
isteğinden doğru yolu
öğrenmesini erteledik. Ki en önemlisi, hataları
yönetmenin daha gelişmiş bir
yolunu öğrenmekti. Yine , ses verisini
yükleme yöntemini
tekrar düzenleyeceğiz. Metotlarımızda yanlış
olan herhangi bir şey yok, ama süreç daha organize
ve esnek bir yaklaşıma ihtiyaç duyar.
İlkin, bitirdiğimiz
zaman bize çokça yardımı olacak birkaç
fonksiyonu değerlendireceğiz.
string GetALErrorString(ALenum err);
/*
* 1) hata kodunu çıkarsar.
* 2) hata, string olarak döndürülür.
*/
string GetALCErrorString(ALenum err);
/*
* 1) hata kodunu çıkarsar.
* 2) hata, string olarak döndürülür.
*/
ALuint LoadALBuffer(string path);
/*
* 1) bir bufer yaratır.
* 2) buffer’a bir wav dosyası yükler.
* 3) buffer id döndürülür.
*/
ALuint GetLoadedALBuffer(string path);
/*
* 1) dosyanın hali hazırda yüklülüğü kontrol edilir.
* 2) yüklüyse, buffer id döndürülür.
* 3) yüklü değilse, yükleyip buffer id döndürülür.
*/
ALuint LoadALSample(string path, bool loop);
/*
* 1) bir source yaratır.
* 2) dizinle 'GetLoadedALBuffer' çağrılıp
* source’ünün buffer’ı olarak , döndürülen buffer id’i kullanır.
* 3) source id döndürülür.
*/
void KillALLoadedData();
/*
* 1) geçici yüklü faz veriyi serbest bırakır.
*/
bool LoadALData();
/*
* 1) uygulama için tüm buffer’lar ve source’leri yükler.
*/
void KillALData();
/*
* 1) tüm bufferları serbest bırakır.
* 2) tüm sourcelerı serbest bırakır.
*/
vectorLoadedFiles; // geçici yüklü dosya dizinlerini tutar.
vectorBuffers; // tüm yüklü buffer’ları tutar.
vectorSources; // tüm geçerli source’leri tutar.
Fonksiyonlara
yakından bakıp
ne yaptıklarını anlamayı çalışalım.
Basitçe,
artık bufferlar ve sourceler
arasındaki ilişkiden
endişe duymak zorunda olmayacağımız sistemi yaratmaya
giriştik.
Bir dosyadan bir source'nin yaratılması için çağırabildiğimiz ve kendi bufferını yaratmayı yönetecek bu sistem de buffer kopyalaması oluşmaz (Aynı verili iki
buffer'ın
olmaz). Bu sistem, sınırlı bir
resource olarak bufferları yönetir,
Ve verimli bir
şekilde bu resource'i
yönetir.
string GetALErrorString(ALenum err)
{
switch(err)
{
case AL_NO_ERROR:
return string("AL_NO_ERROR");
break;
case AL_INVALID_NAME:
return string("AL_INVALID_NAME");
break;
case AL_INVALID_ENUM:
return string("AL_INVALID_ENUM");
break;
case AL_INVALID_VALUE:
return string("AL_INVALID_VALUE");
break;
case AL_INVALID_OPERATION:
return string("AL_INVALID_OPERATION");
break;
case AL_OUT_OF_MEMORY:
return string("AL_OUT_OF_MEMORY");
break;
};
}
Bu fonksiyon, OpenAL hata kodunu string’e çevirir böylece konsolda okunabilir (Veya bir diğer çıktı aygıtında). OpenAL sdk mevcut versiyonunda 'AL_OUT_OF_MEMORY' hatası bahsedilen tek istisnadır. Tabi, biz tüm hataları dikkate alacağız ki kodumuz sonraki sürümlerle uyumlu olsun.
string GetALCErrorString(ALenum err)
{
switch(err)
{
case ALC_NO_ERROR:
return string("AL_NO_ERROR");
break;
case ALC_INVALID_DEVICE:
return string("ALC_INVALID_DEVICE");
break;
case ALC_INVALID_CONTEXT:
return string("ALC_INVALID_CONTEXT");
break;
case ALC_INVALID_ENUM:
return string("ALC_INVALID_ENUM");
break;
case ALC_INVALID_VALUE:
return string("ALC_INVALID_VALUE");
break;
case ALC_OUT_OF_MEMORY:
return string("ALC_OUT_OF_MEMORY");
break;
};
}
Bu
fonksiyon, Alc hatalarını yorumlanması için
öncekine benzer bir iş yapar. OpenAL ve Alc, ortak
id'ler paylaşır,
Tabi
tamamen
ortak değil ve Her ikisi için aynı
fonksiyonu kullanacak kadar
değil.
'alGetError'
fonksiyonu hakkında bir not
daha: OpenAL sdk,
aynı
anda
sadece
tek bir
hatanın tutulduğunu
belirtmektedir (yığma
yok). Fonksiyon çağrıldığı
zaman almış olduğu ilk hatayı döndürür ve sonra hata
bitini 'AL_NO_ERROR' e
getirir. Diğer bir deyişle
hali
hazırda
orada depolu önceden bir hata yoksa hata bitinde sadece bir hata
depolu olacaktır.ALuint LoadALBuffer(string path)
{
// Buffer’ı tanımlayan bilgiyi depolayacak değişkenler.
ALenum format;
ALsizei size;
ALvoid* data;
ALsizei freq;
ALboolean loop;
// Buffer id ve hata kontrol değişkenleri.
ALuint buffer;
ALenum result;
// bir buffer yarat. Başarıyla yaratıldığını kontrol et.
alGenBuffers(1, &buffer);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// dosyadan wav datayı oku. Doğru yüklülüğünü kontrol et.
alutLoadWAVFile(szFilePath, &format, &data, &size, &freq, &loop);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// buffer’a wav datasını yolla. Doğru şekilde alındığını kontrol et.
alBufferData(buffer, format, data, size, freq);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// geçici datadan kurtul.
alutUnloadWAV(format, data, size, freq);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// buffer id’i döndür.
return buffer;
}
Anlayacağınız üzere yükleme
aşamasında mümkün tüm hataların kontrolünü
yaparız. Bir hatanın
fırlatılmasına(throw) sebebiyet verecek
birçok şey bu aşamada olabilir. Mesela bufferı yaratmak veya
veriyi yüklemek için yeterli
sistem belleği yoktur, Wav
dosyasının kendi bile var
olmayabilir veya bir hata üretecek
OpenAL fonksiyonlarının herhangi birisine geçersiz bir parametre değeri
geçilmiş
olabilir.
ALuint GetLoadedALBuffer(string path)
{
int count = 0; // 'count', buffer listesinin indeksi olacak.
ALuint buffer; // yüklü bufferın Buffer id’si.
// listede her bir dosya dizini için tekrarla.
for(vector::iterator iter = LoadedFiles.begin(); iter != LoadedFiles.end(); ++iter, count++)
{
// Eğer bu dosya yolu hali hazırda yüklü olanla aynıysa, sadece buffer ID’si döndürülür.
if(*iter == path)
return Buffers[count];
}
// Eğer bir şey çıkartılamazsa bu dosya yenidir ve onun için bir buffer yaratırız.
buffer = LoadALBuffer(path);
// Listeye bu yeni buffer’ı ekleyip bu dosyanın hali hazırda yüklendiği kaydedilir.
Buffers.push_back(buffer);
LoadedFiles.push_back(path);
return buffer;
}
Bu muhtemelen, çoğunuzu sıkıntıya sokacak kod parçası olacak, Ama o gerçekte karmaşık değildir.
Şimdiye kadar yüklediğimiz bütün
wav dosya yollarını içeren
bir listede bir arama yapıyoruz. Eğer
dizinlerin biri yüklemeyi istediğimizle
aynıysa, sadece ilk yüklediğimizdeki onun buffer id'sini döndüreceğiz. Bu fonksiyon ile dosyalarımızı yüklediğimiz sürece
, asla kopyalama
nedenli
buffer israf etmiş
olmayacağız. Bu şekilde yüklenen
her dosya yine kendi
listesiyle teması
kesmemeli. 'Bufferların’ listesi,
'yüklüdosyalar' listesine
paraleldir. Demek istediğim, ‘Bufferlar’
indeksindeki her buffer, bufferların
yaratıldığı ‘yüklüdosyalar’’daki indeksin dizini aynıdır.
ALuint LoadALSample(string path, bool loop)
{
ALuint source;
ALuint buffer;
ALenum result;
// dosyanın buffer id’sini elde et (gerekiyorsa onu yükle).
buffer = GetLoadedALBuffer(path);
// bir source üret.
alGenSources(1 &source);
if ((result = alGetError()) != AL_NO_ERROR)
throw GetALErrorString(result);
// source özelliklerini yapılandır
alSourcei (source, AL_BUFFER, buffer );
alSourcef (source, AL_PITCH, 1.0 );
alSourcef (source, AL_GAIN, 1.0 );
alSourcefv(source, AL_POSITION, SourcePos);
alSourcefv(source, AL_VELOCITY, SourceVel);
alSourcei (source, AL_LOOPING, loop );
// source id’i kaydet.
Sources.push_back(source);
// source id’i döndür.
return source;
}
Bizin için bufferları yönetecek bir sistemi yarattığımız düşünülürse, sadece source'leri pişpişleyecek bir ilaveye ihtiyaç duyarız. Bu kodda, yüklü dosyanın buffer id’sini dosya arama sonucu olarak elde ederiz. Bu buffer yeni source'e bağlanır. Source id'i kaydedip onu döndürürüz.
void KillALLoadedData()
{
LoadedFiles.clear();
}
'gLoadedFilesv'
global vektörü bir buffera yüklü tüm wav dosyalarının dosya yolunu depolar. Verimizin hepsini yükledikten sonra ayakaltında bu veriyi tutmanın anlamı
yoktur, bundan dolayı ondan
kurtulacağız.
// Source id'leri.
ALuint phaser1;
ALuint phaser2;
void LoadALData()
{
// Bu şeyler uygulamanızda burada. Bufferlar hakkında endişe yok.
phaser1 = LoadALSample("wavdata/phaser.wav", false);
phaser2 = LoadALSample("wavdata/phaser.wav", true);
KillLoadedALData();
}
Önceki derslerde bu fonksiyonu gördük. Program tarafından kullanılan tüm wav'ları yükleyen programın bir bölümünü temsil ediyor. Onda, sistemimizin, neden faydalı olduğunu görebiliriz. İki farklı source’e aynı wav dosyasını yüklemek üzere çağrıyı yapmış olsak da, 'phaser.wav' dosyası bufferı sadece bir kez yaratılacak, ve 'gPhaser1' ve 'gPhaser2' sourcelerinin ikisi de çalma için bu bufferı kullanacak. Bufferların yönetiminde artık kaygı yok çünkü Sistem otomatik olarak onları yönetecek.
void KillALData()
{
// tüm buffer datasının serbest bırak.
for (vector::iterator iter = Buffers.begin(); iter != Buffers.end(); ++iter)
alDeleteBuffers(1, iter);
// tüm source datasının serbest bırak.
for (vector::iterator iter = Sources.begin(); iter != Sources.end(); ++iter)
alDeleteBuffers(1, iter);
// listeleri yok et.
Buffers.clear();
Sources.clear();
}
En başından, buffer ve
source id'lerini stl vektörlerinde depoladık.
. Buffer ve sourcelerin tümünü boşaltıp ayrı ayrı onları
serbest bırakırız. Sonra listelerini yok ederiz. Şimdi yapmaya ihtiyaç
duyduğumuz tek şey, yakalayacağımız(catch) OpenAL hatalarını
fırlattırmaktır(throw).
try
{
InitOpenAL();
LoadALData();
}
catch(string err)
{
cout << "OpenAL error: " << err.c_str() << endl;
}
Eğer bir şey,
yükleme esnasında yanlış gitse
derhal bundan haberdar
edileceğiz. Hatayı yakaladığımız
(catch) zaman bu konsolda
bildirilecek. Debuglama veya genel hata raporlamada bunu
kullanabiliriz.
Hepsi bu
kadar. Hataları raporlamanın
daha
ileri bir yolu, Ve wav
dosyalarınız yüklemenin daha
sağlam bir yolu.
Daha
fazla esnekliği imkan
vermesi
için gelecekte bazı değişiklikler yapmaya ihtiyaç duyabiliriz, Ama şimdilik gelecek
derslerde temel dosya yüklemesi
için bu
kaynağı kullanacağız.
Bu kodu
genişletmek için gelecek dersleri bekle.
Dersin Orjinali : http://www.devmaster.net/articles/openal-tutorials/lesson6.php