Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
sample_stream_scheduler.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <algorithm>
6#include <cmath>
7#include <cstddef>
8#include <cstdint>
9#include <iterator>
10#include <optional>
11#include <vector>
12
13namespace pulp::audio {
14
15enum class SampleStreamDemandClass : std::uint8_t {
19};
20
22 std::uint64_t source_id = 0;
23 std::uint64_t source_generation = 0;
24 std::uint64_t requester_id = 0;
25 std::uint64_t requester_generation = 0;
26 std::uint64_t page_index = 0;
27 std::uint64_t start_frame = 0;
28 std::uint64_t frame_count = 0;
29 std::uint64_t resident_source_frames = 0;
32 std::uint64_t sequence = 0;
33};
34
41
43 std::size_t pending = 0;
44 std::size_t capacity = 0;
45 std::uint64_t inserted = 0;
46 std::uint64_t refreshed = 0;
47 std::uint64_t rejected_full = 0;
48 std::uint64_t rejected_invalid = 0;
49 std::uint64_t displaced_less_urgent = 0;
50 std::uint64_t coalesced = 0;
51 std::uint64_t cancelled = 0;
52};
53
54template<std::size_t Capacity>
56public:
57 bool try_push(const SampleStreamPageRequest& request) noexcept {
58 return queue_.try_push(request);
59 }
60
61 std::optional<SampleStreamPageRequest> try_pop() noexcept {
62 return queue_.try_pop();
63 }
64
66 return queue_.telemetry();
67 }
68
69 static constexpr std::size_t capacity() noexcept { return Capacity; }
70
71private:
73};
74
80public:
81 bool prepare(std::size_t capacity) {
82 reset();
83 if (capacity == 0) return false;
84 try {
85 pending_.reserve(capacity);
86 } catch (...) {
87 return false;
88 }
89 capacity_ = capacity;
90 return true;
91 }
92
93 void reset() noexcept {
94 pending_.clear();
95 capacity_ = 0;
96 next_sequence_ = 1;
97 stats_ = {};
98 }
99
100 bool prepared() const noexcept { return capacity_ != 0; }
101
103 SampleStreamPageRequest request) noexcept {
104 if (!prepared() || !valid(request)) {
105 ++stats_.rejected_invalid;
107 }
108
109 auto existing = std::find_if(pending_.begin(), pending_.end(),
110 [&request](const SampleStreamPageRequest& candidate) noexcept {
111 return same_demand(candidate, request);
112 });
113 if (existing != pending_.end()) {
114 const auto stable_sequence = existing->sequence;
115 *existing = request;
116 existing->sequence = stable_sequence;
117 ++stats_.refreshed;
119 }
120
121 if (pending_.size() >= capacity_) {
122 auto least = pending_.begin();
123 for (auto candidate = std::next(least); candidate != pending_.end(); ++candidate) {
124 if (more_urgent(*least, *candidate)) least = candidate;
125 }
126 if (!strictly_more_urgent(request, *least)) {
127 ++stats_.rejected_full;
129 }
130 pending_.erase(least);
131 ++stats_.displaced_less_urgent;
132 }
133 if (next_sequence_ == 0) renumber_sequences();
134 request.sequence = next_sequence_++;
135 pending_.push_back(request);
136 ++stats_.inserted;
138 }
139
140 template<std::size_t Capacity>
141 std::size_t drain(SampleStreamRequestInbox<Capacity>& inbox) noexcept {
142 std::size_t drained = 0;
143 while (auto request = inbox.try_pop()) {
144 submit_or_refresh(*request);
145 ++drained;
146 }
147 return drained;
148 }
149
150 std::optional<SampleStreamPageRequest> most_urgent() const noexcept {
151 return most_urgent_if([](const SampleStreamPageRequest&) noexcept {
152 return true;
153 });
154 }
155
156 template<typename Predicate>
157 std::optional<SampleStreamPageRequest> most_urgent_if(
158 Predicate&& predicate) const noexcept {
159 if (pending_.empty()) return std::nullopt;
160 auto best = pending_.end();
161 for (auto candidate = pending_.begin(); candidate != pending_.end(); ++candidate) {
162 if (!predicate(*candidate)) continue;
163 if (best == pending_.end() || more_urgent(*candidate, *best)) best = candidate;
164 }
165 if (best == pending_.end()) return std::nullopt;
166 return *best;
167 }
168
169 bool has_page_interest(const SampleStreamPageRequest& request) const noexcept {
170 return std::any_of(pending_.begin(), pending_.end(),
171 [&request](const SampleStreamPageRequest& candidate) noexcept {
172 return same_page(candidate, request);
173 });
174 }
175
176 std::size_t complete_page(const SampleStreamPageRequest& request) noexcept {
177 const auto old_size = pending_.size();
178 std::erase_if(pending_, [&request](const SampleStreamPageRequest& candidate) noexcept {
179 return same_page(candidate, request);
180 });
181 const auto removed = old_size - pending_.size();
182 if (removed > 1) stats_.coalesced += removed - 1;
183 return removed;
184 }
185
186 std::optional<SampleStreamPageRequest> pop_most_urgent() noexcept {
187 auto request = most_urgent();
188 if (!request) return std::nullopt;
189 complete_page(*request);
190 return request;
191 }
192
193 std::size_t cancel_source_generation(std::uint64_t source_id,
194 std::uint64_t source_generation) noexcept {
195 const auto old_size = pending_.size();
196 std::erase_if(pending_, [source_id, source_generation](
197 const SampleStreamPageRequest& request) noexcept {
198 return request.source_id == source_id &&
199 request.source_generation == source_generation;
200 });
201 const auto removed = old_size - pending_.size();
202 stats_.cancelled += removed;
203 return removed;
204 }
205
206 std::size_t cancel_requester(std::uint64_t requester_id,
207 std::uint64_t requester_generation) noexcept {
208 const auto old_size = pending_.size();
209 std::erase_if(pending_, [requester_id, requester_generation](
210 const SampleStreamPageRequest& request) noexcept {
211 return request.requester_id == requester_id &&
212 request.requester_generation == requester_generation;
213 });
214 const auto removed = old_size - pending_.size();
215 stats_.cancelled += removed;
216 return removed;
217 }
218
220 auto result = stats_;
221 result.pending = pending_.size();
222 result.capacity = capacity_;
223 return result;
224 }
225
226private:
227 static bool valid(const SampleStreamPageRequest& request) noexcept {
228 return request.source_id != 0 && request.source_generation != 0 &&
229 request.requester_id != 0 && request.requester_generation != 0 &&
230 request.frame_count != 0 &&
231 request.consumption_frames_per_second > 0.0 &&
232 std::isfinite(request.consumption_frames_per_second);
233 }
234
235 static bool same_page(const SampleStreamPageRequest& left,
236 const SampleStreamPageRequest& right) noexcept {
237 return left.source_id == right.source_id &&
238 left.source_generation == right.source_generation &&
239 left.page_index == right.page_index;
240 }
241
242 static bool same_demand(const SampleStreamPageRequest& left,
243 const SampleStreamPageRequest& right) noexcept {
244 return same_page(left, right) &&
245 left.requester_id == right.requester_id &&
246 left.requester_generation == right.requester_generation;
247 }
248
249 static bool more_urgent(const SampleStreamPageRequest& left,
250 const SampleStreamPageRequest& right) noexcept {
251 const long double left_scaled =
252 static_cast<long double>(left.resident_source_frames) *
253 static_cast<long double>(right.consumption_frames_per_second);
254 const long double right_scaled =
255 static_cast<long double>(right.resident_source_frames) *
256 static_cast<long double>(left.consumption_frames_per_second);
257 if (left_scaled != right_scaled) return left_scaled < right_scaled;
258 if (left.demand_class != right.demand_class)
259 return left.demand_class < right.demand_class;
260 return left.sequence < right.sequence;
261 }
262
263 static bool strictly_more_urgent(const SampleStreamPageRequest& left,
264 const SampleStreamPageRequest& right) noexcept {
265 const long double left_scaled =
266 static_cast<long double>(left.resident_source_frames) *
267 static_cast<long double>(right.consumption_frames_per_second);
268 const long double right_scaled =
269 static_cast<long double>(right.resident_source_frames) *
270 static_cast<long double>(left.consumption_frames_per_second);
271 if (left_scaled != right_scaled) return left_scaled < right_scaled;
272 return left.demand_class < right.demand_class;
273 }
274
275 void renumber_sequences() noexcept {
276 std::sort(pending_.begin(), pending_.end(),
277 [](const auto& left, const auto& right) noexcept {
278 return left.sequence < right.sequence;
279 });
280 std::uint64_t sequence = 1;
281 for (auto& request : pending_) request.sequence = sequence++;
282 next_sequence_ = sequence;
283 }
284
285 std::vector<SampleStreamPageRequest> pending_;
286 std::size_t capacity_ = 0;
287 std::uint64_t next_sequence_ = 1;
288 SampleStreamSchedulerStats stats_{};
289};
290
291} // namespace pulp::audio
Definition sample_stream_scheduler.hpp:55
runtime::SpscQueueTelemetry telemetry() const noexcept
Definition sample_stream_scheduler.hpp:65
std::optional< SampleStreamPageRequest > try_pop() noexcept
Definition sample_stream_scheduler.hpp:61
bool try_push(const SampleStreamPageRequest &request) noexcept
Definition sample_stream_scheduler.hpp:57
static constexpr std::size_t capacity() noexcept
Definition sample_stream_scheduler.hpp:69
Single-owner background scheduler for shared sample-cache page fills.
Definition sample_stream_scheduler.hpp:79
SampleStreamSchedulerStats stats() const noexcept
Definition sample_stream_scheduler.hpp:219
SampleStreamScheduleStatus submit_or_refresh(SampleStreamPageRequest request) noexcept
Definition sample_stream_scheduler.hpp:102
std::optional< SampleStreamPageRequest > most_urgent() const noexcept
Definition sample_stream_scheduler.hpp:150
std::optional< SampleStreamPageRequest > most_urgent_if(Predicate &&predicate) const noexcept
Definition sample_stream_scheduler.hpp:157
void reset() noexcept
Definition sample_stream_scheduler.hpp:93
bool prepared() const noexcept
Definition sample_stream_scheduler.hpp:100
bool prepare(std::size_t capacity)
Definition sample_stream_scheduler.hpp:81
std::size_t complete_page(const SampleStreamPageRequest &request) noexcept
Definition sample_stream_scheduler.hpp:176
std::size_t drain(SampleStreamRequestInbox< Capacity > &inbox) noexcept
Definition sample_stream_scheduler.hpp:141
std::size_t cancel_requester(std::uint64_t requester_id, std::uint64_t requester_generation) noexcept
Definition sample_stream_scheduler.hpp:206
bool has_page_interest(const SampleStreamPageRequest &request) const noexcept
Definition sample_stream_scheduler.hpp:169
std::size_t cancel_source_generation(std::uint64_t source_id, std::uint64_t source_generation) noexcept
Definition sample_stream_scheduler.hpp:193
std::optional< SampleStreamPageRequest > pop_most_urgent() noexcept
Definition sample_stream_scheduler.hpp:186
Definition spsc_queue.hpp:23
Definition analyzer_provider.hpp:12
SampleStreamDemandClass
Definition sample_stream_scheduler.hpp:15
@ Sustain
Definition sample_stream_scheduler.hpp:17
SampleStreamScheduleStatus
Definition sample_stream_scheduler.hpp:35
@ Inserted
Definition sample_stream_scheduler.hpp:36
@ Refreshed
Definition sample_stream_scheduler.hpp:37
@ Invalid
Definition sample_stream_scheduler.hpp:39
@ Full
Definition sample_stream_scheduler.hpp:38
@ Release
Definition instrument_envelope.hpp:14
@ Sustain
Definition instrument_envelope.hpp:13
@ Attack
Definition instrument_envelope.hpp:10
@ Invalid
Definition sample_mip_sidecar.hpp:13
@ right
Definition canvas.hpp:221
@ left
Definition canvas.hpp:221
void request(Permission p, RequestCallback cb)
Async request.
Definition sample_stream_scheduler.hpp:21
SampleStreamDemandClass demand_class
Definition sample_stream_scheduler.hpp:31
std::uint64_t page_index
Definition sample_stream_scheduler.hpp:26
std::uint64_t requester_id
Definition sample_stream_scheduler.hpp:24
std::uint64_t source_id
Definition sample_stream_scheduler.hpp:22
std::uint64_t requester_generation
Definition sample_stream_scheduler.hpp:25
std::uint64_t start_frame
Definition sample_stream_scheduler.hpp:27
std::uint64_t resident_source_frames
Definition sample_stream_scheduler.hpp:29
std::uint64_t sequence
Definition sample_stream_scheduler.hpp:32
std::uint64_t frame_count
Definition sample_stream_scheduler.hpp:28
std::uint64_t source_generation
Definition sample_stream_scheduler.hpp:23
double consumption_frames_per_second
Definition sample_stream_scheduler.hpp:30
Definition sample_stream_scheduler.hpp:42
std::uint64_t coalesced
Definition sample_stream_scheduler.hpp:50
std::size_t pending
Definition sample_stream_scheduler.hpp:43
std::uint64_t refreshed
Definition sample_stream_scheduler.hpp:46
std::uint64_t rejected_invalid
Definition sample_stream_scheduler.hpp:48
std::uint64_t inserted
Definition sample_stream_scheduler.hpp:45
std::uint64_t cancelled
Definition sample_stream_scheduler.hpp:51
std::uint64_t rejected_full
Definition sample_stream_scheduler.hpp:47
std::size_t capacity
Definition sample_stream_scheduler.hpp:44
std::uint64_t displaced_less_urgent
Definition sample_stream_scheduler.hpp:49
Definition spsc_queue.hpp:12