Mercurial > hg > openttd
changeset 12179:ce025491d1e5 draft
Implement OpenAL sound driver.
author | Michael Lutz <michi@icosahedron.de> |
---|---|
date | Thu, 12 Jun 2008 23:58:52 +0200 |
parents | 08d76f3dda8b |
children | 71a198100dd0 |
files | src/sound/openal_s.cpp |
diffstat | 1 files changed, 98 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/src/sound/openal_s.cpp +++ b/src/sound/openal_s.cpp @@ -6,17 +6,115 @@ #include "../driver.h" #include "../mixer.h" +#include "../thread.h" +#include "../gfx_func.h" #include "openal_s.h" +#include <AL/al.h> +#include <AL/alc.h> + +#define NUM_BUFFERS 3 static FSoundDriver_OpenAL iFSoundDriver_OpenAL; +static ALCdevice *_al_device; +static ALCcontext *_al_context; + +static ALuint _buffers[NUM_BUFFERS]; +static ALuint _source; + +static int _buf_size; + +static ThreadObject *_al_thread = NULL; +static volatile bool _al_running; ///< Simple semaphore signalling the thread when to stop + +void * CDECL OAL_ThreadFunc(void *) +{ + void *data = alloca(_buf_size); + + while (_al_running) { + ALint buffers_processed = 0; + alGetSourcei(_source, AL_BUFFERS_PROCESSED, &buffers_processed); + for (; buffers_processed > 0; --buffers_processed) { + ALuint buffer = 0; + alSourceUnqueueBuffers(_source, 1, &buffer); + /* Note: MxMixSamples is not completely thread-safe, but it's good enough + * to be usable. As mixer channels are only stopped by MxMixSamples itself, + * we'll never have the problem that the sound data is going to vanish during + * use. The worst that could happen is that a newly allocated channel is + * missed, but as it will get picked up on the next buffer fill, this is not + * critical in any way. */ + MxMixSamples(data, _buf_size / 4); + alBufferData(buffer, AL_FORMAT_STEREO16, data, _buf_size, 11025); + alSourceQueueBuffers(_source, 1, &buffer); + } + + ALint state; + alGetSourcei(_source, AL_SOURCE_STATE, &state); + if (state != AL_PLAYING) { + /* The source ran out of buffers, restart playing. */ + alSourcePlay(_source); + } + + /* Try to sleep as long as it takes to play a buffer. */ + CSleep(_buf_size / 4 * 1000 / 11025); + } + return NULL; +} + const char *SoundDriver_OpenAL::Start(const char * const *parm) { + /* Open device */ + _al_device = alcOpenDevice(NULL); + if (!_al_device) return "alcOpenDevice failed"; + + /* Create context */ + ALCint attribs[] = {ALC_STEREO_SOURCES, 0, ALC_FREQUENCY, 11025}; + attribs[3] = GetDriverParamInt(parm, "hz", 11025); + + _al_context = alcCreateContext(_al_device, attribs); + if (!_al_context) return "alcCreateContext failed"; + alcMakeContextCurrent(_al_context); + + /* Prepare buffers */ + alGenBuffers(NUM_BUFFERS, _buffers); + alGenSources(1, &_source); + if (alGetError() != AL_NO_ERROR) return "Can't create buffers"; + + /* Buffer size has to be a multiple of 4 */ + _buf_size = GetDriverParamInt(parm, "bufsize", 1024); + _buf_size &= ~3U; + + /* Fill all buffers initially */ + void *data = alloca(_buf_size); + for (int i = 0; i < NUM_BUFFERS; ++i) { + MxMixSamples(data, _buf_size / 4); + alBufferData(_buffers[i], AL_FORMAT_STEREO16, data, _buf_size, 11025); + alSourceQueueBuffers(_source, 1, &_buffers[i]); + } + alSourcePlay(_source); + + _al_running = true; + _al_thread = ThreadObject::New(&OAL_ThreadFunc, NULL); + _al_thread->SetThreadPriority(ThreadObject::THREAD_PRIO_HIGH); + return NULL; } void SoundDriver_OpenAL::Stop() { + _al_running = false; + if (_al_thread != NULL) { + _al_thread->Join(); + delete _al_thread; + _al_thread = NULL; + } + + alDeleteSources(1, &_source); + alDeleteBuffers(NUM_BUFFERS, _buffers); + + alcMakeContextCurrent(NULL); + alcDestroyContext(_al_context); + alcCloseDevice(_al_device); } #endif /* WITH_OPENAL */