Tutorials/Hello OpenAL
From GameDevID
penulis: uray
kemaren liat di article GDI ternyata udah ada 2 orang yang bikin article tentang sound untuk game, jadi kepingin juga bikin, akhirnya pas pulang kantor langsung bikin ini article termasuk source codenya (from scratch) dan selesai dalam waktu kira2 dua jam.
Bukannya gw mau sombong, gw cuman mau menggambarkan betapa mudahnya membuat sound untuk game dalam bentuk 3D, ya 3D maksudnya sumber suara (audio source) dan pendengar suara (listener) berada dalam dunia 3D, kita bisa merubah parameter mereka (misal posisi dalam 3D : x,y,z) dan hardware (soundcard) akan langsung menghitung berubahan suara yang terdengar di speaker sesuai dengan jarak, orientasi dan parameter lainnya dari soundsource pada listener secara otomatis.
Contents |
Introduction
Kita akan menggunakan openAL, openAL bukan AudioEngine melainkan API (application programming interface) khusus untuk 3D sound dan memang didesain dengan tujuan utama untuk game. Perbedaan API dan soundengine adalah pada spesialisasi dan fleksibelitasnya, maksudnya API dibuat lebih fleksibel dibanding audio engine yang juga berakibat API lebih njlimet ato lebih banyak effort untuk menggunakannya.
walaupun openAL adalah API yang merupakan pasangannya openGL (3D library API), tetapi openAL jauh lebih mudah dan simpel. dalam artikel ini gw kasih contoh mbikin aplikasi yang simpel (setingkat hello world), dimana aplikasi ini akan meload file .wav (audio file), kemudian user bisa menggerak2an posisi audiosource dan listener dan mendengar perubahan suara hasil kalkulasi openAL.
apa yang dibutuhkan :
- static library file : openAL32.lib
- header file : al.h dan alc.h
- DLL file : openAL32.lib
- utility untuk decode wav
gw mbikin program ini dalam VisualStudio2005, dalam bahasa C, compiler/IDE lain seharusnya bisa tapi gw lom test, kemudian gw kasi file namanya alWAVLoader.h, didalam file ini ada utility untuk load dan decode file .wav, dimana mekanisme didalamnya tidak dibahas dalam artikel ini karena selain tidak terlalu berhubungan dengan konsep yang akan dipelajari di openAL dan juga terlalu teknis dan njlimet, gw juga cuman copy paste dari openAL SDK dan rubah didalemnya biar ga ada dependency ke library microsoft. jadi jangan ambil pusing cuekin aje yang penting ngerti bahwa fungsi didalam file itu adalah untuk load file .wav dan decode menjadi raw data, kalau mau mempelajari bisa UTFG / RTFW.
dependency to compile & run:
- openAL header (al.h, alc.h)
- openAL DLL (openAL32.lib)
- standard C library (stdio.h,conio.h,memory.h,string.h)
- wav file (sebagai input dengan formatnya harus 16-bit mono PCM)
openAL concept
ada beberapa hal2 dasar yang perlu dipahami ketika coding dengan openAL. ada 6 elemen dasar dalam openAL yaitu :
- device
- context
- listener
- source
- buffer
- environment
device
device disini maksudnya hardware sound card yang akan kita gunakan, ketika pertama kali program jalan, kita harus memilih soundcard mana yang akan digunakan, karena ada kemungkinan dalam satu PC bisa lebih dari 1 soundcard terpasang, jadi kita kasi tawu openAL soundcard yang akan kita gunakan dengan menggunakan fungsi :
alcOpenDevice("nama hardware");
nama hardware disini berupa string, bisa kita dapatkan dengan fungsi enumerasi device, namun pada umumnya dan seringnya kita akan menggunakan default device, yaitu device yang disetting sebagai device default dalam windows (OS), untuk menggunakan default device kita hanya tinggal inputkan parameter string itu dengan NULL. contoh :
ALCdevice* audiodevice = alcOpenDevice(NULL);
context
selanjutnya setelah memilih device, setiap aplikasi harus membuat "context", anggap ajalah context itu something (heu heu) intinya kenapa ada context karena satu soundcard bisa digunakan lebih dari satu program, nah biar openAL ga bingung object mana yang dimiliki oleh aplikasi mana, maka kita definisikan context kita, entar semua object yang kita create dalam context akan berada dalam context tersebut (dimiliki oleh aplikasi kita). fungsi untuk mbikin context :
alcCreateContext(audiodevice, attribute);
audiodevice adalah pointer pada device yang kita pilih sebelumnya dengan fungsi alcOpenDevice (jadi setiap context selalu terkait dengan sebuah device), sedangkan attribute adalah advanced option yang lu ga perlu tau sekarang deh, ato kalo mau tau baca aja openAL programmers reference, so jadi di artikel ini untuk atribute kita masukkan NULL.
suatu aplikasi dapat memiliki lebih dari satu context, jadi kita bisa membuat context tambahan, feature ini biasanya dalam game dipake untuk feature multiple listener (macam split screen untuk 2 player game) dimana dalam satu aplikasi ada 2 dunia audio yang dimainkan bersamaan. Karena adanya feature ini maka sebelum melakukan "manipulasi" terhadap object openAL kita mesti tentuin dulu mau kerja dalam context yang mana, untuk memilih context menggunakan fungsi:
alcMakeContextCurrent(audiocontext);
dimana audiocontext adalah pointer ke object context yang kita bikin sebelumnya dengan menggunakan fungsi alcCreateContext().
listener
listener adalah sebuah object yang otomatis dibuat oleh openAL untuk kita manipulasi, setiap context selalu terdapat sebuah listener (dan hanya bole satu). object listener digunakan sebagai acuan kalkulasi openAL untuk melakukan proses manipulasi suara, sebagai contoh object sumber suara apabila menjauh dari listener (pendengar) maka suaranya akan terdengar lebih kecil, ato apabila sumber suara digerakkan pada sisi kiri listener, maka seharusnya kita mendengar suara dari kiri.
ada 4 jenis fungsi untuk merubah parameter listener yaitu fungsi untuk merubah parameter posisi, orientasi, gain (kepekaan telinga hahaha..) dan kecepatan, ya kecepatan karena openAL bisa menghitung secara otomatis effect doppler yang dihasilkan suara apabila pendengar bergerak, kita tinggal masukkan aja kecepatannya berapa (asik ni, tapi pada tawu effect doppler tuh apaan kaga?).
source
apabila ada listener (pendengar) maka mesti ada soundsource (sumber suara) soalnya kalo kaga ya sama aja boong mau dengerin apa si listener, jadi source disini adalah object yang kita bikin untuk kita mainkan ( yu now lah wat ai min)
parameter yang bisa kita mainkan untuk source lebih banyak lagi, diantaranya : gain, pitch, cone angle, cone gain, rolloff factor, reference distance, position, velocity, direction, state dan ntah pa pa lah.
buffer
ketika kita udah memiliki listener dan source, masih ada satu lagi yang kurang yaitu data suara, yah iya dunk ada source tapi source-nya ga tau mau nge-play apa kan sama aja sunyi senyap.
jadi buffer ini adalah ee...ya buffer... apayah...anu.. tempat nyimpen data deh, jadi object buffer ini untuk nyimpen data suara, umumnya data suara disimpen dalam memory soundcard, kalo ga cukup dialokasikan di memory system, nanti setiap source akan merefer pada buffer, jadi ketika kita perintahkan sebuah object source untuk play() maka dia akan load data dari buffer dan memainkannya.
environment
haha.. gimana gw jelasinnya... intinya gini suara yang terjadi didalam lapangan, gunung, studio ama yang didalam kamar mandi kan pada tau lah ya pasti kedengeran berbeda, nah parameter environment ini untuk memainkan sifat itu.
tapi sayangnya dalam article ini kita belum akan membahas, suatu saat nanti deh, environment disini akan menggunakan extension EFX dan EAX dari creative, caranya sebenarnya gampang, tapi gw mesti tidur ni udah jam 1 jadi mesti cepet selesai.
Initialize openAL
nih codenya
bool initOpenAL(void)
{
ALCcontext* audiocontext;
ALCdevice* audiodevice = alcOpenDevice(NULL);
if(audiodevice)
{
audiocontext = alcCreateContext(audiodevice, NULL);
if(audiocontext)
{
alcMakeContextCurrent(audiocontext);
return true;
}
alcCloseDevice(audiodevice);
}
return false;
}
mestinya sih udah ngerti lah, kan udah gw bahas diatas, jadi intinya lu open deh itu device, kemudian lu create context pada device itu, nah kalo create berhasil lu set itu context yang akan digunakan, kalo gagal ya close device, exit dah, ya mo gimana lagi hari gini ga punya soundcard... payah.
Shutdown openAL
yah sebagai programmer yang disiplin beretika dan bertanggung jawab, kalo kita udah bikin sesuatu ya wajiblah buat kita untuk mendestroynya, ini codenya yang gw pake kalo mo exit program karena gw programmer yang bertanggung jawab:
bool shutdownOpenAL(void)
{
ALCcontext* audiocontext = alcGetCurrentContext();
ALCdevice* audiodevice = alcGetContextsDevice(audiocontext);
alcMakeContextCurrent(NULL);
alcDestroyContext(audiocontext);
alcCloseDevice(audiodevice);
return true;
}
disini flownya kita dapetin pointer pada context yang sekarang sedang digunakan [alcGetCurrentContext()] , kemudian berdasarkan context itu kita bisa dapetin device yang sedang digunakan[alcGetContextsDevice], kemudian unselect context [alcMakeContextCurrent(NULL);] (maksudnya kita ga akan pake context itu lagi alias keluar dari context) kemudian destroy context [alcDestroyContext(audiocontext)] dan close device [alcCloseDevice(audiodevice)], selesai.
Hello World!
ini codenya untuk main function (program)
void main(void)
{
if( initOpenAL() )
{
//[1]load and decode wave file
ALint datasize;
ALint frequency;
ALenum dataformat;
char* audiorawdata = loadWAVData("test.wav",datasize,frequency);
if(audiorawdata)
{
//[2]generate audio buffer
ALuint audiobuffer;
alGenBuffers( 1,&audiobuffer );
//[3]copy audiodata to buffer
alBufferData( audiobuffer,AL_FORMAT_MONO16,audiorawdata,datasize,frequency );
delete[] audiorawdata;
//[4]generate audio source
ALuint audiosource;
alGenSources( 1, &audiosource );
//[5]attach buffer to audio source
alSourcei( audiosource, AL_BUFFER, audiobuffer );
//[6]loop play the source
alSourcei(audiosource,AL_LOOPING,AL_TRUE);
alSourcePlay( audiosource );
//[7]play with source or listener
havingfun(audiosource);
//[8]preparing to shutdown
alSourceStop(audiosource);
alDeleteSources(1, &audiosource);
alDeleteBuffers(1, &audiobuffer);
}
shutdownOpenAL();
}
}
gw jelasin aja dikit, ini simpel banged ko, dan gw yakin lu pada pasti ngerti apa maksud code itu, jadi tahap2nya kaya gini dimulai dengan initializeOpenAL dan diakhiri dengan shutdownOpenAL, selalu!, karena gw programmer yang cute. kalo initializeOpenAL gagal lu keluar aja alias pundung soalnya lu ga bisa dapetin device dan context
pertama kita load file dan decode file itu menjadi rawdata untuk digunakan di buffer openAL nanti, proses load dan decodenya anggap aja dah beres dengan menggunakan fungsi loadWAVData().
kemudian kita mbikin object audio buffer (object untuk menyimpan data suara) dengan menggunakan fungsi alGenBuffers( 1,&audiobuffer );, maksudnya bikin satu biji buffer dan dituntuk oleh pointer audiobuffer. telen aja!
karena audiobuffer buat nyimpen data, maka kita perlu copy data dari file yang udah didecode sebelumnya ke buffer dengan menggunakan fungsi alBufferData( audiobuffer, AL_FORMAT_MONO16, audiorawdata, datasize, frequency );, cara membaca statement ini : copy data yang ditunjuk oleh audiorawdata pada buffer yang ditunjuk oleh audiobuffer, sebesar datasize, dan data ini punya format mono channel 16 bit (AL_FORMAT_MONO16), dengan frequency sebesar nilai variable frequency. Parameter format dan frequency dari data didapat dari output fungsi loadWAVData() sebelumnya
setelah itu kita bikin satu object source (step 4) kemudian object source ini kita hubungkan dengan audiobuffer yang udah kita bikin sebelumnya (step 5), jadi ketika di play (pada step6) source akan memainkan data yang ada didalam audiobuffer.
pada step6 statement pertama : alSourcei(audiosource,AL_LOOPING,AL_TRUE);, maksudnya kita set flag kalo buffernya akan dimainkan dengan looping, jadi ga ada stopnya, terus aja di-play.,kemudian statement kedua kita play deh, nah sekarang mestinya udah ada suara di speaker, jangan lupa periksa audio level ama mute status di windows.
step 7 akan dijelaskan kemudian disini kita mulai memainkan source dan listener.
step 8 itu birokrasi aja, jadi telen aja, kalo mau udahan maen ya stop sourcenya kemudian delete source dan buffer (inget programmer yang bertanggung jawab)
Having fun!
codenya sih ya gini2 aja ga ada yang aneh :
void havingfun(ALuint audiosource)
{
int key;
float listenerPos[3] = {0.0f, 0.0f, 0.0f};
float sourcePos[3] = {0.0f, 0.0f, 0.0f};
while(key != 'x' && key !='X')
{
printf("\rlistener(%4.2f,%4.2f,%4.2f) source(%4.2f,%4.2f,%4.2f) ",
listenerPos[0],listenerPos[1],listenerPos[2],
sourcePos[0],sourcePos[1],sourcePos[2]);
alListenerfv(AL_POSITION,listenerPos);
alSourcefv(audiosource,AL_POSITION,sourcePos);
key = getch();
switch(key)
{
case 'a' : listenerPos[0] -= 0.01; break;
case 'd' : listenerPos[0] += 0.01; break;
case 's' : listenerPos[1] -= 0.01; break;
case 'w' : listenerPos[1] += 0.01; break;
case 'q' : listenerPos[2] -= 0.01; break;
case 'e' : listenerPos[2] += 0.01; break;
case 'A' : sourcePos[0] -= 0.01; break;
case 'D' : sourcePos[0] += 0.01; break;
case 'S' : sourcePos[1] -= 0.01; break;
case 'W' : sourcePos[1] += 0.01; break;
case 'Q' : sourcePos[2] -= 0.01; break;
case 'E' : sourcePos[2] += 0.01; break;
}
}
}
awalnya kita set posisi listener dan source pada (x=0,y=0,z=0), kemudian kalo dipencet w,a,s,d (as in FPS game) listener posisinya berubah, kalo pake capslock (W,A,S,D) posisi source yang dirubah, ya coba aja de. oh iya kalo mo keluar pencet 'x', hurup x maksudnya bukan silang di window buat close.
oh iya gw lupa, itu untuk ngerubah posisi dari listener pake fungsi alListenerfv(AL_POSITION,listenerPos); kalo buat source pake fungsi alSourcefv(audiosource,AL_POSITION,sourcePos);, penjelasan lebih lengkap ada di openAL programmers reference, tapi ya gw anggap ngerti lah maksudnya fungsi itu.
dah mawu tidur gw... have fun!
downloads : [ compiled binary ] [ project files+source codes ] [ openAL documentations + manuals ]
more information : http://www.openal.org , http://developer.creative.com kalo ada masalah boleh reply di sini, ato tanya gw langsung, u know lah how to reach me...
-- all codes are copylefted. do what ever you want! --
