52#include <shared_mutex>
69 : active_(
std::move(initial)) {}
82 std::shared_lock<std::shared_mutex> lock(mutex_, std::try_to_lock);
83 if (!lock.owns_lock() || !active_) {
85 contention_blocks_.fetch_add(1, std::memory_order_relaxed);
88 active_->process(out, in, midi_in, midi_out, ctx);
89 mix_fade_out(out, in, ctx);
100 [[nodiscard]] std::unique_ptr<Processor>
swap(std::unique_ptr<Processor> next) {
101 std::unique_ptr<Processor> displaced;
102 std::unique_ptr<Processor> superseded;
104 std::unique_lock<std::shared_mutex> lock(mutex_);
114 if (active_ && next) {
120 const std::vector<std::byte> blob = active_->serialize_dsp_state();
121 if (!blob.empty()) next->restore_dsp_state(blob);
126 if (!crossfade_ready()) {
127 displaced = std::move(active_);
128 active_ = std::move(next);
151 superseded = std::move(fading_out_);
152 fading_out_ = std::move(active_);
153 active_ = std::move(next);
154 fade_mixer_.configure(fade_samples_, fade_mixer_.curve());
156 fade_done_.store(fading_out_ ==
nullptr, std::memory_order_release);
167 std::unique_lock<std::shared_mutex> lock(mutex_);
168 if (fading_out_ && fade_done_.load(std::memory_order_acquire)) {
169 auto old = std::move(fading_out_);
182 std::unique_lock<std::shared_mutex> lock(mutex_);
183 if (active_) active_->prepare(ctx);
190 std::unique_lock<std::shared_mutex> lock(mutex_);
191 fade_samples_ = samples;
199 std::unique_lock<std::shared_mutex> lock(mutex_);
200 fade_mixer_.set_curve(curve);
208 std::unique_lock<std::shared_mutex> lock(mutex_);
209 scratch_frames_ = max_frames;
210 scratch_channels_ = max_channels;
211 scratch_storage_.assign(max_frames * max_channels, 0.0f);
212 scratch_ptrs_.resize(max_channels);
213 for (std::size_t c = 0; c < max_channels; ++c)
214 scratch_ptrs_[c] = scratch_storage_.data() + c * max_frames;
219 fade_midi_in_.reserve(64, 0);
220 fade_midi_out_.reserve(64, 0);
221 fade_midi_in_.set_realtime_capacity_limit(
true);
222 fade_midi_out_.set_realtime_capacity_limit(
true);
227 std::shared_lock<std::shared_mutex> lock(mutex_);
228 return active_ !=
nullptr;
254 auto with_active(Fn&& fn)
const ->
decltype(fn(std::declval<Processor&>())) {
255 std::shared_lock<std::shared_mutex> lock(mutex_);
256 if (active_)
return fn(*active_);
257 return decltype(fn(std::declval<Processor&>())){};
262 std::shared_lock<std::shared_mutex> lock(mutex_);
263 return fading_out_ !=
nullptr && !fade_done_.load(std::memory_order_acquire);
269 return contention_blocks_.load(std::memory_order_relaxed);
273 bool crossfade_ready()
const {
274 return fade_samples_ > 0 && scratch_frames_ > 0 && scratch_channels_ > 0;
283 if (!fading_out_ || fade_done_.load(std::memory_order_relaxed))
return;
285 if (fade_mixer_.
length() == 0 || frames > scratch_frames_) {
291 fade_done_.store(
true, std::memory_order_release);
294 const std::size_t ch =
296 audio::BufferView<float> sv(scratch_ptrs_.data(), ch, frames);
297 fade_midi_in_.
clear();
298 fade_midi_out_.
clear();
299 fading_out_->process(sv, in, fade_midi_in_, fade_midi_out_, ctx);
305 fade_done_.store(
true, std::memory_order_release);
308 static void passthrough(audio::BufferView<float>& out,
309 const audio::BufferView<const float>& in) {
315 mutable std::shared_mutex mutex_;
316 std::unique_ptr<Processor> active_;
317 std::atomic<std::uint64_t> contention_blocks_{0};
320 std::unique_ptr<Processor> fading_out_;
321 std::size_t fade_samples_ = 0;
323 std::atomic<bool> fade_done_{
true};
324 std::vector<float> scratch_storage_;
325 std::vector<float*> scratch_ptrs_;
326 std::size_t scratch_frames_ = 0;
327 std::size_t scratch_channels_ = 0;
328 midi::MidiBuffer fade_midi_in_;
329 midi::MidiBuffer fade_midi_out_;
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
std::size_t num_samples() const
Definition buffer.hpp:84
std::size_t num_channels() const
Definition buffer.hpp:83
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
void clear()
Remove all events.
Definition buffer.hpp:118
std::size_t length() const
Definition transition_mixer.hpp:54
TransitionMixerT< float > TransitionMixer
Definition transition_mixer.hpp:83
TransitionCurve
Definition transition_mixer.hpp:34
bool blend_fade_out(Mixer &mixer, OutView &out, const OldView &old_render) noexcept
Blend an already-rendered fading-out buffer into the live output under a TransitionMixer's old->new g...
Definition transition_mixer.hpp:97
Proving the audio thread is outside process() while plugin state is restored under it.
Definition prepare_resources.hpp:102
Process context — passed every audio callback with transport state.
Definition process_context.hpp:89
Shared click-free crossfade primitive.