Improve formatting for setting the UWP default device callback
[openal-soft.git] / alc / effects / compressor.cpp
blob3197119c7318650893c7d8d00d7b2f18f2a4d966
1 /**
2 * This file is part of the OpenAL Soft cross platform audio library
4 * Copyright (C) 2013 by Anis A. Hireche
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
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.
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.
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.
33 #include "config.h"
35 #include <algorithm>
36 #include <array>
37 #include <cmath>
38 #include <cstdlib>
39 #include <variant>
41 #include "alc/effects/base.h"
42 #include "alspan.h"
43 #include "core/ambidefs.h"
44 #include "core/bufferline.h"
45 #include "core/device.h"
46 #include "core/effects/base.h"
47 #include "core/effectslot.h"
48 #include "core/mixer/defs.h"
49 #include "intrusive_ptr.h"
51 struct BufferStorage;
52 struct ContextBase;
55 namespace {
57 constexpr float AmpEnvelopeMin{0.5f};
58 constexpr float AmpEnvelopeMax{2.0f};
60 constexpr float AttackTime{0.1f}; /* 100ms to rise from min to max */
61 constexpr float ReleaseTime{0.2f}; /* 200ms to drop from max to min */
64 struct CompressorState final : public EffectState {
65 /* Effect gains for each channel */
66 struct TargetGain {
67 uint mTarget{InvalidChannelIndex};
68 float mGain{1.0f};
70 std::array<TargetGain,MaxAmbiChannels> mChans;
72 /* Effect parameters */
73 bool mEnabled{true};
74 float mAttackMult{1.0f};
75 float mReleaseMult{1.0f};
76 float mEnvFollower{1.0f};
77 alignas(16) FloatBufferLine mGains{};
80 void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override;
81 void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
82 const EffectTarget target) override;
83 void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
84 const al::span<FloatBufferLine> samplesOut) override;
87 void CompressorState::deviceUpdate(const DeviceBase *device, const BufferStorage*)
89 /* Number of samples to do a full attack and release (non-integer sample
90 * counts are okay).
92 const float attackCount{static_cast<float>(device->Frequency) * AttackTime};
93 const float releaseCount{static_cast<float>(device->Frequency) * ReleaseTime};
95 /* Calculate per-sample multipliers to attack and release at the desired
96 * rates.
98 mAttackMult = std::pow(AmpEnvelopeMax/AmpEnvelopeMin, 1.0f/attackCount);
99 mReleaseMult = std::pow(AmpEnvelopeMin/AmpEnvelopeMax, 1.0f/releaseCount);
102 void CompressorState::update(const ContextBase*, const EffectSlot *slot,
103 const EffectProps *props, const EffectTarget target)
105 mEnabled = std::get<CompressorProps>(*props).OnOff;
107 mOutTarget = target.Main->Buffer;
108 auto set_channel = [this](size_t idx, uint outchan, float outgain)
110 mChans[idx].mTarget = outchan;
111 mChans[idx].mGain = outgain;
113 target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel);
116 void CompressorState::process(const size_t samplesToDo,
117 const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
119 /* Generate the per-sample gains from the signal envelope. */
120 float env{mEnvFollower};
121 if(mEnabled)
123 for(size_t i{0u};i < samplesToDo;++i)
125 /* Clamp the absolute amplitude to the defined envelope limits,
126 * then attack or release the envelope to reach it.
128 const float amplitude{std::clamp(std::fabs(samplesIn[0][i]), AmpEnvelopeMin,
129 AmpEnvelopeMax)};
130 if(amplitude > env)
131 env = std::min(env*mAttackMult, amplitude);
132 else if(amplitude < env)
133 env = std::max(env*mReleaseMult, amplitude);
135 /* Apply the reciprocal of the envelope to normalize the volume
136 * (compress the dynamic range).
138 mGains[i] = 1.0f / env;
141 else
143 /* Same as above, except the amplitude is forced to 1. This helps
144 * ensure smooth gain changes when the compressor is turned on and off.
146 for(size_t i{0u};i < samplesToDo;++i)
148 const float amplitude{1.0f};
149 if(amplitude > env)
150 env = std::min(env*mAttackMult, amplitude);
151 else if(amplitude < env)
152 env = std::max(env*mReleaseMult, amplitude);
154 mGains[i] = 1.0f / env;
157 mEnvFollower = env;
159 /* Now compress the signal amplitude to output. */
160 auto chan = mChans.cbegin();
161 for(const auto &input : samplesIn)
163 const size_t outidx{chan->mTarget};
164 if(outidx != InvalidChannelIndex)
166 const auto dst = al::span{samplesOut[outidx]};
167 const float gain{chan->mGain};
168 if(!(std::fabs(gain) > GainSilenceThreshold))
170 for(size_t i{0u};i < samplesToDo;++i)
171 dst[i] += input[i] * mGains[i] * gain;
174 ++chan;
179 struct CompressorStateFactory final : public EffectStateFactory {
180 al::intrusive_ptr<EffectState> create() override
181 { return al::intrusive_ptr<EffectState>{new CompressorState{}}; }
184 } // namespace
186 EffectStateFactory *CompressorStateFactory_getFactory()
188 static CompressorStateFactory CompressorFactory{};
189 return &CompressorFactory;