299 static_assert(MaxVoices > 0 && MaxInputs > 0);
300 static constexpr frame_t frame_limit = std::numeric_limits<frame_t>::max() / 4;
301 enum class State : std::uint8_t
307 enum class EndKind : std::uint8_t
319 std::uint8_t source{}, pitch{}, velocity{}, channel{};
320 std::uint64_t serial{};
321 frame_t arrival_frame{}, not_before{};
322 std::int64_t model_anchor{};
323 position_t arrival_quarter{}, start_target{}, end_target{};
327 position_t activation_quarter{}, min_quarters{}, max_quarters{};
328 double start_quant{}, tightness{}, end_quant{}, duration{};
329 Bound min_duration{}, max_duration{};
341 static constexpr std::size_t max_voices = MaxVoices;
342 static constexpr std::size_t max_inputs = MaxInputs;
343 static constexpr std::size_t wire_notes = 16 * 128;
345 static constexpr std::size_t max_output_events
346 = 2 * MaxInputs + 2 * MaxVoices + wire_notes;
348 void seed(std::uint64_t value)
noexcept { random_.seed(value); }
349 [[nodiscard]]
const Statistics& statistics()
const noexcept {
return stats_; }
350 [[nodiscard]] std::size_t active_count()
const noexcept
352 return count(State::active);
354 [[nodiscard]] std::size_t pending_count()
const noexcept
356 return count(State::pending);
358 [[nodiscard]] std::size_t deferred_release_count()
const noexcept
360 return owed_offs_.count();
362 [[nodiscard]]
bool needs_service()
const noexcept
364 return active_count() || pending_count() || owed_offs_.any();
370 void request_reset()
noexcept
372 for(
auto& v : voices_)
374 if(v.state == State::active)
376 v.state = State::free;
378 have_previous_ =
false;
382 void begin_input()
noexcept
389 if(input_count_ == MaxInputs)
394 inputs_[input_count_] = {frame, note, input_count_};
398 void input_overflow()
noexcept { overflow_ =
true; }
402 template <
typename Sink>
403 bool drain_releases(
int frame,
int frames, Sink&& sink)
noexcept
405 if(frame < 0 || frame >= frames)
406 return !owed_offs_.any();
407 for(std::size_t k = 0; k < wire_notes; ++k)
413 frame,
static_cast<std::uint8_t
>(k / 128),
414 static_cast<std::uint8_t
>(k % 128), 0,
false},
422 template <
typename Sink>
423 void process(
const Block& b,
Settings settings, Sink&& sink)
noexcept
429 if(b.frames < 0 || b.discontinuity || b.end_discontinuity
430 || b.model_begin != b.model_end || b.quarters_begin != b.quarters_end
431 || changed_position(b))
438 ++stats_.invalid_blocks;
440 drain_releases(0, b.frames, sink);
444 const int md = direction(b.model_begin, b.model_end);
445 const int qd = direction(b.quarters_begin, b.quarters_end);
448 && ((md && previous_model_direction_ && md != previous_model_direction_)
449 || (qd && previous_quarter_direction_
450 && qd != previous_quarter_direction_));
451 if(b.discontinuity || changed_position(b) || reversed)
458 if(!drain_releases(0, b.frames, sink))
471 ++stats_.input_overflows;
473 drain_releases(0, b.frames, sink);
479 settings = sanitize(settings);
480 std::size_t kept = 0;
481 for(std::size_t i = 0; i < input_count_; ++i)
482 if(inputs_[i].frame >= 0 && inputs_[i].frame < b.frames
483 && inputs_[i].note.pitch <= 127 && inputs_[i].note.velocity <= 127)
484 inputs_[kept++] = inputs_[i];
486 ++stats_.rejected_inputs;
489 inputs_.begin(), inputs_.begin() + input_count_,
490 [](
const Input& a,
const Input& c) {
491 return a.frame != c.frame ? a.frame < c.frame : a.order < c.order;
495 const bool meter_changed = have_previous_
496 && (b.numerator != previous_numerator_
497 || b.denominator != previous_denominator_
498 || b.last_signature != previous_signature_);
499 for(
auto& v : voices_)
501 if(v.state == State::pending && meter_changed && v.start_quant > 0. && qd)
502 set_start_target(v, grid, b, qd);
503 if(v.state == State::active && v.end_kind == EndKind::grid && meter_changed && qd
504 && qd * (v.end_target - b.quarters_begin) > 0.L)
505 v.end_target = clamp_grid_end(
506 v, v.activation_quarter, grid.next(b.quarters_begin, v.end_quant, qd),
508 if(v.state != State::free)
509 v.due = deadline(v, b, grid);
513 std::size_t input = 0;
516 int now = input < input_count_ ? inputs_[input].frame : b.frames;
517 for(
const auto& v : voices_)
518 if(v.state != State::free)
519 now = std::min(now, v.due);
524 for(
auto& v : voices_)
525 if(v.state == State::active && v.due == now)
526 if(!release(v, now, sink))
533 while(input < input_count_ && inputs_[input].frame == now)
535 const auto note = inputs_[input++].note;
536 if(note.velocity == 0)
537 ok = cancel_source(note.pitch, now, sink);
539 enqueue(note, now, settings, b, grid);
547 for(
auto& v : voices_)
549 if(v.state != State::pending || v.due != now)
551 for(
const auto& other : voices_)
552 if(other.state == State::pending && other.due == now
553 && other.channel == v.channel && other.pitch == v.pitch
554 && other.serial > v.serial)
556 v.state = State::free;
557 ++stats_.coalesced_starts;
562 for(
const auto& p : voices_)
564 if(p.state != State::pending || p.due != now)
566 for(
auto& a : voices_)
567 if(a.state == State::active && a.channel == p.channel && a.pitch == p.pitch)
569 ok = release(a, now, sink);
581 Voice* next =
nullptr;
582 for(
auto& v : voices_)
583 if(v.state == State::pending && v.due == now
584 && (!next || v.serial < next->serial))
588 if(!activate(*next, now, b, grid, sink))
604 if(b.end_discontinuity)
609 [[nodiscard]] std::size_t count(
State state)
const noexcept
611 return std::count_if(voices_.begin(), voices_.end(), [state](
const Voice& v) {
612 return v.state == state;
615 void owe_off(
const Voice& v)
noexcept
617 owed_offs_.set(std::size_t(v.channel) * 128 + v.pitch);
619 template <
typename Sink>
620 bool send(
const MidiEvent& e, Sink& sink)
noexcept
622 if constexpr(std::is_void_v<std::invoke_result_t<Sink&, const MidiEvent&>>)
631 ++stats_.output_failures;
635 static bool valid(
const Block& b)
noexcept
637 auto position = [](
double q) {
return std::isfinite(q) && std::abs(q) <= 1e9; };
638 return b.first_frame >= -frame_limit && b.first_frame <= frame_limit - b.frames
639 && position(b.quarters_begin) && position(b.quarters_end)
640 && position(b.bar_begin) && position(b.bar_end) && position(b.last_signature)
641 && b.numerator >= 0 && b.numerator <= 1024 && b.denominator >= 0
642 && b.denominator <= 1024;
644 bool changed_position(
const Block& b)
const noexcept
646 return have_previous_
647 && (b.first_frame != previous_frame_ || b.model_begin != previous_model_
648 || std::abs(position_t(b.quarters_begin) - previous_quarter_) > 1e-8L);
650 static position_t at(position_t a, position_t z,
int f,
int frames)
noexcept
652 return a + (z - a) * (position_t(f) / frames);
654 static position_t quarter_at(
const Block& b,
int f)
noexcept
656 return at(b.quarters_begin, b.quarters_end, f, b.frames);
662 static position_t resolve_bound(
const Bound& bound,
const Block& b)
noexcept
664 if(!(bound.value > 0.))
666 if(bound.unit == DurationUnit::quarters)
667 return position_t(bound.value);
669 = std::abs(model_delta(b.model_end, b.model_begin)) / flicks_per_second;
670 const auto quarters = std::abs(position_t(b.quarters_end) - position_t(b.quarters_begin));
671 if(!(seconds > 0.L) || !(quarters > 0.L))
673 return position_t(bound.value) * (quarters / seconds);
679 static position_t clamp_grid_end(
680 const Voice& v, position_t from, position_t target,
const Grid& grid)
noexcept
682 const int dir = v.direction;
685 if(v.min_quarters > 0.L)
687 const auto floor_at = from + dir * v.min_quarters;
688 if(dir * (floor_at - target) > 0.L)
689 target = grid.next(floor_at, v.end_quant, dir);
691 if(v.max_quarters > 0.L)
693 const auto ceil_at = from + dir * std::max(v.max_quarters, v.min_quarters);
694 if(dir * (target - ceil_at) > 0.L)
702 static position_t model_delta(std::int64_t to, std::int64_t from)
noexcept
705 return position_t(std::uint64_t(to) - std::uint64_t(from));
706 return -position_t(std::uint64_t(from) - std::uint64_t(to));
709 frame_of(position_t a, position_t z, position_t target,
int frames,
bool ceil)
noexcept
713 const auto x = clean_integer((target - a) / (z - a) * frames);
714 if(!std::isfinite(x) || x >= frames)
718 return static_cast<int>(ceil ? std::ceil(x) : std::floor(x));
720 static int local_frame(
const Block& b, frame_t absolute)
noexcept
722 if(absolute <= b.first_frame)
724 if(absolute >= b.first_frame + b.frames)
726 return static_cast<int>(absolute - b.first_frame);
729 set_start_target(Voice& v,
const Grid& grid,
const Block& b,
int dir)
noexcept
731 const auto from = dir > 0
732 ? std::max(v.arrival_quarter, position_t(b.quarters_begin))
733 : std::min(v.arrival_quarter, position_t(b.quarters_begin));
734 v.start_target = onset_target(grid, from, v.start_quant, v.tightness, dir);
736 int deadline(
const Voice& v,
const Block& b,
const Grid& grid)
const noexcept
738 if(v.state == State::pending)
740 const int earliest = local_frame(b, v.arrival_frame);
741 if(v.start_quant == 0. || v.tightness == 0.)
745 frame_of(b.quarters_begin, b.quarters_end, v.start_target, b.frames,
true));
747 const int earliest = local_frame(b, v.not_before);
750 case EndKind::sample:
755 0.L, model_delta(b.model_end, b.model_begin),
756 model_delta(v.model_anchor, b.model_begin) + v.end_target,
758 case EndKind::musical:
761 frame_of(b.quarters_begin, b.quarters_end, v.end_target, b.frames,
true));
765 frame_of(b.quarters_begin, b.quarters_end, v.end_target, b.frames,
true));
771 template <
typename Sink>
772 bool release(Voice& v,
int frame, Sink& sink)
noexcept
774 const bool ok = send(
MidiEvent{frame, v.channel, v.pitch, 0,
false}, sink);
777 v.state = State::free;
780 template <
typename Sink>
781 bool cancel_source(std::uint8_t source,
int frame, Sink& sink)
noexcept
783 for(
auto& v : voices_)
784 if(v.state != State::free && v.source == source)
786 if(v.state == State::active)
788 if(!release(v, frame, sink))
792 v.state = State::free;
798 const Grid& grid)
noexcept
802 Voice* free =
nullptr;
803 for(
auto& v : voices_)
805 if(v.state == State::free && !free)
810 ++stats_.dropped_triggers;
815 v.state = State::pending;
816 v.source = note.pitch;
817 v.pitch =
static_cast<std::uint8_t
>(std::clamp(
818 int(note.pitch) + s.pitch_shift
820 s.pitch_direction == PitchDirection::Higher ? 0 : -s.pitch_random,
821 s.pitch_direction == PitchDirection::Lower ? 0 : s.pitch_random),
823 v.velocity =
static_cast<std::uint8_t
>(
824 std::clamp(
int(note.velocity) + random_.symmetric(s.velocity_random), 1, 127));
825 v.channel =
static_cast<std::uint8_t
>(s.channel - 1);
826 v.serial = ++serial_;
827 v.arrival_frame = b.first_frame + frame;
828 v.arrival_quarter = quarter_at(b, frame);
829 v.start_quant = s.start_quant;
830 v.tightness = s.tightness;
831 v.end_quant = s.end_quant;
832 v.duration = s.duration;
833 v.min_duration = s.min_duration;
834 v.max_duration = s.max_duration;
836 = s.end_mode == EndMode::duration
837 ? (s.duration == 0. ? EndKind::sample
838 : s.duration_unit == DurationUnit::model_seconds ? EndKind::model
840 : (s.end_quant > 0. ? EndKind::grid
841 : s.end_quant < 0. ? EndKind::hold
843 const int qd = direction(b.quarters_begin, b.quarters_end);
844 v.direction = qd ? qd : direction(b.model_begin, b.model_end);
845 if(s.start_quant > 0.)
846 set_start_target(v, grid, b, v.direction);
847 v.due = deadline(v, b, grid);
849 template <
typename Sink>
851 activate(Voice& v,
int frame,
const Block& b,
const Grid& grid, Sink& sink)
noexcept
853 if(!send(
MidiEvent{frame, v.channel, v.pitch, v.velocity,
true}, sink))
855 v.state = State::free;
858 v.state = State::active;
859 v.not_before = b.first_frame + frame + 1;
860 if(v.end_kind == EndKind::model)
862 v.model_anchor = b.model_begin;
864 = model_delta(b.model_end, b.model_begin) * (position_t(frame) / b.frames)
865 + direction(b.model_begin, b.model_end) * v.duration * flicks_per_second;
867 else if(v.end_kind == EndKind::musical)
868 v.end_target = quarter_at(b, frame) + v.direction * v.duration;
869 else if(v.end_kind == EndKind::grid)
871 v.activation_quarter = quarter_at(b, frame);
872 v.min_quarters = resolve_bound(v.min_duration, b);
873 v.max_quarters = resolve_bound(v.max_duration, b);
874 v.end_target = clamp_grid_end(
875 v, v.activation_quarter,
876 grid.next(v.activation_quarter, v.end_quant, v.direction,
true), grid);
878 v.due = deadline(v, b, grid);
881 void remember(
const Block& b)
noexcept
883 previous_frame_ = b.first_frame + b.frames;
884 previous_model_ = b.model_end;
885 previous_quarter_ = b.quarters_end;
886 previous_model_direction_ = direction(b.model_begin, b.model_end);
887 previous_quarter_direction_ = direction(b.quarters_begin, b.quarters_end);
888 previous_numerator_ = b.numerator;
889 previous_denominator_ = b.denominator;
890 previous_signature_ = b.last_signature;
891 have_previous_ =
true;
894 std::array<Voice, MaxVoices> voices_{};
895 std::array<Input, MaxInputs> inputs_{};
898 std::bitset<wire_notes> owed_offs_{};
899 std::size_t input_count_{};
900 std::uint64_t serial_{};
903 frame_t previous_frame_{};
904 std::int64_t previous_model_{};
905 double previous_quarter_{}, previous_signature_{};
906 int previous_model_direction_{}, previous_quarter_direction_{};
907 int previous_numerator_{}, previous_denominator_{};
908 bool have_previous_{}, overflow_{};