2 * OpenAL cross platform audio library
3 * Copyright (C) 2009 by Chris Robinson.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
28 #include "alAuxEffectSlot.h"
33 typedef struct ALechoState
{
34 // Must be first in all effects!
37 ALfloat
*SampleBuffer
;
40 // The echo is two tap. The delay is the number of samples from before the
46 /* The panning gains for the two taps */
47 ALfloat Gain
[2][MAXCHANNELS
];
55 static ALvoid
EchoDestroy(ALeffectState
*effect
)
57 ALechoState
*state
= (ALechoState
*)effect
;
60 free(state
->SampleBuffer
);
61 state
->SampleBuffer
= NULL
;
66 static ALboolean
EchoDeviceUpdate(ALeffectState
*effect
, ALCdevice
*Device
)
68 ALechoState
*state
= (ALechoState
*)effect
;
71 // Use the next power of 2 for the buffer length, so the tap offsets can be
72 // wrapped using a mask instead of a modulo
73 maxlen
= fastf2u(AL_ECHO_MAX_DELAY
* Device
->Frequency
) + 1;
74 maxlen
+= fastf2u(AL_ECHO_MAX_LRDELAY
* Device
->Frequency
) + 1;
75 maxlen
= NextPowerOf2(maxlen
);
77 if(maxlen
!= state
->BufferLength
)
81 temp
= realloc(state
->SampleBuffer
, maxlen
* sizeof(ALfloat
));
84 state
->SampleBuffer
= temp
;
85 state
->BufferLength
= maxlen
;
87 for(i
= 0;i
< state
->BufferLength
;i
++)
88 state
->SampleBuffer
[i
] = 0.0f
;
93 static ALvoid
EchoUpdate(ALeffectState
*effect
, ALCcontext
*Context
, const ALeffectslot
*Slot
)
95 ALechoState
*state
= (ALechoState
*)effect
;
96 ALCdevice
*Device
= Context
->Device
;
97 ALuint frequency
= Device
->Frequency
;
98 ALfloat dirGain
, ambientGain
;
99 const ALfloat
*speakerGain
;
100 ALfloat lrpan
, cw
, g
, gain
;
103 state
->Tap
[0].delay
= fastf2u(Slot
->effect
.Echo
.Delay
* frequency
) + 1;
104 state
->Tap
[1].delay
= fastf2u(Slot
->effect
.Echo
.LRDelay
* frequency
);
105 state
->Tap
[1].delay
+= state
->Tap
[0].delay
;
107 lrpan
= Slot
->effect
.Echo
.Spread
;
109 state
->FeedGain
= Slot
->effect
.Echo
.Feedback
;
111 cw
= aluCos(F_PI
*2.0f
* LOWPASSFREQREF
/ frequency
);
112 g
= 1.0f
- Slot
->effect
.Echo
.Damping
;
113 state
->iirFilter
.coeff
= lpCoeffCalc(g
, cw
);
116 for(i
= 0;i
< MAXCHANNELS
;i
++)
118 state
->Gain
[0][i
] = 0.0f
;
119 state
->Gain
[1][i
] = 0.0f
;
122 ambientGain
= aluSqrt(1.0f
/Device
->NumChan
);
123 dirGain
= aluFabs(lrpan
);
125 /* First tap panning */
126 pos
= aluCart2LUTpos(0.0f
, ((lrpan
>0.0f
)?-1.0f
:1.0f
));
127 speakerGain
= Device
->PanningLUT
[pos
];
129 for(i
= 0;i
< Device
->NumChan
;i
++)
131 enum Channel chan
= Device
->Speaker2Chan
[i
];
132 state
->Gain
[0][chan
] = lerp(ambientGain
, speakerGain
[chan
], dirGain
) * gain
;
135 /* Second tap panning */
136 pos
= aluCart2LUTpos(0.0f
, ((lrpan
>0.0f
)?1.0f
:-1.0f
));
137 speakerGain
= Device
->PanningLUT
[pos
];
139 for(i
= 0;i
< Device
->NumChan
;i
++)
141 enum Channel chan
= Device
->Speaker2Chan
[i
];
142 state
->Gain
[1][chan
] = lerp(ambientGain
, speakerGain
[chan
], dirGain
) * gain
;
146 static ALvoid
EchoProcess(ALeffectState
*effect
, ALuint SamplesToDo
, const ALfloat
*SamplesIn
, ALfloat (*SamplesOut
)[MAXCHANNELS
])
148 ALechoState
*state
= (ALechoState
*)effect
;
149 const ALuint mask
= state
->BufferLength
-1;
150 const ALuint tap1
= state
->Tap
[0].delay
;
151 const ALuint tap2
= state
->Tap
[1].delay
;
152 ALuint offset
= state
->Offset
;
156 for(i
= 0;i
< SamplesToDo
;i
++,offset
++)
159 smp
= state
->SampleBuffer
[(offset
-tap1
) & mask
];
160 for(k
= 0;k
< MAXCHANNELS
;k
++)
161 SamplesOut
[i
][k
] += smp
* state
->Gain
[0][k
];
164 smp
= state
->SampleBuffer
[(offset
-tap2
) & mask
];
165 for(k
= 0;k
< MAXCHANNELS
;k
++)
166 SamplesOut
[i
][k
] += smp
* state
->Gain
[1][k
];
168 // Apply damping and feedback gain to the second tap, and mix in the
170 smp
= lpFilter2P(&state
->iirFilter
, 0, smp
+SamplesIn
[i
]);
171 state
->SampleBuffer
[offset
&mask
] = smp
* state
->FeedGain
;
173 state
->Offset
= offset
;
176 ALeffectState
*EchoCreate(void)
180 state
= malloc(sizeof(*state
));
184 state
->state
.Destroy
= EchoDestroy
;
185 state
->state
.DeviceUpdate
= EchoDeviceUpdate
;
186 state
->state
.Update
= EchoUpdate
;
187 state
->state
.Process
= EchoProcess
;
189 state
->BufferLength
= 0;
190 state
->SampleBuffer
= NULL
;
192 state
->Tap
[0].delay
= 0;
193 state
->Tap
[1].delay
= 0;
196 state
->iirFilter
.coeff
= 0.0f
;
197 state
->iirFilter
.history
[0] = 0.0f
;
198 state
->iirFilter
.history
[1] = 0.0f
;
200 return &state
->state
;