]> git.tdb.fi Git - ext/openal.git/blob - alc/effects/compressor.cpp
Import OpenAL Soft 1.23.1 sources
[ext/openal.git] / alc / effects / compressor.cpp
1 /**
2  * This file is part of the OpenAL Soft cross platform audio library
3  *
4  * Copyright (C) 2013 by Anis A. Hireche
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * * Redistributions of source code must retain the above copyright notice,
10  *   this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright notice,
13  *   this list of conditions and the following disclaimer in the documentation
14  *   and/or other materials provided with the distribution.
15  *
16  * * Neither the name of Spherical-Harmonic-Transform nor the names of its
17  *   contributors may be used to endorse or promote products derived from
18  *   this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "config.h"
34
35 #include <array>
36 #include <cstdlib>
37 #include <iterator>
38 #include <utility>
39
40 #include "alc/effects/base.h"
41 #include "almalloc.h"
42 #include "alnumeric.h"
43 #include "alspan.h"
44 #include "core/ambidefs.h"
45 #include "core/bufferline.h"
46 #include "core/devformat.h"
47 #include "core/device.h"
48 #include "core/effectslot.h"
49 #include "core/mixer.h"
50 #include "core/mixer/defs.h"
51 #include "intrusive_ptr.h"
52
53 struct ContextBase;
54
55
56 namespace {
57
58 #define AMP_ENVELOPE_MIN  0.5f
59 #define AMP_ENVELOPE_MAX  2.0f
60
61 #define ATTACK_TIME  0.1f /* 100ms to rise from min to max */
62 #define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
63
64
65 struct CompressorState final : public EffectState {
66     /* Effect gains for each channel */
67     struct {
68         uint mTarget{InvalidChannelIndex};
69         float mGain{1.0f};
70     } mChans[MaxAmbiChannels];
71
72     /* Effect parameters */
73     bool mEnabled{true};
74     float mAttackMult{1.0f};
75     float mReleaseMult{1.0f};
76     float mEnvFollower{1.0f};
77
78
79     void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override;
80     void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
81         const EffectTarget target) override;
82     void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
83         const al::span<FloatBufferLine> samplesOut) override;
84
85     DEF_NEWDEL(CompressorState)
86 };
87
88 void CompressorState::deviceUpdate(const DeviceBase *device, const BufferStorage*)
89 {
90     /* Number of samples to do a full attack and release (non-integer sample
91      * counts are okay).
92      */
93     const float attackCount{static_cast<float>(device->Frequency) * ATTACK_TIME};
94     const float releaseCount{static_cast<float>(device->Frequency) * RELEASE_TIME};
95
96     /* Calculate per-sample multipliers to attack and release at the desired
97      * rates.
98      */
99     mAttackMult  = std::pow(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount);
100     mReleaseMult = std::pow(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
101 }
102
103 void CompressorState::update(const ContextBase*, const EffectSlot *slot,
104     const EffectProps *props, const EffectTarget target)
105 {
106     mEnabled = props->Compressor.OnOff;
107
108     mOutTarget = target.Main->Buffer;
109     auto set_channel = [this](size_t idx, uint outchan, float outgain)
110     {
111         mChans[idx].mTarget = outchan;
112         mChans[idx].mGain = outgain;
113     };
114     target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel);
115 }
116
117 void CompressorState::process(const size_t samplesToDo,
118     const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
119 {
120     for(size_t base{0u};base < samplesToDo;)
121     {
122         float gains[256];
123         const size_t td{minz(256, samplesToDo-base)};
124
125         /* Generate the per-sample gains from the signal envelope. */
126         float env{mEnvFollower};
127         if(mEnabled)
128         {
129             for(size_t i{0u};i < td;++i)
130             {
131                 /* Clamp the absolute amplitude to the defined envelope limits,
132                  * then attack or release the envelope to reach it.
133                  */
134                 const float amplitude{clampf(std::fabs(samplesIn[0][base+i]), AMP_ENVELOPE_MIN,
135                     AMP_ENVELOPE_MAX)};
136                 if(amplitude > env)
137                     env = minf(env*mAttackMult, amplitude);
138                 else if(amplitude < env)
139                     env = maxf(env*mReleaseMult, amplitude);
140
141                 /* Apply the reciprocal of the envelope to normalize the volume
142                  * (compress the dynamic range).
143                  */
144                 gains[i] = 1.0f / env;
145             }
146         }
147         else
148         {
149             /* Same as above, except the amplitude is forced to 1. This helps
150              * ensure smooth gain changes when the compressor is turned on and
151              * off.
152              */
153             for(size_t i{0u};i < td;++i)
154             {
155                 const float amplitude{1.0f};
156                 if(amplitude > env)
157                     env = minf(env*mAttackMult, amplitude);
158                 else if(amplitude < env)
159                     env = maxf(env*mReleaseMult, amplitude);
160
161                 gains[i] = 1.0f / env;
162             }
163         }
164         mEnvFollower = env;
165
166         /* Now compress the signal amplitude to output. */
167         auto chan = std::cbegin(mChans);
168         for(const auto &input : samplesIn)
169         {
170             const size_t outidx{chan->mTarget};
171             if(outidx != InvalidChannelIndex)
172             {
173                 const float *RESTRICT src{input.data() + base};
174                 float *RESTRICT dst{samplesOut[outidx].data() + base};
175                 const float gain{chan->mGain};
176                 if(!(std::fabs(gain) > GainSilenceThreshold))
177                 {
178                     for(size_t i{0u};i < td;i++)
179                         dst[i] += src[i] * gains[i] * gain;
180                 }
181             }
182             ++chan;
183         }
184
185         base += td;
186     }
187 }
188
189
190 struct CompressorStateFactory final : public EffectStateFactory {
191     al::intrusive_ptr<EffectState> create() override
192     { return al::intrusive_ptr<EffectState>{new CompressorState{}}; }
193 };
194
195 } // namespace
196
197 EffectStateFactory *CompressorStateFactory_getFactory()
198 {
199     static CompressorStateFactory CompressorFactory{};
200     return &CompressorFactory;
201 }