2#include <ossia/detail/flicks.hpp>
4#include <ossia/detail/small_vector.hpp>
5#include <ossia/editor/scenario/time_signature.hpp>
13#if defined(_LIBCPP_CONSTEXPR_SINCE_CXX23) || defined(_GLIBCXX23_CONSTEXPR)
14#define ossia_constexpr_msvc_workaround constexpr
16#define ossia_constexpr_msvc_workaround
20using quarter_note = double;
42using quantification_points = ossia::small_vector<quantification_point, 8>;
46 constexpr token_request() noexcept = default;
47 constexpr token_request(const token_request&) noexcept = default;
48 constexpr token_request(token_request&&) noexcept = default;
49 constexpr token_request& operator=(const token_request&) noexcept = default;
50 constexpr token_request& operator=(token_request&&) noexcept = default;
52 constexpr token_request(
53 ossia::time_value prev_d,
ossia::time_value d,
ossia::time_value parent_duration,
54 ossia::time_value off,
double s, time_signature sig,
double tempo) noexcept
57 , parent_duration{parent_duration}
69 [[nodiscard]]
constexpr token_request add_offset(
ossia::time_value t)
const noexcept
71 token_request other = *
this;
77 template <
typename Exec,
typename Transport>
80 Transport transport)
const noexcept
82 if(loop_duration.impl <= 0)
84 token_request other = add_offset(start_offset);
89 ossia::token_request other = *
this;
97 const int64_t total_amount
98 = tick_amount.impl >= 0 ? tick_amount.impl : -tick_amount.impl;
100 = start_sample >= 0 && length_sample >= 0 && total_amount > 0;
101 int64_t consumed = 0;
102 const auto set_piece_span = [&](int64_t piece)
constexpr {
105 const int64_t s0 = start_sample
106 + (int64_t{length_sample} * consumed + total_amount / 2)
110 + (int64_t{length_sample} * (consumed + piece) + total_amount / 2)
112 other.start_sample = int32_t(s0);
113 other.length_sample = int32_t(s1 - s0);
118 if(tick_amount >= 0_tv)
121 while(tick_amount > 0_tv)
123 const time_value cur_from{orig_from % loop_duration};
124 if(cur_from + tick_amount < loop_duration)
126 other.prev_date = cur_from + start_offset;
127 other.date = other.prev_date + tick_amount;
128 set_piece_span(tick_amount.impl);
134 auto this_tick = loop_duration - cur_from;
136 tick_amount -= this_tick;
137 orig_from += this_tick;
138 other.prev_date = cur_from + start_offset;
139 other.date = other.prev_date + this_tick;
141 set_piece_span(this_tick.impl);
144 transport(start_offset);
145 other.offset += this_tick;
152 const int64_t loop_dur = loop_duration.impl;
153 int64_t remaining = -tick_amount.impl;
157 int64_t cur_from = orig_from.impl % loop_dur;
159 cur_from += loop_dur;
163 transport(start_offset + loop_duration);
167 const int64_t this_tick = remaining < cur_from ? remaining : cur_from;
169 other.prev_date = time_value{cur_from} + start_offset;
170 other.date = other.prev_date - time_value{this_tick};
172 set_piece_span(this_tick);
175 remaining -= this_tick;
176 orig_from -= time_value{this_tick};
177 other.offset += time_value{this_tick};
183 [[nodiscard]]
constexpr time_value model_read_duration() const noexcept
185 return date - prev_date;
188 [[nodiscard]]
static constexpr double abs_speed(
double s)
noexcept
190 return s < 0. ? -s : s;
196 [[nodiscard]]
constexpr physical_time
197 start_date_to_physical(
double ratio)
const noexcept
201 return this->prev_date.impl * ratio / abs_speed(speed);
208 [[nodiscard]]
constexpr physical_time
212 return constexpr_floor(tick_position.impl * ratio / abs_speed(speed));
218 [[nodiscard]]
constexpr physical_time physical_start(
double ratio)
const noexcept
221 if(start_sample >= 0)
223 return sample_at(this->offset, ratio);
229 [[nodiscard]]
constexpr physical_time
230 physical_read_duration(
double ratio)
const noexcept
234 const auto a = prev_date.impl < date.impl ? prev_date.impl : date.impl;
235 const auto b = prev_date.impl < date.impl ? date.impl : prev_date.impl;
236 return constexpr_floor(b * ratio) - constexpr_floor(a * ratio);
243 [[nodiscard]]
constexpr physical_time
244 physical_write_duration(
double ratio)
const noexcept
247 if(length_sample >= 0)
248 return length_sample;
249 return sample_at(this->offset + abs(date - prev_date), ratio)
250 - sample_at(this->offset, ratio);
254 [[nodiscard]]
constexpr physical_time
255 safe_physical_write_duration(
double ratio,
int bufferSize)
const noexcept
258 return bufferSize - physical_start(ratio);
272 [[nodiscard]]
constexpr physical_time
273 physical_position(
double musical_position,
double ratio)
const noexcept
278 if(speed == 0. && length_sample < 0)
281 const int64_t len = physical_write_duration(ratio);
285 const double musical_tick_duration = musical_end_position - musical_start_position;
286 if(musical_tick_duration == 0.)
292 = (musical_position - musical_start_position) / musical_tick_duration;
293 const int64_t s = constexpr_floor(r * len);
294 return s < 0 ? int64_t(0) : (s >= len ? len - 1 : s);
298 [[nodiscard]]
constexpr bool in_range(
ossia::time_value global_time)
const noexcept
300 return global_time.impl >= prev_date.impl && global_time.impl < date.impl;
305 [[nodiscard]]
constexpr physical_time
306 to_physical_time_in_tick(
ossia::time_value global_time,
double ratio)
const noexcept
309 const int64_t in_tick
310 = speed < 0. ? (prev_date - global_time).impl : (global_time - prev_date).impl;
315 if(start_sample >= 0 && length_sample >= 0)
317 const int64_t dt = abs(date - prev_date).impl;
320 int64_t s = start_sample
321 + constexpr_floor(
double(in_tick) /
double(dt) * length_sample);
324 else if(s > int64_t(start_sample) + length_sample)
325 s = int64_t(start_sample) + length_sample;
335 [[nodiscard]]
constexpr physical_time
336 to_physical_time_in_tick(int64_t global_time,
double ratio)
const noexcept
343 [[nodiscard]]
constexpr time_value
344 from_physical_time_in_tick(ossia::physical_time phys_time,
double ratio)
const noexcept
347 const double in_tick = phys_time - physical_start(ratio);
348 return time_value{constexpr_floor(in_tick * (speed / ratio) + prev_date.impl)};
353 [[nodiscard]]
constexpr double position() const noexcept
355 return parent_duration.impl > 0 ? date.impl / double(parent_duration.impl) : 0.;
359 [[nodiscard]]
constexpr bool forward() const noexcept {
return date > prev_date; }
362 [[nodiscard]]
constexpr bool paused() const noexcept {
return date == prev_date; }
365 [[nodiscard]]
constexpr bool backward() const noexcept {
return date < prev_date; }
375 template <
typename F>
376 void for_each_bar_segment(
double lo,
double hi, F&& fn)
const noexcept
378 const bool valid_sig = signature.upper > 0 && signature.lower > 0;
379 const double quarters_in_bar
380 = valid_sig ? 4. * signature.upper / signature.lower : 4.;
381 if(!(quarters_in_bar > 0.))
384 constexpr double eps = 1e-9;
385 const bool rewinding = date < prev_date;
386 const double near_bar = rewinding ? musical_end_last_bar : musical_start_last_bar;
387 const double far_bar = rewinding ? musical_start_last_bar : musical_end_last_bar;
391 for(
int i = 0; i < 1024 && b < far_bar - eps; i++)
393 const double next = (b + quarters_in_bar < far_bar) ? b + quarters_in_bar : far_bar;
394 if(next > lo + eps && b < hi + eps)
396 b += quarters_in_bar;
400 b = (far_bar > near_bar) ? far_bar : near_bar;
401 for(
int i = 0; i < 1024 && b < hi + eps; i++)
403 fn(b, b + quarters_in_bar);
404 b += quarters_in_bar;
415 [[nodiscard]] std::optional<quantification_point>
416 get_quantification_point(
double rate)
const noexcept
418 if(prev_date == date)
425 const auto pts = get_quantification_dates(rate);
437 [[nodiscard]] std::optional<time_value>
438 get_quantification_date(
double rate)
const noexcept
440 if(
const auto pt = get_quantification_point(rate))
452 [[nodiscard]] quantification_points
453 get_quantification_dates(
double rate)
const noexcept
455 quantification_points res;
457 if(prev_date == date)
460 const double musical_tick_duration = musical_end_position - musical_start_position;
461 const bool rewinding = date < prev_date;
465 if(rate <= 0. || musical_tick_duration == 0.
466 || (musical_tick_duration < 0.) != rewinding)
468 res.push_back({prev_date, 0, musical_start_position});
475 constexpr double eps = 1e-9;
476 const time_value tick_duration = date - prev_date;
477 const bool valid_sig = signature.upper > 0 && signature.lower > 0;
478 const double quarters_in_bar = valid_sig ? 4. * signature.upper / signature.lower : 4.;
482 const auto try_push = [&](
double musical_position, int64_t index) {
488 const double scale = double(tick_duration.impl) / musical_tick_duration;
490 = prev_date + (musical_position - musical_start_position) * scale;
499 if(musical_position <= musical_end_position)
508 if(musical_position >= musical_end_position)
516 res.push_back({d, index, musical_position});
519 return res.size() < 1024;
526 const double unit = quarters_in_bar / rate;
530 const double origin = musical_start_last_signature;
531 const double start = (musical_start_position - origin) / unit;
532 const double end = (musical_end_position - origin) / unit;
533 const int64_t first = rewinding ? int64_t(std::floor(start + eps))
534 : int64_t(std::ceil(start - eps));
536 for(int64_t k = first; rewinding ? (k > end + eps) : (k < end - eps);
537 k += rewinding ? -1 : 1)
539 if(!try_push(k * unit + origin, k))
550 const double unit = 4. / rate;
551 if(!(unit > 0.) || !(quarters_in_bar > 0.))
554 const double lo = rewinding ? musical_end_position : musical_start_position;
555 const double hi = rewinding ? musical_start_position : musical_end_position;
559 ossia::small_vector<std::pair<double, double>, 8> segments;
560 for_each_bar_segment(lo, hi, [&](
double bar_line,
double next_bar) {
561 if(segments.size() < 1024)
562 segments.push_back({bar_line, next_bar});
565 const auto walk_segment = [&](
double bar_line,
double next_bar) {
566 const int divs = int(std::ceil((next_bar - bar_line) / unit)) + 1;
569 for(int64_t k = 0; k <= divs; k++)
571 const double p = bar_line + k * unit;
572 if(p >= next_bar - eps || p > hi + eps)
582 for(int64_t k = divs; k >= 0; k--)
584 const double p = bar_line + k * unit;
585 if(p >= next_bar - eps || p > hi + eps)
598 for(
const auto& [b, n] : segments)
599 if(!walk_segment(b, n))
604 for(
auto it = segments.rbegin(); it != segments.rend(); ++it)
605 if(!walk_segment(it->first, it->second))
616 [[nodiscard]] ossia_constexpr_msvc_workaround std::optional<physical_time>
617 get_physical_quantification_date(
double rate,
double modelToSamples)
const noexcept
619 if(
auto pt = get_quantification_point(rate))
620 return physical_start(modelToSamples)
621 + physical_position(pt->position, modelToSamples);
625 template <
typename Tick,
typename Tock>
627 metronome(
double modelToSamplesRatio, Tick tick, Tock tock)
const noexcept
629 const double musical_tick_duration = musical_end_position - musical_start_position;
630 const bool rewinding = backward();
634 if(musical_tick_duration == 0. || (musical_tick_duration < 0.) != rewinding)
640 const int64_t samples_tick_duration = physical_write_duration(modelToSamplesRatio);
641 if(samples_tick_duration < 0)
646 const auto sample_of = [&](
double musical_position) {
647 return physical_position(musical_position, modelToSamplesRatio);
650 const double quarters_in_bar = 4. * signature.upper / signature.lower;
651 if(!(quarters_in_bar > 0.))
657 const double lo = rewinding ? musical_end_position : musical_start_position;
658 const double hi = rewinding ? musical_start_position : musical_end_position;
659 const double bar0 = rewinding ? musical_end_last_bar : musical_start_last_bar;
665 const auto emit = [&](
double p,
bool is_bar) {
666 if(rewinding ? (p > hi || p <= lo) : (p < lo || p >= hi))
679 for_each_bar_segment(lo, hi, [&](
double bar_line,
double next_bar) {
682 seg_lo[n_seg] = bar_line;
683 seg_hi[n_seg] = next_bar;
688 const auto walk_segment = [&](
double bar_line,
double next_bar) {
691 emit(bar_line,
true);
692 for(
double q = bar_line + 1.; q < next_bar - 1e-9; q += 1.)
697 double last = bar_line;
698 for(
double q = bar_line + 1.; q < next_bar - 1e-9; q += 1.)
700 for(
double q = last; q > bar_line + 1e-9; q -= 1.)
702 emit(bar_line,
true);
707 for(
int i = 0; i < n_seg; i++)
708 walk_segment(seg_lo[i], seg_hi[i]);
710 for(
int i = n_seg - 1; i >= 0; i--)
711 walk_segment(seg_lo[i], seg_hi[i]);
714 [[nodiscard]]
constexpr bool unexpected_bar_change() const noexcept
716 double bar_difference = musical_end_last_bar - musical_start_last_bar;
717 if(bar_difference != 0.)
725 if(bar_difference < 0.)
726 bar_difference = -bar_difference;
728 double quarters_sig = 4. * double(signature.upper) / signature.lower;
729 double div = bar_difference / quarters_sig;
730 bool unexpected = div - int64_t(div) > 0.000001;
739 [[nodiscard]]
constexpr double tick_fraction_at(time_value t)
const noexcept
741 const double total = double(date.impl - prev_date.impl);
744 double f = double(t.impl - prev_date.impl) / total;
752 constexpr void set_end_time(time_value t)
noexcept
755 if(length_sample > 0)
756 length_sample = int32_t(length_sample * tick_fraction_at(t) + 0.5);
758 const auto old_date = date;
761 if(old_date.impl > 0)
763 double ratio = t.impl / double(old_date.impl);
764 musical_end_position *= ratio;
770 constexpr void set_start_time(time_value t)
noexcept
773 if(length_sample > 0)
775 const auto skipped = int32_t(length_sample * tick_fraction_at(t) + 0.5);
776 start_sample += skipped;
777 length_sample -= skipped;
780 const auto old_date = prev_date;
783 if(old_date.impl > 0)
785 double ratio = t.impl / double(old_date.impl);
786 musical_start_position *= ratio;
813 double tempo{ossia::root_tempo};
814 time_signature signature{};
827 int32_t start_sample{-1};
828 int32_t length_sample{-1};
830 bool start_discontinuous{};
831 bool end_discontinuous{};
833 ossia::quarter_note musical_start_last_signature{};
836 ossia::quarter_note musical_start_last_bar{};
838 ossia::quarter_note musical_start_position{};
839 ossia::quarter_note musical_end_last_bar{};
841 ossia::quarter_note musical_end_position{};
846static_assert(std::is_trivially_copyable_v<token_request>);
847static_assert(std::is_standard_layout_v<token_request>);
848static_assert(
sizeof(token_request) == 104);
850inline bool operator==(
const token_request& lhs,
const token_request& rhs)
852 return lhs.prev_date == rhs.prev_date && lhs.date == rhs.date
853 && lhs.parent_duration == rhs.parent_duration && lhs.offset == rhs.offset
854 && lhs.speed == rhs.speed && lhs.tempo == rhs.tempo
855 && lhs.signature == rhs.signature
856 && lhs.musical_start_last_bar == rhs.musical_start_last_bar
857 && lhs.musical_start_position == rhs.musical_start_position
858 && lhs.musical_end_last_bar == rhs.musical_end_last_bar
859 && lhs.musical_end_position == rhs.musical_end_position
860 && lhs.start_discontinuous == rhs.start_discontinuous
861 && lhs.end_discontinuous == rhs.end_discontinuous;
864inline bool operator!=(
const token_request& lhs,
const token_request& rhs)
866 return !(lhs == rhs);
870struct simple_token_request
872 time_value prev_date{};
874 time_value parent_duration{};
877 operator token_request() const noexcept
879 return ossia::token_request{prev_date, date, parent_duration, offset, 1.0,
883 friend bool operator==(
const token_request& lhs,
const simple_token_request& self)
885 return lhs.prev_date == self.prev_date && lhs.date == self.date
886 && lhs.offset == self.offset;
888 friend bool operator==(
const simple_token_request& self,
const token_request& rhs)
892 friend bool operator!=(
const token_request& lhs,
const simple_token_request& self)
894 return !(lhs == self);
896 friend bool operator!=(
const simple_token_request& self,
const token_request& rhs)
898 return !(rhs == self);
Definition ossia/dataflow/token_request.hpp:25
double position
Definition ossia/dataflow/token_request.hpp:36
The time_value class.
Definition ossia/editor/scenario/time_value.hpp:30