openal-alsa-creative/src/al_source.c

940 lines
16 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 <float.h>
#include <alloca.h>
#include "al_source.h"
#include "al_vector.h"
#include "al_error.h"
#include "alc_device.h"
#include "alc_context.h"
#define AL_FIRST_SOURCE_ID 0x4000
AL_source *_alFindSource(AL_context *ctx, ALuint cid)
{
cid -= AL_FIRST_SOURCE_ID;
if (cid >= ctx->device->subdevs)
{
return 0;
}
return ctx->sources[cid];
}
static AL_source *_alGenSource(AL_context *ctx)
{
AL_source *src;
if (!(src = malloc(sizeof(AL_source))))
{
_alSetError(AL_OUT_OF_MEMORY);
return 0;
}
src->context = ctx;
src->handle = 0;
src->vol_ctl = 0;
src->send_ctl = 0;
src->subdev = -1;
src->freq = 0;
src->state = AL_INITIAL;
src->playing = AL_FALSE;
src->buffer = 0;
src->index = 0;
src->first_q = 0;
src->current_q = 0;
src->last_q = &src->first_q;
if (!_alcOpenSource(src))
{
_alcCloseSource(src);
free(src);
_alSetError(AL_OUT_OF_MEMORY);
return 0;
}
src->relative = AL_FALSE;
src->looping = AL_FALSE;
src->conic = AL_FALSE;
src->position[0] = 0.0f;
src->position[1] = 0.0f;
src->position[2] = 0.0f;
src->direction[0] = 0.0f;
src->direction[1] = 0.0f;
src->direction[2] = 0.0f;
src->velocity[0] = 0.0f;
src->velocity[1] = 0.0f;
src->velocity[2] = 0.0f;
src->pitch = 1.0f;
src->gain = 1.0f;
src->min_gain = 0.0f;
src->max_gain = 1.0f;
src->reference_distance = 1.0f;
src->rolloff_factor = 1.0f;
src->max_distance = FLT_MAX;
src->cone_inner_angle = 360.0f;
src->cone_outer_angle = 360.0f;
src->cone_outer_gain = 0.0f;
ctx->sources[src->subdev] = src;
return src;
}
ALvoid _alDeleteSource(AL_source *src)
{
AL_queue *que;
AL_context *ctx = src->context;
ctx->sources[src->subdev] = 0;
_alcCloseSource(src);
que = src->first_q;
while (que)
{
AL_queue *next = que->next;
_alUnlockBuffer(que->buffer);
free(que);
que = next;
}
if (src->buffer)
{
_alUnlockBuffer(src->buffer);
}
free(src);
}
ALvoid alGenSources(ALsizei n, ALuint *sources)
{
AL_context *ctx;
AL_source **temp;
ALsizei i;
if (n == 0)
{
return;
}
if (n < 0)
{
_alSetError(AL_INVALID_VALUE);
return;
}
if (!(ctx = _alcCurrentContext))
{
_alSetError(AL_INVALID_OPERATION);
return;
}
_alcLockContext(ctx);
temp = alloca(n * sizeof(AL_source *));
for (i = 0; i < n; i++)
{
if (!(temp[i] = _alGenSource(ctx)))
{
ALsizei j;
for (j = 0; j < i; j++)
_alDeleteSource(temp[j]);
goto unlock;
}
}
for (i = 0; i < n; i++)
{
sources[i] = temp[i]->subdev + AL_FIRST_SOURCE_ID;
}
unlock:
_alcUnlockContext(ctx);
}
ALvoid alDeleteSources(ALsizei n, ALuint* sources)
{
AL_context *ctx;
AL_source **temp;
ALsizei i;
if (n == 0)
{
return;
}
if (n < 0)
{
_alSetError(AL_INVALID_VALUE);
return;
}
if (!(ctx = _alcCurrentContext))
{
_alSetError(AL_INVALID_OPERATION);
return;
}
_alcLockContext(ctx);
temp = alloca(n * sizeof(AL_source *));
for (i = 0; i < n; i++)
{
if (!(temp[i] = _alFindSource(ctx, sources[i])))
{
_alSetError(AL_INVALID_NAME);
goto unlock;
}
}
for (i = 0; i < n; i++)
{
_alDeleteSource(temp[i]);
}
unlock:
_alcUnlockContext(ctx);
}
ALboolean alIsSource(ALuint sid)
{
AL_context *ctx;
ALboolean value;
if (!(ctx = _alcCurrentContext))
{
_alSetError(AL_INVALID_OPERATION);
return AL_FALSE;
}
_alcLockContext(ctx);
value = _alFindSource(ctx, sid) ? AL_TRUE : AL_FALSE;
_alcUnlockContext(ctx);
return value;
}
static ALvoid _alNormalizeDirection(AL_source *src)
{
ALfloat mag = _alVectorMagnitude(src->direction);
if (mag)
{
src->conic = AL_TRUE;
src->direction[0] /= mag;
src->direction[1] /= mag;
src->direction[2] /= mag;
}
else
{
src->conic = AL_FALSE;
}
}
ALvoid alSourcei(ALuint sid, ALenum param, ALint value)
{
AL_context *ctx;
AL_source *src;
if (!(ctx = _alcCurrentContext))
{
_alSetError(AL_INVALID_OPERATION);
return;
}
_alcLockContext(ctx);
if (!(src = _alFindSource(ctx, sid)))
{
_alSetError(AL_INVALID_NAME);
goto unlock;
}
switch(param)
{
case AL_SOURCE_RELATIVE:
_alRangedAssignB(src->relative, value);
break;
case AL_LOOPING:
_alRangedAssignB(src->looping, value);
break;
case AL_PITCH:
_alRangedAssign1(src->pitch, value, 0);
break;
case AL_GAIN:
_alRangedAssign1(src->gain, value, 0);
break;
case AL_MIN_GAIN:
_alRangedAssign2(src->min_gain, value, 0, 1);
break;
case AL_MAX_GAIN:
_alRangedAssign2(src->max_gain, value, 0, 1);
break;
case AL_REFERENCE_DISTANCE:
_alRangedAssign1(src->reference_distance, value, 0);
break;
case AL_ROLLOFF_FACTOR:
_alRangedAssign1(src->rolloff_factor, value, 0);
break;
case AL_MAX_DISTANCE:
_alRangedAssign1(src->max_distance, value, 0);
break;
case AL_CONE_INNER_ANGLE:
_alRangedAssign2(src->cone_inner_angle, value, 0, 360);
break;
case AL_CONE_OUTER_ANGLE:
_alRangedAssign2(src->cone_outer_angle, value, 0, 360);
break;
case AL_CONE_OUTER_GAIN:
_alRangedAssign2(src->cone_outer_gain, value, 0, 1);
break;
case AL_BUFFER:
{
AL_buffer *buf;
if (value)
{
if (!(buf = _alLockBuffer(value)))
{
_alSetError(AL_INVALID_VALUE);
break;
}
}
else
{
buf = 0;
}
if (src->buffer)
{
_alUnlockBuffer(src->buffer);
}
src->buffer = buf;
}
break;
default:
_alSetError(AL_INVALID_ENUM);
break;
}
unlock:
_alcUnlockContext(ctx);
}
ALvoid alSourcef(ALuint sid, ALenum param, ALfloat value)
{
AL_context *ctx;
AL_source *src;
if (!(ctx = _alcCurrentContext))
{
_alSetError(AL_INVALID_OPERATION);
return;
}
_alcLockContext(ctx);
if (!(src = _alFindSource(ctx, sid)))
{
_alSetError(AL_INVALID_NAME);
goto unlock;
}
switch(param)
{
case AL_SOURCE_RELATIVE:
_alRangedAssignB(src->relative, value);
break;
case AL_LOOPING:
_alRangedAssignB(src->looping, value);
break;
case AL_PITCH:
_alRangedAssign1(src->pitch, value, 0.0f);
break;
case AL_GAIN:
_alRangedAssign1(src->gain, value, 0.0f);
break;
case AL_MIN_GAIN:
_alRangedAssign2(src->min_gain, value, 0.0f, 1.0f);
break;
case AL_MAX_GAIN:
_alRangedAssign2(src->max_gain, value, 0.0f, 1.0f);
break;
case AL_REFERENCE_DISTANCE:
_alRangedAssign1(src->reference_distance, value, 0.0f);
break;
case AL_ROLLOFF_FACTOR:
_alRangedAssign1(src->rolloff_factor, value, 0.0f);
break;
case AL_MAX_DISTANCE:
_alRangedAssign1(src->max_distance, value, 0.0f);
break;
case AL_CONE_INNER_ANGLE:
_alRangedAssign2(src->cone_inner_angle, value, 0.0f, 360.0f);
break;
case AL_CONE_OUTER_ANGLE:
_alRangedAssign2(src->cone_outer_angle, value, 0.0f, 360.0f);
break;
case AL_CONE_OUTER_GAIN:
_alRangedAssign2(src->cone_outer_gain, value, 0.0f, 1.0f);
break;
default:
_alSetError(AL_INVALID_ENUM);
break;
}
unlock:
_alcUnlockContext(ctx);
}
ALvoid alSource3f(ALuint sid, ALenum param, ALfloat f1, ALfloat f2, ALfloat f3)
{
AL_context *ctx;
AL_source *src;
if (!(ctx = _alcCurrentContext))
{
_alSetError(AL_INVALID_OPERATION);
return;
}
_alcLockContext(ctx);
if (!(src = _alFindSource(ctx, sid)))
{
_alSetError(AL_INVALID_NAME);
goto unlock;
}
switch(param)
{
case AL_POSITION:
src->position[0] = f1;
src->position[1] = f2;
src->position[2] = f3;
break;
case AL_DIRECTION:
src->direction[0] = f1;
src->direction[1] = f2;
src->direction[2] = f3;
_alNormalizeDirection(src);
break;
case AL_VELOCITY:
src->velocity[0] = f1;
src->velocity[1] = f2;
src->velocity[2] = f3;
break;
default:
_alSetError(AL_INVALID_ENUM);
break;
}
unlock:
_alcUnlockContext(ctx);
}
ALvoid alSourcefv(ALuint sid, ALenum param, ALfloat *values)
{
AL_context *ctx;
AL_source *src;
if (!(ctx = _alcCurrentContext))
{
_alSetError(AL_INVALID_OPERATION);
return;
}
_alcLockContext(ctx);
if (!(src = _alFindSource(ctx, sid)))
{
_alSetError(AL_INVALID_NAME);
goto unlock;
}
switch(param)
{
case AL_SOURCE_RELATIVE:
_alRangedAssignB(src->relative, values[0]);
break;
case AL_LOOPING:
_alRangedAssignB(src->looping, values[0]);
break;
case AL_PITCH:
_alRangedAssign1(src->pitch, values[0], 0.0f);
break;
case AL_GAIN:
_alRangedAssign1(src->gain, values[0], 0.0f);
break;
case AL_MIN_GAIN:
_alRangedAssign2(src->min_gain, values[0], 0.0f, 1.0f);
break;
case AL_MAX_GAIN:
_alRangedAssign2(src->max_gain, values[0], 0.0f, 1.0f);
break;
case AL_REFERENCE_DISTANCE:
_alRangedAssign1(src->reference_distance, values[0], 0.0f);
break;
case AL_ROLLOFF_FACTOR:
_alRangedAssign1(src->rolloff_factor, values[0], 0.0f);
break;
case AL_MAX_DISTANCE:
_alRangedAssign1(src->max_distance, values[0], 0.0f);
break;
case AL_CONE_INNER_ANGLE:
_alRangedAssign2(src->cone_inner_angle, values[0],
0.0f, 360.0f);
break;
case AL_CONE_OUTER_ANGLE:
_alRangedAssign2(src->cone_outer_angle, values[0],
0.0f, 360.0f);
break;
case AL_CONE_OUTER_GAIN:
_alRangedAssign2(src->cone_outer_gain, values[0], 0.0f, 1.0f);
break;
case AL_POSITION:
src->position[0] = values[0];
src->position[1] = values[1];
src->position[2] = values[2];
break;
case AL_DIRECTION:
src->direction[0] = values[0];
src->direction[1] = values[1];
src->direction[2] = values[2];
_alNormalizeDirection(src);
break;
case AL_VELOCITY:
src->velocity[0] = values[0];
src->velocity[1] = values[1];
src->velocity[2] = values[2];
break;
default:
_alSetError(AL_INVALID_ENUM);
break;
}
unlock:
_alcUnlockContext(ctx);
}
static ALint _alBuffersQueued(AL_source *src)
{
AL_queue *q;
ALint count = 0;
for (q = src->first_q; q; q = q->next)
{
count++;
}
return count;
}
static ALint _alBuffersProcessed(AL_source *src)
{
AL_queue *q;
ALint count = 0;
for (q = src->first_q; q; q = q->next)
{
if (q->state != AL_PROCESSED)
{
break;
}
count++;
}
return count;
}
ALvoid alGetSourcei(ALuint sid, ALenum param, ALint *value)
{
ALint values[3];
values[0] = *value;
alGetSourceiv(sid, param, values);
*value = values[0];
}
ALvoid alGetSourceiv(ALuint sid, ALenum param, ALint *values)
{
AL_context *ctx;
AL_source *src;
if (!(ctx = _alcCurrentContext))
{
_alSetError(AL_INVALID_OPERATION);
return;
}
_alcLockContext(ctx);
if (!(src = _alFindSource(ctx, sid)))
{
_alSetError(AL_INVALID_NAME);
goto unlock;
}
switch(param)
{
case AL_SOURCE_RELATIVE:
values[0] = src->relative;
break;
case AL_SOURCE_STATE:
values[0] = src->state;
break;
case AL_LOOPING:
values[0] = src->looping;
break;
case AL_PITCH:
values[0] = (ALint)src->pitch;
break;
case AL_GAIN:
values[0] = (ALint)src->gain;
break;
case AL_MIN_GAIN:
values[0] = (ALint)src->min_gain;
break;
case AL_MAX_GAIN:
values[0] = (ALint)src->max_gain;
break;
case AL_REFERENCE_DISTANCE:
values[0] = (ALint)src->reference_distance;
break;
case AL_ROLLOFF_FACTOR:
values[0] = (ALint)src->rolloff_factor;
break;
case AL_MAX_DISTANCE:
values[0] = (ALint)src->max_distance;
break;
case AL_CONE_INNER_ANGLE:
values[0] = (ALint)src->cone_inner_angle;
break;
case AL_CONE_OUTER_ANGLE:
values[0] = (ALint)src->cone_outer_angle;
break;
case AL_CONE_OUTER_GAIN:
values[0] = (ALint)src->cone_outer_gain;
break;
case AL_POSITION:
values[0] = (ALint)src->position[0];
values[1] = (ALint)src->position[1];
values[2] = (ALint)src->position[2];
break;
case AL_DIRECTION:
values[0] = (ALint)src->direction[0];
values[1] = (ALint)src->direction[1];
values[2] = (ALint)src->direction[2];
break;
case AL_VELOCITY:
values[0] = (ALint)src->velocity[0];
values[1] = (ALint)src->velocity[1];
values[2] = (ALint)src->velocity[2];
break;
case AL_BUFFER:
values[0] = src->buffer ? src->buffer->id : 0;
break;
case AL_BUFFERS_QUEUED:
values[0] = _alBuffersQueued(src);
break;
case AL_BUFFERS_PROCESSED:
values[0] = _alBuffersProcessed(src);
break;
default:
_alSetError(AL_INVALID_ENUM);
break;
}
unlock:
_alcUnlockContext(ctx);
}
ALvoid alGetSourcef(ALuint sid, ALenum param, ALfloat *value)
{
ALfloat values[3];
values[0] = *value;
alGetSourcefv(sid, param, values);
*value = values[0];
}
ALvoid alGetSource3f(ALuint sid, ALenum param,
ALfloat *f1, ALfloat *f2, ALfloat *f3)
{
ALfloat values[6];
values[0] = *f1;
values[1] = *f2;
values[2] = *f3;
alGetSourcefv(sid, param, values);
*f1 = values[0];
*f2 = values[1];
*f3 = values[2];
}
ALvoid alGetSourcefv(ALuint sid, ALenum param, ALfloat* values)
{
AL_context *ctx;
AL_source *src;
if (!(ctx = _alcCurrentContext))
{
_alSetError(AL_INVALID_OPERATION);
return;
}
_alcLockContext(ctx);
if (!(src = _alFindSource(ctx, sid)))
{
_alSetError(AL_INVALID_NAME);
goto unlock;
}
switch(param)
{
case AL_SOURCE_RELATIVE:
values[0] = src->relative;
break;
case AL_SOURCE_STATE:
values[0] = (ALfloat)src->state;
break;
case AL_LOOPING:
values[0] = (ALfloat)src->looping;
break;
case AL_PITCH:
values[0] = src->pitch;
break;
case AL_GAIN:
values[0] = src->gain;
break;
case AL_MIN_GAIN:
values[0] = src->min_gain;
break;
case AL_MAX_GAIN:
values[0] = src->max_gain;
break;
case AL_REFERENCE_DISTANCE:
values[0] = src->reference_distance;
break;
case AL_ROLLOFF_FACTOR:
values[0] = src->rolloff_factor;
break;
case AL_MAX_DISTANCE:
values[0] = src->max_distance;
break;
case AL_CONE_INNER_ANGLE:
values[0] = src->cone_inner_angle;
break;
case AL_CONE_OUTER_ANGLE:
values[0] = src->cone_outer_angle;
break;
case AL_CONE_OUTER_GAIN:
values[0] = src->cone_outer_gain;
break;
case AL_POSITION:
values[0] = src->position[0];
values[1] = src->position[1];
values[2] = src->position[2];
break;
case AL_DIRECTION:
values[0] = src->direction[0];
values[1] = src->direction[1];
values[2] = src->direction[2];
break;
case AL_VELOCITY:
values[0] = src->velocity[0];
values[1] = src->velocity[1];
values[2] = src->velocity[2];
break;
case AL_BUFFERS_QUEUED:
values[0] = (ALfloat)_alBuffersQueued(src);
break;
case AL_BUFFERS_PROCESSED:
values[0] = (ALfloat)_alBuffersProcessed(src);
break;
default:
_alSetError(AL_INVALID_ENUM);
break;
}
unlock:
_alcUnlockContext(ctx);
}
ALvoid alSourceQueueBuffers(ALuint sid, ALsizei n, ALuint *buffers)
{
AL_context *ctx;
AL_source *src;
ALsizei i;
AL_queue *first_q;
AL_queue **last_q;
if (n == 0)
{
return;
}
if (n < 0)
{
_alSetError(AL_INVALID_VALUE);
return;
}
if (!(ctx = _alcCurrentContext))
{
_alSetError(AL_INVALID_OPERATION);
return;
}
_alcLockContext(ctx);
if (!(src = _alFindSource(ctx, sid)))
{
_alSetError(AL_INVALID_NAME);
goto unlock;
}
first_q = 0;
last_q = &first_q;
for (i = 0; i < n; i++)
{
AL_buffer *buf;
AL_queue *que;
if (!buffers[i])
continue;
if (!(buf = _alLockBuffer(buffers[i])))
{
while ((que = first_q))
{
first_q = que->next;
_alUnlockBuffer(que->buffer);
free(que);
}
_alSetError(AL_INVALID_NAME);
goto unlock;
}
if (!(que = malloc(sizeof(AL_queue))))
{
while ((que = first_q))
{
first_q = que->next;
_alUnlockBuffer(que->buffer);
free(que);
}
_alSetError(AL_OUT_OF_MEMORY);
goto unlock;
}
que->next = 0;
que->buffer = buf;
que->state = AL_PENDING;
*last_q = que;
last_q = &que->next;
}
if (first_q)
{
*src->last_q = first_q;
src->last_q = last_q;
}
unlock:
_alcUnlockContext(ctx);
}
ALvoid alSourceUnqueueBuffers(ALuint sid, ALsizei n, ALuint *buffers)
{
AL_context *ctx;
AL_source *src;
ALsizei i;
if (n == 0)
{
return;
}
if (n < 0)
{
_alSetError(AL_INVALID_VALUE);
return;
}
if (!(ctx = _alcCurrentContext))
{
_alSetError(AL_INVALID_OPERATION);
return;
}
_alcLockContext(ctx);
if (!(src = _alFindSource(ctx, sid)))
{
_alSetError(AL_INVALID_NAME);
goto unlock;
}
if (_alBuffersProcessed(src) < n)
{
_alSetError(AL_INVALID_OPERATION);
goto unlock;
}
for (i = 0; i < n; i++)
{
AL_queue *que = src->first_q;
src->first_q = que->next;
buffers[i] = que->buffer->id;
_alUnlockBuffer(que->buffer);
free(que);
}
unlock:
_alcUnlockContext(ctx);
}