254 static_assert(MaxLanes > 0);
255 static_assert(MaxSteps > 0);
256 static_assert(MaxRatchetHits > 0);
257 static_assert(MaximumStepsPerBlock > 0);
258 static_assert(MaxLanes <= std::numeric_limits<std::size_t>::max() / MaxSteps);
259 static_assert(MaxLanes <= std::numeric_limits<std::size_t>::max() / (MaxRatchetHits + 1));
260 static_assert(MaxLanes <= 32,
"lane parking uses a 32-bit mask");
278 std::array<StepPlayerLaneSpec, MaxLanes>
lanes{};
284 constexpr auto burst_events = 2 * MaxRatchetHits;
285 constexpr auto generated =
286 MaximumStepsPerBlock > (std::numeric_limits<std::size_t>::max() -
289 ? std::numeric_limits<std::size_t>::max()
293 MaximumStepsPerBlock,
304 return lane.channel <= 15 && lane.base_note <= 127 && lane.step_count >= 1 &&
305 lane.step_count <= MaxSteps && lane.step_duration.value > 0 &&
306 lane.step_duration.value <= kMaximumStepDuration &&
311 if (
spec.lane_count < 1 ||
spec.lane_count > MaxLanes)
313 for (std::size_t lane = 0; lane <
spec.lane_count; ++lane)
322 constexpr bool valid() const noexcept {
330 if (step.velocity < 1 || step.velocity > 127)
332 if (step.gate_percent < 1 || step.gate_percent > 100)
334 if (step.probability_percent > 100)
336 if (step.ratchet_count < 1 || step.ratchet_count > MaxRatchetHits)
338 if (step.pitch_offset < -48 || step.pitch_offset > 48)
350 if (lane >= spec_.lane_count)
352 if (index >= spec_.lanes[lane].step_count)
356 const auto interval = spec_.lanes[lane].step_duration.value;
357 if (step.timing_offset_ticks < 0 &&
358 static_cast<std::int64_t
>(-step.timing_offset_ticks) >= interval)
360 steps_[lane * MaxSteps + index] = step;
365 if (!valid_ || lane >= spec_.lane_count || index >= spec_.lanes[lane].step_count)
367 return &steps_[lane * MaxSteps + index];
373 for (
auto& step : steps_)
378 std::size_t count = 0;
379 for (
const auto& note : sounding_)
388 return {0, input.size(), 0,
false};
391 if (!ordered(input)) {
394 passthrough(input, output, report);
398 finish_block_without_generation();
404 passthrough(input, output, report);
409 passthrough(input, output, report);
415 current_block_ = █
416 update_anchor(block);
417 steps_this_block_ = 0;
418 const bool discontinuity = detects_discontinuity(block);
420 for (
auto& clock : clocks_)
422 if (!release_all(output, report, 0)) {
423 passthrough(input, output, report);
430 const bool should_play =
433 if (!release_all(output, report, 0)) {
434 passthrough(input, output, report);
439 for (
auto& clock : clocks_)
442 align_clocks(block.tick_start);
445 for (
const auto& event : input) {
446 const auto offset =
event.sample_offset;
447 if (offset < 0 || offset >= block.sample_count) {
452 if (!render_until(offset,
false, should_play, output, report)) {
462 if (!render_until(block.sample_count,
false, should_play, output, report)) {
479 release_all(output, report, 0);
480 for (
auto& clock : clocks_)
489 for (
auto& note : sounding_)
491 last_block_valid_ =
false;
503 if (!valid_ || lane >= spec_.lane_count || spec_.lanes[lane].step_count == 0 ||
504 spec_.lanes[lane].step_duration.value <= 0)
506 return coordinate_for(spec_.lanes[lane], lane, tick);
512 if (!valid_ || lane >= spec_.lane_count || spec_.lanes[lane].step_count == 0 ||
513 spec_.lanes[lane].step_duration.value <= 0 || !clocks_[lane].initialized)
515 return coordinate_for(spec_.lanes[lane], lane, clocks_[lane].next_tick);
524 std::size_t hits_done = 0;
525 bool initialized =
false;
528 struct SoundingNote {
529 std::uint8_t channel = 0;
530 std::uint8_t note = 0;
531 std::uint8_t lane = 0;
532 std::int64_t release_sample = 0;
536 static bool valid_block(
const StepPlayerBlock& block)
noexcept {
537 if (
block.sample_count < 0)
539 if (
block.tempo_map !=
nullptr)
540 return block.tempo_map->sample_rate().valid();
544 static bool ordered(
const MidiBuffer& input)
noexcept {
545 std::int32_t previous = std::numeric_limits<std::int32_t>::min();
546 for (
const auto& event : input) {
547 if (event.sample_offset < previous)
549 previous =
event.sample_offset;
554 void passthrough(
const MidiBuffer& input, MidiBuffer& output,
555 MidiUtilityProcessReport& report)
noexcept {
556 for (
const auto& event : input)
561 void finish_block_without_generation() noexcept {
562 last_block_valid_ =
false;
565 bool detects_discontinuity(
const StepPlayerBlock& block)
const noexcept {
568 if (!last_block_valid_)
570 if (
block.sample_start.value != last_sample_end_ ||
block.playing != last_playing_)
576 void finish_block(
const StepPlayerBlock& block)
noexcept {
579 if (
block.tempo_map !=
nullptr) {
580 const auto anchor =
block.tempo_map->fractional_ticks_to_samples(
581 static_cast<long double>(
block.tick_start.value));
583 block.tempo_map->fractional_samples_to_ticks(anchor +
584 static_cast<long double>(
585 block.sample_count)));
587 const auto ticks =
static_cast<long double>(
block.sample_count) *
588 static_cast<long double>(
block.tempo_bpm) *
590 (60.0L *
block.sample_rate.as_long_double());
594 last_playing_ =
block.playing;
595 last_block_valid_ =
true;
596 current_block_ =
nullptr;
599 void align_clocks(timebase::TickPosition tick)
noexcept {
600 for (std::size_t lane = 0; lane < spec_.lane_count; ++lane) {
601 auto& clock = clocks_[lane];
602 if (clock.initialized || !spec_.lanes[lane].enabled)
604 const auto duration = spec_.lanes[lane].step_duration.value;
607 if (candidate < tick.value && ordinal != std::numeric_limits<std::int64_t>::max())
610 clock.initialized =
true;
614 void advance_clock(std::size_t lane)
noexcept {
615 auto& clock = clocks_[lane];
617 clock.next_tick.value, spec_.lanes[lane].step_duration.value)};
621 std::int64_t sample_for_tick(timebase::TickPosition tick)
const noexcept {
622 return saturating_sample_of(
static_cast<long double>(anchor_sample_) +
623 (tick_axis(*current_block_, tick.value) - anchor_axis_));
629 long double tick_axis(
const StepPlayerBlock& block, std::int64_t tick)
const noexcept {
630 if (
block.tempo_map !=
nullptr)
631 return block.tempo_map->fractional_ticks_to_samples(
static_cast<long double>(tick));
633 block.sample_rate.as_long_double() * 60.0L /
634 (
static_cast<long double>(
block.tempo_bpm) *
651 void update_anchor(
const StepPlayerBlock& block)
noexcept {
652 const auto axis = tick_axis(block,
block.tick_start.value);
653 const auto offset =
static_cast<long double>(
block.sample_start.value) - axis;
655 std::fabs(offset - (
static_cast<long double>(anchor_sample_) - anchor_axis_)) <= 0.5L)
657 anchor_sample_ =
block.sample_start.value;
662 static std::int64_t saturating_sample_of(
long double absolute)
noexcept {
663 constexpr auto minimum =
static_cast<long double>(std::numeric_limits<std::int64_t>::min());
664 constexpr auto maximum =
static_cast<long double>(std::numeric_limits<std::int64_t>::max());
665 if (absolute <= minimum)
666 return std::numeric_limits<std::int64_t>::min();
667 if (absolute >= maximum)
668 return std::numeric_limits<std::int64_t>::max();
669 return static_cast<std::int64_t
>(std::llround(absolute));
672 timebase::TickPosition tick_at_block_offset(std::int32_t offset)
const noexcept {
673 const auto&
block = *current_block_;
674 if (
block.tempo_map !=
nullptr) {
675 const auto anchor =
block.tempo_map->fractional_ticks_to_samples(
676 static_cast<long double>(
block.tick_start.value));
677 const auto tick =
block.tempo_map->fractional_samples_to_ticks(
678 anchor +
static_cast<long double>(offset));
681 const auto ticks =
static_cast<long double>(offset) *
682 static_cast<long double>(
block.tempo_bpm) *
684 (60.0L *
block.sample_rate.as_long_double());
689 std::int32_t block_offset(std::int64_t sample)
const noexcept {
690 if (current_block_ ==
nullptr)
692 const auto start = current_block_->sample_start.value;
695 const auto maximum_offset = std::max<std::int32_t>(0, current_block_->sample_count - 1);
697 static_cast<std::uint64_t
>(sample) -
static_cast<std::uint64_t
>(start);
698 if (difference >=
static_cast<std::uint64_t
>(maximum_offset))
699 return maximum_offset;
703 bool render_until(std::int32_t offset,
bool include_step_at_offset,
bool playing,
704 MidiBuffer& output, MidiUtilityProcessReport& report)
noexcept {
705 const auto absolute_limit =
707 const bool limit_is_inside_block = offset < current_block_->sample_count;
709 return release_due(absolute_limit, limit_is_inside_block, output, report);
713 std::uint32_t parked = 0;
717 std::size_t best_lane = spec_.lane_count;
718 std::int64_t best_sample = std::numeric_limits<std::int64_t>::max();
719 for (std::size_t lane = 0; lane < spec_.lane_count; ++lane) {
720 if (!clocks_[lane].initialized || !spec_.lanes[lane].enabled ||
721 (parked & (std::uint32_t{1} << lane)) != 0)
723 const auto sample = next_onset_sample(lane);
724 if (best_lane == spec_.lane_count || sample < best_sample) {
726 best_sample = sample;
729 if (best_lane == spec_.lane_count)
732 include_step_at_offset ? best_sample <= absolute_limit : best_sample < absolute_limit;
738 if (best_sample < current_block_->sample_start.value &&
739 clocks_[best_lane].hits_done == 0) {
740 advance_clock(best_lane);
741 report.complete =
false;
745 if (steps_this_block_ == MaximumStepsPerBlock) {
746 report.complete =
false;
748 realign_clocks_past(offset);
751 const auto hits_before = clocks_[best_lane].hits_done;
752 if (!fire_step(best_lane, best_sample, absolute_limit, output, report))
754 if (clocks_[best_lane].hits_done == 0) {
755 advance_clock(best_lane);
757 }
else if (clocks_[best_lane].hits_done == hits_before) {
760 parked |= std::uint32_t{1} << best_lane;
763 return release_due(absolute_limit, limit_is_inside_block, output, report);
766 void realign_clocks_past(std::int32_t offset)
noexcept {
767 const auto tick = tick_at_block_offset(offset);
768 for (std::size_t lane = 0; lane < spec_.lane_count; ++lane) {
769 auto& clock = clocks_[lane];
770 if (!clock.initialized)
772 const auto duration = spec_.lanes[lane].step_duration.value;
775 if (candidate <= tick.value && ordinal != std::numeric_limits<std::int64_t>::max())
782 std::size_t walk_index(
const StepPlayerLaneSpec& lane, std::int64_t ordinal,
783 timebase::TickPosition tick, std::size_t lane_index,
784 std::uint64_t cycle)
const noexcept {
785 const auto length =
static_cast<std::size_t
>(lane.step_count);
786 switch (lane.direction) {
792 const auto period = length * 2 - 2;
794 return position < length ? position : period - position;
799 return static_cast<std::size_t
>(
802 {tick, static_cast<std::uint64_t>(lane_index), cycle, 1}) %
814 StepPlayerCoordinate coordinate_for(
const StepPlayerLaneSpec& lane, std::size_t lane_index,
815 timebase::TickPosition tick)
const noexcept {
816 StepPlayerCoordinate coordinate{};
817 coordinate.step_tick = tick;
818 coordinate.following_tick = timebase::TickPosition{
822 coordinate.ordinal,
static_cast<std::int64_t
>(lane.step_count)));
823 coordinate.index = walk_index(lane, coordinate.ordinal, tick, lane_index, coordinate.cycle);
835 std::int64_t bounded_timing_offset(
const StepPlayerStep& step,
836 std::int64_t interval_ticks)
const noexcept {
837 if (!step.on || step.timing_offset_ticks == 0)
839 const auto limit = std::max<std::int64_t>(0, interval_ticks - 1);
840 return std::clamp(
static_cast<std::int64_t
>(step.timing_offset_ticks), -limit, limit);
849 std::int64_t next_onset_sample(std::size_t lane_index)
const noexcept {
850 const auto& lane = spec_.lanes[lane_index];
851 const auto tick = clocks_[lane_index].next_tick;
852 if (lane.step_count == 0 || lane.step_duration.value <= 0)
853 return sample_for_tick(tick);
854 const auto coordinate = coordinate_for(lane, lane_index, tick);
855 const auto offset = bounded_timing_offset(
856 steps_[lane_index * MaxSteps + coordinate.index], lane.step_duration.value);
860 bool fire_step(std::size_t lane_index, std::int64_t step_sample, std::int64_t absolute_limit,
861 MidiBuffer& output, MidiUtilityProcessReport& report)
noexcept {
862 const auto& lane = spec_.lanes[lane_index];
863 auto& clock = clocks_[lane_index];
864 const auto coordinate = coordinate_for(lane, lane_index, clock.next_tick);
865 const auto step_tick = coordinate.step_tick;
866 const auto following_tick = coordinate.following_tick;
867 const auto cycle = coordinate.cycle;
868 const auto index = coordinate.index;
869 const auto& step = steps_[lane_index * MaxSteps +
index];
873 if (step.probability_percent < 100) {
874 if (step.probability_percent == 0)
878 {step_tick, static_cast<std::uint64_t>(lane_index), cycle, 0},
879 step.probability_percent, 100);
880 if (!chance.has_value() || !chance.value())
884 const auto pitch =
static_cast<std::uint8_t
>(std::clamp(
885 static_cast<int>(lane.base_note) +
static_cast<int>(step.pitch_offset), 0, 127));
886 const auto interval_ticks = following_tick.value - step_tick.value;
892 step_tick.value, bounded_timing_offset(step, interval_ticks))};
896 const auto gate_ticks = std::max<std::int64_t>(
898 const auto step_release_tick =
904 if (clock.hits_done == 0) {
909 if (
auto* match = find_sounding(lane_index, lane.channel, pitch)) {
910 match->release_sample =
911 std::max(
match->release_sample, sample_for_tick(step_release_tick));
912 return release_due(step_sample,
true, output, report);
919 const auto overlap_sample =
920 std::max(step_sample + 1,
922 onset_tick.value, 1)}));
923 for (
auto& sounding : sounding_)
924 if (sounding.active && sounding.lane == lane_index && sounding.note != pitch)
925 sounding.release_sample =
926 std::max(sounding.release_sample, overlap_sample);
929 if (lane.choke_group != 0 &&
930 !emit_choke(lane_index, lane.choke_group, step_sample, output, report))
934 std::array<timebase::TickPosition, MaxRatchetHits> hits{};
935 std::size_t hit_count = 1;
936 hits[0] = onset_tick;
937 if (step.ratchet_count > 1) {
942 onset_tick, following_tick, step.ratchet_count, onset_tick, following_tick,
943 std::span<timebase::TickPosition>(hits.data(), hits.size()));
944 if (projection && projection.event_count > 0)
945 hit_count = projection.event_count;
949 const auto hit_gate_ticks =
950 std::max<std::int64_t>(1, gate_ticks /
static_cast<std::int64_t
>(hit_count));
952 for (std::size_t hit = clock.hits_done; hit < hit_count; ++hit) {
953 const auto hit_tick = hits[hit];
955 hit_tick.value, hit_gate_ticks)};
956 if (hit + 1 < hit_count) {
957 const auto next_tick = hits[hit + 1].value;
958 if (next_tick <= release_tick.value)
959 release_tick = {std::max(next_tick - 1, hit_tick.value)};
961 const auto hit_sample = sample_for_tick(hit_tick);
965 if (hit_sample >= absolute_limit)
967 if (!release_due(hit_sample,
true, output, report))
969 auto* slot = free_sounding_slot();
970 if (slot ==
nullptr) {
974 report.complete =
false;
978 attack.sample_offset = block_offset(hit_sample);
981 *slot = {lane.channel,
983 static_cast<std::uint8_t
>(lane_index),
984 sample_for_tick(release_tick),
986 clock.hits_done = hit + 1;
988 if (clock.hits_done == hit_count)
993 SoundingNote* find_sounding(std::size_t lane, std::uint8_t channel, std::uint8_t note)
noexcept {
994 for (
auto& sounding : sounding_)
995 if (sounding.active && sounding.lane == lane && sounding.channel == channel &&
996 sounding.note == note)
1001 SoundingNote* free_sounding_slot() noexcept {
1002 for (
auto& note : sounding_)
1008 bool emit_choke(std::size_t firing_lane, std::uint8_t group, std::int64_t sample,
1009 MidiBuffer& output, MidiUtilityProcessReport& report)
noexcept {
1010 for (
auto& sounding : sounding_) {
1011 if (!sounding.active || sounding.lane == firing_lane)
1013 if (spec_.lanes[sounding.lane].choke_group != group)
1015 if (!emit_release(sounding, sample, output, report))
1021 bool release_due(std::int64_t sample,
bool include_at_sample, MidiBuffer& output,
1022 MidiUtilityProcessReport& report)
noexcept {
1023 for (
auto& sounding : sounding_) {
1024 const bool due = sounding.release_sample < sample ||
1025 (include_at_sample && sounding.release_sample == sample);
1026 if (sounding.active && due &&
1027 !emit_release(sounding, sounding.release_sample, output, report))
1033 bool release_all(MidiBuffer& output, MidiUtilityProcessReport& report,
1034 std::int32_t offset)
noexcept {
1036 current_block_ !=
nullptr
1039 for (
auto& sounding : sounding_)
1040 if (sounding.active && !emit_release(sounding, sample, output, report))
1045 bool emit_release(SoundingNote& sounding, std::int64_t sample, MidiBuffer& output,
1046 MidiUtilityProcessReport& report)
noexcept {
1048 release.sample_offset = block_offset(sample);
1050 sounding.release_sample = sample;
1060 std::array<StepPlayerStep, kMaximumCells> steps_{};
1061 std::array<LaneClock, MaxLanes> clocks_{};
1062 std::array<SoundingNote, kMaximumSoundingNotes> sounding_{};
1063 const StepPlayerBlock* current_block_ =
nullptr;
1064 bool anchored_ =
false;
1065 std::int64_t anchor_sample_ = 0;
1066 long double anchor_axis_ = 0.0L;
1067 std::int64_t last_sample_end_ = 0;
1068 std::int64_t projected_tick_end_ = 0;
1069 bool last_playing_ =
false;
1070 bool last_block_valid_ =
false;
1071 std::size_t steps_this_block_ = 0;