/* * 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 #include "al_buffer.h" #include "al_error.h" #define HASH_SIZE 0x100 #define HASH_MASK 0x0FF static pthread_mutex_t _al_buffer_mutex = PTHREAD_MUTEX_INITIALIZER; static ALuint _al_last_buffer_id = 0; static AL_buffer *_al_buffers[HASH_SIZE]; static AL_buffer *_alFindBuffer(ALuint bid) { AL_buffer *buf; for (buf = _al_buffers[bid & HASH_MASK]; buf; buf = buf->next) { if (buf->id == bid) { return buf; } } return 0; } AL_buffer *_alLockBuffer(ALuint bid) { AL_buffer *buf; pthread_mutex_lock(&_al_buffer_mutex); if ((buf = _alFindBuffer(bid))) { buf->used++; } pthread_mutex_unlock(&_al_buffer_mutex); return buf; } ALvoid _alUnlockBuffer(AL_buffer *buf) { pthread_mutex_lock(&_al_buffer_mutex); buf->used--; pthread_mutex_unlock(&_al_buffer_mutex); } static AL_buffer *_alGenBuffer(ALvoid) { AL_buffer *buf; ALuint bid; if (!(buf = malloc(sizeof(AL_buffer)))) { _alSetError(AL_OUT_OF_MEMORY); return 0; } buf->used = 0; buf->data = 0; buf->size = 0; buf->freq = 0; bid = _al_last_buffer_id; while (!++bid || _alFindBuffer(bid)); _al_last_buffer_id = bid; buf->id = bid; buf->next = _al_buffers[bid & HASH_MASK]; _al_buffers[bid & HASH_MASK] = buf; return buf; } static ALvoid _alDeleteBuffer(AL_buffer *buf) { AL_buffer *b, **p; for (p = &_al_buffers[buf->id & HASH_MASK]; (b = *p); p = &b->next) { if (b->id == buf->id) { *p = b->next; break; } } if (buf->data) free(buf->data); free(buf); } ALvoid alGenBuffers(ALsizei n, ALuint *buffers) { ALsizei i; AL_buffer **temp; if (n == 0) return; if (n < 0) { _alSetError(AL_INVALID_VALUE); return; } temp = alloca(n * sizeof(AL_buffer *)); pthread_mutex_lock(&_al_buffer_mutex); for (i = 0; i < n; i++) { if (!(temp[i] = _alGenBuffer())) { ALsizei j; for (j = 0; j < i; j++) { _alDeleteBuffer(temp[j]); } goto unlock; } } for (i = 0; i < n; i++) { buffers[i] = temp[i]->id; } unlock: pthread_mutex_unlock(&_al_buffer_mutex); } ALvoid alDeleteBuffers(ALsizei n, ALuint *buffers) { ALsizei i; AL_buffer **temp; if (n == 0) { return; } if (n < 0) { _alSetError(AL_INVALID_VALUE); return; } temp = alloca(n * sizeof(AL_buffer *)); pthread_mutex_lock(&_al_buffer_mutex); for (i = 0; i < n; i++) { if (!(temp[i] = _alFindBuffer(buffers[i]))) { _alSetError(AL_INVALID_NAME); goto unlock; } if (temp[i]->used) { _alSetError(AL_INVALID_OPERATION); goto unlock; } } for (i = 0; i < n; i++) { _alDeleteBuffer(temp[i]); } unlock: pthread_mutex_unlock(&_al_buffer_mutex); } ALboolean alIsBuffer(ALuint bid) { ALboolean value; pthread_mutex_lock(&_al_buffer_mutex); value = _alFindBuffer(bid) ? AL_TRUE : AL_FALSE; pthread_mutex_unlock(&_al_buffer_mutex); return value; } static ALvoid _alBufferMono8(AL_buffer *buf, ALvoid *data, ALsizei size, ALsizei freq) { ALvoid *copy; ALsizei frames = size; if (!(copy = malloc(frames << 2))) { _alSetError(AL_OUT_OF_MEMORY); return; } { __uint8_t *from = data; __int16_t *to = copy; ALsizei i; for (i = 0; i < frames; i++) { __int16_t value = (*(from++) - 128) << 8; *(to++) = value; *(to++) = value; } } buf->data = copy; buf->size = frames; buf->freq = freq; } static ALvoid _alBufferMono16(AL_buffer *buf, ALvoid *data, ALsizei size, ALsizei freq) { ALvoid *copy; ALsizei frames = size >> 1; if (!(copy = malloc(frames << 2))) { _alSetError(AL_OUT_OF_MEMORY); return; } { __int16_t *from = data; __int16_t *to = copy; ALsizei i; for (i = 0; i < frames; i++) { __int16_t value = *(from++); *(to++) = value; *(to++) = value; } } buf->data = copy; buf->size = frames; buf->freq = freq; } static ALvoid _alBufferStereo8(AL_buffer *buf, ALvoid *data, ALsizei size, ALsizei freq) { ALvoid *copy; ALsizei frames = size >> 1; if (!(copy = malloc(frames << 2))) { _alSetError(AL_OUT_OF_MEMORY); return; } { __uint8_t *from = data; __int16_t *to = copy; ALsizei i; for (i = 0; i < frames; i++) { *(to++) = (*(from++) - 128) << 8; *(to++) = (*(from++) - 128) << 8; } } buf->data = copy; buf->size = frames; buf->freq = freq; } static ALvoid _alBufferStereo16(AL_buffer *buf, ALvoid *data, ALsizei size, ALsizei freq) { ALvoid *copy; ALsizei frames = size >> 2; if (!(copy = malloc(frames << 2))) { _alSetError(AL_OUT_OF_MEMORY); return; } { __int32_t *from = data; __int32_t *to = copy; ALsizei i; for (i = 0; i < frames; i++) { *(to++) = *(from++); } } buf->data = copy; buf->size = frames; buf->freq = freq; } ALvoid alBufferData(ALuint bid, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) { AL_buffer *buf; if (!(buf = _alLockBuffer(bid))) { _alSetError(AL_INVALID_NAME); return; } if (buf->used > 1) { _alSetError(AL_INVALID_OPERATION); goto unlock; } if (buf->data) { free(buf->data); buf->data = 0; buf->size = 0; } switch (format) { case AL_FORMAT_MONO8: _alBufferMono8(buf, data, size, freq); break; case AL_FORMAT_MONO16: _alBufferMono16(buf, data, size, freq); break; case AL_FORMAT_STEREO8: _alBufferStereo8(buf, data, size, freq); break; case AL_FORMAT_STEREO16: _alBufferStereo16(buf, data, size, freq); break; default: _alSetError(AL_INVALID_ENUM); break; } unlock: _alUnlockBuffer(buf); } ALvoid alGetBufferi(ALuint bid, ALenum param, ALint *value) { AL_buffer *buf; pthread_mutex_lock(&_al_buffer_mutex); if (!(buf = _alFindBuffer(bid))) { _alSetError(AL_INVALID_NAME); goto unlock; } switch (param) { case AL_FREQUENCY: *value = (ALint)buf->freq; break; case AL_BITS: *value = 16; break; case AL_CHANNELS: *value = 2; break; case AL_SIZE: *value = (ALint)(buf->size << 2); break; default: _alSetError(AL_INVALID_ENUM); break; } unlock: pthread_mutex_unlock(&_al_buffer_mutex); } ALvoid alGetBufferf(ALuint bid, ALenum param, ALfloat *value) { AL_buffer *buf; pthread_mutex_lock(&_al_buffer_mutex); if (!(buf = _alLockBuffer(bid))) { _alSetError(AL_INVALID_NAME); goto unlock; } switch (param) { case AL_FREQUENCY: *value = (ALfloat)buf->freq; break; case AL_BITS: *value = 16.0f; break; case AL_CHANNELS: *value = 2.0f; break; case AL_SIZE: *value = (ALfloat)(buf->size << 2); break; default: _alSetError(AL_INVALID_ENUM); break; } unlock: pthread_mutex_lock(&_al_buffer_mutex); } ALvoid alGetBufferiv(ALuint bid, ALenum param, ALint *values) { alGetBufferi(bid, param, values); } ALvoid alGetBufferfv(ALuint bid, ALenum param, ALfloat *values) { alGetBufferf(bid, param, values); }