12 std::uint8_t channel, std::uint8_t note)
noexcept {
16 packet.
words[0] = (std::uint32_t{0x2} << 28) |
17 (
static_cast<std::uint32_t
>(group & 0x0f) << 24) |
18 (
static_cast<std::uint32_t
>(0x80 | (channel & 0x0f)) << 16) |
19 (
static_cast<std::uint32_t
>(note & 0x7f) << 8);
27 constexpr bool empty() const noexcept {
return total_ == 0; }
29 if (midi_debt_total_ != 0 || ump_debt_total_ != 0)
31 return !has_forwarded(midi_entries_, midi_size_) &&
32 !has_forwarded(ump_entries_, ump_size_);
37 if (!is_attack(event))
return true;
39 return midi_overflow_suppressed_[key] == 0 && midi_size_ < midi_entries_.size();
42 if (!is_attack(packet))
return true;
43 const auto key = ump_key(packet);
44 return ump_overflow_suppressed_[key] == 0 && ump_size_ < ump_entries_.size();
48 if (!is_release(event))
return false;
49 return consume_suppressed(midi_entries_, midi_size_, midi_flushed_suppressed_,
50 midi_overflow_suppressed_,
54 if (!is_release(packet))
return false;
55 return consume_suppressed(ump_entries_, ump_size_, ump_flushed_suppressed_,
56 ump_overflow_suppressed_,
63 record_attack(midi_entries_, midi_size_, midi_overflow_suppressed_, key,
65 else if (is_release(event))
66 record_release(midi_entries_, midi_size_, midi_release_debt_,
67 midi_debt_total_, key, forwarded);
70 const auto key = ump_key(packet);
71 if (is_attack(packet))
72 record_attack(ump_entries_, ump_size_, ump_overflow_suppressed_, key,
74 else if (is_release(packet))
75 record_release(ump_entries_, ump_size_, ump_release_debt_,
76 ump_debt_total_, key, forwarded);
80 for (
const auto& event : input) {
83 if (
const auto* ump = input.ump())
84 for (
const auto& event : *ump) {
86 record(event.packet,
false);
90 template <
typename Emit>
92 return drain_pending(midi_release_debt_, midi_debt_total_, midi_drain_cursor_,
93 [&](std::size_t key) {
94 return emit(
static_cast<std::uint8_t
>(key / 128),
95 static_cast<std::uint8_t
>(key % 128));
99 template <
typename Emit>
101 return drain_pending(ump_release_debt_, ump_debt_total_, ump_drain_cursor_,
102 [&](std::size_t key) {
103 constexpr std::size_t kKeysPerProtocol = 16 * 16 * 128;
104 const bool midi2 = key >= kKeysPerProtocol;
105 const auto protocol_key = key % kKeysPerProtocol;
106 const auto group =
static_cast<std::uint8_t
>(
107 protocol_key / (16 * 128));
108 const auto remainder = protocol_key % (16 * 128);
111 static_cast<std::uint8_t
>(remainder / 128),
112 static_cast<std::uint8_t
>(remainder % 128));
116 template <
typename EmitM
idi,
typename EmitUmp>
117 bool flush(EmitMidi&& emit_midi, EmitUmp&& emit_ump)
const noexcept {
120 if (!flush_forwarded(midi_entries_, midi_size_, midi_flushed_suppressed_,
121 [&](std::uint32_t key) {
122 return emit_midi(
static_cast<std::uint8_t
>(key / 128),
123 static_cast<std::uint8_t
>(key % 128));
126 if (!flush_forwarded(ump_entries_, ump_size_, ump_flushed_suppressed_,
127 [&](std::uint32_t key) {
128 constexpr std::size_t kKeysPerProtocol = 16 * 16 * 128;
129 const bool midi2 = key >= kKeysPerProtocol;
130 const auto protocol_key = key % kKeysPerProtocol;
131 const auto group =
static_cast<std::uint8_t
>(protocol_key / (16 * 128));
132 const auto remainder = protocol_key % (16 * 128);
133 return emit_ump(midi2, group,
134 static_cast<std::uint8_t
>(remainder / 128),
135 static_cast<std::uint8_t
>(remainder % 128));
142 enum class OwnershipState : std::uint8_t {
146 struct OwnershipEntry {
147 std::uint32_t key = 0;
148 std::uint32_t count = 0;
149 OwnershipState
state = OwnershipState::Suppressed;
152 static std::size_t ump_key(
const UmpPacket& packet)
noexcept {
153 constexpr std::size_t kKeysPerProtocol = 16 * 16 * 128;
154 const std::size_t protocol =
156 return protocol * kKeysPerProtocol +
157 (
static_cast<std::size_t
>(packet.group()) * 16 + packet.channel()) * 128 +
158 packet.note_number();
160 static bool is_attack(
const MidiEvent& event)
noexcept {
161 return event.is_note_on() &&
event.velocity() != 0;
163 static bool is_release(
const MidiEvent& event)
noexcept {
164 return event.is_note_off() || (
event.is_note_on() && event.velocity() == 0);
166 static bool is_attack(
const UmpPacket& packet)
noexcept {
168 const auto status =
static_cast<std::uint8_t
>(packet.status() & 0xf0);
169 if (status != 0x90)
return false;
171 (packet.words[0] & 0x7f) != 0;
173 static bool is_release(
const UmpPacket& packet)
noexcept {
175 const auto status =
static_cast<std::uint8_t
>(packet.status() & 0xf0);
176 return status == 0x80 ||
178 (packet.words[0] & 0x7f) == 0);
180 template <std::
size_t N, std::
size_t KeyCount>
181 bool consume_suppressed(std::array<OwnershipEntry, N>& entries,
183 std::array<std::uint64_t, KeyCount>& flushed,
184 std::array<std::uint32_t, KeyCount>& overflow,
185 std::uint32_t key)
noexcept {
186 if (flushed[key] != 0) {
191 for (std::size_t i = 0;
i <
size; ++
i) {
192 if (entries[i].key != key)
continue;
193 if (entries[i].state == OwnershipState::Forwarded)
195 if (--entries[i].count == 0) erase(entries, size, i);
199 if (overflow[key] == 0)
return false;
205 template <std::
size_t N, std::
size_t KeyCount>
206 void record_attack(std::array<OwnershipEntry, N>& entries, std::size_t& size,
207 std::array<std::uint32_t, KeyCount>& overflow,
208 std::uint32_t key,
bool forwarded)
noexcept {
209 if (overflow[key] != 0 || size == entries.size()) {
210 if (overflow[key] != std::numeric_limits<std::uint32_t>::max()) {
216 const auto state = forwarded ? OwnershipState::Forwarded
217 : OwnershipState::Suppressed;
218 for (std::size_t i = size;
i != 0; --
i) {
219 auto& previous = entries[
i - 1];
220 if (previous.key != key)
continue;
221 if (previous.state == state &&
222 previous.count != std::numeric_limits<std::uint32_t>::max()) {
229 entries[
size++] = {key, 1, state};
233 template <std::
size_t N, std::
size_t KeyCount>
234 void record_release(std::array<OwnershipEntry, N>& entries,
236 std::array<std::uint32_t, KeyCount>& debt,
237 std::size_t& debt_total, std::uint32_t key,
238 bool forwarded)
noexcept {
239 for (std::size_t i = 0;
i <
size; ++
i) {
240 auto& entry = entries[
i];
241 if (entry.key != key || entry.state != OwnershipState::Forwarded)
243 if (--entry.count == 0) erase(entries, size, i);
246 else if (debt[key] != std::numeric_limits<std::uint32_t>::max()) {
254 template <std::
size_t N,
typename Emit>
255 bool drain_pending(std::array<std::uint32_t, N>& debt,
256 std::size_t& debt_total, std::size_t& cursor,
257 Emit&& emit)
const noexcept {
258 if (debt_total == 0)
return true;
259 constexpr std::size_t kAttemptsPerBlock = 32;
260 std::size_t attempts = 0;
261 std::size_t visited = 0;
262 while (visited < debt.size() && attempts < kAttemptsPerBlock) {
263 const auto key = cursor;
264 cursor = (cursor + 1) % debt.size();
266 if (debt[key] == 0)
continue;
274 return debt_total == 0;
277 template <std::
size_t N, std::
size_t KeyCount,
typename Emit>
278 bool flush_forwarded(std::array<OwnershipEntry, N>& entries,
280 std::array<std::uint64_t, KeyCount>& flushed,
281 Emit&& emit)
const noexcept {
282 constexpr std::size_t kAttemptsPerCall = 32;
283 std::size_t attempts = 0;
284 for (std::size_t i = 0;
i <
size;) {
285 auto& entry = entries[
i];
286 if (entry.state == OwnershipState::Suppressed) {
290 while (entry.count != 0 && attempts < kAttemptsPerCall) {
291 if (flushed[entry.key] == std::numeric_limits<std::uint64_t>::max())
294 if (!
emit(entry.key))
297 ++flushed[entry.key];
299 if (entry.count != 0)
301 erase(entries, size, i);
306 template <std::
size_t N>
307 static bool has_forwarded(
const std::array<OwnershipEntry, N>& entries,
308 std::size_t size)
noexcept {
309 for (std::size_t i = 0;
i <
size; ++
i)
310 if (entries[i].state == OwnershipState::Forwarded)
315 void clear_all() const noexcept {
316 midi_entries_.fill({});
317 ump_entries_.fill({});
318 midi_overflow_suppressed_.fill(0);
319 ump_overflow_suppressed_.fill(0);
320 midi_flushed_suppressed_.fill(0);
321 ump_flushed_suppressed_.fill(0);
322 midi_release_debt_.fill(0);
323 ump_release_debt_.fill(0);
326 midi_drain_cursor_ = 0;
327 ump_drain_cursor_ = 0;
328 midi_debt_total_ = 0;
333 template <std::
size_t N>
334 static void erase(std::array<OwnershipEntry, N>& entries,
335 std::size_t& size, std::size_t index)
noexcept {
336 for (std::size_t i = index + 1;
i <
size; ++
i)
337 entries[i - 1] = entries[i];
341 mutable std::array<OwnershipEntry, kMaxTrackedOwnership> midi_entries_{};
342 mutable std::array<OwnershipEntry, kMaxTrackedOwnership> ump_entries_{};
343 mutable std::array<std::uint32_t, 16 * 128> midi_overflow_suppressed_{};
344 mutable std::array<std::uint32_t, 2 * 16 * 16 * 128> ump_overflow_suppressed_{};
345 mutable std::array<std::uint64_t, 16 * 128> midi_flushed_suppressed_{};
346 mutable std::array<std::uint64_t, 2 * 16 * 16 * 128> ump_flushed_suppressed_{};
347 mutable std::array<std::uint32_t, 16 * 128> midi_release_debt_{};
348 mutable std::array<std::uint32_t, 2 * 16 * 16 * 128> ump_release_debt_{};
349 mutable std::size_t midi_size_ = 0;
350 mutable std::size_t ump_size_ = 0;
351 mutable std::size_t midi_drain_cursor_ = 0;
352 mutable std::size_t ump_drain_cursor_ = 0;
353 mutable std::size_t midi_debt_total_ = 0;
354 mutable std::size_t ump_debt_total_ = 0;
355 mutable std::size_t total_ = 0;
363 8, 9, 10, 11, 12, 13, 14, 15};
375 for (
const auto channel : spec.output_channel)
387 return valid_ && held_notes_ !=
nullptr;
390 if (!held_notes_ || !
valid_spec(spec) || !held_notes_->empty())
400 return {0, 0, 0,
false};
402 if (held_notes_->output_ownership_empty())
405 return {0, 0, 1,
false};
406 report.
complete = held_notes_->flush(
407 [&](std::uint8_t channel, std::uint8_t note) {
414 [&](
bool midi2, std::uint8_t group, std::uint8_t channel, std::uint8_t note) {
417 {routing_detail::note_off_packet(
418 midi2, group, spec_.output_channel[channel], note),
432 auto report =
flush(output);
434 held_notes_->discard_input_ownership();
441 return {0, 0, 0,
false};
443 auto report =
flush(output);
444 if (report.complete) {
453 return {0, input.
size(), 0,
false};
456 return {0, input.
size(), 0,
false};
459 held_notes_->retain_unprocessed_ownership(input);
460 return {0, input.
size(), 0,
false};
463 held_notes_->retain_unprocessed_ownership(input);
464 return {0, input.
size(), 0,
false};
466 const bool midi_drained =
467 held_notes_->drain_midi_releases([&](std::uint8_t channel, std::uint8_t note) {
474 const bool ump_drained = held_notes_->drain_ump_releases(
475 [&](
bool midi2, std::uint8_t group, std::uint8_t channel, std::uint8_t note) {
478 {routing_detail::note_off_packet(
479 midi2, group, spec_.output_channel[channel], note),
487 if (!midi_drained || !ump_drained) {
488 held_notes_->retain_unprocessed_ownership(input);
493 for (
const auto& event : input) {
498 if (held_notes_->consume_suppressed_release(event))
500 const auto channel =
event.channel();
501 if ((spec_.accepted_channels & (std::uint16_t{1} << channel)) == 0) {
502 held_notes_->record(event,
false);
505 if (!held_notes_->can_forward(event)) {
506 held_notes_->record(event,
false);
513 held_notes_->record(event, forwarded);
516 if (
const auto* source = input.
ump()) {
517 for (
const auto& event : *source) {
523 if (held_notes_->consume_suppressed_release(event.packet))
525 const auto channel =
event.packet.channel();
526 if ((spec_.accepted_channels & (std::uint16_t{1} << channel)) == 0) {
527 held_notes_->record(event.packet,
false);
533 if (!held_notes_->can_forward(event.packet)) {
534 held_notes_->record(event.packet,
false);
540 held_notes_->record(event.packet, forwarded);
541 copied_sidecars = forwarded && copied_sidecars;
544 if (!copied_sidecars) {
554 std::unique_ptr<routing_detail::HeldNoteLedger> held_notes_;
559static_assert(
sizeof(ChannelRouter) <= 4096);
575 return spec.lowest <= 127 && spec.highest <= 127 && spec.lowest <= spec.highest;
584 return valid_ && held_notes_ !=
nullptr;
587 if (!held_notes_ || !
valid_spec(spec) || !held_notes_->empty())
597 return {0, 0, 0,
false};
599 if (held_notes_->output_ownership_empty())
602 return {0, 0, 1,
false};
603 report.
complete = held_notes_->flush(
604 [&](std::uint8_t channel, std::uint8_t note) {
607 [&](
bool midi2, std::uint8_t group, std::uint8_t channel, std::uint8_t note) {
610 {routing_detail::note_off_packet(midi2, group, channel, note), 0});
623 auto report =
flush(output);
625 held_notes_->discard_input_ownership();
632 return {0, 0, 0,
false};
634 auto report =
flush(output);
635 if (report.complete) {
644 return {0, input.
size(), 0,
false};
647 return {0, input.
size(), 0,
false};
650 held_notes_->retain_unprocessed_ownership(input);
651 return {0, input.
size(), 0,
false};
654 held_notes_->retain_unprocessed_ownership(input);
655 return {0, input.
size(), 0,
false};
657 const bool midi_drained =
658 held_notes_->drain_midi_releases([&](std::uint8_t channel, std::uint8_t note) {
662 const bool ump_drained = held_notes_->drain_ump_releases(
663 [&](
bool midi2, std::uint8_t group, std::uint8_t channel, std::uint8_t note) {
666 {routing_detail::note_off_packet(midi2, group, channel, note), 0});
673 if (!midi_drained || !ump_drained) {
674 held_notes_->retain_unprocessed_ownership(input);
679 for (
const auto& event : input) {
680 if (held_notes_->consume_suppressed_release(event))
683 (event.note() < spec_.lowest || event.note() > spec_.highest)) {
684 held_notes_->record(event,
false);
687 if (!held_notes_->can_forward(event)) {
688 held_notes_->record(event,
false);
694 held_notes_->record(event, forwarded);
697 if (
const auto* source = input.
ump()) {
698 for (
const auto& event : *source) {
699 if (held_notes_->consume_suppressed_release(event.packet))
702 (event.packet.note_number() < spec_.lowest ||
703 event.packet.note_number() > spec_.highest)) {
704 held_notes_->record(event.packet,
false);
707 if (!held_notes_->can_forward(event.packet)) {
708 held_notes_->record(event.packet,
false);
714 held_notes_->record(event.packet, forwarded);
715 copied_sidecars = forwarded && copied_sidecars;
718 if (!copied_sidecars) {
728 std::unique_ptr<routing_detail::HeldNoteLedger> held_notes_;
733static_assert(
sizeof(NoteRangeFilter) <= 4096);
751 return spec.split_note <= 127 && spec.lower_channel <= 15 && spec.upper_channel <= 15;
760 return valid_ && held_notes_ !=
nullptr;
763 if (!held_notes_ || !
valid_spec(spec) || !held_notes_->empty())
774 return {0, 0, 0,
false};
776 if (held_notes_->output_ownership_empty())
779 return {0, 0, 1,
false};
780 report.
complete = held_notes_->flush(
781 [&](std::uint8_t, std::uint8_t note) {
782 const bool is_upper =
783 note > spec_.split_note ||
784 (note == spec_.split_note && spec_.split_note_is_upper);
786 is_upper ? upper : lower,
788 is_upper ? spec_.upper_channel : spec_.lower_channel, note),
791 [&](
bool midi2, std::uint8_t group, std::uint8_t, std::uint8_t note) {
792 const bool is_upper =
793 note > spec_.split_note ||
794 (note == spec_.split_note && spec_.split_note_is_upper);
796 is_upper ? upper.ump() : lower.ump(),
797 {routing_detail::note_off_packet(
799 is_upper ? spec_.upper_channel : spec_.lower_channel, note),
813 auto report =
flush(lower, upper);
815 held_notes_->discard_input_ownership();
824 return {0, 0, 0,
false};
826 auto report =
flush(lower, upper);
827 if (report.complete) {
839 return {0, input.
size(), 0,
false};
843 return {0, input.
size(), 0,
false};
846 held_notes_->retain_unprocessed_ownership(input);
847 return {0, input.
size(), 0,
false};
850 held_notes_->retain_unprocessed_ownership(input);
851 return {0, input.
size(), 0,
false};
853 const bool midi_drained =
854 held_notes_->drain_midi_releases([&](std::uint8_t channel, std::uint8_t note) {
855 const bool is_upper =
856 note > spec_.split_note ||
857 (note == spec_.split_note && spec_.split_note_is_upper);
859 is_upper ? upper : lower,
861 is_upper ? spec_.upper_channel : spec_.lower_channel, note),
864 const bool ump_drained = held_notes_->drain_ump_releases(
865 [&](
bool midi2, std::uint8_t group, std::uint8_t channel, std::uint8_t note) {
867 const bool is_upper =
868 note > spec_.split_note ||
869 (note == spec_.split_note && spec_.split_note_is_upper);
871 is_upper ? upper.
ump() : lower.
ump(),
872 {routing_detail::note_off_packet(
874 is_upper ? spec_.upper_channel : spec_.lower_channel, note),
882 if (!midi_drained || !ump_drained) {
883 held_notes_->retain_unprocessed_ownership(input);
888 for (
const auto& event : input) {
895 const bool is_upper =
896 event.note() > spec_.split_note ||
897 (
event.note() == spec_.split_note && spec_.split_note_is_upper);
899 : spec_.lower_channel);
900 if (held_notes_->consume_suppressed_release(event))
902 if (!held_notes_->can_forward(event)) {
903 held_notes_->record(event,
false);
909 is_upper ? upper : lower, routed, report);
910 held_notes_->record(event, forwarded);
920 if (
const auto* source = input.
ump()) {
921 for (
const auto& event : *source) {
930 const bool is_upper =
event.packet.note_number() > spec_.split_note ||
931 (
event.packet.note_number() == spec_.split_note &&
932 spec_.split_note_is_upper);
935 event.packet, is_upper ? spec_.upper_channel : spec_.lower_channel);
936 if (held_notes_->consume_suppressed_release(event.packet))
938 if (!held_notes_->can_forward(event.packet)) {
939 held_notes_->record(event.packet,
false);
945 is_upper ? upper.
ump() : lower.
ump(), routed);
946 held_notes_->record(event.packet, forwarded);
947 copied_sidecars = forwarded && copied_sidecars;
950 auto lower_event = event;
955 auto upper_event = event;
962 if (!copied_sidecars) {
972 std::unique_ptr<routing_detail::HeldNoteLedger> held_notes_;
977static_assert(
sizeof(KeyboardSplit) <= 4096);
static constexpr bool valid_spec(const ChannelRouteSpec &spec) noexcept
Definition routing_utility_kernels.hpp:374
bool replace_spec(ChannelRouteSpec spec) noexcept
Definition routing_utility_kernels.hpp:389
MidiUtilityProcessReport reset(MidiBuffer &output) const noexcept
Definition routing_utility_kernels.hpp:431
MidiUtilityProcessReport replace_spec(ChannelRouteSpec spec, MidiBuffer &output) noexcept
Definition routing_utility_kernels.hpp:438
MidiUtilityProcessReport process(const MidiBuffer &input, MidiBuffer &output) const
Definition routing_utility_kernels.hpp:451
MidiUtilityProcessReport flush(MidiBuffer &output) const noexcept
Definition routing_utility_kernels.hpp:397
bool valid() const noexcept
Definition routing_utility_kernels.hpp:386
static constexpr MidiUtilityContract contract() noexcept
Definition routing_utility_kernels.hpp:368
ChannelRouter(ChannelRouteSpec spec={})
Definition routing_utility_kernels.hpp:382
bool valid() const noexcept
Definition routing_utility_kernels.hpp:759
MidiUtilityProcessReport reset(MidiBuffer &lower, MidiBuffer &upper) const noexcept
Definition routing_utility_kernels.hpp:812
MidiUtilityProcessReport process(const MidiBuffer &input, MidiBuffer &lower, MidiBuffer &upper) const
Definition routing_utility_kernels.hpp:834
MidiUtilityProcessReport flush(MidiBuffer &lower, MidiBuffer &upper) const noexcept
Definition routing_utility_kernels.hpp:770
MidiUtilityProcessReport replace_spec(KeyboardSplitSpec spec, MidiBuffer &lower, MidiBuffer &upper) noexcept
Definition routing_utility_kernels.hpp:819
static constexpr bool valid_spec(KeyboardSplitSpec spec) noexcept
Definition routing_utility_kernels.hpp:750
KeyboardSplit(KeyboardSplitSpec spec={})
Definition routing_utility_kernels.hpp:755
static constexpr MidiUtilityContract contract() noexcept
Definition routing_utility_kernels.hpp:744
bool replace_spec(KeyboardSplitSpec spec) noexcept
Definition routing_utility_kernels.hpp:762
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
std::size_t size() const
Definition buffer.hpp:123
const class UmpBuffer * ump() const
Attached UmpBuffer or nullptr.
Definition buffer.hpp:217
MidiUtilityProcessReport process(const MidiBuffer &input, MidiBuffer &output) const
Definition routing_utility_kernels.hpp:642
bool valid() const noexcept
Definition routing_utility_kernels.hpp:583
NoteRangeFilter(NoteRangeSpec spec={})
Definition routing_utility_kernels.hpp:579
MidiUtilityProcessReport reset(MidiBuffer &output) const noexcept
Definition routing_utility_kernels.hpp:622
MidiUtilityProcessReport replace_spec(NoteRangeSpec spec, MidiBuffer &output) noexcept
Definition routing_utility_kernels.hpp:629
static constexpr bool valid_spec(NoteRangeSpec spec) noexcept
Definition routing_utility_kernels.hpp:574
MidiUtilityProcessReport flush(MidiBuffer &output) const noexcept
Definition routing_utility_kernels.hpp:594
bool replace_spec(NoteRangeSpec spec) noexcept
Definition routing_utility_kernels.hpp:586
static constexpr MidiUtilityContract contract() noexcept
Definition routing_utility_kernels.hpp:568
Definition routing_utility_kernels.hpp:23
bool consume_suppressed_release(const MidiEvent &event) noexcept
Definition routing_utility_kernels.hpp:47
bool drain_midi_releases(Emit &&emit) const noexcept
Definition routing_utility_kernels.hpp:91
bool flush(EmitMidi &&emit_midi, EmitUmp &&emit_ump) const noexcept
Definition routing_utility_kernels.hpp:117
bool output_ownership_empty() const noexcept
Definition routing_utility_kernels.hpp:28
bool drain_ump_releases(Emit &&emit) const noexcept
Definition routing_utility_kernels.hpp:100
static constexpr std::size_t kMaxTrackedOwnership
Definition routing_utility_kernels.hpp:25
void discard_input_ownership() const noexcept
Definition routing_utility_kernels.hpp:34
void record(const UmpPacket &packet, bool forwarded) noexcept
Definition routing_utility_kernels.hpp:69
bool can_forward(const MidiEvent &event) const noexcept
Definition routing_utility_kernels.hpp:36
constexpr bool empty() const noexcept
Definition routing_utility_kernels.hpp:27
bool consume_suppressed_release(const UmpPacket &packet) noexcept
Definition routing_utility_kernels.hpp:53
bool can_forward(const UmpPacket &packet) const noexcept
Definition routing_utility_kernels.hpp:41
void retain_unprocessed_ownership(const MidiBuffer &input) noexcept
Definition routing_utility_kernels.hpp:79
void record(const MidiEvent &event, bool forwarded) noexcept
Definition routing_utility_kernels.hpp:60
Definition routing_utility_kernels.hpp:9
UmpPacket note_off_packet(bool midi2, std::uint8_t group, std::uint8_t channel, std::uint8_t note) noexcept
Definition routing_utility_kernels.hpp:11
bool is_note_addressed(const UmpPacket &packet) noexcept
Definition utility_contract.hpp:124
UmpPacket with_channel(UmpPacket packet, std::uint8_t channel) noexcept
Definition utility_contract.hpp:134
bool copy_sysex_sidecar(const MidiBuffer &input, MidiBuffer &output) noexcept
Definition utility_contract.hpp:104
bool is_channel_voice(const UmpPacket &packet) noexcept
Definition utility_contract.hpp:119
bool emit_ump(UmpBuffer *output, const UmpEvent &event) noexcept
Definition utility_contract.hpp:115
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
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
@ None
Definition utility_contract.hpp:31
@ RetainReleaseDebt
Definition utility_contract.hpp:22
@ Midi2ChannelVoice
Definition ump.hpp:27
@ Midi1ChannelVoice
Definition ump.hpp:25
@ InputStable
Definition utility_contract.hpp:26
@ size
Definition config.hpp:216
@ i
Definition formant_filter_bank.hpp:20
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48
Definition routing_utility_kernels.hpp:360
std::uint16_t accepted_channels
Definition routing_utility_kernels.hpp:361
std::array< std::uint8_t, 16 > output_channel
Definition routing_utility_kernels.hpp:362
Definition routing_utility_kernels.hpp:735
std::uint8_t upper_channel
Definition routing_utility_kernels.hpp:739
bool split_note_is_upper
Definition routing_utility_kernels.hpp:737
std::uint8_t lower_channel
Definition routing_utility_kernels.hpp:738
std::uint8_t split_note
Definition routing_utility_kernels.hpp:736
Timestamped MIDI event combining a choc::midi::ShortMessage with timing info.
Definition message.hpp:32
static MidiEvent note_off(uint8_t channel, uint8_t note, uint8_t velocity=0)
Create a Note Off event.
Definition message.hpp:54
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
Definition routing_utility_kernels.hpp:561
std::uint8_t lowest
Definition routing_utility_kernels.hpp:562
std::uint8_t highest
Definition routing_utility_kernels.hpp:563
static UmpPacket note_off_2(uint8_t group, uint8_t channel, uint8_t note, uint16_t velocity=0)
Definition ump.hpp:157
std::array< uint32_t, 4 > words
Definition ump.hpp:85
int word_count
Definition ump.hpp:86