#pragma once #include "analytics.h" #include "timing/piecewise_linear.h" #include #include #include template auto boundedSubdivide(F func, const T &timeMap, DomainTime start, double sampleRate, size_t frameBound) { using DomainTimeDelta = time_units::TypedTimeDelta; using CodomainTime = typename T::CodomainTime; static_assert(std::is_same_v, "DomainTime must be the same as the time map's domain time"); static_assert(std::is_same_v>, "CodomainTime must be Seconds"); auto currentPosition = start; auto currentFrame = 0; auto it = timeMap.getSegmentIteratorAt(currentPosition); constexpr int maxSegmentsWithoutProgress = 100; while (it != timeMap.end() && currentFrame < frameBound) { auto segment = *it; if (currentPosition < segment.source_range.start || currentPosition >= segment.source_range.end) { // Somehow we're out of bounds. Reset to the start of the segment. currentPosition = segment.source_range.start; } auto mapped = segment.map_point(currentPosition); assert(mapped.has_value()); auto mappedValue = *mapped; auto remainingSegmentDuration = segment.target_range.end - mappedValue; auto segmentDurationFrames = frameBound - currentFrame; if (remainingSegmentDuration.is_finite()) { segmentDurationFrames = std::min(segmentDurationFrames, std::max(1UL, static_cast( std::round(remainingSegmentDuration.raw() * (double)sampleRate)))); } const auto segmentDurationDomain = DomainTimeDelta( (double)segmentDurationFrames / (sampleRate * segment.slope)); std::optional> maybeSeekTo = func(segment, currentPosition, mappedValue, currentFrame, segmentDurationFrames, segmentDurationDomain); currentPosition += segmentDurationDomain; currentFrame += segmentDurationFrames; if (maybeSeekTo.has_value()) { currentPosition = *maybeSeekTo; it = timeMap.getSegmentIteratorAt(currentPosition); continue; } int segmentsWithoutProgress = 0; while (currentPosition >= segment.source_range.end && segmentsWithoutProgress++ < maxSegmentsWithoutProgress) { ++it; if (it == timeMap.end()) { break; } segment = *it; } assert(segmentsWithoutProgress < maxSegmentsWithoutProgress); } return currentPosition; } struct RenderContext { RenderContext(time_units::Beats position, double bps, bool isContinuous, const time_transform::TimelineTempoMap &globalBeatsToSeconds, AnalyticsObserver *analyticsObserver, time_units::BeatsDelta duration) : position(position), bps(bps), isContinuous(isContinuous), globalBeatsToSeconds(globalBeatsToSeconds), analyticsObserver(analyticsObserver), duration(duration) {} time_units::Beats position; double bps; bool isContinuous; const time_transform::TimelineTempoMap &globalBeatsToSeconds; AnalyticsObserver *analyticsObserver; time_units::BeatsDelta duration; };