]> git.tdb.fi Git - ext/openal.git/blob - alc/effects/equalizer.cpp
Import OpenAL Soft 1.23.1 sources
[ext/openal.git] / alc / effects / equalizer.cpp
1 /**
2  * OpenAL cross platform audio library
3  * Copyright (C) 2013 by Mike Gorchak
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.
8  *
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.
13  *
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
19  */
20
21 #include "config.h"
22
23 #include <algorithm>
24 #include <array>
25 #include <cstdlib>
26 #include <functional>
27 #include <iterator>
28 #include <utility>
29
30 #include "alc/effects/base.h"
31 #include "almalloc.h"
32 #include "alspan.h"
33 #include "core/ambidefs.h"
34 #include "core/bufferline.h"
35 #include "core/context.h"
36 #include "core/devformat.h"
37 #include "core/device.h"
38 #include "core/effectslot.h"
39 #include "core/filters/biquad.h"
40 #include "core/mixer.h"
41 #include "intrusive_ptr.h"
42
43
44 namespace {
45
46 /*  The document  "Effects Extension Guide.pdf"  says that low and high  *
47  *  frequencies are cutoff frequencies. This is not fully correct, they  *
48  *  are corner frequencies for low and high shelf filters. If they were  *
49  *  just cutoff frequencies, there would be no need in cutoff frequency  *
50  *  gains, which are present.  Documentation for  "Creative Proteus X2"  *
51  *  software describes  4-band equalizer functionality in a much better  *
52  *  way.  This equalizer seems  to be a predecessor  of  OpenAL  4-band  *
53  *  equalizer.  With low and high  shelf filters  we are able to cutoff  *
54  *  frequencies below and/or above corner frequencies using attenuation  *
55  *  gains (below 1.0) and amplify all low and/or high frequencies using  *
56  *  gains above 1.0.                                                     *
57  *                                                                       *
58  *     Low-shelf       Low Mid Band      High Mid Band     High-shelf    *
59  *      corner            center             center          corner      *
60  *     frequency        frequency          frequency       frequency     *
61  *    50Hz..800Hz     200Hz..3000Hz      1000Hz..8000Hz  4000Hz..16000Hz *
62  *                                                                       *
63  *          |               |                  |               |         *
64  *          |               |                  |               |         *
65  *   B -----+            /--+--\            /--+--\            +-----    *
66  *   O      |\          |   |   |          |   |   |          /|         *
67  *   O      | \        -    |    -        -    |    -        / |         *
68  *   S +    |  \      |     |     |      |     |     |      /  |         *
69  *   T      |   |    |      |      |    |      |      |    |   |         *
70  * ---------+---------------+------------------+---------------+-------- *
71  *   C      |   |    |      |      |    |      |      |    |   |         *
72  *   U -    |  /      |     |     |      |     |     |      \  |         *
73  *   T      | /        -    |    -        -    |    -        \ |         *
74  *   O      |/          |   |   |          |   |   |          \|         *
75  *   F -----+            \--+--/            \--+--/            +-----    *
76  *   F      |               |                  |               |         *
77  *          |               |                  |               |         *
78  *                                                                       *
79  * Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
80  * up to +18dB amplification. Band width varies from 0.01 up to 1.0 in   *
81  * octaves for two mid bands.                                            *
82  *                                                                       *
83  * Implementation is based on the "Cookbook formulae for audio EQ biquad *
84  * filter coefficients" by Robert Bristow-Johnson                        *
85  * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt                   */
86
87
88 struct EqualizerState final : public EffectState {
89     struct {
90         uint mTargetChannel{InvalidChannelIndex};
91
92         /* Effect parameters */
93         BiquadFilter mFilter[4];
94
95         /* Effect gains for each channel */
96         float mCurrentGain{};
97         float mTargetGain{};
98     } mChans[MaxAmbiChannels];
99
100     alignas(16) FloatBufferLine mSampleBuffer{};
101
102
103     void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override;
104     void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
105         const EffectTarget target) override;
106     void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
107         const al::span<FloatBufferLine> samplesOut) override;
108
109     DEF_NEWDEL(EqualizerState)
110 };
111
112 void EqualizerState::deviceUpdate(const DeviceBase*, const BufferStorage*)
113 {
114     for(auto &e : mChans)
115     {
116         e.mTargetChannel = InvalidChannelIndex;
117         std::for_each(std::begin(e.mFilter), std::end(e.mFilter),
118             std::mem_fn(&BiquadFilter::clear));
119         e.mCurrentGain = 0.0f;
120     }
121 }
122
123 void EqualizerState::update(const ContextBase *context, const EffectSlot *slot,
124     const EffectProps *props, const EffectTarget target)
125 {
126     const DeviceBase *device{context->mDevice};
127     auto frequency = static_cast<float>(device->Frequency);
128     float gain, f0norm;
129
130     /* Calculate coefficients for the each type of filter. Note that the shelf
131      * and peaking filters' gain is for the centerpoint of the transition band,
132      * while the effect property gains are for the shelf/peak itself. So the
133      * property gains need their dB halved (sqrt of linear gain) for the
134      * shelf/peak to reach the provided gain.
135      */
136     gain = std::sqrt(props->Equalizer.LowGain);
137     f0norm = props->Equalizer.LowCutoff / frequency;
138     mChans[0].mFilter[0].setParamsFromSlope(BiquadType::LowShelf, f0norm, gain, 0.75f);
139
140     gain = std::sqrt(props->Equalizer.Mid1Gain);
141     f0norm = props->Equalizer.Mid1Center / frequency;
142     mChans[0].mFilter[1].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain,
143         props->Equalizer.Mid1Width);
144
145     gain = std::sqrt(props->Equalizer.Mid2Gain);
146     f0norm = props->Equalizer.Mid2Center / frequency;
147     mChans[0].mFilter[2].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain,
148         props->Equalizer.Mid2Width);
149
150     gain = std::sqrt(props->Equalizer.HighGain);
151     f0norm = props->Equalizer.HighCutoff / frequency;
152     mChans[0].mFilter[3].setParamsFromSlope(BiquadType::HighShelf, f0norm, gain, 0.75f);
153
154     /* Copy the filter coefficients for the other input channels. */
155     for(size_t i{1u};i < slot->Wet.Buffer.size();++i)
156     {
157         mChans[i].mFilter[0].copyParamsFrom(mChans[0].mFilter[0]);
158         mChans[i].mFilter[1].copyParamsFrom(mChans[0].mFilter[1]);
159         mChans[i].mFilter[2].copyParamsFrom(mChans[0].mFilter[2]);
160         mChans[i].mFilter[3].copyParamsFrom(mChans[0].mFilter[3]);
161     }
162
163     mOutTarget = target.Main->Buffer;
164     auto set_channel = [this](size_t idx, uint outchan, float outgain)
165     {
166         mChans[idx].mTargetChannel = outchan;
167         mChans[idx].mTargetGain = outgain;
168     };
169     target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel);
170 }
171
172 void EqualizerState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
173 {
174     const al::span<float> buffer{mSampleBuffer.data(), samplesToDo};
175     auto chan = std::begin(mChans);
176     for(const auto &input : samplesIn)
177     {
178         const size_t outidx{chan->mTargetChannel};
179         if(outidx != InvalidChannelIndex)
180         {
181             const al::span<const float> inbuf{input.data(), samplesToDo};
182             DualBiquad{chan->mFilter[0], chan->mFilter[1]}.process(inbuf, buffer.begin());
183             DualBiquad{chan->mFilter[2], chan->mFilter[3]}.process(buffer, buffer.begin());
184
185             MixSamples(buffer, samplesOut[outidx].data(), chan->mCurrentGain, chan->mTargetGain,
186                 samplesToDo);
187         }
188         ++chan;
189     }
190 }
191
192
193 struct EqualizerStateFactory final : public EffectStateFactory {
194     al::intrusive_ptr<EffectState> create() override
195     { return al::intrusive_ptr<EffectState>{new EqualizerState{}}; }
196 };
197
198 } // namespace
199
200 EffectStateFactory *EqualizerStateFactory_getFactory()
201 {
202     static EqualizerStateFactory EqualizerFactory{};
203     return &EqualizerFactory;
204 }