Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
graph_runtime_queue.hpp
Go to the documentation of this file.
1#pragma once
2
6
7#include <algorithm>
8#include <array>
9#include <atomic>
10#include <cstddef>
11#include <cstdint>
12#include <span>
13#include <type_traits>
14
15namespace pulp::graph {
16
27
28enum class GraphCommandTimingType : std::uint8_t {
31};
32
37
49
52 std::uint32_t block_offset = 0;
53 std::uint32_t order = 0;
54};
55
67
68struct GraphEvent {
69 std::uint64_t sequence_id = 0;
72 std::uint32_t target_id = 0;
73 std::uint32_t block_offset = 0;
74 float value = 0.0f;
75 bool bool_value = false;
76};
77
79 std::uint64_t sequence_id = 0;
82 std::uint32_t block_offset = 0;
84};
85
87 std::uint64_t commands_enqueued = 0;
88 std::uint64_t commands_drained = 0;
89 std::uint64_t events_enqueued = 0;
90 std::uint64_t midi_outputs_enqueued = 0;
91 std::uint64_t dropped_commands = 0;
92 std::uint64_t dropped_events = 0;
93 std::uint64_t dropped_midi_outputs = 0;
94};
95
108template<std::size_t CommandCapacity = 128,
109 std::size_t EventCapacity = 256,
110 std::size_t MidiOutputCapacity = 256>
112 static_assert(CommandCapacity > 0, "CommandCapacity must be > 0");
113 static_assert(EventCapacity > 0, "EventCapacity must be > 0");
114 static_assert(MidiOutputCapacity > 0, "MidiOutputCapacity must be > 0");
115 static_assert(std::is_nothrow_copy_constructible_v<GraphCommand> &&
116 std::is_nothrow_copy_assignable_v<GraphCommand>,
117 "GraphCommand must stay RT-copyable");
118 static_assert(std::is_nothrow_copy_constructible_v<GraphEvent> &&
119 std::is_nothrow_copy_assignable_v<GraphEvent>,
120 "GraphEvent must stay RT-copyable");
121 static_assert(std::is_nothrow_copy_constructible_v<GraphMidiOutputEvent> &&
122 std::is_nothrow_copy_assignable_v<GraphMidiOutputEvent>,
123 "GraphMidiOutputEvent must stay RT-copyable");
124
125public:
126 bool enqueue_command(const GraphCommand& command) noexcept {
127 if (!commands_.try_push(command)) {
128 dropped_commands_.fetch_add(1, std::memory_order_relaxed);
129 return false;
130 }
131 commands_enqueued_.fetch_add(1, std::memory_order_relaxed);
132 return true;
133 }
134
135 std::uint32_t drain_commands_for_block(std::span<GraphTimedCommand> out,
136 std::uint32_t block_frames) noexcept {
137 std::array<GraphTimedCommand, CommandCapacity> staged{};
138 std::uint32_t staged_count = 0;
139 std::uint32_t order = 0;
140 while (auto popped = commands_.try_pop()) {
141 if (staged_count >= staged.size()) {
142 dropped_commands_.fetch_add(1, std::memory_order_relaxed);
143 ++order;
144 continue;
145 }
146 auto command = *popped;
147 staged[staged_count++] = {
148 command,
149 command_offset(command.timing, block_frames),
150 order++,
151 };
152 }
153
154 std::span<GraphTimedCommand> sorted(staged.data(), staged_count);
155 sort_timed_commands(sorted);
156
157 const auto count = static_cast<std::uint32_t>(
158 std::min<std::size_t>(out.size(), staged_count));
159 for (std::uint32_t i = 0; i < count; ++i) {
160 out[i] = sorted[i];
161 }
162 if (staged_count > count) {
163 dropped_commands_.fetch_add(staged_count - count, std::memory_order_relaxed);
164 }
165 commands_drained_.fetch_add(count, std::memory_order_relaxed);
166 return count;
167 }
168
169 bool push_event_from_realtime(const GraphEvent& event) noexcept {
170 if (!events_.try_push(event)) {
171 dropped_events_.fetch_add(1, std::memory_order_relaxed);
172 return false;
173 }
174 events_enqueued_.fetch_add(1, std::memory_order_relaxed);
175 return true;
176 }
177
178 bool pop_event(GraphEvent& event) noexcept {
179 auto popped = events_.try_pop();
180 if (!popped.has_value()) return false;
181 event = *popped;
182 return true;
183 }
184
186 if (!midi_outputs_.try_push(event)) {
187 dropped_midi_outputs_.fetch_add(1, std::memory_order_relaxed);
188 return false;
189 }
190 midi_outputs_enqueued_.fetch_add(1, std::memory_order_relaxed);
191 return true;
192 }
193
194 bool pop_midi_output(GraphMidiOutputEvent& event) noexcept {
195 auto popped = midi_outputs_.try_pop();
196 if (!popped.has_value()) return false;
197 event = *popped;
198 return true;
199 }
200
204 void reset_offline() noexcept {
205 while (commands_.try_pop().has_value()) {}
206 while (events_.try_pop().has_value()) {}
207 while (midi_outputs_.try_pop().has_value()) {}
208 commands_enqueued_.store(0, std::memory_order_relaxed);
209 commands_drained_.store(0, std::memory_order_relaxed);
210 events_enqueued_.store(0, std::memory_order_relaxed);
211 midi_outputs_enqueued_.store(0, std::memory_order_relaxed);
212 dropped_commands_.store(0, std::memory_order_relaxed);
213 dropped_events_.store(0, std::memory_order_relaxed);
214 dropped_midi_outputs_.store(0, std::memory_order_relaxed);
215 }
216
217 GraphRuntimeQueueStats stats() const noexcept {
218 return {
219 commands_enqueued_.load(std::memory_order_relaxed),
220 commands_drained_.load(std::memory_order_relaxed),
221 events_enqueued_.load(std::memory_order_relaxed),
222 midi_outputs_enqueued_.load(std::memory_order_relaxed),
223 dropped_commands_.load(std::memory_order_relaxed),
224 dropped_events_.load(std::memory_order_relaxed),
225 dropped_midi_outputs_.load(std::memory_order_relaxed),
226 };
227 }
228
229 static constexpr std::size_t command_capacity() noexcept { return CommandCapacity; }
230 static constexpr std::size_t event_capacity() noexcept { return EventCapacity; }
231 static constexpr std::size_t midi_output_capacity() noexcept { return MidiOutputCapacity; }
232
233private:
234 static std::uint32_t command_offset(GraphCommandTiming timing,
235 std::uint32_t block_frames) noexcept {
236 if (timing.type == GraphCommandTimingType::Immediate || block_frames == 0) {
237 return 0;
238 }
239 return std::min(timing.frame_offset, block_frames - 1);
240 }
241
242 static void sort_timed_commands(std::span<GraphTimedCommand> commands) noexcept {
243 for (std::size_t i = 1; i < commands.size(); ++i) {
244 auto current = commands[i];
245 auto j = i;
246 while (j > 0 && comes_before(current, commands[j - 1])) {
247 commands[j] = commands[j - 1];
248 --j;
249 }
250 commands[j] = current;
251 }
252 }
253
254 static bool comes_before(const GraphTimedCommand& a,
255 const GraphTimedCommand& b) noexcept {
256 if (a.block_offset != b.block_offset) return a.block_offset < b.block_offset;
257 return a.order < b.order;
258 }
259
260 runtime::SpscQueue<GraphCommand, CommandCapacity> commands_;
261 runtime::SpscQueue<GraphEvent, EventCapacity> events_;
262 runtime::SpscQueue<GraphMidiOutputEvent, MidiOutputCapacity> midi_outputs_;
263
264 std::atomic<std::uint64_t> commands_enqueued_{0};
265 std::atomic<std::uint64_t> commands_drained_{0};
266 std::atomic<std::uint64_t> events_enqueued_{0};
267 std::atomic<std::uint64_t> midi_outputs_enqueued_{0};
268 std::atomic<std::uint64_t> dropped_commands_{0};
269 std::atomic<std::uint64_t> dropped_events_{0};
270 std::atomic<std::uint64_t> dropped_midi_outputs_{0};
271};
272
273} // namespace pulp::graph
Fixed-capacity graph command/event queues for realtime graph runtimes.
Definition graph_runtime_queue.hpp:111
static constexpr std::size_t midi_output_capacity() noexcept
Definition graph_runtime_queue.hpp:231
static constexpr std::size_t command_capacity() noexcept
Definition graph_runtime_queue.hpp:229
std::uint32_t drain_commands_for_block(std::span< GraphTimedCommand > out, std::uint32_t block_frames) noexcept
Definition graph_runtime_queue.hpp:135
void reset_offline() noexcept
Offline-only reset.
Definition graph_runtime_queue.hpp:204
bool enqueue_command(const GraphCommand &command) noexcept
Definition graph_runtime_queue.hpp:126
bool push_event_from_realtime(const GraphEvent &event) noexcept
Definition graph_runtime_queue.hpp:169
bool pop_event(GraphEvent &event) noexcept
Definition graph_runtime_queue.hpp:178
GraphRuntimeQueueStats stats() const noexcept
Definition graph_runtime_queue.hpp:217
bool pop_midi_output(GraphMidiOutputEvent &event) noexcept
Definition graph_runtime_queue.hpp:194
bool push_midi_output_from_realtime(const GraphMidiOutputEvent &event) noexcept
Definition graph_runtime_queue.hpp:185
static constexpr std::size_t event_capacity() noexcept
Definition graph_runtime_queue.hpp:230
Definition graph_runtime_buffer_assignment.hpp:8
GraphEventType
Definition graph_runtime_queue.hpp:56
@ NodeReset
Definition graph_runtime_queue.hpp:62
@ CommandRejected
Definition graph_runtime_queue.hpp:58
@ CommandDropped
Definition graph_runtime_queue.hpp:59
@ SnapshotActivated
Definition graph_runtime_queue.hpp:60
@ Diagnostic
Definition graph_runtime_queue.hpp:65
@ TailChanged
Definition graph_runtime_queue.hpp:64
@ CommandAccepted
Definition graph_runtime_queue.hpp:57
@ LatencyChanged
Definition graph_runtime_queue.hpp:63
@ SnapshotDeactivated
Definition graph_runtime_queue.hpp:61
std::uint32_t NodeId
Definition graph_types.hpp:7
GraphCommandType
Definition graph_runtime_queue.hpp:17
@ ActivateSnapshot
Definition graph_runtime_queue.hpp:24
@ ResetNode
Definition graph_runtime_queue.hpp:21
@ DeactivateSnapshot
Definition graph_runtime_queue.hpp:25
@ SetNodeGain
Definition graph_runtime_queue.hpp:18
@ TransportJump
Definition graph_runtime_queue.hpp:23
@ InjectMidi
Definition graph_runtime_queue.hpp:22
@ SetNodeParameter
Definition graph_runtime_queue.hpp:19
@ SetNodeBypass
Definition graph_runtime_queue.hpp:20
GraphCommandTimingType
Definition graph_runtime_queue.hpp:28
@ Immediate
Definition graph_runtime_queue.hpp:29
@ BlockOffset
Definition graph_runtime_queue.hpp:30
std::uint32_t PortIndex
Definition graph_types.hpp:8
@ a
Definition pitch.hpp:29
@ b
Definition pitch.hpp:31
@ j
Definition input_events.hpp:40
Definition graph_runtime_queue.hpp:33
GraphCommandTimingType type
Definition graph_runtime_queue.hpp:34
std::uint32_t frame_offset
Definition graph_runtime_queue.hpp:35
Definition graph_runtime_queue.hpp:38
float value
Definition graph_runtime_queue.hpp:45
GraphCommandType type
Definition graph_runtime_queue.hpp:40
std::uint32_t target_id
Definition graph_runtime_queue.hpp:43
GraphCommandTiming timing
Definition graph_runtime_queue.hpp:44
std::uint64_t sequence_id
Definition graph_runtime_queue.hpp:39
PortIndex port
Definition graph_runtime_queue.hpp:42
bool bool_value
Definition graph_runtime_queue.hpp:46
midi::MidiEvent midi
Definition graph_runtime_queue.hpp:47
NodeId node_id
Definition graph_runtime_queue.hpp:41
Definition graph_runtime_queue.hpp:68
NodeId node_id
Definition graph_runtime_queue.hpp:71
std::uint64_t sequence_id
Definition graph_runtime_queue.hpp:69
float value
Definition graph_runtime_queue.hpp:74
bool bool_value
Definition graph_runtime_queue.hpp:75
GraphEventType type
Definition graph_runtime_queue.hpp:70
std::uint32_t block_offset
Definition graph_runtime_queue.hpp:73
std::uint32_t target_id
Definition graph_runtime_queue.hpp:72
Definition graph_runtime_queue.hpp:78
NodeId node_id
Definition graph_runtime_queue.hpp:80
std::uint32_t block_offset
Definition graph_runtime_queue.hpp:82
PortIndex port
Definition graph_runtime_queue.hpp:81
midi::MidiEvent event
Definition graph_runtime_queue.hpp:83
std::uint64_t sequence_id
Definition graph_runtime_queue.hpp:79
Definition graph_runtime_queue.hpp:86
std::uint64_t dropped_events
Definition graph_runtime_queue.hpp:92
std::uint64_t events_enqueued
Definition graph_runtime_queue.hpp:89
std::uint64_t commands_enqueued
Definition graph_runtime_queue.hpp:87
std::uint64_t midi_outputs_enqueued
Definition graph_runtime_queue.hpp:90
std::uint64_t commands_drained
Definition graph_runtime_queue.hpp:88
std::uint64_t dropped_midi_outputs
Definition graph_runtime_queue.hpp:93
std::uint64_t dropped_commands
Definition graph_runtime_queue.hpp:91
Definition graph_runtime_queue.hpp:50
GraphCommand command
Definition graph_runtime_queue.hpp:51
std::uint32_t order
Definition graph_runtime_queue.hpp:53
std::uint32_t block_offset
Definition graph_runtime_queue.hpp:52
Timestamped MIDI event combining a choc::midi::ShortMessage with timing info.
Definition message.hpp:32