Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
panic.hpp
Go to the documentation of this file.
1#pragma once
2
27
29#include <pulp/midi/message.hpp>
30
31#include <cstddef>
32#include <cstdint>
33#include <optional>
34#include <span>
35
36namespace pulp::midi {
37
46
48struct PanicScope {
50 std::uint16_t channel_mask = 0xFFFFu;
52 bool all_sound_off = true;
53 bool all_notes_off = true;
55 bool sustain_off = true;
56 bool pitch_bend_centre = true;
57
59 static constexpr PanicScope release_only() {
60 PanicScope scope;
61 scope.all_sound_off = false;
62 return scope;
63 }
64
65 constexpr bool includes_channel(unsigned channel) const {
66 return channel < 16u && ((channel_mask >> channel) & 1u) != 0u;
67 }
68};
69
71inline constexpr std::size_t kMaxPanicMessages = 16u * 5u;
72
74inline constexpr std::size_t kMaxNoteOffs = 16u * 128u;
75
76namespace panic_detail {
77inline constexpr std::uint8_t kCcSustain = 64;
78inline constexpr std::uint8_t kCcResetAllControllers = 121;
79inline constexpr std::uint8_t kCcAllSoundOff = 120;
80inline constexpr std::uint8_t kCcAllNotesOff = 123;
81inline constexpr std::uint16_t kPitchBendCentre = 8192;
82
83inline constexpr std::size_t kind_count(const PanicScope& scope) {
84 return std::size_t{scope.all_sound_off} + std::size_t{scope.all_notes_off} +
85 std::size_t{scope.reset_all_controllers} + std::size_t{scope.sustain_off} +
86 std::size_t{scope.pitch_bend_centre};
87}
88
89inline constexpr unsigned popcount16(std::uint16_t mask) {
90 unsigned n = 0;
91 for (; mask != 0u; mask = static_cast<std::uint16_t>(mask & (mask - 1u)))
92 ++n;
93 return n;
94}
95} // namespace panic_detail
96
98inline constexpr std::size_t panic_message_count(const PanicScope& scope) {
99 return panic_detail::kind_count(scope) * panic_detail::popcount16(scope.channel_mask);
100}
101
113inline std::size_t panic_messages(const PanicScope& scope, std::span<MidiEvent> out,
114 std::int32_t sample_offset = 0) noexcept {
115 using namespace panic_detail;
116 std::size_t n = 0;
117 const auto put = [&](MidiEvent event) {
118 if (n >= out.size())
119 return false;
120 event.sample_offset = sample_offset;
121 out[n++] = event;
122 return true;
123 };
124 for (unsigned ch = 0; ch < 16u; ++ch) {
125 if (!scope.includes_channel(ch))
126 continue;
127 const auto channel = static_cast<std::uint8_t>(ch);
128 if (scope.sustain_off && !put(MidiEvent::cc(channel, kCcSustain, 0)))
129 return n;
130 if (scope.all_notes_off && !put(MidiEvent::cc(channel, kCcAllNotesOff, 0)))
131 return n;
132 if (scope.all_sound_off && !put(MidiEvent::cc(channel, kCcAllSoundOff, 0)))
133 return n;
134 if (scope.reset_all_controllers && !put(MidiEvent::cc(channel, kCcResetAllControllers, 0)))
135 return n;
136 if (scope.pitch_bend_centre && !put(MidiEvent::pitch_bend(channel, kPitchBendCentre)))
137 return n;
138 }
139 return n;
140}
141
146template <typename Fn>
147inline void for_each_held_note(const MidiKeyboardState& keys, std::uint16_t channel_mask,
148 Fn&& fn) noexcept(noexcept(fn(std::uint8_t{}, std::uint8_t{}))) {
149 for (unsigned ch = 0; ch < 16u; ++ch) {
150 if (((channel_mask >> ch) & 1u) == 0u)
151 continue;
152 for (unsigned note = 0; note < 128u; ++note) {
153 if (keys.is_note_on(static_cast<std::uint8_t>(ch), static_cast<std::uint8_t>(note)))
154 fn(static_cast<std::uint8_t>(ch), static_cast<std::uint8_t>(note));
155 }
156 }
157}
158
163inline std::size_t held_note_offs(const MidiKeyboardState& keys, std::span<MidiEvent> out,
164 std::uint16_t channel_mask = 0xFFFFu,
165 std::int32_t sample_offset = 0) noexcept {
166 std::size_t n = 0;
167 for_each_held_note(keys, channel_mask, [&](std::uint8_t ch, std::uint8_t note) {
168 if (n >= out.size())
169 return;
170 auto event = MidiEvent::note_off(ch, note);
171 event.sample_offset = sample_offset;
172 out[n++] = event;
173 });
174 return n;
175}
176
180inline std::size_t all_note_offs(std::span<MidiEvent> out, std::uint16_t channel_mask = 0xFFFFu,
181 std::int32_t sample_offset = 0) noexcept {
182 std::size_t n = 0;
183 for (unsigned ch = 0; ch < 16u; ++ch) {
184 if (((channel_mask >> ch) & 1u) == 0u)
185 continue;
186 for (unsigned note = 0; note < 128u; ++note) {
187 if (n >= out.size())
188 return n;
189 auto event =
190 MidiEvent::note_off(static_cast<std::uint8_t>(ch), static_cast<std::uint8_t>(note));
191 event.sample_offset = sample_offset;
192 out[n++] = event;
193 }
194 }
195 return n;
196}
197
201inline std::optional<PanicMessageKind> classify_panic_message(const MidiEvent& event) noexcept {
202 using namespace panic_detail;
203 const auto& msg = event.message;
204 if (msg.isController()) {
205 if (msg.getControllerValue() != 0)
206 return std::nullopt;
207 switch (msg.getControllerNumber()) {
208 case kCcAllSoundOff:
210 case kCcAllNotesOff:
212 case kCcResetAllControllers:
214 case kCcSustain:
216 default:
217 return std::nullopt;
218 }
219 }
220 if (msg.isPitchWheel() && msg.getPitchWheelValue() == kPitchBendCentre)
222 return std::nullopt;
223}
224
225} // namespace pulp::midi
Tracks the state of all 128 MIDI keys across 16 channels.
Definition keyboard_state.hpp:35
Polyphonic key tracking with velocity and listener callbacks.
Definition panic.hpp:76
constexpr std::uint8_t kCcSustain
Definition panic.hpp:77
constexpr std::size_t kind_count(const PanicScope &scope)
Definition panic.hpp:83
constexpr std::uint16_t kPitchBendCentre
Definition panic.hpp:81
constexpr std::uint8_t kCcResetAllControllers
Definition panic.hpp:78
constexpr std::uint8_t kCcAllNotesOff
Definition panic.hpp:80
constexpr unsigned popcount16(std::uint16_t mask)
Definition panic.hpp:89
constexpr std::uint8_t kCcAllSoundOff
Definition panic.hpp:79
Definition custom_node_events.hpp:26
PanicMessageKind
One kind of message a panic can contain.
Definition panic.hpp:39
@ reset_all_controllers
CC 121 = 0.
Definition panic.hpp:42
@ all_notes_off
CC 123 = 0, release.
Definition panic.hpp:41
@ sustain_off
CC 64 = 0.
Definition panic.hpp:43
@ pitch_bend_centre
pitch bend 8192
Definition panic.hpp:44
@ all_sound_off
CC 120 = 0, immediate stop.
Definition panic.hpp:40
std::size_t held_note_offs(const MidiKeyboardState &keys, std::span< MidiEvent > out, std::uint16_t channel_mask=0xFFFFu, std::int32_t sample_offset=0) noexcept
Note-off events for exactly the notes keys reports as held, on the channels in channel_mask,...
Definition panic.hpp:163
constexpr std::size_t panic_message_count(const PanicScope &scope)
Number of events panic_messages() will write for scope.
Definition panic.hpp:98
std::size_t all_note_offs(std::span< MidiEvent > out, std::uint16_t channel_mask=0xFFFFu, std::int32_t sample_offset=0) noexcept
Brute-force fallback: a note-off for every key on the channels in channel_mask, whether or not it was...
Definition panic.hpp:180
std::size_t panic_messages(const PanicScope &scope, std::span< MidiEvent > out, std::int32_t sample_offset=0) noexcept
Write the panic messages for scope into out, channel by channel.
Definition panic.hpp:113
std::optional< PanicMessageKind > classify_panic_message(const MidiEvent &event) noexcept
Classify a message as one of the panic kinds, or nullopt for anything else.
Definition panic.hpp:201
void for_each_held_note(const MidiKeyboardState &keys, std::uint16_t channel_mask, Fn &&fn) noexcept(noexcept(fn(std::uint8_t{}, std::uint8_t{})))
Call fn(channel, note) for every key keys reports as held on the channels in channel_mask,...
Definition panic.hpp:147
constexpr std::size_t kMaxNoteOffs
Largest number of events held_note_offs() / all_note_offs() can produce.
Definition panic.hpp:74
constexpr std::size_t kMaxPanicMessages
Largest number of events panic_messages() can produce (16 channels x 5).
Definition panic.hpp:71
Timestamped MIDI event combining a choc::midi::ShortMessage with timing info.
Definition message.hpp:32
static MidiEvent cc(uint8_t channel, uint8_t controller, uint8_t value)
Create a Control Change event.
Definition message.hpp:64
static MidiEvent note_off(uint8_t channel, uint8_t note, uint8_t velocity=0)
Create a Note Off event.
Definition message.hpp:54
static MidiEvent pitch_bend(uint8_t channel, uint16_t value)
Create a Pitch Bend event.
Definition message.hpp:73
Which channels and which message kinds a panic covers.
Definition panic.hpp:48
bool reset_all_controllers
Definition panic.hpp:54
std::uint16_t channel_mask
Bit n set means channel n (0-15) is included. Default: all 16.
Definition panic.hpp:50
bool all_notes_off
Definition panic.hpp:53
static constexpr PanicScope release_only()
Panic that lets release tails ring out (no CC 120).
Definition panic.hpp:59
bool sustain_off
Definition panic.hpp:55
bool all_sound_off
Immediate-stop CC 120. Clear for a release-only panic.
Definition panic.hpp:52
constexpr bool includes_channel(unsigned channel) const
Definition panic.hpp:65
bool pitch_bend_centre
Definition panic.hpp:56