56 constexpr bool valid() const noexcept {
66 return {0, input.size(), 0,
false};
74 drain_pending_releases(output, report);
75 for (
const auto& event : input) {
76 const bool is_attack =
event.is_note_on() &&
event.velocity() != 0;
77 const bool is_release =
78 event.is_note_off() || (
event.is_note_on() && event.velocity() == 0);
79 if (!is_attack && !is_release) {
85 handle_attack(key, event, output, report);
87 handle_release(key, event, output, report);
103 drain_pending_releases(output, report);
104 if (pending_release_total_ != 0)
106 for (std::size_t key = 0; key <
kKeySpace && owned_total_ != 0; ++key) {
107 while (owned_depth_[key] != 0) {
117 physical_depth_.fill(0);
120 if (report.
complete && pending_spec_) {
121 spec_ = *pending_spec_;
123 pending_spec_.reset();
129 return flush(output);
138 return {0, 0, 0,
false};
140 auto report =
flush(output);
141 if (report.complete) {
144 pending_spec_.reset();
146 pending_spec_ =
spec;
152 return owned_total_ == 0;
156 std::uint32_t
owned_depth(std::uint8_t channel, std::uint8_t note)
const noexcept {
166 increment(physical_depth_[
static_cast<std::size_t
>(key)]);
167 increment(physical_down_);
171 release_all_owned(event.sample_offset, output, report);
172 if (spec_.
mode ==
LatchMode::Hold && physical_depth_[
static_cast<std::size_t
>(key)] == 0 &&
173 owned_depth_[
static_cast<std::size_t
>(key)] != 0) {
177 increment(physical_depth_[
static_cast<std::size_t
>(key)]);
178 increment(physical_down_);
182 owned_depth_[
static_cast<std::size_t
>(key)] != 0) {
185 release_key(key, event.sample_offset, output, report);
186 increment(physical_depth_[
static_cast<std::size_t
>(key)]);
187 increment(physical_down_);
192 increment(physical_depth_[
static_cast<std::size_t
>(key)]);
193 increment(physical_down_);
196 void handle_release(
int key,
const MidiEvent& event, MidiBuffer& output,
197 MidiUtilityProcessReport& report)
noexcept {
198 auto& physical = physical_depth_[
static_cast<std::size_t
>(key)];
201 if (physical_down_ != 0)
208 queue_pending_release(key);
215 void release_key(
int key, std::int32_t sample_offset, MidiBuffer& output,
216 MidiUtilityProcessReport& report)
noexcept {
217 while (owned_depth_[
static_cast<std::size_t
>(key)] != 0) {
222 report.complete =
false;
232 void acquire(
int key)
noexcept {
233 auto& depth = owned_depth_[
static_cast<std::size_t
>(key)];
234 if (depth == std::numeric_limits<std::uint32_t>::max())
237 increment(owned_total_);
240 void surrender(
int key)
noexcept {
241 auto& depth = owned_depth_[
static_cast<std::size_t
>(key)];
245 if (owned_total_ != 0)
249 void release_all_owned(std::int32_t sample_offset, MidiBuffer& output,
250 MidiUtilityProcessReport& report)
noexcept {
255 if (owned_total_ == 0)
257 for (std::size_t key = 0; key <
kKeySpace; ++key)
258 release_key(
static_cast<int>(key), sample_offset, output, report);
261 void queue_pending_release(
int key)
noexcept {
262 auto&
pending = pending_release_[
static_cast<std::size_t
>(key)];
263 if (pending != std::numeric_limits<std::uint32_t>::max()) {
265 ++pending_release_total_;
269 void drain_pending_releases(MidiBuffer& output, MidiUtilityProcessReport& report)
noexcept {
270 for (std::size_t key = 0; key <
kKeySpace && pending_release_total_ != 0; ++key) {
271 auto&
pending = pending_release_[key];
272 while (pending != 0) {
276 --pending_release_total_;
277 surrender(
static_cast<int>(key));
282 static MidiEvent note_off_for(std::size_t key)
noexcept {
284 static_cast<std::uint8_t
>(key % 128));
287 template <
typename Counter>
static void increment(Counter& value)
noexcept {
288 if (value != std::numeric_limits<Counter>::max())
294 std::optional<LatchSpec> pending_spec_{};
295 std::array<std::uint32_t, kKeySpace> owned_depth_{};
296 std::array<std::uint32_t, kKeySpace> physical_depth_{};
297 std::array<std::uint32_t, kKeySpace> pending_release_{};
298 std::uint32_t pending_release_total_ = 0;
299 std::uint32_t physical_down_ = 0;
300 std::uint32_t owned_total_ = 0;
static constexpr bool valid_spec(LatchSpec spec) noexcept
Definition latch.hpp:48
static constexpr std::size_t kKeySpace
Definition latch.hpp:40
constexpr bool valid() const noexcept
Definition latch.hpp:56
MidiUtilityProcessReport process(const MidiBuffer &input, MidiBuffer &output) noexcept
Definition latch.hpp:64
constexpr LatchSpec spec() const noexcept
Definition latch.hpp:60
MidiUtilityProcessReport replace_spec(LatchSpec spec, MidiBuffer &output) noexcept
Swap the retention mode.
Definition latch.hpp:135
MidiUtilityProcessReport reset(MidiBuffer &output) noexcept
Definition latch.hpp:128
constexpr Latch(LatchSpec spec={}) noexcept
Definition latch.hpp:53
MidiUtilityProcessReport flush(MidiBuffer &output) noexcept
Release every note the latch still owns.
Definition latch.hpp:96
std::uint32_t owned_depth(std::uint8_t channel, std::uint8_t note) const noexcept
Number of note-offs the latch still owes for channel/note.
Definition latch.hpp:156
static constexpr MidiUtilityContract contract() noexcept
Definition latch.hpp:42
bool empty() const noexcept
Definition latch.hpp:151
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
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
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
@ FailOpenBalanced
Definition utility_contract.hpp:21
LatchMode
How a latch retains the notes a player releases.
Definition latch.hpp:19
@ Toggle
Definition latch.hpp:22
@ Hold
Definition latch.hpp:21
@ Off
Definition latch.hpp:20
@ ReleaseBeforeAttack
Definition utility_contract.hpp:27
@ pending
Definition gpu_pass.hpp:556
@ off
Definition nonlinear_shaping.hpp:24
@ value
the widget's primary value (Knob, Fader, RangeSlider, Toggle, ProgressBar)
Definition binding_diagnostics.hpp:19
LatchMode mode
Definition latch.hpp:26
constexpr auto operator<=>(const LatchSpec &) const =default
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