diff Alc/alcEcho.c @ 0:f9476ff7637e

initial forking of open-al to create multiple listeners
author Robert McIntyre <rlm@mit.edu>
date Tue, 25 Oct 2011 13:02:31 -0700
parents
children
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/Alc/alcEcho.c	Tue Oct 25 13:02:31 2011 -0700
     1.3 @@ -0,0 +1,192 @@
     1.4 +/**
     1.5 + * OpenAL cross platform audio library
     1.6 + * Copyright (C) 2009 by Chris Robinson.
     1.7 + * This library is free software; you can redistribute it and/or
     1.8 + *  modify it under the terms of the GNU Library General Public
     1.9 + *  License as published by the Free Software Foundation; either
    1.10 + *  version 2 of the License, or (at your option) any later version.
    1.11 + *
    1.12 + * This library is distributed in the hope that it will be useful,
    1.13 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.14 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.15 + *  Library General Public License for more details.
    1.16 + *
    1.17 + * You should have received a copy of the GNU Library General Public
    1.18 + *  License along with this library; if not, write to the
    1.19 + *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    1.20 + *  Boston, MA  02111-1307, USA.
    1.21 + * Or go to http://www.gnu.org/copyleft/lgpl.html
    1.22 + */
    1.23 +
    1.24 +#include "config.h"
    1.25 +
    1.26 +#include <math.h>
    1.27 +#include <stdlib.h>
    1.28 +
    1.29 +#include "alMain.h"
    1.30 +#include "alFilter.h"
    1.31 +#include "alAuxEffectSlot.h"
    1.32 +#include "alError.h"
    1.33 +#include "alu.h"
    1.34 +
    1.35 +
    1.36 +typedef struct ALechoState {
    1.37 +    // Must be first in all effects!
    1.38 +    ALeffectState state;
    1.39 +
    1.40 +    ALfloat *SampleBuffer;
    1.41 +    ALuint BufferLength;
    1.42 +
    1.43 +    // The echo is two tap. The delay is the number of samples from before the
    1.44 +    // current offset
    1.45 +    struct {
    1.46 +        ALuint delay;
    1.47 +    } Tap[2];
    1.48 +    ALuint Offset;
    1.49 +    // The LR gains for the first tap. The second tap uses the reverse
    1.50 +    ALfloat GainL;
    1.51 +    ALfloat GainR;
    1.52 +
    1.53 +    ALfloat FeedGain;
    1.54 +
    1.55 +    ALfloat Gain[MAXCHANNELS];
    1.56 +
    1.57 +    FILTER iirFilter;
    1.58 +    ALfloat history[2];
    1.59 +} ALechoState;
    1.60 +
    1.61 +static ALvoid EchoDestroy(ALeffectState *effect)
    1.62 +{
    1.63 +    ALechoState *state = (ALechoState*)effect;
    1.64 +    if(state)
    1.65 +    {
    1.66 +        free(state->SampleBuffer);
    1.67 +        state->SampleBuffer = NULL;
    1.68 +        free(state);
    1.69 +    }
    1.70 +}
    1.71 +
    1.72 +static ALboolean EchoDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
    1.73 +{
    1.74 +    ALechoState *state = (ALechoState*)effect;
    1.75 +    ALuint maxlen, i;
    1.76 +
    1.77 +    // Use the next power of 2 for the buffer length, so the tap offsets can be
    1.78 +    // wrapped using a mask instead of a modulo
    1.79 +    maxlen  = (ALuint)(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
    1.80 +    maxlen += (ALuint)(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
    1.81 +    maxlen  = NextPowerOf2(maxlen);
    1.82 +
    1.83 +    if(maxlen != state->BufferLength)
    1.84 +    {
    1.85 +        void *temp;
    1.86 +
    1.87 +        temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
    1.88 +        if(!temp)
    1.89 +            return AL_FALSE;
    1.90 +        state->SampleBuffer = temp;
    1.91 +        state->BufferLength = maxlen;
    1.92 +    }
    1.93 +    for(i = 0;i < state->BufferLength;i++)
    1.94 +        state->SampleBuffer[i] = 0.0f;
    1.95 +
    1.96 +    return AL_TRUE;
    1.97 +}
    1.98 +
    1.99 +static ALvoid EchoUpdate(ALeffectState *effect, ALCcontext *Context, const ALeffectslot *Slot)
   1.100 +{
   1.101 +    ALechoState *state = (ALechoState*)effect;
   1.102 +    ALCdevice *Device = Context->Device;
   1.103 +    ALuint frequency = Device->Frequency;
   1.104 +    ALfloat lrpan, cw, g, gain;
   1.105 +    ALuint i;
   1.106 +
   1.107 +    state->Tap[0].delay = (ALuint)(Slot->effect.Params.Echo.Delay * frequency) + 1;
   1.108 +    state->Tap[1].delay = (ALuint)(Slot->effect.Params.Echo.LRDelay * frequency);
   1.109 +    state->Tap[1].delay += state->Tap[0].delay;
   1.110 +
   1.111 +    lrpan = Slot->effect.Params.Echo.Spread*0.5f + 0.5f;
   1.112 +    state->GainL = aluSqrt(     lrpan);
   1.113 +    state->GainR = aluSqrt(1.0f-lrpan);
   1.114 +
   1.115 +    state->FeedGain = Slot->effect.Params.Echo.Feedback;
   1.116 +
   1.117 +    cw = cos(2.0*M_PI * LOWPASSFREQCUTOFF / frequency);
   1.118 +    g = 1.0f - Slot->effect.Params.Echo.Damping;
   1.119 +    state->iirFilter.coeff = lpCoeffCalc(g, cw);
   1.120 +
   1.121 +    gain = Slot->Gain;
   1.122 +    for(i = 0;i < MAXCHANNELS;i++)
   1.123 +        state->Gain[i] = 0.0f;
   1.124 +    for(i = 0;i < Device->NumChan;i++)
   1.125 +    {
   1.126 +        enum Channel chan = Device->Speaker2Chan[i];
   1.127 +        state->Gain[chan] = gain;
   1.128 +    }
   1.129 +}
   1.130 +
   1.131 +static ALvoid EchoProcess(ALeffectState *effect, const ALeffectslot *Slot, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS])
   1.132 +{
   1.133 +    ALechoState *state = (ALechoState*)effect;
   1.134 +    const ALuint mask = state->BufferLength-1;
   1.135 +    const ALuint tap1 = state->Tap[0].delay;
   1.136 +    const ALuint tap2 = state->Tap[1].delay;
   1.137 +    ALuint offset = state->Offset;
   1.138 +    ALfloat samp[2], smp;
   1.139 +    ALuint i;
   1.140 +    (void)Slot;
   1.141 +
   1.142 +    for(i = 0;i < SamplesToDo;i++,offset++)
   1.143 +    {
   1.144 +        // Sample first tap
   1.145 +        smp = state->SampleBuffer[(offset-tap1) & mask];
   1.146 +        samp[0] = smp * state->GainL;
   1.147 +        samp[1] = smp * state->GainR;
   1.148 +        // Sample second tap. Reverse LR panning
   1.149 +        smp = state->SampleBuffer[(offset-tap2) & mask];
   1.150 +        samp[0] += smp * state->GainR;
   1.151 +        samp[1] += smp * state->GainL;
   1.152 +
   1.153 +        // Apply damping and feedback gain to the second tap, and mix in the
   1.154 +        // new sample
   1.155 +        smp = lpFilter2P(&state->iirFilter, 0, smp+SamplesIn[i]);
   1.156 +        state->SampleBuffer[offset&mask] = smp * state->FeedGain;
   1.157 +
   1.158 +        SamplesOut[i][FRONT_LEFT]  += state->Gain[FRONT_LEFT]  * samp[0];
   1.159 +        SamplesOut[i][FRONT_RIGHT] += state->Gain[FRONT_RIGHT] * samp[1];
   1.160 +        SamplesOut[i][SIDE_LEFT]   += state->Gain[SIDE_LEFT]   * samp[0];
   1.161 +        SamplesOut[i][SIDE_RIGHT]  += state->Gain[SIDE_RIGHT]  * samp[1];
   1.162 +        SamplesOut[i][BACK_LEFT]   += state->Gain[BACK_LEFT]   * samp[0];
   1.163 +        SamplesOut[i][BACK_RIGHT]  += state->Gain[BACK_RIGHT]  * samp[1];
   1.164 +    }
   1.165 +    state->Offset = offset;
   1.166 +}
   1.167 +
   1.168 +ALeffectState *EchoCreate(void)
   1.169 +{
   1.170 +    ALechoState *state;
   1.171 +
   1.172 +    state = malloc(sizeof(*state));
   1.173 +    if(!state)
   1.174 +        return NULL;
   1.175 +
   1.176 +    state->state.Destroy = EchoDestroy;
   1.177 +    state->state.DeviceUpdate = EchoDeviceUpdate;
   1.178 +    state->state.Update = EchoUpdate;
   1.179 +    state->state.Process = EchoProcess;
   1.180 +
   1.181 +    state->BufferLength = 0;
   1.182 +    state->SampleBuffer = NULL;
   1.183 +
   1.184 +    state->Tap[0].delay = 0;
   1.185 +    state->Tap[1].delay = 0;
   1.186 +    state->Offset = 0;
   1.187 +    state->GainL = 0.0f;
   1.188 +    state->GainR = 0.0f;
   1.189 +
   1.190 +    state->iirFilter.coeff = 0.0f;
   1.191 +    state->iirFilter.history[0] = 0.0f;
   1.192 +    state->iirFilter.history[1] = 0.0f;
   1.193 +
   1.194 +    return &state->state;
   1.195 +}