3#include <pulp/midi/detail/note_schedule.hpp>
33template <std::
size_t MaximumScheduledNotes = 128>
class NoteRepeat {
35 using Block = note_schedule::Block;
44 return spec.interval.value > 0 &&
spec.count >= 1 &&
spec.gate_percent >= 1 &&
45 spec.gate_percent <= 100 &&
spec.probability_percent <= 100 &&
46 spec.velocity_decay_percent <= 100;
59 return note_schedule::samples_for_duration(block,
spec.interval);
64 std::int64_t attack_absolute, std::size_t index)
noexcept {
66 attack_absolute,
hit_stride(
spec, block) *
static_cast<std::int64_t
>(index));
71 std::size_t index)
noexcept {
72 return note_schedule::decayed_velocity(velocity,
spec.velocity_decay_percent, index);
79 std::int64_t attack_absolute, std::size_t index)
noexcept {
80 if (
spec.probability_percent >= 100)
82 if (
spec.probability_percent == 0)
87 static_cast<std::uint64_t
>(index), kProbabilityStream};
90 return chance.has_value() && chance.value();
96 constexpr bool valid() const noexcept {
105 const Block& block)
noexcept {
107 return {0, input.size(), 0,
false};
115 const std::int64_t block_end =
117 for (
const auto& event : input) {
118 const auto absolute =
120 emit_due_before(absolute, block, output, report);
121 const bool is_attack =
event.is_note_on() &&
event.velocity() != 0;
122 const bool is_release =
123 event.is_note_off() || (
event.is_note_on() && event.velocity() == 0);
124 if (
is_identity(spec_) || (!is_attack && !is_release)) {
129 schedule_series(event, absolute, block, report);
135 report.
deferred += queue_.cancel_unstarted_key(event.channel(), event.note());
137 emit_due_before(block_end, block, output, report);
152 report.
deferred += queue_.cancel_unstarted();
153 queue_.release_sounding_at(block.sample_start.value);
155 queue_.drain([&](
const note_schedule::ScheduledNote& slot,
bool attack, std::int64_t at) {
156 return emit_edge(slot, attack, at, block, output, report);
164 if (report.
complete && pending_spec_) {
165 spec_ = *pending_spec_;
167 pending_spec_.reset();
173 return flush(output, block);
177 const Block& block)
noexcept {
180 return {0, 0, 0,
false};
182 auto report =
flush(output, block);
183 if (report.complete) {
186 pending_spec_.reset();
188 pending_spec_ =
spec;
194 return queue_.empty();
198 return queue_.size();
202 return queue_.sounding();
206 static constexpr std::uint64_t kProbabilityStream = 3;
208 void schedule_series(
const MidiEvent& event, std::int64_t absolute,
const Block& block,
212 std::max<std::int64_t>(1, stride *
static_cast<std::int64_t
>(spec_.
gate_percent) / 100);
213 for (std::size_t index = 0; index < spec_.
count; ++index) {
214 if (!
hit_sounds(spec_, event.channel(), event.note(), absolute, index))
216 auto* slot = queue_.acquire();
217 if (slot ==
nullptr) {
221 report.complete =
false;
225 absolute, stride *
static_cast<std::int64_t
>(index));
228 slot->channel =
event.channel();
229 slot->note =
event.note();
230 slot->velocity =
hit_velocity(spec_, event.velocity(), index);
234 void emit_due_before(std::int64_t boundary,
const Block& block, MidiBuffer& output,
235 MidiUtilityProcessReport& report)
noexcept {
236 queue_.emit_due_before(
237 boundary, [&](
const note_schedule::ScheduledNote& slot,
bool attack, std::int64_t at) {
238 return emit_edge(slot, attack, at, block, output, report);
242 static bool emit_edge(
const note_schedule::ScheduledNote& slot,
bool attack, std::int64_t at,
243 const Block& block, MidiBuffer& output,
244 MidiUtilityProcessReport& report)
noexcept {
245 const auto offset = note_schedule::offset_in_block(block, at);
247 : MidiEvent::note_off(slot.channel, slot.note);
251 NoteRepeatSpec spec_{};
253 std::optional<NoteRepeatSpec> pending_spec_{};
254 note_schedule::ScheduleQueue<MaximumScheduledNotes> queue_{};
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
static constexpr bool is_identity(NoteRepeatSpec spec) noexcept
True when the kernel cannot change the stream, which is its declared bypass identity.
Definition note_repeat.hpp:51
constexpr bool valid() const noexcept
Definition note_repeat.hpp:96
MidiUtilityProcessReport replace_spec(NoteRepeatSpec spec, MidiBuffer &output, const Block &block) noexcept
Definition note_repeat.hpp:176
MidiUtilityProcessReport process(const MidiBuffer &input, MidiBuffer &output, const Block &block) noexcept
Definition note_repeat.hpp:104
std::size_t scheduled() const noexcept
Definition note_repeat.hpp:197
static constexpr std::uint8_t hit_velocity(NoteRepeatSpec spec, std::uint8_t velocity, std::size_t index) noexcept
Velocity of hit index under the shipped decay law.
Definition note_repeat.hpp:70
static constexpr MidiUtilityContract contract() noexcept
Definition note_repeat.hpp:37
std::size_t sounding() const noexcept
Definition note_repeat.hpp:201
note_schedule::Block Block
Definition note_repeat.hpp:35
bool empty() const noexcept
Definition note_repeat.hpp:193
MidiUtilityProcessReport reset(MidiBuffer &output, const Block &block) noexcept
Definition note_repeat.hpp:172
constexpr NoteRepeat(NoteRepeatSpec spec={}) noexcept
Definition note_repeat.hpp:93
static constexpr bool valid_spec(NoteRepeatSpec spec) noexcept
Definition note_repeat.hpp:43
static bool hit_sounds(NoteRepeatSpec spec, std::uint8_t channel, std::uint8_t note, std::int64_t attack_absolute, std::size_t index) noexcept
Whether hit index of the attack at attack_absolute survives its probability draw.
Definition note_repeat.hpp:78
static std::int64_t hit_stride(NoteRepeatSpec spec, const Block &block) noexcept
Sample stride between consecutive hits, derived from division arithmetic alone.
Definition note_repeat.hpp:58
MidiUtilityProcessReport flush(MidiBuffer &output, const Block &block) noexcept
Cancel unstarted hits and release every note the kernel still owns.
Definition note_repeat.hpp:145
constexpr NoteRepeatSpec spec() const noexcept
Definition note_repeat.hpp:100
static std::int64_t hit_sample(NoteRepeatSpec spec, const Block &block, std::int64_t attack_absolute, std::size_t index) noexcept
Absolute sample of hit index for an attack at attack_absolute.
Definition note_repeat.hpp:63
MidiEvent at(MidiEvent event, std::int32_t sample_offset) noexcept
Definition utility_contract.hpp:140
void copy_sidecars(const MidiBuffer &input, MidiBuffer &output, MidiUtilityProcessReport &report) noexcept
Definition utility_contract.hpp:96
void clear_output(MidiBuffer &output) noexcept
Definition utility_contract.hpp:82
bool blocks_alias(const MidiBuffer &first, const MidiBuffer &second) noexcept
Definition utility_contract.hpp:92
constexpr std::int64_t saturating_sample_add(std::int64_t position, std::int64_t offset) noexcept
Definition utility_contract.hpp:59
bool ready(const MidiBuffer &output) noexcept
Definition utility_contract.hpp:86
bool emit(MidiBuffer &output, const MidiEvent &event, MidiUtilityProcessReport &report)
Definition utility_contract.hpp:67
int key_index(std::uint8_t channel, std::uint8_t note) noexcept
Definition utility_contract.hpp:55
Definition custom_node_events.hpp:26
@ FlushOnDiscontinuity
Definition utility_contract.hpp:33
@ DropUnstarted
Definition utility_contract.hpp:20
@ ReleaseBeforeAttack
Definition utility_contract.hpp:27
@ attack
Definition envelope.hpp:58
constexpr std::int64_t kTicksPerQuarter
Definition tick.hpp:54
runtime::Result< bool, ProbabilityError > coordinate_chance(std::uint64_t seed, RandomCoordinate coordinate, std::uint64_t numerator, std::uint64_t denominator) noexcept
Definition coordinate_random.hpp:63
Timestamped MIDI event combining a choc::midi::ShortMessage with timing info.
Definition message.hpp:32
static MidiEvent note_on(uint8_t channel, uint8_t note, uint8_t velocity)
Create a Note On event.
Definition message.hpp:46
Definition utility_contract.hpp:36
Definition utility_contract.hpp:46
bool complete
Definition utility_contract.hpp:50
std::size_t dropped
Definition utility_contract.hpp:48
std::size_t deferred
Definition utility_contract.hpp:49
Clock-divided note repeat / retrigger.
Definition note_repeat.hpp:23
std::uint8_t count
Definition note_repeat.hpp:25
std::uint8_t probability_percent
Definition note_repeat.hpp:27
std::uint64_t seed
Definition note_repeat.hpp:29
timebase::TickDuration interval
Definition note_repeat.hpp:24
constexpr auto operator<=>(const NoteRepeatSpec &) const =default
std::uint8_t velocity_decay_percent
Definition note_repeat.hpp:28
std::uint8_t gate_percent
Definition note_repeat.hpp:26
Definition coordinate_random.hpp:14