112 constexpr explicit operator bool() const noexcept {
119constexpr bool checked_grid_add(std::int64_t lhs, std::int64_t rhs, std::int64_t& result)
noexcept {
120 constexpr auto min = std::numeric_limits<std::int64_t>::min();
121 constexpr auto max = std::numeric_limits<std::int64_t>::max();
122 if ((rhs > 0 && lhs > max - rhs) || (rhs < 0 && lhs < min - rhs))
129 std::int64_t& result)
noexcept {
130 constexpr auto min = std::numeric_limits<std::int64_t>::min();
131 constexpr auto max = std::numeric_limits<std::int64_t>::max();
132 if ((rhs > 0 && lhs < min + rhs) || (rhs < 0 && lhs > max + rhs))
170 double ticks_per_frame,
double frame_delta)
noexcept {
171 const auto tick_magnitude = std::abs(source_tick) + std::abs(anchor_source_tick);
172 const auto scale = tick_magnitude / std::abs(ticks_per_frame) + std::abs(frame_delta);
190 const auto nearest = std::nearbyint(frame_delta);
196 if (std::abs(frame_delta - nearest) <= budget)
208 std::int64_t& result)
noexcept {
209 constexpr auto sign_bit = std::uint64_t{1} << 63U;
210 constexpr auto signed_max =
211 static_cast<std::uint64_t
>(std::numeric_limits<std::int64_t>::max());
212 const auto unsigned_max_exclusive = std::ldexp(1.0, 64);
213 if (!std::isfinite(integral_delta))
216 const auto assign_negative = [&](std::uint64_t magnitude)
noexcept {
217 if (magnitude > sign_bit)
219 result = magnitude == sign_bit ? std::numeric_limits<std::int64_t>::min()
220 : -
static_cast<std::int64_t
>(magnitude);
223 const auto anchor_magnitude = [&] {
224 return anchor < 0 ? static_cast<std::uint64_t>(-(anchor + 1)) + 1U
225 :
static_cast<std::uint64_t
>(anchor);
228 if (integral_delta >= 0.0) {
229 if (!(integral_delta < unsigned_max_exclusive))
231 const auto magnitude =
static_cast<std::uint64_t
>(integral_delta);
233 if (magnitude > signed_max - anchor_magnitude)
235 result = anchor +
static_cast<std::int64_t
>(magnitude);
238 if (magnitude < anchor_magnitude)
239 return assign_negative(anchor_magnitude - magnitude);
240 const auto positive = magnitude - anchor_magnitude;
241 if (positive > signed_max)
243 result =
static_cast<std::int64_t
>(positive);
247 const auto magnitude_as_double = -integral_delta;
248 if (!(magnitude_as_double < unsigned_max_exclusive))
250 const auto magnitude =
static_cast<std::uint64_t
>(magnitude_as_double);
252 if (magnitude > sign_bit - anchor_magnitude)
254 return assign_negative(anchor_magnitude + magnitude);
256 if (magnitude <= anchor_magnitude) {
257 result =
static_cast<std::int64_t
>(anchor_magnitude - magnitude);
260 return assign_negative(magnitude - anchor_magnitude);
266constexpr bool ceil_grid(std::int64_t value, std::int64_t quantum, std::int64_t& result)
noexcept {
269 const auto remainder = value % quantum;
270 if (remainder == 0) {
280 if (range.timeline_tick_end < range.timeline_tick_start ||
281 range.monotonic_end < range.monotonic_start)
283 std::int64_t timeline_duration = 0;
284 std::int64_t monotonic_duration = 0;
286 timeline_duration) ||
288 range.monotonic_start.position.value, monotonic_duration))
290 if (timeline_duration != monotonic_duration)
292 std::int64_t sample_end = 0;
293 if (!
checked_grid_add(range.timeline_sample_start.value, range.frame_count, sample_end))
295 std::uint64_t frame_end = 0;
296 frame_end =
static_cast<std::uint64_t
>(range.frame_offset) + range.frame_count;
297 const auto frame_domain_end =
298 static_cast<std::uint64_t
>(std::numeric_limits<std::uint32_t>::max()) + 1U;
299 if (frame_end > frame_domain_end)
301 if (range.host_beat_mapping) {
302 const auto tick_start = range.has_precise_host_ticks
303 ? range.host_tick_start
304 :
static_cast<double>(range.timeline_tick_start.value);
305 const auto tick_end = range.has_precise_host_ticks
306 ? range.host_tick_end
307 :
static_cast<double>(range.timeline_tick_end.value);
308 if (!std::isfinite(tick_start) || !std::isfinite(tick_end) ||
309 (range.frame_count != 0 && !(tick_start < tick_end)))
311 std::int64_t absolute_frame_end = 0;
312 if (!range.has_host_anchor || !std::isfinite(range.host_anchor.source_tick) ||
313 !std::isfinite(range.host_anchor.ticks_per_frame) ||
314 !(range.host_anchor.ticks_per_frame > 0.0) ||
315 !std::isfinite(range.document_to_source_tick_offset) ||
316 !
checked_grid_add(range.absolute_frame_start, range.frame_count, absolute_frame_end))
324 std::uint32_t& output_offset,
326 const auto tick_start = range.has_precise_host_ticks
327 ? range.host_tick_start
328 :
static_cast<double>(range.timeline_tick_start.value);
329 const auto tick_end = range.has_precise_host_ticks
330 ? range.host_tick_end
331 :
static_cast<double>(range.timeline_tick_end.value);
332 if (!range.host_beat_mapping || range.frame_count == 0 || !(tick_start < tick_end))
337 const auto document =
static_cast<double>(document_tick.value);
338 if (document < tick_start || !(document < tick_end))
340 const auto source_tick = document + range.document_to_source_tick_offset;
341 const auto frame_delta =
342 (source_tick - range.host_anchor.source_tick) / range.host_anchor.ticks_per_frame;
346 range.host_anchor.ticks_per_frame, frame_delta),
348 std::int64_t absolute_frame = 0;
351 std::int64_t absolute_frame_end = 0;
352 if (!
checked_grid_add(range.absolute_frame_start, range.frame_count, absolute_frame_end))
358 if (absolute_frame < range.absolute_frame_start)
359 absolute_frame = range.absolute_frame_start;
360 if (absolute_frame >= absolute_frame_end)
361 absolute_frame = absolute_frame_end - 1;
362 const auto local =
static_cast<std::uint64_t
>(absolute_frame) -
363 static_cast<std::uint64_t
>(range.absolute_frame_start);
364 const auto total =
static_cast<std::uint64_t
>(range.frame_offset) + local;
365 if (total > std::numeric_limits<std::uint32_t>::max())
367 output_offset =
static_cast<std::uint32_t
>(total);
372 std::int64_t quantum,
bool inclusive_end)
noexcept {
373 if (inclusive_end ? start > end : start >= end)
375 const auto last = inclusive_end ? end : end - 1;
376 std::int64_t first = 0;
377 if (!
ceil_grid(start, quantum, first) || first > last)
379 const auto distance =
static_cast<std::uint64_t
>(last) -
static_cast<std::uint64_t
>(first);
380 return distance /
static_cast<std::uint64_t
>(quantum) + 1U;
386 std::uint64_t& candidates)
noexcept {
391 if (range.frame_count == 0)
397 const auto inclusive_end =
true;
399 range.timeline_tick_start.value, range.timeline_tick_end.value, grid.value, inclusive_end);
403 candidates = grid_count;
407 const auto last_tick =
408 inclusive_end ? range.timeline_tick_end.value : range.timeline_tick_end.value - 1;
409 const auto first_bar = meter.tick_to_bar(range.timeline_tick_start).bar.value;
410 const auto last_bar = meter.tick_to_bar({last_tick}).bar.value;
411 if (last_bar < first_bar)
413 const auto bar_count =
414 static_cast<std::uint64_t
>(last_bar) -
static_cast<std::uint64_t
>(first_bar) + 1U;
420 candidates = grid_count + bar_count;
424template <
typename Emit>
428 Emit&& emit)
noexcept {
432 if (range.frame_count == 0)
435 std::int64_t sample_end = 0;
436 (void)
checked_grid_add(range.timeline_sample_start.value, range.frame_count, sample_end);
438 auto emit_timeline = [&](std::int64_t timeline_tick)
noexcept {
439 std::uint32_t output_frame = 0;
440 if (range.host_beat_mapping) {
444 if (timeline_tick < range.timeline_tick_start.value ||
445 timeline_tick > range.timeline_tick_end.value)
447 const auto sample = tempo.ticks_to_samples({timeline_tick});
448 if (sample.value < range.timeline_sample_start.value || sample.value >= sample_end)
450 const auto local_frame =
451 static_cast<std::uint64_t
>(sample.value - range.timeline_sample_start.value);
452 const auto projected_frame =
453 static_cast<std::uint64_t
>(range.frame_offset) + local_frame;
454 if (projected_frame > std::numeric_limits<std::uint32_t>::max())
456 output_frame =
static_cast<std::uint32_t
>(projected_frame);
458 std::int64_t local_tick = 0;
459 std::int64_t transport_tick = 0;
461 !
checked_grid_add(range.monotonic_start.position.value, local_tick, transport_tick))
466 meter.tick_to_bar({timeline_tick}),
467 range.loop_pass_index});
471 std::int64_t candidate = 0;
472 if (!
ceil_grid(range.timeline_tick_start.value, grid.value, candidate))
474 while (candidate <= range.timeline_tick_end.value) {
475 const auto error = emit_timeline(candidate);
484 auto bar = meter.tick_to_bar(range.timeline_tick_start).bar;
486 const auto bar_start = meter.bar_to_tick(bar);
487 if (bar_start > range.timeline_tick_end)
489 if (bar.value == std::numeric_limits<std::int64_t>::max())
491 const auto next_bar = meter.bar_to_tick({bar.value + 1});
492 if (next_bar <= bar_start)
494 std::int64_t local_begin = 0;
495 if (range.timeline_tick_start > bar_start &&
498 std::int64_t local = 0;
499 if (!
ceil_grid(local_begin, grid.value, local))
501 std::int64_t bar_duration = 0;
504 while (local < bar_duration) {
505 std::int64_t candidate = 0;
508 if (candidate > range.timeline_tick_end.value)
510 const auto error = emit_timeline(candidate);
521template <
typename Emit>
523 std::span<const GridProjectionRange> ranges,
TickDuration grid,
525 Emit&& emit)
noexcept {
526 std::uint64_t previous_end = 0;
527 bool have_previous =
false;
528 for (
const auto& range : ranges) {
529 if (have_previous && range.frame_offset < previous_end)
531 const auto error =
enumerate_range(tempo, meter, range, grid, anchor, placement, emit);
534 previous_end =
static_cast<std::uint64_t
>(range.frame_offset) + range.frame_count;
535 have_previous =
true;
550 std::span<const GridProjectionRange> ranges,
551 std::span<GridProjectionPoint> output)
noexcept {
557 if (!request.playing)
562 std::uint64_t candidate_total = 0;
563 for (
const auto& range : ranges) {
564 std::uint64_t candidates = 0;
566 request.anchor, candidates);
568 return {bound, 0, 0};
571 candidate_total += candidates;
574 std::size_t required = 0;
575 const auto count_error =
578 if (required == kMaximumGridProjectionPoints)
579 return GridProjectionError::ProjectionLimitExceeded;
581 return GridProjectionError::None;
584 return {count_error, 0, required};
585 if (required > output.size())
588 std::size_t count = 0;
589 const auto write_error =
592 output[count++] = point;
593 return GridProjectionError::None;
596 return {write_error, 0, required};
Definition compiled_meter_map.hpp:64
Definition compiled_tempo_map.hpp:91
constexpr double kGridMaxSnapFrames
Definition grid_projection.hpp:165
GridProjectionError preflight_candidate_bound(const CompiledMeterMap &meter, const GridProjectionRange &range, TickDuration grid, GridAnchor anchor, std::uint64_t &candidates) noexcept
Definition grid_projection.hpp:383
constexpr double kGridFrameErrorUlps
Definition grid_projection.hpp:154
constexpr bool checked_grid_add(std::int64_t lhs, std::int64_t rhs, std::int64_t &result) noexcept
Definition grid_projection.hpp:119
constexpr bool ceil_grid(std::int64_t value, std::int64_t quantum, std::int64_t &result) noexcept
Definition grid_projection.hpp:266
double grid_frame_error_budget(double source_tick, double anchor_source_tick, double ticks_per_frame, double frame_delta) noexcept
Definition grid_projection.hpp:169
GridProjectionError enumerate_ranges(const CompiledTempoMap &tempo, const CompiledMeterMap &meter, std::span< const GridProjectionRange > ranges, TickDuration grid, GridAnchor anchor, GridPlacement placement, Emit &&emit) noexcept
Definition grid_projection.hpp:522
bool checked_grid_add_integral_delta(std::int64_t anchor, double integral_delta, std::int64_t &result) noexcept
Definition grid_projection.hpp:207
constexpr bool checked_grid_subtract(std::int64_t lhs, std::int64_t rhs, std::int64_t &result) noexcept
Definition grid_projection.hpp:128
std::uint64_t grid_candidate_count(std::int64_t start, std::int64_t end, std::int64_t quantum, bool inclusive_end) noexcept
Definition grid_projection.hpp:371
GridProjectionError enumerate_range(const CompiledTempoMap &tempo, const CompiledMeterMap &meter, const GridProjectionRange &range, TickDuration grid, GridAnchor anchor, GridPlacement placement, Emit &&emit) noexcept
Definition grid_projection.hpp:425
double grid_integral_frame_delta(double frame_delta, double budget, GridPlacement placement=GridPlacement::Floor) noexcept
Definition grid_projection.hpp:188
GridProjectionError validate_range(const GridProjectionRange &range) noexcept
Definition grid_projection.hpp:279
bool host_mapped_grid_output_offset(const GridProjectionRange &range, TickPosition document_tick, std::uint32_t &output_offset, GridPlacement placement=GridPlacement::Floor) noexcept
Definition grid_projection.hpp:323
Definition beat_division.hpp:11
BeatDivision
Definition beat_division.hpp:15
@ Quarter
Definition beat_division.hpp:22
GridProjectionResult project_grid(const CompiledTempoMap &tempo, const CompiledMeterMap &meter, const GridProjectionRequest &request, std::span< const GridProjectionRange > ranges, std::span< GridProjectionPoint > output) noexcept
Definition grid_projection.hpp:547
GridAnchor
Definition grid_projection.hpp:15
@ Timeline
Definition grid_projection.hpp:16
@ Bar
Definition grid_projection.hpp:17
runtime::Result< TickDuration, BeatDivisionError > division_ticks(BeatDivision division) noexcept
Definition beat_division.hpp:88
GridPlacement
Definition grid_projection.hpp:73
@ Nearest
Definition grid_projection.hpp:75
@ Floor
Definition grid_projection.hpp:74
@ InvalidDivision
Definition beat_division.hpp:47
@ SampleRangeExceeded
Definition compiled_tempo_map.hpp:39
constexpr std::size_t kMaximumGridProjectionPoints
Definition grid_projection.hpp:94
GridProjectionError
Definition grid_projection.hpp:96
@ InvalidDivision
Definition grid_projection.hpp:98
@ None
Definition grid_projection.hpp:97
@ SampleRangeExceeded
Definition grid_projection.hpp:101
@ InvalidAnchor
Definition grid_projection.hpp:99
@ ProjectionLimitExceeded
Definition grid_projection.hpp:103
@ InvalidRange
Definition grid_projection.hpp:100
@ TickRangeExceeded
Definition grid_projection.hpp:102
@ OutputTooSmall
Definition grid_projection.hpp:104
Definition compiled_meter_map.hpp:31
Definition grid_projection.hpp:85
std::uint32_t frame_offset
Definition grid_projection.hpp:86
BarTickPosition bar_tick
Definition grid_projection.hpp:89
MonotonicBeat transport_tick
Definition grid_projection.hpp:88
constexpr auto operator<=>(const GridProjectionPoint &) const =default
std::uint64_t loop_pass_index
Definition grid_projection.hpp:90
TickPosition timeline_tick
Definition grid_projection.hpp:87
Definition grid_projection.hpp:35
bool has_precise_host_ticks
Definition grid_projection.hpp:47
TickPosition timeline_tick_start
Definition grid_projection.hpp:39
MonotonicBeat monotonic_end
Definition grid_projection.hpp:42
bool host_beat_mapping
Definition grid_projection.hpp:44
double host_tick_end
Definition grid_projection.hpp:46
constexpr auto operator<=>(const GridProjectionRange &) const =default
MonotonicBeat monotonic_start
Definition grid_projection.hpp:41
double host_tick_start
Definition grid_projection.hpp:45
std::uint32_t frame_offset
Definition grid_projection.hpp:36
std::uint32_t frame_count
Definition grid_projection.hpp:37
TickPosition timeline_tick_end
Definition grid_projection.hpp:40
HostGridAnchor host_anchor
Definition grid_projection.hpp:48
bool has_host_anchor
Definition grid_projection.hpp:51
double document_to_source_tick_offset
Definition grid_projection.hpp:50
SamplePosition timeline_sample_start
Definition grid_projection.hpp:38
std::int64_t absolute_frame_start
Definition grid_projection.hpp:49
std::uint64_t loop_pass_index
Definition grid_projection.hpp:43
Definition grid_projection.hpp:78
bool playing
Definition grid_projection.hpp:81
BeatDivision division
Definition grid_projection.hpp:79
GridAnchor anchor
Definition grid_projection.hpp:80
GridPlacement placement
Definition grid_projection.hpp:82
Definition grid_projection.hpp:107
std::size_t required
Definition grid_projection.hpp:110
std::size_t count
Definition grid_projection.hpp:109
GridProjectionError error
Definition grid_projection.hpp:108
Definition grid_projection.hpp:25
double ticks_per_frame
Definition grid_projection.hpp:28
constexpr auto operator<=>(const HostGridAnchor &) const =default
std::int64_t frame
Definition grid_projection.hpp:27
double source_tick
Definition grid_projection.hpp:26
Definition rational_time.hpp:9