643 lines
11 KiB
C
643 lines
11 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/time.h>
|
|
#include <math.h>
|
|
#include <alloca.h>
|
|
#include <string.h>
|
|
|
|
#include "al_source.h"
|
|
#include "al_error.h"
|
|
#include "al_vector.h"
|
|
#include "alc_device.h"
|
|
#include "alc_context.h"
|
|
|
|
static ALfloat _alCalculateGainAndPitch(AL_source *src)
|
|
{
|
|
AL_context *ctx = src->context;
|
|
ALCdevice *dev = ctx->device;
|
|
ALfloat position[3];
|
|
|
|
position[0] = src->position[0];
|
|
position[1] = src->position[1];
|
|
position[2] = src->position[2];
|
|
|
|
if (!src->relative)
|
|
{
|
|
position[0] -= ctx->listener.position[0];
|
|
position[1] -= ctx->listener.position[1];
|
|
position[2] -= ctx->listener.position[2];
|
|
}
|
|
|
|
/* Master Gain */
|
|
{
|
|
ALint volume;
|
|
ALfloat gain = ctx->listener.gain;
|
|
ALfloat dist = _alVectorMagnitude(position);
|
|
|
|
gain *= ctx->distance_func(src, dist);
|
|
|
|
if (dist)
|
|
{
|
|
position[0] /= dist;
|
|
position[1] /= dist;
|
|
position[2] /= dist;
|
|
}
|
|
|
|
if (src->conic)
|
|
{
|
|
ALfloat a;
|
|
|
|
a = _alVectorDotProduct(position, src->direction);
|
|
|
|
a = acos(-a) * 360.0 / M_PI;
|
|
|
|
if (a > src->cone_inner_angle)
|
|
{
|
|
if (a >= src->cone_outer_angle)
|
|
{
|
|
gain *= src->cone_outer_gain;
|
|
}
|
|
else
|
|
{
|
|
a -= src->cone_inner_angle;
|
|
a *= (src->cone_outer_gain - 1.0f);
|
|
a /= (src->cone_outer_angle -
|
|
src->cone_inner_angle);
|
|
gain *= (1.0f + a);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gain > src->max_gain)
|
|
{
|
|
gain = src->max_gain;
|
|
}
|
|
else if (gain < src->min_gain)
|
|
{
|
|
gain = src->min_gain;
|
|
}
|
|
|
|
volume = (ALint)(65535.0f * gain);
|
|
|
|
snd_ctl_elem_value_set_integer(src->vol_ctl, 1, volume);
|
|
snd_ctl_elem_value_set_integer(src->vol_ctl, 2, volume);
|
|
}
|
|
|
|
/* Speaker Gains */
|
|
{
|
|
ALuint i;
|
|
ALint volume[_ALC_NUM_SPEAKERS];
|
|
|
|
for (i = 0; i < _ALC_NUM_SPEAKERS; i++)
|
|
{
|
|
AL_speaker *speaker = &ctx->listener.speakers[i];
|
|
|
|
volume[i] = speaker->gain ?
|
|
(ALint)((_alVectorDotProduct
|
|
(position, speaker->position) + 1.0f)
|
|
* speaker->gain) : 0;
|
|
}
|
|
|
|
if (dev->send_count == 24)
|
|
{
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 8,
|
|
volume[0]);
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 10,
|
|
volume[2]);
|
|
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 12,
|
|
(volume[4] + 1) >> 1);
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 13,
|
|
(volume[5] + 1) >> 1);
|
|
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 14,
|
|
volume[6]);
|
|
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 17,
|
|
volume[1]);
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 19,
|
|
volume[3]);
|
|
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 20,
|
|
volume[4] >> 1);
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 21,
|
|
volume[5] >> 1);
|
|
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 23,
|
|
volume[7]);
|
|
}
|
|
else
|
|
{
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 4,
|
|
volume[0]);
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 5,
|
|
volume[2]);
|
|
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 6,
|
|
(volume[4] + 1) >> 1);
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 7,
|
|
(volume[5] + 1) >> 1);
|
|
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 8,
|
|
volume[1]);
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 9,
|
|
volume[3]);
|
|
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 10,
|
|
volume[4] >> 1);
|
|
snd_ctl_elem_value_set_integer(src->send_ctl, 11,
|
|
volume[5] >> 1);
|
|
}
|
|
}
|
|
|
|
/* Pitch */
|
|
{
|
|
ALfloat vl, vs;
|
|
ALfloat pitch = src->pitch;
|
|
|
|
if (ctx->doppler_factor)
|
|
{
|
|
vl = _alVectorDotProduct(ctx->listener.velocity,
|
|
position);
|
|
vs = _alVectorDotProduct(src->velocity, position);
|
|
|
|
vl *= ctx->doppler_factor;
|
|
vs *= ctx->doppler_factor;
|
|
|
|
vl += ctx->doppler_velocity;
|
|
vs += ctx->doppler_velocity;
|
|
|
|
pitch *= vl / vs;
|
|
}
|
|
|
|
if (pitch < 0.0f)
|
|
{
|
|
pitch = 0.0f;
|
|
}
|
|
|
|
return pitch;
|
|
}
|
|
}
|
|
|
|
static snd_pcm_uframes_t _alWriteData(AL_source *src, ALfloat pitch,
|
|
int32_t *dest, snd_pcm_uframes_t frames)
|
|
{
|
|
AL_queue *que;
|
|
AL_buffer *buf;
|
|
ALfloat f;
|
|
ALuint inc, acc;
|
|
ALuint i;
|
|
|
|
if (!src->playing)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if ((que = src->current_q) ||
|
|
((que = src->first_q) && (que->state == AL_PENDING)))
|
|
{
|
|
buf = que->buffer;
|
|
}
|
|
else if ((buf = src->buffer))
|
|
{
|
|
que = 0;
|
|
}
|
|
else
|
|
{
|
|
src->playing = AL_FALSE;
|
|
return 0;
|
|
}
|
|
|
|
f = pitch * (ALfloat)buf->freq / (ALfloat)src->freq;
|
|
inc = (f >= 65535.0) ? 0xFFFF0000 : (ALuint)(f * 65536.0);
|
|
acc = 0;
|
|
i = 0;
|
|
|
|
while (i < frames)
|
|
{
|
|
ALuint j = src->index;
|
|
|
|
if (j >= buf->size)
|
|
{
|
|
src->index = 0;
|
|
|
|
if (que)
|
|
{
|
|
que->state = AL_PROCESSED;
|
|
src->current_q = que->next;
|
|
}
|
|
else if(!src->looping)
|
|
{
|
|
src->playing = AL_FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
dest[i++] = buf->data[j];
|
|
acc += inc;
|
|
src->index = j + (acc >> 16);
|
|
acc &= 0xFFFF;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
ALvoid _alProcessSource(AL_source *src)
|
|
{
|
|
const snd_pcm_channel_area_t *area;
|
|
snd_pcm_sframes_t avail;
|
|
ALfloat pitch;
|
|
int state;
|
|
|
|
if (src->state != AL_PLAYING)
|
|
{
|
|
return;
|
|
}
|
|
|
|
snd_pcm_hwsync(src->handle);
|
|
|
|
state = snd_pcm_state(src->handle);
|
|
|
|
if (state != SND_PCM_STATE_RUNNING)
|
|
{
|
|
if (src->playing)
|
|
{
|
|
snd_pcm_prepare(src->handle);
|
|
}
|
|
else
|
|
{
|
|
src->state = AL_STOPPED;
|
|
return;
|
|
}
|
|
}
|
|
|
|
pitch = _alCalculateGainAndPitch(src);
|
|
|
|
avail = snd_pcm_avail_update(src->handle);
|
|
|
|
while (avail > 0)
|
|
{
|
|
snd_pcm_uframes_t offset;
|
|
snd_pcm_uframes_t frames = avail;
|
|
snd_pcm_uframes_t written = 0;
|
|
int32_t *map;
|
|
|
|
if (snd_pcm_mmap_begin(src->handle, &area, &offset, &frames))
|
|
{
|
|
return;
|
|
}
|
|
|
|
avail -= frames;
|
|
|
|
map = area->addr + ((area->first + area->step * offset) >> 3);
|
|
|
|
while (frames)
|
|
{
|
|
snd_pcm_uframes_t f;
|
|
|
|
if (!(f = _alWriteData(src, pitch, map, frames)))
|
|
{
|
|
bzero(map, frames << 2);
|
|
avail = 0;
|
|
break;
|
|
}
|
|
|
|
map += f;
|
|
written += f;
|
|
frames -= f;
|
|
}
|
|
|
|
snd_pcm_mmap_commit(src->handle, offset, written);
|
|
}
|
|
|
|
{
|
|
AL_context *ctx = src->context;
|
|
ALCdevice *dev = ctx->device;
|
|
|
|
snd_ctl_elem_write(dev->ctl, src->vol_ctl);
|
|
snd_ctl_elem_write(dev->ctl, src->send_ctl);
|
|
}
|
|
|
|
if (state != SND_PCM_STATE_RUNNING)
|
|
{
|
|
if (!snd_pcm_delay(src->handle, &avail))
|
|
{
|
|
if (avail)
|
|
{
|
|
snd_pcm_start(src->handle);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!src->playing)
|
|
{
|
|
snd_pcm_drain(src->handle);
|
|
}
|
|
}
|
|
|
|
static ALvoid _alSourcePlay(AL_source *src)
|
|
{
|
|
switch(src->state)
|
|
{
|
|
case AL_PAUSED:
|
|
snd_pcm_pause(src->handle, 0);
|
|
break;
|
|
case AL_PLAYING:
|
|
src->index = 0;
|
|
break;
|
|
}
|
|
src->state = AL_PLAYING;
|
|
src->playing = AL_TRUE;
|
|
}
|
|
|
|
static ALvoid _alSourceStop(AL_source *src)
|
|
{
|
|
switch (src->state)
|
|
{
|
|
case AL_PAUSED:
|
|
snd_pcm_pause(src->handle, 0);
|
|
break;
|
|
case AL_PLAYING:
|
|
snd_pcm_drop(src->handle);
|
|
break;
|
|
}
|
|
src->state = AL_STOPPED;
|
|
src->index = 0;
|
|
}
|
|
|
|
static ALvoid _alSourcePause(AL_source *src)
|
|
{
|
|
if (src->state == AL_PLAYING)
|
|
{
|
|
snd_pcm_pause(src->handle, 1);
|
|
src->state = AL_PAUSED;
|
|
}
|
|
}
|
|
|
|
static ALvoid _alSourceRewind(AL_source *src)
|
|
{
|
|
switch (src->state)
|
|
{
|
|
case AL_PAUSED:
|
|
snd_pcm_pause(src->handle, 0);
|
|
break;
|
|
case AL_PLAYING:
|
|
snd_pcm_drop(src->handle);
|
|
break;
|
|
}
|
|
src->state = AL_INITIAL;
|
|
src->index = 0;
|
|
}
|
|
|
|
ALvoid alSourcePlay(ALuint sid)
|
|
{
|
|
AL_context *ctx;
|
|
AL_source *src;
|
|
|
|
if (!(ctx = _alcCurrentContext))
|
|
{
|
|
_alSetError(AL_INVALID_OPERATION);
|
|
return;
|
|
}
|
|
|
|
if (!(src = _alFindSource(ctx, sid)))
|
|
{
|
|
_alSetError(AL_INVALID_NAME);
|
|
}
|
|
else
|
|
{
|
|
_alSourcePlay(src);
|
|
}
|
|
|
|
_alcUnlockContext(ctx);
|
|
}
|
|
|
|
ALvoid alSourceStop(ALuint sid)
|
|
{
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
_alSourceStop(src);
|
|
}
|
|
|
|
_alcUnlockContext(ctx);
|
|
}
|
|
|
|
ALvoid alSourcePause(ALuint sid)
|
|
{
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
_alSourcePause(src);
|
|
}
|
|
|
|
_alcUnlockContext(ctx);
|
|
}
|
|
|
|
ALvoid alSourceRewind(ALuint sid)
|
|
{
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
_alSourceRewind(src);
|
|
}
|
|
|
|
_alcUnlockContext(ctx);
|
|
}
|
|
|
|
ALvoid alSourcePlayv(ALsizei ns, ALuint *ids)
|
|
{
|
|
AL_context *ctx;
|
|
AL_source **src;
|
|
ALsizei i;
|
|
|
|
if (!(ctx = _alcCurrentContext))
|
|
{
|
|
_alSetError(AL_INVALID_OPERATION);
|
|
return;
|
|
}
|
|
|
|
_alcLockContext(ctx);
|
|
|
|
src = alloca(ns * sizeof(AL_source *));
|
|
|
|
for (i = 0; i < ns; i++)
|
|
{
|
|
if (!(src[i] = _alFindSource(ctx, ids[i])))
|
|
{
|
|
_alSetError(AL_INVALID_NAME);
|
|
goto unlock;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ns; i++)
|
|
{
|
|
_alSourcePlay(src[i]);
|
|
}
|
|
|
|
unlock:
|
|
_alcUnlockContext(ctx);
|
|
}
|
|
|
|
ALvoid alSourceStopv(ALsizei ns, ALuint *ids)
|
|
{
|
|
AL_context *ctx;
|
|
AL_source **src;
|
|
ALsizei i;
|
|
|
|
if (!(ctx = _alcCurrentContext))
|
|
{
|
|
_alSetError(AL_INVALID_OPERATION);
|
|
return;
|
|
}
|
|
|
|
_alcLockContext(ctx);
|
|
|
|
src = alloca(ns * sizeof(AL_source *));
|
|
|
|
for (i = 0; i < ns; i++)
|
|
{
|
|
if (!(src[i] = _alFindSource(ctx, ids[i])))
|
|
{
|
|
_alSetError(AL_INVALID_NAME);
|
|
goto unlock;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ns; i++)
|
|
{
|
|
_alSourceStop(src[i]);
|
|
}
|
|
|
|
unlock:
|
|
_alcUnlockContext(ctx);
|
|
}
|
|
|
|
ALvoid alSourcePausev(ALsizei ns, ALuint *ids)
|
|
{
|
|
AL_context *ctx;
|
|
AL_source **src;
|
|
ALsizei i;
|
|
|
|
if (!(ctx = _alcCurrentContext))
|
|
{
|
|
_alSetError(AL_INVALID_OPERATION);
|
|
return;
|
|
}
|
|
|
|
_alcLockContext(ctx);
|
|
|
|
src = alloca(ns * sizeof(AL_source *));
|
|
|
|
for (i = 0; i < ns; i++)
|
|
{
|
|
if (!(src[i] = _alFindSource(ctx, ids[i])))
|
|
{
|
|
_alSetError(AL_INVALID_NAME);
|
|
goto unlock;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ns; i++)
|
|
{
|
|
_alSourcePause(src[i]);
|
|
}
|
|
|
|
unlock:
|
|
_alcUnlockContext(ctx);
|
|
}
|
|
|
|
ALvoid alSourceRewindv(ALsizei ns, ALuint *ids)
|
|
{
|
|
AL_context *ctx;
|
|
AL_source **src;
|
|
ALsizei i;
|
|
|
|
if (!(ctx = _alcCurrentContext))
|
|
{
|
|
_alSetError(AL_INVALID_OPERATION);
|
|
return;
|
|
}
|
|
|
|
_alcLockContext(ctx);
|
|
|
|
src = alloca(ns * sizeof(AL_source *));
|
|
|
|
for (i = 0; i < ns; i++)
|
|
{
|
|
if (!(src[i] = _alFindSource(ctx, ids[i])))
|
|
{
|
|
_alSetError(AL_INVALID_NAME);
|
|
goto unlock;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ns; i++)
|
|
{
|
|
_alSourceRewind(src[i]);
|
|
}
|
|
|
|
unlock:
|
|
_alcUnlockContext(ctx);
|
|
}
|