Arama Motoru

OpenAL Ders 5: Sourcelerin Bufferları paylaşması

14/06/2003
25/04/2010

OpenAL dizisinin bu aşamasında, bufferlarınızın birden çok source arasında paylaşılması metodunu göstereceğim. Bu, çok makul ve doğal bir adım, ve bu o kadar kolay ki bazılarınız hali hazırda bunu kendisi yapmış olabilir. Eğer bunu yaptıysanızm bu dersi şimdi atlayıp sonrakine geçebilir. Ama verdiğim bilginin tümünü okumaya hevesli zekiler için ilginç bulanabilir bu ders. Artı, direkt Alc katmanını uygulayacağız öyle ki ders 4'de kazanılan bilginin bir kısmını kullanacağız. Bunlara ek, hatta kullanabileceğiniz bir program yaratacağız!
Güzel, hadi. Kodun çoğu şimdiye kadar seride tekrarlandığından dolayı sadece önemli olan kod kısmını incelemeye karar verdim. İndirilen kaynak kodundan tamamına bakılabilir. Bir şey de: Bu ders, STL'den vektörleri kullanacak, bundan dolayı onun kurulu olduğundan emin olun ve çalışması hakkında biraz bilginizin vardır umarım. Burada STL'i anlatmayacağım çünkü bu bir OpenAL dersidir.

// bufferlar indeksi.
#define THUNDER     0
#define WATERDROP   1
#define STREAM      2
#define RAIN        3
#define CHIMES      4
#define OCEAN       5
#define NUM_BUFFERS 6
 
 
// Bufferlar ses datasını tutar.
ALuint Buffers[NUM_BUFFERS];
 
// çoklu çalma için sourcelerin vektör listesi.
vector Sources;

İlkin, buffer dizisini indekslemede kullandığımız benim yazdığım birkaç makro. Farklı wav dosyalarını kullanıyor olacağız bundan dolayı burada birden çok buffera ihtiyaç duyarız. Sourceleri depolamak için bir diziyi kullanmak yerine bir STL vektörünü kullanacağız. Bunu yapmayı seçtik çünkü sourcelerin sayısını dinamik belirlememize olanak verir. Sourceleri sahnede sadece OpenAL onları tüketinceye kadar ekli tutarız. Bu, aynı şekilde bizim, sourceleri bir resource olarak değindiğimiz ilk derstir. Ve Evet, tükenirler; onlar sonludur.

ALboolean InitOpenAL()
{
    ALCdevice* pDevice;
    ALCcontext* pContext;
    ALCubyte* deviceSpecifier;
    ALCubyte deviceName[] = "DirectSound3D";
 
    // aygıt handleyini el et.
    pDevice = alcOpenDevice(deviceName);
 
    // aygıt belirtecini elde et.
    deviceSpecifier = alcGetString(pDevice, ALC_DEVICE_SPECIFIER);
 
    printf("Using device '%s'.\n", szDeviceSpecifier);
 
    // ses context’i yarat.
    pContext = alcCreateContext(pDevice, NULL);
 
    // aktif geçerli context olarak yap.
    alcMakeContextCurrent(pContext);
 
    // hata kontrolünü yap.
    if (alcGetError() != ALC_NO_ERROR)
        return AL_FALSE;
 
    return AL_TRUE;
}

Bu, bizim, son derste öğrendiğimiz örnek kodun bir kısmıdır. "DirectSound3D" aygıtına bir handle elde ederiz, ve sonra bizim uygulamamız için bir renderleme context'i elde ederiz. Bu context aktif geçerli olarak set edilir ve fonksiyon başarıyla dönmeden evvel her şey düzgün gittiğinin kontrolü yapılır.

void ExitOpenAL()
{
    ALCcontext* pCurContext;
    ALCdevice* pCurDevice;
 
    // aktif geçerli context’i elde et.
    pCurContext = alcGetCurrentContext();
 
    // bu context tarafından kullanılan aygıtı elde et.
    pCurDevice = alcGetContextsDevice(pCurContext);
 
    // aktif geçerli context’i NULL’a getir.
    alcMakeContextCurrent(NULL);
 
    // context ve aygıtı serbest bırak.
    alcDestroyContext(pCurContext);
    alcCloseDevice(pCurDevice);
}

Bu, bizim önceki kodda yaptığımızın tersini yapacak. O, Uygulamamızın kullanıp serbest bırakacağı context ve aygıtı elde eder. O hem de, OpenAL'e yollanan herhangi bir verinin işleme tabi tutulmasını askıya alacak aktif geçerli context’e NULL 'u set eder . Onun aktif geçerli context olarak NULL ayarlanması önemlidir yoksa veriyi işleme tabi tutmak için geçersiz bir contexta sahip olacaksınız. Bunu yapmanın sonuçları, tahmin edilemeyebilir.
Eğer bir multi-context uygulamasını kullanıyorsanız ilkleme ve sonlamayla uğraşan daha ileri bir yönteme sahip olmaya ihtiyaç duyabilirsin. Bütün aygıtları ve contextleri global yapıp aktif geçerli context'i geri almadan daha önce onları ayrı ayrı kapatmanı tavsiye ederim.

