Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
event_compensation.hpp
Go to the documentation of this file.
1#pragma once
2
6
7#include <array>
8#include <cstddef>
9#include <cstdint>
10#include <limits>
11#include <span>
12
13namespace pulp::playback {
14
34 std::int64_t samples = 0;
35
36 constexpr bool compensating() const noexcept {
37 return samples != 0;
38 }
39 constexpr bool operator==(const EventCompensationShift&) const noexcept = default;
40};
41
52
58 std::int64_t actual = 0;
59 std::int64_t limit = 0;
61 std::size_t index = 0;
62
63 constexpr explicit operator bool() const noexcept {
65 }
66};
67
78constexpr EventCompensationResult
79accumulate_event_chain_shift(std::span<const int> device_latency_samples,
80 int ceiling_samples) noexcept {
82 if (ceiling_samples < 0) {
84 result.limit = ceiling_samples;
85 return result;
86 }
87 const auto limit = static_cast<std::int64_t>(ceiling_samples);
88 result.limit = limit;
89 std::int64_t total = 0;
90 for (std::size_t index = 0; index < device_latency_samples.size(); ++index) {
91 const auto latency = static_cast<std::int64_t>(device_latency_samples[index]);
92 if (latency < 0) {
94 result.actual = latency;
95 result.index = index;
96 return result;
97 }
98 if (latency > limit) {
100 result.actual = latency;
101 result.index = index;
102 return result;
103 }
104 // Bounded by the ceiling on both the addend and the running total, so
105 // the sum cannot overflow before the range check sees it.
106 total += latency;
107 if (total > limit) {
109 result.actual = total;
110 result.index = index;
111 return result;
112 }
113 }
114 result.shift = {total};
115 return result;
116}
117
127 EventCompensationShift shift) noexcept {
128 constexpr auto maximum = std::numeric_limits<std::int64_t>::max();
129 constexpr auto minimum = std::numeric_limits<std::int64_t>::min();
130 const auto origin = range.timeline_sample_start.value;
131 if (shift.samples > 0 && origin > maximum - shift.samples)
132 return {maximum};
133 if (shift.samples < 0 && origin < minimum - shift.samples)
134 return {minimum};
135 return {origin + shift.samples};
136}
137
146 EventCompensationShift shift) noexcept {
147 return !shift.compensating() || !range.host_beat_mapping;
148}
149
161 timebase::SamplePosition loop_end_samples) noexcept {
162 if (!shift.compensating())
163 return true;
164 const auto origin = shifted_range_origin(range, shift);
165 const auto frames = static_cast<std::int64_t>(range.frame_count);
166 return origin.value <= loop_end_samples.value - frames;
167}
168
175 std::uint32_t frame_count = 0;
178 std::uint32_t range_offset = 0;
182 std::uint64_t pass_offset = 0;
183};
184
195 std::array<CompensatedReadSegment, 2> segments{};
196 std::uint8_t segment_count = 0;
201 bool wraps_inside_range = false;
202};
203
208 EventCompensationShift shift) noexcept {
210 plan.segments[0] = {shifted_range_origin(range, shift), range.frame_count, 0, 0};
211 plan.segment_count = 1;
212 return plan;
213}
214
223constexpr CompensatedReadPlan
225 timebase::SamplePosition loop_start_samples,
226 timebase::SamplePosition loop_end_samples) noexcept {
228 auto start = shifted_range_origin(range, shift);
229 const auto length = loop_end_samples.value - loop_start_samples.value;
230 if (length <= 0)
231 return plan_compensated_read(range, shift);
232 const auto span = static_cast<std::uint64_t>(length);
233 std::uint64_t pass_offset = 0;
234 if (start.value >= loop_end_samples.value) {
235 const auto past = static_cast<std::uint64_t>(start.value - loop_start_samples.value);
236 pass_offset = past / span;
237 start = {loop_start_samples.value + static_cast<std::int64_t>(past % span)};
238 }
239 auto head = range.frame_count;
240 if (start.value < loop_end_samples.value) {
241 const auto until_wrap = static_cast<std::uint64_t>(loop_end_samples.value - start.value);
242 if (until_wrap < head)
243 head = static_cast<std::uint32_t>(until_wrap);
244 }
245 plan.segments[0] = {start, head, 0, pass_offset};
246 plan.segment_count = 1;
247 if (head < range.frame_count) {
248 plan.segments[1] = {loop_start_samples, range.frame_count - head, head, pass_offset + 1};
249 plan.segment_count = 2;
250 plan.wraps_inside_range = true;
251 }
252 return plan;
253}
254
266 ProviderSelectorProgram provider) noexcept {
267 return !shift.compensating() || !provider.available(ProviderKind::ExternalInput);
268}
269
270} // namespace pulp::playback
Definition audio_renderer.hpp:20
constexpr bool loop_admits_event_compensation(const TransportRange &range, EventCompensationShift shift, timebase::SamplePosition loop_end_samples) noexcept
Whether a compensating shift's read-ahead stays inside an enabled loop.
Definition event_compensation.hpp:159
constexpr CompensatedReadPlan plan_compensated_read(const TransportRange &range, EventCompensationShift shift) noexcept
The one run a window that no loop bounds resolves to.
Definition event_compensation.hpp:207
constexpr timebase::SamplePosition shifted_range_origin(const TransportRange &range, EventCompensationShift shift) noexcept
The sample position an event stream at a compensated injection point must be read from for range.
Definition event_compensation.hpp:126
@ ExternalInput
Definition program.hpp:46
@ Ok
Definition automation_cursor.hpp:29
constexpr EventCompensationResult accumulate_event_chain_shift(std::span< const int > device_latency_samples, int ceiling_samples) noexcept
Accumulates the event-domain latency of one chain into a scheduling shift.
Definition event_compensation.hpp:79
EventCompensationCode
Definition event_compensation.hpp:42
@ ChainShiftOutOfRange
The accumulated chain shift exceeds the ceiling the caller supplied.
Definition event_compensation.hpp:50
@ NegativeDeviceLatency
A device reported a negative latency.
Definition event_compensation.hpp:46
@ DeviceLatencyOutOfRange
A device's own reported latency exceeds the ceiling the caller supplied.
Definition event_compensation.hpp:48
@ Ok
Definition event_compensation.hpp:43
constexpr bool range_admits_event_compensation(const TransportRange &range, EventCompensationShift shift) noexcept
Whether a compensating shift has a defined meaning for range.
Definition event_compensation.hpp:145
constexpr bool event_compensation_admits_live_input(EventCompensationShift shift, ProviderSelectorProgram provider) noexcept
Whether a track may offer live input while its event chain is compensated.
Definition event_compensation.hpp:265
How a compensated read window resolves against an enabled loop.
Definition event_compensation.hpp:194
std::uint8_t segment_count
Definition event_compensation.hpp:196
std::array< CompensatedReadSegment, 2 > segments
Definition event_compensation.hpp:195
bool wraps_inside_range
True when the loop point falls INSIDE this window.
Definition event_compensation.hpp:201
One contiguous run of a compensated read window after an enabled loop has been applied to it.
Definition event_compensation.hpp:171
std::uint64_t pass_offset
Loop passes between range.loop_pass_index and the pass this run reads.
Definition event_compensation.hpp:182
timebase::SamplePosition document_start
Document sample the run reads the program from.
Definition event_compensation.hpp:173
std::uint32_t frame_count
Length of the run, in frames.
Definition event_compensation.hpp:175
std::uint32_t range_offset
Where the run starts inside its TransportRange.
Definition event_compensation.hpp:178
Definition event_compensation.hpp:53
std::size_t index
Index into the supplied latencies for a per-device failure.
Definition event_compensation.hpp:61
std::int64_t limit
Definition event_compensation.hpp:59
std::int64_t actual
The offending value and the ceiling that rejected it, so a caller can report the exact number rather ...
Definition event_compensation.hpp:58
EventCompensationShift shift
Definition event_compensation.hpp:55
EventCompensationCode code
Definition event_compensation.hpp:54
One event-injection point's prepared scheduling shift, counted in samples at the program's compiled s...
Definition event_compensation.hpp:33
std::int64_t samples
Definition event_compensation.hpp:34
constexpr bool operator==(const EventCompensationShift &) const noexcept=default
constexpr bool compensating() const noexcept
Definition event_compensation.hpp:36
Definition transport.hpp:45
Definition rational_time.hpp:9