Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
processing_switch_crossfade.hpp
Go to the documentation of this file.
1#pragma once
2
34
35#include "transition_mixer.hpp"
36
37#include <algorithm>
38#include <cmath>
39#include <cstddef>
40#include <cstdint>
41
42namespace pulp::signal {
43
47 std::int64_t warm_samples = 0;
50 std::int64_t fade_samples = 1;
51
52 [[nodiscard]] constexpr std::int64_t total_samples() const noexcept {
54 }
55};
56
61[[nodiscard]] inline ProcessingSwitchPlan plan_processing_switch(std::int64_t incoming_latency,
62 std::int64_t incoming_history,
63 double sample_rate,
64 double fade_seconds) noexcept {
66 plan.warm_samples =
67 std::max<std::int64_t>(0, incoming_latency) + std::max<std::int64_t>(0, incoming_history);
68 const double rate = (std::isfinite(sample_rate) && sample_rate > 0.0) ? sample_rate : 48000.0;
69 const double fade = (std::isfinite(fade_seconds) && fade_seconds > 0.0) ? fade_seconds : 0.0;
70 plan.fade_samples = std::max<std::int64_t>(1, std::llround(fade * rate));
71 return plan;
72}
73
76template <typename SampleType = float>
77inline void processing_switch_gains_at(const ProcessingSwitchPlan& plan, std::int64_t position,
78 SampleType& outgoing, SampleType& incoming) noexcept {
79 const std::int64_t into_fade = position - plan.warm_samples;
80 if (into_fade < 0) {
81 outgoing = SampleType{1};
82 incoming = SampleType{0};
83 return;
84 }
85 if (into_fade >= plan.fade_samples) {
86 outgoing = SampleType{0};
87 incoming = SampleType{1};
88 return;
89 }
91 mixer.configure(static_cast<std::size_t>(plan.fade_samples), TransitionCurve::EqualPower);
92 mixer.gains_at(static_cast<std::size_t>(into_fade), outgoing, incoming);
93}
94
102template <typename SampleType = float> class ProcessingSwitchCrossfadeT {
103 public:
104 void begin(const ProcessingSwitchPlan& plan) noexcept {
105 plan_ = plan;
106 if (plan_.fade_samples < 1)
107 plan_.fade_samples = 1;
108 mixer_.configure(static_cast<std::size_t>(plan_.fade_samples), TransitionCurve::EqualPower);
109 position_ = 0;
110 active_ = true;
111 }
112 void cancel() noexcept {
113 active_ = false;
114 position_ = 0;
115 }
116
117 [[nodiscard]] bool active() const noexcept {
118 return active_;
119 }
120 [[nodiscard]] bool finished() const noexcept {
121 return active_ && position_ >= plan_.total_samples();
122 }
123 [[nodiscard]] bool warming() const noexcept {
124 return active_ && position_ < plan_.warm_samples;
125 }
126 [[nodiscard]] std::int64_t position() const noexcept {
127 return position_;
128 }
129 [[nodiscard]] const ProcessingSwitchPlan& plan() const noexcept {
130 return plan_;
131 }
132
136 void mix(SampleType* const* output, const SampleType* const* incoming, int channels,
137 int num_samples) noexcept {
138 if (!active_ || num_samples <= 0)
139 return;
140 for (int i = 0; i < num_samples; ++i) {
141 const std::int64_t into_fade = position_ - plan_.warm_samples;
142 if (into_fade >= plan_.fade_samples) {
143 for (int ch = 0; ch < channels; ++ch)
144 output[ch][i] = incoming[ch][i];
145 } else if (into_fade >= 0) {
146 SampleType g_out{}, g_in{};
147 mixer_.gains_at(static_cast<std::size_t>(into_fade), g_out, g_in);
148 for (int ch = 0; ch < channels; ++ch)
149 output[ch][i] = g_out * output[ch][i] + g_in * incoming[ch][i];
150 }
151 ++position_;
152 }
153 }
154
155 private:
156 ProcessingSwitchPlan plan_{};
157 TransitionMixerT<SampleType> mixer_{};
158 std::int64_t position_ = 0;
159 bool active_ = false;
160};
161
164
165} // namespace pulp::signal
The audio-thread state of one switch.
Definition processing_switch_crossfade.hpp:102
bool finished() const noexcept
Definition processing_switch_crossfade.hpp:120
void begin(const ProcessingSwitchPlan &plan) noexcept
Definition processing_switch_crossfade.hpp:104
std::int64_t position() const noexcept
Definition processing_switch_crossfade.hpp:126
bool warming() const noexcept
Definition processing_switch_crossfade.hpp:123
void cancel() noexcept
Definition processing_switch_crossfade.hpp:112
bool active() const noexcept
Definition processing_switch_crossfade.hpp:117
void mix(SampleType *const *output, const SampleType *const *incoming, int channels, int num_samples) noexcept
Blend num_samples of the outgoing output (in output) with the incoming output, in place,...
Definition processing_switch_crossfade.hpp:136
const ProcessingSwitchPlan & plan() const noexcept
Definition processing_switch_crossfade.hpp:129
Definition transition_mixer.hpp:37
void gains_at(std::size_t fade_pos, SampleType &old_gain, SampleType &new_gain) const
Old/new blend gains at an ABSOLUTE fade position (pure — no state change), so a caller can compute pe...
Definition transition_mixer.hpp:61
void configure(std::size_t length_samples, TransitionCurve curve)
Configure the fade length (samples) + curve and rewind to the start.
Definition transition_mixer.hpp:43
Definition additive_bank.hpp:218
ProcessingSwitchCrossfadeT< float > ProcessingSwitchCrossfade
Definition processing_switch_crossfade.hpp:162
void processing_switch_gains_at(const ProcessingSwitchPlan &plan, std::int64_t position, SampleType &outgoing, SampleType &incoming) noexcept
Gains for the outgoing and incoming outputs at absolute position samples into a switch following plan...
Definition processing_switch_crossfade.hpp:77
ProcessingSwitchCrossfadeT< double > ProcessingSwitchCrossfade64
Definition processing_switch_crossfade.hpp:163
ProcessingSwitchPlan plan_processing_switch(std::int64_t incoming_latency, std::int64_t incoming_history, double sample_rate, double fade_seconds) noexcept
The plan for switching INTO a realisation with incoming_latency samples of delay that needs incoming_...
Definition processing_switch_crossfade.hpp:61
@ i
Definition formant_filter_bank.hpp:20
@ EqualPower
Definition transition_mixer.hpp:34
How long each phase of a switch lasts, in samples.
Definition processing_switch_crossfade.hpp:45
std::int64_t fade_samples
Samples the fade lasts.
Definition processing_switch_crossfade.hpp:50
std::int64_t warm_samples
Samples the incoming realisation processes unheard before the fade.
Definition processing_switch_crossfade.hpp:47
constexpr std::int64_t total_samples() const noexcept
Definition processing_switch_crossfade.hpp:52
Shared click-free crossfade primitive.