ALboolean LoadALData()
{
    // alutLoadWAVFile çağrısıyla dolacak değişkenler.
    ALenum format;
    ALsizei size;
    ALvoid* data;
    ALsizei freq;
    ALboolean loop;
 
    // bufferlara wav datasını yükle.
    alGenBuffers(NUM_BUFFERS, Buffers);
 
    if(alGetError() != AL_NO_ERROR)
        return AL_FALSE;
 
    alutLoadWAVFile("wavdata/thunder.wav", &format, &data, &size, &freq, &loop);
    alBufferData(Buffers[THUNDER], format, data, size, freq);
    alutUnloadWAV(format, data, size, freq);
 
    alutLoadWAVFile("wavdata/waterdrop.wav", &format, &data, &size, &freq, &loop);
    alBufferData(Buffers[WATERDROP], format, data, size, freq);
    alutUnloadWAV(format, data, size, freq);
 
    alutLoadWAVFile("wavdata/stream.wav", &format, &data, &size, &freq, &loop);
    alBufferData(Buffers[STREAM], format, data, size, freq);
    alutUnloadWAV(format, data, size, freq);
 
    alutLoadWAVFile("wavdata/rain.wav", &format, &data, &size, &freq, &loop);
    alBufferData(Buffers[RAIN], format, data, size, freq);
    alutUnloadWAV(format, data, size, freq);
 
    alutLoadWAVFile("wavdata/ocean.wav", &format, &data, &size, &freq, &loop);
    alBufferData(Buffers[OCEAN], format, data, size, freq);
    alutUnloadWAV(format, data, size, freq);
 
    alutLoadWAVFile("wavdata/chimes.wav", &format, &data, &size, &freq, &loop);
    alBufferData(Buffers[CHIMES], format, data, size, freq);
    alutUnloadWAV(format, data, size, freq);
 
    // diğer hata kontrolunu yapıp döndür.
    if (alGetError() != AL_NO_ERROR)
        return AL_FALSE;
 
    return AL_TRUE;
}

Bu fonksiyondan sourceleri birlikte üretimini çıkardık. Çünkü bundan sonra, ayrı sourceleri ilkleyeceğiz.

void AddSource(ALint type)
{
    ALuint source;
 
    alGenSources(1, &source);
 
    if (alGetError() != AL_NO_ERROR)
    {
        printf("Error generating audio source.");
        exit(-1);
    }
 
    alSourcei (source, AL_BUFFER,   Buffers[type]);
    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,  AL_TRUE      );
 
    alSourcePlay(source);
 
    Sources.push_back(source);
}

Bizim için sourceleri oluşturacak olan fonksiyon budur. Bu fonksiyon, önceki kodla oluşturduğumuz yüklü bufferların her birisi için tek bir source oluşturur. Verilen buffer , indeks “tipindedir”,  bu dersin başında yarattığımız makrolardan biridir. Çalabilecek bir source'e sahip olduğumuzdan emin olmak için bir hata kontrolü yaparız (dediğim gibi, onlar sonludur). Eğer bir source tahsis edilemezse, programdan çıkılacak.

void KillALData()
{
    for (vector::iterator iter = Sources.begin(); iter != Sources.end(); ++iter)
        alDeleteSources(1, iter);
    Sources.clear();
    alDeleteBuffers(NUM_BUFFERS, Buffers);
    ExitOpenAL();
}

Bu fonksiyon, STL listesi kullanımı sebebiyle biraz değiştirildi. Ayrı ayrı listedeki her source'i silip sonra etkili olarak onu yok edecek olan liste temizliği zorunluluktur.

ALubyte c = ' ';
 
    while (c != 'q')
    {
        c = getche();
 
        switch (c)
        {
            case 'w': AddSource(WATERDROP); break;
            case 't': AddSource(THUNDER);   break;
            case 's': AddSource(STREAM);    break;
            case 'r': AddSource(RAIN);      break;
            case 'o': AddSource(OCEAN);     break;
            case 'c': AddSource(CHIMES);    break;
        };
    }

Bu, main'imizdeki programın iç döngüsüdür. O basitçe, bazı klavye girdilerini bekler ve belirli tuşun basılmasıyla, belirli türde yeni source’i yaratıp ses sahnesine ekler. Aslında bizim burada yarattığımız , insanların gevşeme için dinlediği doğa kaydı gibi bir şey. Arka planda istedikleri sesleri özelleştirmek için kullanıcıya izin verdiğinden dolayı bizimki biraz daha iyidir. Oldukça etkili he? Ben, kodlama yaparken kendiminkini dinlemekteyim. O, bir Zen tecrübesidir (şu an onu dinliyorum).
Program, daha çok wav dosyasını kullanmak için genişletilip, sourceleri sahne civarı keyfi pozisyona konumlatma özelliği eklemeye sahip. Döngületmek yerine siz de verilecek bir frekansla  sourceleri çaldırtabilirsiniz. Bununla birlikte bu dersin alanının ötesinde olan GUI rutinlerini gerektirir.  Tüm özellikleriyle oyun motorunu yapmak için güzel bir program olurdu. ;)


* Bu dersin java versiyonu için Java Bindings for OpenAL sayfasını ziyaret edin (Athomas Goldberg tarafından adapte edilmiştir)

Dersin Orjinali : http://www.devmaster.net/articles/openal-tutorials/lesson5.php