openal-alsa-creative/src/al_buffer.c

463 lines
7.4 KiB
C

/*
* 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 <sys/types.h>
#include <stdlib.h>
#include <alloca.h>
#include <pthread.h>
#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);
}