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.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
30 #include "alcontext.h"
31 #include "alAuxEffectSlot.h"
34 #include "filters/biquad.h"
38 #define MAX_UPDATE_SAMPLES 128
40 #define WAVEFORM_FRACBITS 24
41 #define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
42 #define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
44 static inline ALfloat
Sin(ALsizei index
)
46 return std::sin(static_cast<ALfloat
>(index
) * (al::MathDefs
<float>::Tau() / static_cast<ALfloat
>WAVEFORM_FRACONE
));
49 static inline ALfloat
Saw(ALsizei index
)
51 return static_cast<ALfloat
>(index
)*(2.0f
/WAVEFORM_FRACONE
) - 1.0f
;
54 static inline ALfloat
Square(ALsizei index
)
56 return static_cast<ALfloat
>(((index
>>(WAVEFORM_FRACBITS
-2))&2) - 1);
59 static inline ALfloat
One(ALsizei
UNUSED(index
))
64 template<ALfloat
func(ALsizei
)>
65 static void Modulate(ALfloat
*RESTRICT dst
, ALsizei index
, const ALsizei step
, ALsizei todo
)
68 for(i
= 0;i
< todo
;i
++)
71 index
&= WAVEFORM_FRACMASK
;
77 struct ALmodulatorState final
: public EffectState
{
78 void (*mGetSamples
)(ALfloat
*RESTRICT
, ALsizei
, const ALsizei
, ALsizei
){};
86 ALfloat CurrentGains
[MAX_OUTPUT_CHANNELS
]{};
87 ALfloat TargetGains
[MAX_OUTPUT_CHANNELS
]{};
88 } mChans
[MAX_EFFECT_CHANNELS
];
91 ALboolean
deviceUpdate(const ALCdevice
*device
) override
;
92 void update(const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
, const EffectTarget target
) override
;
93 void process(ALsizei samplesToDo
, const ALfloat (*RESTRICT samplesIn
)[BUFFERSIZE
], ALfloat (*RESTRICT samplesOut
)[BUFFERSIZE
], ALsizei numChannels
) override
;
95 DEF_NEWDEL(ALmodulatorState
)
98 ALboolean
ALmodulatorState::deviceUpdate(const ALCdevice
*UNUSED(device
))
100 for(auto &e
: mChans
)
103 std::fill(std::begin(e
.CurrentGains
), std::end(e
.CurrentGains
), 0.0f
);
108 void ALmodulatorState::update(const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
, const EffectTarget target
)
110 const ALCdevice
*device
= context
->Device
;
114 mStep
= fastf2i(props
->Modulator
.Frequency
/ static_cast<ALfloat
>(device
->Frequency
) * WAVEFORM_FRACONE
);
115 mStep
= clampi(mStep
, 0, WAVEFORM_FRACONE
-1);
118 mGetSamples
= Modulate
<One
>;
119 else if(props
->Modulator
.Waveform
== AL_RING_MODULATOR_SINUSOID
)
120 mGetSamples
= Modulate
<Sin
>;
121 else if(props
->Modulator
.Waveform
== AL_RING_MODULATOR_SAWTOOTH
)
122 mGetSamples
= Modulate
<Saw
>;
123 else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
124 mGetSamples
= Modulate
<Square
>;
126 f0norm
= props
->Modulator
.HighPassCutoff
/ static_cast<ALfloat
>(device
->Frequency
);
127 f0norm
= clampf(f0norm
, 1.0f
/512.0f
, 0.49f
);
128 /* Bandwidth value is constant in octaves. */
129 mChans
[0].Filter
.setParams(BiquadType::HighPass
, 1.0f
, f0norm
,
130 calc_rcpQ_from_bandwidth(f0norm
, 0.75f
));
131 for(i
= 1;i
< MAX_EFFECT_CHANNELS
;i
++)
132 mChans
[i
].Filter
.copyParamsFrom(mChans
[0].Filter
);
134 mOutBuffer
= target
.FOAOut
->Buffer
;
135 mOutChannels
= target
.FOAOut
->NumChannels
;
136 for(i
= 0;i
< MAX_EFFECT_CHANNELS
;i
++)
137 ComputePanGains(target
.FOAOut
, alu::Matrix::Identity()[i
].data(), slot
->Params
.Gain
,
138 mChans
[i
].TargetGains
);
141 void ALmodulatorState::process(ALsizei SamplesToDo
, const ALfloat (*RESTRICT SamplesIn
)[BUFFERSIZE
], ALfloat (*RESTRICT SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
143 const ALsizei step
= mStep
;
146 for(base
= 0;base
< SamplesToDo
;)
148 alignas(16) ALfloat modsamples
[MAX_UPDATE_SAMPLES
];
149 ALsizei td
= mini(MAX_UPDATE_SAMPLES
, SamplesToDo
-base
);
152 mGetSamples(modsamples
, mIndex
, step
, td
);
153 mIndex
+= (step
*td
) & WAVEFORM_FRACMASK
;
154 mIndex
&= WAVEFORM_FRACMASK
;
156 for(c
= 0;c
< MAX_EFFECT_CHANNELS
;c
++)
158 alignas(16) ALfloat temps
[MAX_UPDATE_SAMPLES
];
160 mChans
[c
].Filter
.process(temps
, &SamplesIn
[c
][base
], td
);
161 for(i
= 0;i
< td
;i
++)
162 temps
[i
] *= modsamples
[i
];
164 MixSamples(temps
, NumChannels
, SamplesOut
, mChans
[c
].CurrentGains
,
165 mChans
[c
].TargetGains
, SamplesToDo
-base
, base
, td
);
173 struct ModulatorStateFactory final
: public EffectStateFactory
{
174 EffectState
*create() override
;
177 EffectState
*ModulatorStateFactory::create()
178 { return new ALmodulatorState
{}; }
180 EffectStateFactory
*ModulatorStateFactory_getFactory()
182 static ModulatorStateFactory ModulatorFactory
{};
183 return &ModulatorFactory
;
187 void ALmodulator_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
189 ALeffectProps
*props
= &effect
->Props
;
192 case AL_RING_MODULATOR_FREQUENCY
:
193 if(!(val
>= AL_RING_MODULATOR_MIN_FREQUENCY
&& val
<= AL_RING_MODULATOR_MAX_FREQUENCY
))
194 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Modulator frequency out of range");
195 props
->Modulator
.Frequency
= val
;
198 case AL_RING_MODULATOR_HIGHPASS_CUTOFF
:
199 if(!(val
>= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF
&& val
<= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF
))
200 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Modulator high-pass cutoff out of range");
201 props
->Modulator
.HighPassCutoff
= val
;
205 alSetError(context
, AL_INVALID_ENUM
, "Invalid modulator float property 0x%04x", param
);
208 void ALmodulator_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
209 { ALmodulator_setParamf(effect
, context
, param
, vals
[0]); }
210 void ALmodulator_setParami(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint val
)
212 ALeffectProps
*props
= &effect
->Props
;
215 case AL_RING_MODULATOR_FREQUENCY
:
216 case AL_RING_MODULATOR_HIGHPASS_CUTOFF
:
217 ALmodulator_setParamf(effect
, context
, param
, static_cast<ALfloat
>(val
));
220 case AL_RING_MODULATOR_WAVEFORM
:
221 if(!(val
>= AL_RING_MODULATOR_MIN_WAVEFORM
&& val
<= AL_RING_MODULATOR_MAX_WAVEFORM
))
222 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid modulator waveform");
223 props
->Modulator
.Waveform
= val
;
227 alSetError(context
, AL_INVALID_ENUM
, "Invalid modulator integer property 0x%04x", param
);
230 void ALmodulator_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
231 { ALmodulator_setParami(effect
, context
, param
, vals
[0]); }
233 void ALmodulator_getParami(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*val
)
235 const ALeffectProps
*props
= &effect
->Props
;
238 case AL_RING_MODULATOR_FREQUENCY
:
239 *val
= static_cast<ALint
>(props
->Modulator
.Frequency
);
241 case AL_RING_MODULATOR_HIGHPASS_CUTOFF
:
242 *val
= static_cast<ALint
>(props
->Modulator
.HighPassCutoff
);
244 case AL_RING_MODULATOR_WAVEFORM
:
245 *val
= props
->Modulator
.Waveform
;
249 alSetError(context
, AL_INVALID_ENUM
, "Invalid modulator integer property 0x%04x", param
);
252 void ALmodulator_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
253 { ALmodulator_getParami(effect
, context
, param
, vals
); }
254 void ALmodulator_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
256 const ALeffectProps
*props
= &effect
->Props
;
259 case AL_RING_MODULATOR_FREQUENCY
:
260 *val
= props
->Modulator
.Frequency
;
262 case AL_RING_MODULATOR_HIGHPASS_CUTOFF
:
263 *val
= props
->Modulator
.HighPassCutoff
;
267 alSetError(context
, AL_INVALID_ENUM
, "Invalid modulator float property 0x%04x", param
);
270 void ALmodulator_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
271 { ALmodulator_getParamf(effect
, context
, param
, vals
); }
273 DEFINE_ALEFFECT_VTABLE(ALmodulator
);