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
29 #include "al/auxeffectslot.h"
31 #include "alcontext.h"
33 #include "filters/biquad.h"
39 #define MAX_UPDATE_SAMPLES 128
41 #define WAVEFORM_FRACBITS 24
42 #define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
43 #define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
45 inline float Sin(ALuint index
)
47 constexpr float scale
{al::MathDefs
<float>::Tau() / WAVEFORM_FRACONE
};
48 return std::sin(static_cast<float>(index
) * scale
);
51 inline float Saw(ALuint index
)
52 { return static_cast<float>(index
)*(2.0f
/WAVEFORM_FRACONE
) - 1.0f
; }
54 inline float Square(ALuint index
)
55 { return static_cast<float>(static_cast<int>((index
>>(WAVEFORM_FRACBITS
-2))&2) - 1); }
57 inline float One(ALuint
) { return 1.0f
; }
59 template<float (&func
)(ALuint
)>
60 void Modulate(float *RESTRICT dst
, ALuint index
, const ALuint step
, size_t todo
)
62 for(size_t i
{0u};i
< todo
;i
++)
65 index
&= WAVEFORM_FRACMASK
;
71 struct ModulatorState final
: public EffectState
{
72 void (*mGetSamples
)(float*RESTRICT
, ALuint
, const ALuint
, size_t){};
80 ALfloat CurrentGains
[MAX_OUTPUT_CHANNELS
]{};
81 ALfloat TargetGains
[MAX_OUTPUT_CHANNELS
]{};
82 } mChans
[MAX_AMBI_CHANNELS
];
85 ALboolean
deviceUpdate(const ALCdevice
*device
) override
;
86 void update(const ALCcontext
*context
, const ALeffectslot
*slot
, const EffectProps
*props
, const EffectTarget target
) override
;
87 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
) override
;
89 DEF_NEWDEL(ModulatorState
)
92 ALboolean
ModulatorState::deviceUpdate(const ALCdevice
*)
97 std::fill(std::begin(e
.CurrentGains
), std::end(e
.CurrentGains
), 0.0f
);
102 void ModulatorState::update(const ALCcontext
*context
, const ALeffectslot
*slot
, const EffectProps
*props
, const EffectTarget target
)
104 const ALCdevice
*device
{context
->mDevice
.get()};
106 const float step
{props
->Modulator
.Frequency
/ static_cast<ALfloat
>(device
->Frequency
)};
107 mStep
= fastf2u(clampf(step
*WAVEFORM_FRACONE
, 0.0f
, ALfloat
{WAVEFORM_FRACONE
-1}));
110 mGetSamples
= Modulate
<One
>;
111 else if(props
->Modulator
.Waveform
== AL_RING_MODULATOR_SINUSOID
)
112 mGetSamples
= Modulate
<Sin
>;
113 else if(props
->Modulator
.Waveform
== AL_RING_MODULATOR_SAWTOOTH
)
114 mGetSamples
= Modulate
<Saw
>;
115 else /*if(props->Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
116 mGetSamples
= Modulate
<Square
>;
118 ALfloat f0norm
{props
->Modulator
.HighPassCutoff
/ static_cast<ALfloat
>(device
->Frequency
)};
119 f0norm
= clampf(f0norm
, 1.0f
/512.0f
, 0.49f
);
120 /* Bandwidth value is constant in octaves. */
121 mChans
[0].Filter
.setParams(BiquadType::HighPass
, 1.0f
, f0norm
,
122 BiquadFilter::rcpQFromBandwidth(f0norm
, 0.75f
));
123 for(size_t i
{1u};i
< slot
->Wet
.Buffer
.size();++i
)
124 mChans
[i
].Filter
.copyParamsFrom(mChans
[0].Filter
);
126 mOutTarget
= target
.Main
->Buffer
;
127 for(size_t i
{0u};i
< slot
->Wet
.Buffer
.size();++i
)
129 auto coeffs
= GetAmbiIdentityRow(i
);
130 ComputePanGains(target
.Main
, coeffs
.data(), slot
->Params
.Gain
, mChans
[i
].TargetGains
);
134 void ModulatorState::process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
)
136 for(size_t base
{0u};base
< samplesToDo
;)
138 alignas(16) ALfloat modsamples
[MAX_UPDATE_SAMPLES
];
139 size_t td
{minz(MAX_UPDATE_SAMPLES
, samplesToDo
-base
)};
141 mGetSamples(modsamples
, mIndex
, mStep
, td
);
142 mIndex
+= static_cast<ALuint
>(mStep
* td
);
143 mIndex
&= WAVEFORM_FRACMASK
;
145 auto chandata
= std::addressof(mChans
[0]);
146 for(const auto &input
: samplesIn
)
148 alignas(16) ALfloat temps
[MAX_UPDATE_SAMPLES
];
150 chandata
->Filter
.process(temps
, &input
[base
], td
);
151 for(size_t i
{0u};i
< td
;i
++)
152 temps
[i
] *= modsamples
[i
];
154 MixSamples({temps
, td
}, samplesOut
, chandata
->CurrentGains
, chandata
->TargetGains
,
155 samplesToDo
-base
, base
);
164 void Modulator_setParamf(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat val
)
168 case AL_RING_MODULATOR_FREQUENCY
:
169 if(!(val
>= AL_RING_MODULATOR_MIN_FREQUENCY
&& val
<= AL_RING_MODULATOR_MAX_FREQUENCY
))
170 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Modulator frequency out of range");
171 props
->Modulator
.Frequency
= val
;
174 case AL_RING_MODULATOR_HIGHPASS_CUTOFF
:
175 if(!(val
>= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF
&& val
<= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF
))
176 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Modulator high-pass cutoff out of range");
177 props
->Modulator
.HighPassCutoff
= val
;
181 context
->setError(AL_INVALID_ENUM
, "Invalid modulator float property 0x%04x", param
);
184 void Modulator_setParamfv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
185 { Modulator_setParamf(props
, context
, param
, vals
[0]); }
186 void Modulator_setParami(EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint val
)
190 case AL_RING_MODULATOR_FREQUENCY
:
191 case AL_RING_MODULATOR_HIGHPASS_CUTOFF
:
192 Modulator_setParamf(props
, context
, param
, static_cast<ALfloat
>(val
));
195 case AL_RING_MODULATOR_WAVEFORM
:
196 if(!(val
>= AL_RING_MODULATOR_MIN_WAVEFORM
&& val
<= AL_RING_MODULATOR_MAX_WAVEFORM
))
197 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid modulator waveform");
198 props
->Modulator
.Waveform
= val
;
202 context
->setError(AL_INVALID_ENUM
, "Invalid modulator integer property 0x%04x", param
);
205 void Modulator_setParamiv(EffectProps
*props
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
206 { Modulator_setParami(props
, context
, param
, vals
[0]); }
208 void Modulator_getParami(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*val
)
212 case AL_RING_MODULATOR_FREQUENCY
:
213 *val
= static_cast<ALint
>(props
->Modulator
.Frequency
);
215 case AL_RING_MODULATOR_HIGHPASS_CUTOFF
:
216 *val
= static_cast<ALint
>(props
->Modulator
.HighPassCutoff
);
218 case AL_RING_MODULATOR_WAVEFORM
:
219 *val
= props
->Modulator
.Waveform
;
223 context
->setError(AL_INVALID_ENUM
, "Invalid modulator integer property 0x%04x", param
);
226 void Modulator_getParamiv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
227 { Modulator_getParami(props
, context
, param
, vals
); }
228 void Modulator_getParamf(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
232 case AL_RING_MODULATOR_FREQUENCY
:
233 *val
= props
->Modulator
.Frequency
;
235 case AL_RING_MODULATOR_HIGHPASS_CUTOFF
:
236 *val
= props
->Modulator
.HighPassCutoff
;
240 context
->setError(AL_INVALID_ENUM
, "Invalid modulator float property 0x%04x", param
);
243 void Modulator_getParamfv(const EffectProps
*props
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
244 { Modulator_getParamf(props
, context
, param
, vals
); }
246 DEFINE_ALEFFECT_VTABLE(Modulator
);
249 struct ModulatorStateFactory final
: public EffectStateFactory
{
250 EffectState
*create() override
{ return new ModulatorState
{}; }
251 EffectProps
getDefaultProps() const noexcept override
;
252 const EffectVtable
*getEffectVtable() const noexcept override
{ return &Modulator_vtable
; }
255 EffectProps
ModulatorStateFactory::getDefaultProps() const noexcept
258 props
.Modulator
.Frequency
= AL_RING_MODULATOR_DEFAULT_FREQUENCY
;
259 props
.Modulator
.HighPassCutoff
= AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF
;
260 props
.Modulator
.Waveform
= AL_RING_MODULATOR_DEFAULT_WAVEFORM
;
266 EffectStateFactory
*ModulatorStateFactory_getFactory()
268 static ModulatorStateFactory ModulatorFactory
{};
269 return &ModulatorFactory
;