/* * Copyright (C) 2004 Christopher John Purnell * cjp@lost.org.uk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include "al_listener.h" #include "al_source.h" #include "alc_context.h" #include "alc_error.h" AL_context *_alcCurrentContext = 0; static ALCvoid *_alcThread(ALCcontext *cc) { AL_context *ctx = cc; ALCdevice *dev = ctx->device; ALuint i; struct timeval tv; struct timespec ts; long ns = 1000000000 / dev->refresh; pthread_mutex_lock(&ctx->mutex); do { gettimeofday(&tv, 0); for (i = 0; i < dev->subdevs; i++) { AL_source *src; if ((src = ctx->sources[i])) { _alProcessSource(src); } } ts.tv_sec = tv.tv_sec; if ((ts.tv_nsec = (tv.tv_usec * 1000) + ns) >= 1000000000) { ts.tv_sec += 1; ts.tv_nsec -= 1000000000; } } while (pthread_cond_timedwait(&ctx->cond, &ctx->mutex, &ts)); pthread_mutex_unlock(&ctx->mutex); return 0; } static ALCboolean _alcCreateContext(AL_context *ctx) { ALCdevice *dev = ctx->device; ALuint i; pthread_mutex_init(&ctx->mutex, 0); if (!(ctx->sources = malloc(dev->subdevs * sizeof(AL_source*)))) { return AL_FALSE; } for (i = 0; i < dev->subdevs; i++) { ctx->sources[i] = 0; } if (!dev->sync) { pthread_cond_init(&ctx->cond, 0); if (pthread_create(&ctx->thread, 0, _alcThread, ctx)) { return AL_FALSE; } } return ALC_TRUE; } static ALCvoid _alcDestroyContext(AL_context *ctx) { ALCdevice *dev; ALuint i; dev = ctx->device; for (i = 0; i < dev->subdevs; i++) { AL_source *src; if ((src = ctx->sources[i])) { _alDeleteSource(src); } } if (ctx->thread) { pthread_cond_signal(&ctx->cond); pthread_join(ctx->thread, 0); pthread_cond_destroy(&ctx->cond); } if (ctx->sources) { free(ctx->sources); } pthread_mutex_destroy(&ctx->mutex); free(ctx); } ALCcontext *alcCreateContext(ALCdevice *dev, ALCint *attrlist) { AL_context *ctx; if (!dev) { _alcSetError(ALC_INVALID_DEVICE); return 0; } if (!(ctx = malloc(sizeof(AL_context)))) { _alcSetError(ALC_OUT_OF_MEMORY); return 0; } ctx->device = dev; while (attrlist) { ALCint value; switch (*(attrlist++)) { case ALC_FREQUENCY: if ((value = *(attrlist++)) > 0) { dev->freq = value; } break; case ALC_REFRESH: if ((value = *(attrlist++)) > 0) { dev->refresh = value; } break; case ALC_SYNC: dev->sync = *(attrlist++) ? AL_TRUE: AL_FALSE; break; default: attrlist = 0; break; } } if (dev->refresh > dev->freq) { dev->refresh = dev->freq; } ctx->sources = 0; ctx->thread = 0; _alcLoadSpeakers(ctx->speakers); _alInitListener(&ctx->listener, ctx->speakers); ctx->doppler_factor = 1.0f; ctx->doppler_velocity = 1.0f; ctx->distance_model = AL_INVERSE_DISTANCE; ctx->distance_func = _alDistanceInverse; if (_alcCreateContext(ctx)) return ctx; _alcDestroyContext(ctx); _alcSetError(ALC_OUT_OF_MEMORY); return 0; } ALCenum alcDestroyContext(ALCcontext *cc) { AL_context *ctx; if (!(ctx = cc)) { _alcSetError(ALC_INVALID_CONTEXT); return ALC_INVALID_CONTEXT; } if (ctx == _alcCurrentContext) { _alcCurrentContext = 0; } _alcDestroyContext(ctx); return ALC_NO_ERROR; } ALCenum alcMakeContextCurrent(ALCcontext *cc) { _alcCurrentContext = cc; return ALC_NO_ERROR; } ALCcontext *alcGetCurrentContext(ALCvoid) { return _alcCurrentContext; } ALCdevice *alcGetContextsDevice(ALCcontext *cc) { AL_context *ctx; if (!(ctx = cc)) { _alcSetError(ALC_INVALID_CONTEXT); return 0; } return ctx->device; } ALCvoid alcSuspendContext(ALCcontext *cc ATTRIBUTE_UNUSED) { /* FIXME */ } ALCvoid *alcProcessContext(ALCcontext *cc) { AL_context *ctx; ALCdevice *dev; ALuint i; if (!(ctx = cc)) { _alcSetError(ALC_INVALID_CONTEXT); return 0; } if (ctx->thread) { return cc; } _alcLockContext(ctx); dev = ctx->device; for (i = 0; i < dev->subdevs; i++) { AL_source *src; if ((src = ctx->sources[i])) { _alProcessSource(src); } } _alcUnlockContext(ctx); return cc; }