Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
parameter_event_queue.hpp
Go to the documentation of this file.
1#pragma once
2
3// Sample-accurate parameter-event queue used for per-block automation.
4//
5// Ordering contract: events are sorted by sample_offset ascending before
6// being handed to consumers. Callers that append events unordered must call
7// sort() before passing the queue on. sort() is stable: equal sample_offsets
8// keep their push order. Input already in time order is detected in one pass.
9// Anything else -- notably VST3, which delivers one time-ordered queue per
10// parameter, so a block's events arrive as one sorted run per parameter --
11// goes through stable_sort_in_place (stable_sort.hpp), O(n log^2 n) and
12// allocation-free. An insertion sort over those
13// interleaved runs was quadratic: 32 parameters with 16 points each cost
14// about 28 us per block. An adapter that knows its block size passes scratch
15// to the two-argument sort(), a stable counting sort on the offset.
16//
17// Realtime contract: fixed-capacity storage only. push(), clear(), sort(),
18// iteration, and events() do not allocate; overflow is reported through
19// overflowed() / dropped_event_count() and the extra event is dropped.
20
23#include <algorithm>
24#include <array>
25#include <atomic>
26#include <cstddef>
27#include <cstdint>
28#include <limits>
29#include <span>
30#include <utility>
31
32namespace pulp::state {
33
36 int32_t sample_offset = 0; // 0..num_samples-1 within the current block
37 float value = 0.0f; // plain parameter domain
39};
40
46 int32_t sample_offset = 0;
47 float amount = 0.0f;
48};
49
51public:
52 static constexpr std::size_t kCapacity = 1024;
53 bool push(const ModulationEvent& e) {
54 if (size_ >= events_.size()) { ++dropped_events_; return false; }
55 events_[size_++] = e; return true;
56 }
57 void clear() { size_ = 0; dropped_events_ = 0; }
58 std::size_t size() const { return size_; }
59 bool overflowed() const { return dropped_events_ != 0; }
60 std::span<const ModulationEvent> events() const { return {events_.data(), size_}; }
61 void sort() {
62 for (std::size_t i = 1; i < size_; ++i) {
63 auto current = events_[i]; auto j = i;
64 while (j > 0 && events_[j - 1].sample_offset > current.sample_offset) {
65 events_[j] = events_[j - 1]; --j;
66 }
67 events_[j] = current;
68 }
69 }
70private:
71 std::array<ModulationEvent, kCapacity> events_{};
72 std::size_t size_ = 0;
73 std::uint32_t dropped_events_ = 0;
74};
75
77 std::size_t size = 0;
78 std::size_t capacity = 0;
79 std::uint64_t overflow_count = 0;
80};
81
83public:
84 static constexpr std::size_t kCapacity = 1024;
85
87
88 bool push(const ParameterEvent& e) {
89 if (size_ >= events_.size()) {
90 record_drop();
91 return false;
92 }
93 events_[size_++] = e;
94 return true;
95 }
96
97 void clear() {
98 size_ = 0;
99 dropped_events_ = 0;
100 }
101 bool empty() const { return size_ == 0; }
102 std::size_t size() const { return size_; }
103 constexpr std::size_t capacity() const { return kCapacity; }
104 bool overflowed() const { return dropped_events_ != 0; }
105 std::uint32_t dropped_event_count() const { return dropped_events_; }
106 std::uint64_t overflow_count() const {
107 return overflow_count_.load(std::memory_order_relaxed);
108 }
110 overflow_count_.store(0, std::memory_order_relaxed);
111 }
112
114 return {
115 .size = size_,
116 .capacity = kCapacity,
117 .overflow_count = overflow_count(),
118 };
119 }
120
121 void sort() {
122 stable_sort_in_place(events_.begin(), events_.begin() + static_cast<std::ptrdiff_t>(size_),
123 [](const ParameterEvent& a, const ParameterEvent& b) {
124 return a.sample_offset < b.sample_offset;
125 });
126 }
127
132 void sort(std::span<ParameterEvent> scratch, std::span<std::uint32_t> offset_counts) {
133 const auto first = events_.begin();
134 const auto last = events_.begin() + static_cast<std::ptrdiff_t>(size_);
135 if (std::is_sorted(first, last, [](const ParameterEvent& a, const ParameterEvent& b) {
136 return a.sample_offset < b.sample_offset;
137 }))
138 return;
140 first, last, [](const ParameterEvent& e) { return e.sample_offset; }, scratch,
141 offset_counts))
142 sort();
143 }
144
145 using iterator = std::array<ParameterEvent, kCapacity>::iterator;
146 using const_iterator = std::array<ParameterEvent, kCapacity>::const_iterator;
147
148 iterator begin() { return events_.begin(); }
149 iterator end() { return events_.begin() + static_cast<std::ptrdiff_t>(size_); }
150 const_iterator begin() const { return events_.begin(); }
151 const_iterator end() const { return events_.begin() + static_cast<std::ptrdiff_t>(size_); }
152
153 std::span<const ParameterEvent> events() const {
154 return {events_.data(), size_};
155 }
156
157private:
158 void record_drop() {
159 if (dropped_events_ < std::numeric_limits<std::uint32_t>::max()) {
160 ++dropped_events_;
161 }
162 overflow_count_.fetch_add(1, std::memory_order_relaxed);
163 }
164
165 std::array<ParameterEvent, kCapacity> events_{};
166 std::size_t size_ = 0;
167 std::uint32_t dropped_events_ = 0;
168 std::atomic<std::uint64_t> overflow_count_{0};
169};
170
171} // namespace pulp::state
Definition parameter_event_queue.hpp:50
std::size_t size() const
Definition parameter_event_queue.hpp:58
void sort()
Definition parameter_event_queue.hpp:61
static constexpr std::size_t kCapacity
Definition parameter_event_queue.hpp:52
void clear()
Definition parameter_event_queue.hpp:57
bool push(const ModulationEvent &e)
Definition parameter_event_queue.hpp:53
std::span< const ModulationEvent > events() const
Definition parameter_event_queue.hpp:60
bool overflowed() const
Definition parameter_event_queue.hpp:59
ParameterEventQueueTelemetry telemetry() const
Definition parameter_event_queue.hpp:113
void sort()
Definition parameter_event_queue.hpp:121
iterator end()
Definition parameter_event_queue.hpp:149
const_iterator begin() const
Definition parameter_event_queue.hpp:150
static constexpr std::size_t kCapacity
Definition parameter_event_queue.hpp:84
std::uint64_t overflow_count() const
Definition parameter_event_queue.hpp:106
constexpr std::size_t capacity() const
Definition parameter_event_queue.hpp:103
void reset_overflow_count()
Definition parameter_event_queue.hpp:109
bool empty() const
Definition parameter_event_queue.hpp:101
std::array< ParameterEvent, kCapacity >::iterator iterator
Definition parameter_event_queue.hpp:145
std::uint32_t dropped_event_count() const
Definition parameter_event_queue.hpp:105
void clear()
Definition parameter_event_queue.hpp:97
bool overflowed() const
Definition parameter_event_queue.hpp:104
std::span< const ParameterEvent > events() const
Definition parameter_event_queue.hpp:153
iterator begin()
Definition parameter_event_queue.hpp:148
std::size_t size() const
Definition parameter_event_queue.hpp:102
bool push(const ParameterEvent &e)
Definition parameter_event_queue.hpp:88
const_iterator end() const
Definition parameter_event_queue.hpp:151
std::array< ParameterEvent, kCapacity >::const_iterator const_iterator
Definition parameter_event_queue.hpp:146
void sort(std::span< ParameterEvent > scratch, std::span< std::uint32_t > offset_counts)
sort() for a caller that can lend scratch storage, typically sized once at prepare time: a stable cou...
Definition parameter_event_queue.hpp:132
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48
bool stable_counting_sort(It first, It last, Key key, std::span< Value > scratch, std::span< Count > counts)
Stable counting sort of [first, last) on a small non-negative integer key, O(n + counts....
Definition stable_sort.hpp:103
void stable_sort_in_place(It first, It last, Less less)
Sorts [first, last) by less, keeping equal elements in their original order, without allocating.
Definition stable_sort.hpp:85
uint32_t ParamID
Unique parameter identifier, stable across plugin versions.
Definition parameter.hpp:55
Timestamped host modulation event.
Definition parameter_event_queue.hpp:44
ParamID param_id
Definition parameter_event_queue.hpp:45
float amount
Definition parameter_event_queue.hpp:47
int32_t sample_offset
Definition parameter_event_queue.hpp:46
Definition parameter_event_queue.hpp:76
std::size_t size
Definition parameter_event_queue.hpp:77
std::uint64_t overflow_count
Definition parameter_event_queue.hpp:79
std::size_t capacity
Definition parameter_event_queue.hpp:78
Definition parameter_event_queue.hpp:34
float value
Definition parameter_event_queue.hpp:37
int32_t ramp_duration_sample_frames
Definition parameter_event_queue.hpp:38
ParamID param_id
Definition parameter_event_queue.hpp:35
int32_t sample_offset
Definition parameter_event_queue.hpp:36