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 LR gains for the first tap. The second tap uses the reverse
52 ALfloat Gain
[OUTPUTCHANNELS
];
58 static ALvoid
EchoDestroy(ALeffectState
*effect
)
60 ALechoState
*state
= (ALechoState
*)effect
;
63 free(state
->SampleBuffer
);
64 state
->SampleBuffer
= NULL
;
69 static ALboolean
EchoDeviceUpdate(ALeffectState
*effect
, ALCdevice
*Device
)
71 ALechoState
*state
= (ALechoState
*)effect
;
74 // Use the next power of 2 for the buffer length, so the tap offsets can be
75 // wrapped using a mask instead of a modulo
76 maxlen
= (ALuint
)(AL_ECHO_MAX_DELAY
* Device
->Frequency
) + 1;
77 maxlen
+= (ALuint
)(AL_ECHO_MAX_LRDELAY
* Device
->Frequency
) + 1;
78 maxlen
= NextPowerOf2(maxlen
);
80 if(maxlen
!= state
->BufferLength
)
84 temp
= realloc(state
->SampleBuffer
, maxlen
* sizeof(ALfloat
));
87 state
->SampleBuffer
= temp
;
88 state
->BufferLength
= maxlen
;
90 for(i
= 0;i
< state
->BufferLength
;i
++)
91 state
->SampleBuffer
[i
] = 0.0f
;
93 for(i
= 0;i
< OUTPUTCHANNELS
;i
++)
94 state
->Gain
[i
] = 0.0f
;
95 for(i
= 0;i
< Device
->NumChan
;i
++)
97 Channel chan
= Device
->Speaker2Chan
[i
];
98 state
->Gain
[chan
] = 1.0f
;
104 static ALvoid
EchoUpdate(ALeffectState
*effect
, ALCcontext
*Context
, const ALeffect
*Effect
)
106 ALechoState
*state
= (ALechoState
*)effect
;
107 ALuint frequency
= Context
->Device
->Frequency
;
108 ALfloat lrpan
, cw
, a
, g
;
110 state
->Tap
[0].delay
= (ALuint
)(Effect
->Echo
.Delay
* frequency
) + 1;
111 state
->Tap
[1].delay
= (ALuint
)(Effect
->Echo
.LRDelay
* frequency
);
112 state
->Tap
[1].delay
+= state
->Tap
[0].delay
;
114 lrpan
= Effect
->Echo
.Spread
*0.5f
+ 0.5f
;
115 state
->GainL
= aluSqrt( lrpan
);
116 state
->GainR
= aluSqrt(1.0f
-lrpan
);
118 state
->FeedGain
= Effect
->Echo
.Feedback
;
120 cw
= cos(2.0*M_PI
* LOWPASSFREQCUTOFF
/ frequency
);
121 g
= 1.0f
- Effect
->Echo
.Damping
;
123 if(g
< 0.9999f
) // 1-epsilon
124 a
= (1 - g
*cw
- aluSqrt(2*g
*(1-cw
) - g
*g
*(1 - cw
*cw
))) / (1 - g
);
125 state
->iirFilter
.coeff
= a
;
128 static ALvoid
EchoProcess(ALeffectState
*effect
, const ALeffectslot
*Slot
, ALuint SamplesToDo
, const ALfloat
*SamplesIn
, ALfloat (*SamplesOut
)[OUTPUTCHANNELS
])
130 ALechoState
*state
= (ALechoState
*)effect
;
131 const ALuint mask
= state
->BufferLength
-1;
132 const ALuint tap1
= state
->Tap
[0].delay
;
133 const ALuint tap2
= state
->Tap
[1].delay
;
134 ALuint offset
= state
->Offset
;
135 const ALfloat gain
= Slot
->Gain
;
136 ALfloat samp
[2], smp
;
139 for(i
= 0;i
< SamplesToDo
;i
++,offset
++)
142 smp
= state
->SampleBuffer
[(offset
-tap1
) & mask
];
143 samp
[0] = smp
* state
->GainL
;
144 samp
[1] = smp
* state
->GainR
;
145 // Sample second tap. Reverse LR panning
146 smp
= state
->SampleBuffer
[(offset
-tap2
) & mask
];
147 samp
[0] += smp
* state
->GainR
;
148 samp
[1] += smp
* state
->GainL
;
150 // Apply damping and feedback gain to the second tap, and mix in the
152 smp
= lpFilter2P(&state
->iirFilter
, 0, smp
+SamplesIn
[i
]);
153 state
->SampleBuffer
[offset
&mask
] = smp
* state
->FeedGain
;
159 SamplesOut
[i
][FRONT_LEFT
] += state
->Gain
[FRONT_LEFT
] * samp
[0];
160 SamplesOut
[i
][FRONT_RIGHT
] += state
->Gain
[FRONT_RIGHT
] * samp
[1];
161 SamplesOut
[i
][SIDE_LEFT
] += state
->Gain
[SIDE_LEFT
] * samp
[0];
162 SamplesOut
[i
][SIDE_RIGHT
] += state
->Gain
[SIDE_RIGHT
] * samp
[1];
163 SamplesOut
[i
][BACK_LEFT
] += state
->Gain
[BACK_LEFT
] * samp
[0];
164 SamplesOut
[i
][BACK_RIGHT
] += state
->Gain
[BACK_RIGHT
] * samp
[1];
166 state
->Offset
= offset
;
169 ALeffectState
*EchoCreate(void)
173 state
= malloc(sizeof(*state
));
177 state
->state
.Destroy
= EchoDestroy
;
178 state
->state
.DeviceUpdate
= EchoDeviceUpdate
;
179 state
->state
.Update
= EchoUpdate
;
180 state
->state
.Process
= EchoProcess
;
182 state
->BufferLength
= 0;
183 state
->SampleBuffer
= NULL
;
185 state
->Tap
[0].delay
= 0;
186 state
->Tap
[1].delay
= 0;
191 state
->iirFilter
.coeff
= 0.0f
;
192 state
->iirFilter
.history
[0] = 0.0f
;
193 state
->iirFilter
.history
[1] = 0.0f
;
195 return &state
->state
;