2#include <ossia/detail/flicks.hpp>
4#include <ossia/detail/small_vector.hpp>
5#include <ossia/editor/scenario/time_signature.hpp>
15#if defined(_LIBCPP_CONSTEXPR_SINCE_CXX23) || defined(_GLIBCXX23_CONSTEXPR)
16#define ossia_constexpr_msvc_workaround constexpr
18#define ossia_constexpr_msvc_workaround
26 double position,
double start,
double end, int64_t frames)
noexcept
28 const auto abs = [](
double x) {
return x < 0. ? -x : x; };
29 const double duration = end - start;
30 const double r = (position - start) / duration;
34 const double tolerance = 16. * std::numeric_limits<double>::epsilon()
35 * std::max({abs(position), abs(start), abs(end)})
37 return constexpr_floor((r + tolerance) *
double(frames));
40using quarter_note = double;
62using quantification_points = ossia::small_vector<quantification_point, 8>;
66 constexpr token_request() noexcept = default;
67 constexpr token_request(const token_request&) noexcept = default;
68 constexpr token_request(token_request&&) noexcept = default;
69 constexpr token_request& operator=(const token_request&) noexcept = default;
70 constexpr token_request& operator=(token_request&&) noexcept = default;
72 constexpr token_request(
73 ossia::time_value prev_d,
ossia::time_value d,
ossia::time_value parent_duration,
74 ossia::time_value off,
double s, time_signature sig,
double tempo) noexcept
77 , parent_duration{parent_duration}
89 [[nodiscard]]
constexpr token_request add_offset(ossia::time_value t)
const noexcept
91 token_request other = *
this;
97 template <
typename Exec,
typename Transport>
99 ossia::time_value start_offset, ossia::time_value loop_duration, Exec f,
100 Transport transport)
const noexcept
102 if(loop_duration.impl <= 0)
104 token_request other = add_offset(start_offset);
109 ossia::token_request other = *
this;
110 ossia::time_value orig_from = other.prev_date;
111 ossia::time_value tick_amount = other.date - other.prev_date;
115 if(tick_amount.impl == 0)
117 other.prev_date = time_value{orig_from.impl % loop_duration.impl} + start_offset;
118 other.date = other.prev_date;
127 const int64_t total_amount
128 = tick_amount.impl >= 0 ? tick_amount.impl : -tick_amount.impl;
130 = start_sample >= 0 && length_sample >= 0 && total_amount > 0;
131 int64_t consumed = 0;
132 const auto set_piece_span = [&](int64_t piece)
constexpr {
135 const int64_t s0 = start_sample
136 + (int64_t{length_sample} * consumed + total_amount / 2)
140 + (int64_t{length_sample} * (consumed + piece) + total_amount / 2)
142 other.start_sample = int32_t(s0);
143 other.length_sample = int32_t(s1 - s0);
148 if(tick_amount >= 0_tv)
151 while(tick_amount > 0_tv)
153 const time_value cur_from{orig_from % loop_duration};
154 if(cur_from + tick_amount < loop_duration)
156 other.prev_date = cur_from + start_offset;
157 other.date = other.prev_date + tick_amount;
158 set_piece_span(tick_amount.impl);
164 auto this_tick = loop_duration - cur_from;
166 tick_amount -= this_tick;
167 orig_from += this_tick;
168 other.prev_date = cur_from + start_offset;
169 other.date = other.prev_date + this_tick;
171 set_piece_span(this_tick.impl);
174 transport(start_offset);
175 other.offset += this_tick;
182 const int64_t loop_dur = loop_duration.impl;
183 int64_t remaining = -tick_amount.impl;
187 int64_t cur_from = orig_from.impl % loop_dur;
189 cur_from += loop_dur;
193 transport(start_offset + loop_duration);
197 const int64_t this_tick = remaining < cur_from ? remaining : cur_from;
199 other.prev_date = time_value{cur_from} + start_offset;
200 other.date = other.prev_date - time_value{this_tick};
202 set_piece_span(this_tick);
205 remaining -= this_tick;
206 orig_from -= time_value{this_tick};
207 other.offset += time_value{this_tick};
213 [[nodiscard]]
constexpr time_value model_read_duration() const noexcept
215 return date - prev_date;
218 [[nodiscard]]
static constexpr double abs_speed(
double s)
noexcept
220 return s < 0. ? -s : s;
226 [[nodiscard]]
constexpr physical_time
227 start_date_to_physical(
double ratio)
const noexcept
231 return this->prev_date.impl * ratio / abs_speed(speed);
238 [[nodiscard]]
constexpr physical_time
239 sample_at(ossia::time_value tick_position,
double ratio)
const noexcept
242 return constexpr_floor(tick_position.impl * ratio / abs_speed(speed));
248 [[nodiscard]]
constexpr physical_time physical_start(
double ratio)
const noexcept
251 if(start_sample >= 0)
253 return sample_at(this->offset, ratio);
259 [[nodiscard]]
constexpr physical_time
260 physical_read_duration(
double ratio)
const noexcept
264 const auto a = prev_date.impl < date.impl ? prev_date.impl : date.impl;
265 const auto b = prev_date.impl < date.impl ? date.impl : prev_date.impl;
266 return constexpr_floor(b * ratio) - constexpr_floor(a * ratio);
273 [[nodiscard]]
constexpr physical_time
274 physical_write_duration(
double ratio)
const noexcept
277 if(length_sample >= 0)
278 return length_sample;
279 return sample_at(this->offset + abs(date - prev_date), ratio)
280 - sample_at(this->offset, ratio);
284 [[nodiscard]]
constexpr physical_time
285 safe_physical_write_duration(
double ratio,
int bufferSize)
const noexcept
288 return bufferSize - physical_start(ratio);
302 [[nodiscard]]
constexpr physical_time
303 physical_position(
double musical_position,
double ratio)
const noexcept
308 if(speed == 0. && length_sample < 0)
311 const int64_t len = physical_write_duration(ratio);
315 const double musical_tick_duration = musical_end_position - musical_start_position;
316 if(musical_tick_duration == 0.)
322 musical_position, musical_start_position, musical_end_position, len);
323 return s < 0 ? int64_t(0) : (s >= len ? len - 1 : s);
327 [[nodiscard]]
constexpr bool in_range(ossia::time_value global_time)
const noexcept
329 return global_time.impl >= prev_date.impl && global_time.impl < date.impl;
334 [[nodiscard]]
constexpr physical_time
335 to_physical_time_in_tick(ossia::time_value global_time,
double ratio)
const noexcept
338 const int64_t in_tick
339 = speed < 0. ? (prev_date - global_time).impl : (global_time - prev_date).impl;
344 if(start_sample >= 0 && length_sample >= 0)
346 const int64_t dt = abs(date - prev_date).impl;
349 int64_t s = start_sample
350 + constexpr_floor(
double(in_tick) /
double(dt) * length_sample);
353 else if(s > int64_t(start_sample) + length_sample)
354 s = int64_t(start_sample) + length_sample;
359 return sample_at(this->offset + ossia::time_value{in_tick}, ratio);
364 [[nodiscard]]
constexpr physical_time
365 to_physical_time_in_tick(int64_t global_time,
double ratio)
const noexcept
367 return to_physical_time_in_tick(ossia::time_value{global_time}, ratio);
372 [[nodiscard]]
constexpr time_value
373 from_physical_time_in_tick(ossia::physical_time phys_time,
double ratio)
const noexcept
376 const double in_tick = phys_time - physical_start(ratio);
377 return time_value{constexpr_floor(in_tick * (speed / ratio) + prev_date.impl)};
382 [[nodiscard]]
constexpr double position() const noexcept
384 return parent_duration.impl > 0 ? date.impl / double(parent_duration.impl) : 0.;
388 [[nodiscard]]
constexpr bool forward() const noexcept {
return date > prev_date; }
391 [[nodiscard]]
constexpr bool paused() const noexcept {
return date == prev_date; }
394 [[nodiscard]]
constexpr bool backward() const noexcept {
return date < prev_date; }
404 template <
typename F>
405 void for_each_bar_segment(
double lo,
double hi, F&& fn)
const noexcept
407 const bool valid_sig = signature.upper > 0 && signature.lower > 0;
408 const double quarters_in_bar
409 = valid_sig ? 4. * signature.upper / signature.lower : 4.;
410 if(!(quarters_in_bar > 0.))
413 constexpr double eps = 1e-9;
414 const bool rewinding = date < prev_date;
415 const double near_bar = rewinding ? musical_end_last_bar : musical_start_last_bar;
416 const double far_bar = rewinding ? musical_start_last_bar : musical_end_last_bar;
420 for(
int i = 0; i < 1024 && b < far_bar - eps; i++)
422 const double next = (b + quarters_in_bar < far_bar) ? b + quarters_in_bar : far_bar;
423 if(next > lo + eps && b < hi + eps)
425 b += quarters_in_bar;
429 b = (far_bar > near_bar) ? far_bar : near_bar;
430 for(
int i = 0; i < 1024 && b < hi + eps; i++)
432 fn(b, b + quarters_in_bar);
433 b += quarters_in_bar;
444 [[nodiscard]] std::optional<quantification_point>
445 get_quantification_point(
double rate)
const noexcept
447 if(prev_date == date)
454 const auto pts = get_quantification_dates(rate);
466 [[nodiscard]] std::optional<time_value>
467 get_quantification_date(
double rate)
const noexcept
469 if(
const auto pt = get_quantification_point(rate))
481 [[nodiscard]] quantification_points
482 get_quantification_dates(
double rate)
const noexcept
484 quantification_points res;
485 for_each_quantification_date(
486 rate, [&](
const quantification_point& p) { res.push_back(p); });
494 template <
typename F>
495 void for_each_quantification_date(
double rate, F&& f)
const noexcept
497 if(prev_date == date)
500 const double musical_tick_duration = musical_end_position - musical_start_position;
501 const bool rewinding = date < prev_date;
505 if(rate <= 0. || musical_tick_duration == 0.
506 || (musical_tick_duration < 0.) != rewinding)
508 f(quantification_point{prev_date, 0, musical_start_position});
515 constexpr double eps = 1e-9;
516 const time_value tick_duration = date - prev_date;
517 const bool valid_sig = signature.upper > 0 && signature.lower > 0;
518 const double quarters_in_bar = valid_sig ? 4. * signature.upper / signature.lower : 4.;
523 const auto try_push = [&](
double musical_position, int64_t index) {
529 const double scale = double(tick_duration.impl) / musical_tick_duration;
531 = prev_date + (musical_position - musical_start_position) * scale;
540 if(musical_position <= musical_end_position)
549 if(musical_position >= musical_end_position)
557 f(quantification_point{d, index, musical_position});
560 return ++count < 1024;
567 const double unit = quarters_in_bar / rate;
571 const double origin = musical_start_last_signature;
572 const double start = (musical_start_position - origin) / unit;
573 const double end = (musical_end_position - origin) / unit;
574 const int64_t first = rewinding ? int64_t(std::floor(start + eps))
575 : int64_t(std::ceil(start - eps));
577 for(int64_t k = first; rewinding ? (k > end + eps) : (k < end - eps);
578 k += rewinding ? -1 : 1)
580 if(!try_push(k * unit + origin, k))
591 const double unit = 4. / rate;
592 if(!(unit > 0.) || !(quarters_in_bar > 0.))
595 const double lo = rewinding ? musical_end_position : musical_start_position;
596 const double hi = rewinding ? musical_start_position : musical_end_position;
598 const auto walk_segment = [&](
double bar_line,
double next_bar) {
599 const int divs = int(std::ceil((next_bar - bar_line) / unit)) + 1;
602 for(int64_t k = 0; k <= divs; k++)
604 const double p = bar_line + k * unit;
605 if(p >= next_bar - eps || p > hi + eps)
615 for(int64_t k = divs; k >= 0; k--)
617 const double p = bar_line + k * unit;
618 if(p >= next_bar - eps || p > hi + eps)
632 for_each_bar_segment(lo, hi, [&](
double bar_line,
double next_bar) {
634 walking = walk_segment(bar_line, next_bar);
642 ossia::small_vector<std::pair<double, double>, 8> segments;
643 for_each_bar_segment(lo, hi, [&](
double bar_line,
double next_bar) {
644 if(segments.size() < 1024)
645 segments.push_back({bar_line, next_bar});
647 for(
auto it = segments.rbegin(); it != segments.rend(); ++it)
648 if(!walk_segment(it->first, it->second))
657 [[nodiscard]] ossia_constexpr_msvc_workaround std::optional<physical_time>
658 get_physical_quantification_date(
double rate,
double modelToSamples)
const noexcept
660 if(
auto pt = get_quantification_point(rate))
661 return physical_start(modelToSamples)
662 + physical_position(pt->position, modelToSamples);
666 template <
typename Tick,
typename Tock>
668 metronome(
double modelToSamplesRatio, Tick tick, Tock tock)
const noexcept
670 const double musical_tick_duration = musical_end_position - musical_start_position;
671 const bool rewinding = backward();
675 if(musical_tick_duration == 0. || (musical_tick_duration < 0.) != rewinding)
681 const int64_t samples_tick_duration = physical_write_duration(modelToSamplesRatio);
682 if(samples_tick_duration < 0)
687 const auto sample_of = [&](
double musical_position) {
688 return physical_position(musical_position, modelToSamplesRatio);
691 const double quarters_in_bar = 4. * signature.upper / signature.lower;
692 if(!(quarters_in_bar > 0.))
698 const double lo = rewinding ? musical_end_position : musical_start_position;
699 const double hi = rewinding ? musical_start_position : musical_end_position;
700 const double bar0 = rewinding ? musical_end_last_bar : musical_start_last_bar;
706 const auto emit = [&](
double p,
bool is_bar) {
707 if(rewinding ? (p > hi || p <= lo) : (p < lo || p >= hi))
720 for_each_bar_segment(lo, hi, [&](
double bar_line,
double next_bar) {
723 seg_lo[n_seg] = bar_line;
724 seg_hi[n_seg] = next_bar;
729 const auto walk_segment = [&](
double bar_line,
double next_bar) {
732 emit(bar_line,
true);
733 for(
double q = bar_line + 1.; q < next_bar - 1e-9; q += 1.)
738 double last = bar_line;
739 for(
double q = bar_line + 1.; q < next_bar - 1e-9; q += 1.)
741 for(
double q = last; q > bar_line + 1e-9; q -= 1.)
743 emit(bar_line,
true);
748 for(
int i = 0; i < n_seg; i++)
749 walk_segment(seg_lo[i], seg_hi[i]);
751 for(
int i = n_seg - 1; i >= 0; i--)
752 walk_segment(seg_lo[i], seg_hi[i]);
755 [[nodiscard]]
constexpr bool unexpected_bar_change() const noexcept
757 double bar_difference = musical_end_last_bar - musical_start_last_bar;
758 if(bar_difference != 0.)
766 if(bar_difference < 0.)
767 bar_difference = -bar_difference;
769 double quarters_sig = 4. * double(signature.upper) / signature.lower;
770 double div = bar_difference / quarters_sig;
771 bool unexpected = div - int64_t(div) > 0.000001;
780 [[nodiscard]]
constexpr double tick_fraction_at(time_value t)
const noexcept
782 const double total = double(date.impl - prev_date.impl);
785 double f = double(t.impl - prev_date.impl) / total;
797 [[nodiscard]]
constexpr double musical_position_at(time_value t)
const noexcept
799 return musical_start_position
800 + (musical_end_position - musical_start_position) * tick_fraction_at(t);
803 constexpr void set_end_time(time_value t)
noexcept
806 const double f = tick_fraction_at(t);
807 if(length_sample > 0)
808 length_sample = int32_t(length_sample * f + 0.5);
810 musical_end_position = musical_position_at(t);
816 constexpr void set_start_time(time_value t)
noexcept
819 if(length_sample > 0)
821 const auto skipped = int32_t(length_sample * tick_fraction_at(t) + 0.5);
822 start_sample += skipped;
823 length_sample -= skipped;
826 musical_start_position = musical_position_at(t);
833 ossia::time_value prev_date{};
834 ossia::time_value date{};
835 ossia::time_value parent_duration{};
850 ossia::time_value offset{};
853 double tempo{ossia::root_tempo};
854 time_signature signature{};
867 int32_t start_sample{-1};
868 int32_t length_sample{-1};
870 bool start_discontinuous{};
871 bool end_discontinuous{};
873 ossia::quarter_note musical_start_last_signature{};
876 ossia::quarter_note musical_start_last_bar{};
878 ossia::quarter_note musical_start_position{};
879 ossia::quarter_note musical_end_last_bar{};
881 ossia::quarter_note musical_end_position{};
886static_assert(std::is_trivially_copyable_v<token_request>);
887static_assert(std::is_standard_layout_v<token_request>);
888static_assert(
sizeof(token_request) == 104);
890inline bool operator==(
const token_request& lhs,
const token_request& rhs)
892 return lhs.prev_date == rhs.prev_date && lhs.date == rhs.date
893 && lhs.parent_duration == rhs.parent_duration && lhs.offset == rhs.offset
894 && lhs.speed == rhs.speed && lhs.tempo == rhs.tempo
895 && lhs.signature == rhs.signature
896 && lhs.musical_start_last_bar == rhs.musical_start_last_bar
897 && lhs.musical_start_position == rhs.musical_start_position
898 && lhs.musical_end_last_bar == rhs.musical_end_last_bar
899 && lhs.musical_end_position == rhs.musical_end_position
900 && lhs.start_discontinuous == rhs.start_discontinuous
901 && lhs.end_discontinuous == rhs.end_discontinuous;
904inline bool operator!=(
const token_request& lhs,
const token_request& rhs)
906 return !(lhs == rhs);
910struct simple_token_request
912 time_value prev_date{};
914 time_value parent_duration{};
917 operator token_request() const noexcept
919 return ossia::token_request{prev_date, date, parent_duration, offset, 1.0,
923 friend bool operator==(
const token_request& lhs,
const simple_token_request& self)
925 return lhs.prev_date == self.prev_date && lhs.date == self.date
926 && lhs.offset == self.offset;
928 friend bool operator==(
const simple_token_request& self,
const token_request& rhs)
932 friend bool operator!=(
const token_request& lhs,
const simple_token_request& self)
934 return !(lhs == self);
936 friend bool operator!=(
const simple_token_request& self,
const token_request& rhs)
938 return !(rhs == self);
@ off
No motion gate: nearest-neighbour within the cost ranking.
Definition point_tracker.hpp:68
constexpr int64_t musical_to_frame(double position, double start, double end, int64_t frames) noexcept
The frame, counted from the start of a span of frames frames covering the musical positions [start; e...
Definition ossia/dataflow/token_request.hpp:25
One quantification point inside a tick: when it happens, where it happens musically,...
Definition ossia/dataflow/token_request.hpp:45
double position
Where the point sits in musical time, in quarters.
Definition ossia/dataflow/token_request.hpp:56
The time_value class.
Definition ossia/editor/scenario/time_value.hpp:30