40template <
typename SampleType =
float>
49 mix_ = std::clamp(
mix, SampleType{0.0f}, SampleType{1.0f});
50 if (ramp_samples_ <= 0) {
53 mix_increment_ = SampleType{0.0f};
56 mix_increment_ = (mix_ - current_mix_) /
57 static_cast<SampleType
>(ramp_samples_);
58 ramp_remaining_ = ramp_samples_;
62 SampleType
mix()
const {
return mix_; }
85 const int new_ramp = std::max(0, samples);
86 if (new_ramp == ramp_samples_)
return;
87 ramp_samples_ = new_ramp;
88 if (ramp_samples_ <= 0) {
93 mix_increment_ = SampleType{0.0f};
96 if (ramp_remaining_ > 0) {
97 ramp_remaining_ = std::min(ramp_remaining_, ramp_samples_);
98 mix_increment_ = ramp_remaining_ > 0
99 ? (mix_ - current_mix_) /
100 static_cast<SampleType
>(ramp_remaining_)
108 if (!(seconds > SampleType{0.0f}) || !(sample_rate > SampleType{0.0f})) {
123 const int new_latency = std::max(0, samples);
124 if (new_latency == latency_)
127 latency_ = new_latency;
130 if (latency_ > 0 && max_channels_ > 0)
131 delay_buffer_.assign(
static_cast<size_t>(latency_ * max_channels_),
134 delay_buffer_.clear();
136 for (
auto& ch : dry_buffer_)
137 std::fill(ch.begin(), ch.end(), SampleType{0.0f});
151 void prepare(
int max_channels,
int max_block_size) {
153 max_channels_ = std::max(0, max_channels);
154 max_block_size_ = std::max(0, max_block_size);
156 if (latency_ > 0 && max_channels_ > 0)
157 delay_buffer_.assign(
static_cast<size_t>(latency_ * max_channels_),
160 delay_buffer_.clear();
162 dry_buffer_.resize(
static_cast<size_t>(max_channels_));
163 for (
auto& ch : dry_buffer_)
164 ch.assign(
static_cast<size_t>(max_block_size_), SampleType{0.0f});
165 active_dry_channels_ = 0;
166 active_dry_samples_ = 0;
170 void push_dry(
const SampleType*
const* channels,
int num_channels,
int num_samples) {
171 if (channels ==
nullptr || num_channels <= 0 || num_samples <= 0) {
172 active_dry_channels_ = 0;
173 active_dry_samples_ = 0;
177 ensure_dry_storage(num_channels, num_samples);
181 for (
int ch = 0; ch < num_channels; ++ch) {
182 std::memcpy(dry_buffer_[ch].data(), channels[ch],
183 static_cast<size_t>(num_samples) *
sizeof(SampleType));
185 active_dry_channels_ = num_channels;
186 active_dry_samples_ = num_samples;
193 const int count = std::min(num_channels, max_channels_);
195 for (
int i = 0;
i < num_samples; ++
i) {
196 for (
int ch = 0; ch < count; ++ch) {
197 size_t idx =
static_cast<size_t>(delay_pos_ * max_channels_ + ch);
198 dry_buffer_[ch][
i] = delay_buffer_[idx];
199 delay_buffer_[idx] = channels[ch][
i];
201 delay_pos_ = (delay_pos_ + 1) % latency_;
203 active_dry_channels_ = count;
204 active_dry_samples_ = num_samples;
208 void mix_wet(SampleType*
const* wet_channels,
int num_channels,
int num_samples) {
209 const int channel_count = std::min(num_channels, active_dry_channels_);
210 const int sample_count = std::min(num_samples, active_dry_samples_);
211 if (channel_count <= 0 || sample_count <= 0)
return;
213 if (ramp_remaining_ <= 0) {
215 SampleType dry_gain, wet_gain;
216 compute_gains(current_mix_, dry_gain, wet_gain);
217 for (
int ch = 0; ch < channel_count; ++ch) {
218 for (
int i = 0;
i < sample_count; ++
i) {
219 wet_channels[ch][
i] = dry_buffer_[ch][
i] * dry_gain +
220 wet_channels[ch][
i] * wet_gain;
228 for (
int i = 0;
i < sample_count; ++
i) {
229 if (ramp_remaining_ > 0) {
230 current_mix_ += mix_increment_;
231 if (--ramp_remaining_ == 0) current_mix_ = mix_;
233 SampleType dry_gain, wet_gain;
234 compute_gains(current_mix_, dry_gain, wet_gain);
235 for (
int ch = 0; ch < channel_count; ++ch) {
236 wet_channels[ch][
i] = dry_buffer_[ch][
i] * dry_gain +
237 wet_channels[ch][
i] * wet_gain;
243 std::fill(delay_buffer_.begin(), delay_buffer_.end(), SampleType{0.0f});
245 for (
auto& ch : dry_buffer_)
246 std::fill(ch.begin(), ch.end(), SampleType{0.0f});
251 SampleType mix_ = SampleType{1.0f};
252 SampleType current_mix_ = SampleType{1.0f};
253 SampleType mix_increment_ = SampleType{0.0f};
254 int ramp_samples_ = 0;
255 int ramp_remaining_ = 0;
258 int max_channels_ = 2;
259 int max_block_size_ = 0;
261 int active_dry_channels_ = 0;
262 int active_dry_samples_ = 0;
263 std::vector<SampleType> delay_buffer_;
264 std::vector<std::vector<SampleType>> dry_buffer_;
266 void ensure_dry_storage(
int num_channels,
int num_samples) {
267 if (num_channels <=
static_cast<int>(dry_buffer_.size())) {
268 for (
int ch = 0; ch < num_channels; ++ch) {
269 if (num_samples >
static_cast<int>(dry_buffer_[ch].
size()))
270 dry_buffer_[ch].resize(
static_cast<size_t>(num_samples));
275 const auto old_size = dry_buffer_.size();
276 dry_buffer_.resize(
static_cast<size_t>(num_channels));
277 for (std::size_t ch = old_size; ch < dry_buffer_.size(); ++ch)
278 dry_buffer_[ch].resize(
static_cast<size_t>(std::max(max_block_size_, num_samples)));
279 for (std::size_t ch = 0; ch < old_size; ++ch) {
280 if (num_samples >
static_cast<int>(dry_buffer_[ch].
size()))
281 dry_buffer_[ch].resize(
static_cast<size_t>(num_samples));
288 mix_increment_ = SampleType{0.0f};
291 void compute_gains(SampleType
ratio, SampleType& dry, SampleType& wet)
const {
292 constexpr SampleType kHalfPi = SampleType{1.57079632679489661923f};
293 const SampleType theta =
ratio * kHalfPi;
296 dry = SampleType{1.0f} -
ratio;
301 dry = std::cos(theta);
302 wet = std::sin(theta);
305 dry =
ratio <= SampleType{0.5f}
307 : (SampleType{1.0f} -
ratio) * SampleType{2.0f};
308 wet =
ratio >= SampleType{0.5f}
310 :
ratio * SampleType{2.0f};
314 const SampleType
c = std::max(SampleType{0.0f}, std::cos(theta));
315 const SampleType
s = std::max(SampleType{0.0f}, std::sin(theta));
316 dry = std::pow(
c, SampleType{1.5f});
317 wet = std::pow(s, SampleType{1.5f});
321 dry = std::cos(theta) * std::cos(theta);
322 wet = std::sin(theta) * std::sin(theta);
325 dry = std::sqrt(SampleType{1.0f} -
ratio);
326 wet = std::sqrt(
ratio);
333 dry = std::pow(SampleType{1.0f} -
ratio, SampleType{0.75f});
334 wet = std::pow(
ratio, SampleType{0.75f});
Definition dry_wet_mixer.hpp:41
bool is_ramping() const
True while the ratio is still travelling toward its target.
Definition dry_wet_mixer.hpp:116
void push_dry(const SampleType *const *channels, int num_channels, int num_samples)
Push dry samples before processing (call before your wet processing).
Definition dry_wet_mixer.hpp:170
void reset()
Definition dry_wet_mixer.hpp:242
void set_ramp_time(SampleType seconds, SampleType sample_rate)
Convenience form of set_ramp_samples() in seconds.
Definition dry_wet_mixer.hpp:107
int ramp_samples() const
Definition dry_wet_mixer.hpp:104
void prepare(int max_channels, int max_block_size)
Prepare for processing.
Definition dry_wet_mixer.hpp:151
void set_wet_latency(int samples)
Set latency in samples for the wet path (compensates dry path delay).
Definition dry_wet_mixer.hpp:122
void set_mix(SampleType mix)
Set the mix ratio (0.0 = fully dry, 1.0 = fully wet).
Definition dry_wet_mixer.hpp:48
SampleType current_mix() const
The ratio the next sample will actually be mixed at.
Definition dry_wet_mixer.hpp:66
void set_curve(MixCurve curve)
Set the mixing curve type.
Definition dry_wet_mixer.hpp:119
float mix() const
Definition dry_wet_mixer.hpp:62
void set_ramp_samples(int samples)
Length of the crossfade applied when set_mix() changes the ratio.
Definition dry_wet_mixer.hpp:84
void mix_wet(SampleType *const *wet_channels, int num_channels, int num_samples)
Mix dry and wet signals, writing result to wet_channels (in-place).
Definition dry_wet_mixer.hpp:208
@ size
Definition config.hpp:216
Definition additive_bank.hpp:218
DryWetMixerT< float > DryWetMixer
Definition dry_wet_mixer.hpp:340
@ c
Definition spectrum_trace.hpp:35
@ ratio
base_frequency_hz * ratio.
Definition fm_operator_engine.hpp:20
@ EqualPower
(cos(u·π/2), sin(u·π/2)) — power sum == 1.
Definition crossfade.hpp:43
@ i
Definition formant_filter_bank.hpp:20
MixCurve
Crossfade curve between dry (mix=0) and wet (mix=1).
Definition dry_wet_mixer.hpp:29
@ Sqrt3dB
Definition dry_wet_mixer.hpp:36
@ Sqrt4_5dB
Definition dry_wet_mixer.hpp:37
@ Linear
Definition dry_wet_mixer.hpp:30
@ EqualPower
Definition dry_wet_mixer.hpp:31
@ Sin4_5dB
Definition dry_wet_mixer.hpp:34
@ Sin6dB
Definition dry_wet_mixer.hpp:35
@ Sin3dB
Definition dry_wet_mixer.hpp:33
@ Balanced
Definition dry_wet_mixer.hpp:32
DryWetMixerT< double > DryWetMixer64
Definition dry_wet_mixer.hpp:341
@ s
Definition input_events.hpp:41