Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
utility_contract.hpp
Go to the documentation of this file.
1#pragma once
2
7
8#include <algorithm>
9#include <array>
10#include <cmath>
11#include <cstddef>
12#include <cstdint>
13#include <limits>
14#include <optional>
15#include <type_traits>
16
17namespace pulp::midi {
18
24
29
35
45
47 std::size_t emitted = 0;
48 std::size_t dropped = 0;
49 std::size_t deferred = 0;
50 bool complete = true;
51};
52
53namespace utility_detail {
54
55inline int key_index(std::uint8_t channel, std::uint8_t note) noexcept {
56 return static_cast<int>(channel & 0x0f) * 128 + static_cast<int>(note & 0x7f);
57}
58
59constexpr std::int64_t saturating_sample_add(std::int64_t position, std::int64_t offset) noexcept {
60 if (offset > 0 && position > std::numeric_limits<std::int64_t>::max() - offset)
61 return std::numeric_limits<std::int64_t>::max();
62 if (offset < 0 && position < std::numeric_limits<std::int64_t>::min() - offset)
63 return std::numeric_limits<std::int64_t>::min();
64 return position + offset;
65}
66
67inline bool emit(MidiBuffer& output, const MidiEvent& event, MidiUtilityProcessReport& report) {
68 if (!output.realtime_capacity_limited()) {
69 ++report.dropped;
70 report.complete = false;
71 return false;
72 }
73 if (output.add(event)) {
74 ++report.emitted;
75 return true;
76 }
77 ++report.dropped;
78 report.complete = false;
79 return false;
80}
81
82inline void clear_output(MidiBuffer& output) noexcept {
83 clear_midi_block(output);
84}
85
86inline bool ready(const MidiBuffer& output) noexcept {
87 const auto* ump = output.ump();
88 return output.realtime_capacity_limited() &&
89 (ump == nullptr || ump->realtime_capacity_limited());
90}
91
92inline bool blocks_alias(const MidiBuffer& first, const MidiBuffer& second) noexcept {
93 return &first == &second || (first.ump() != nullptr && first.ump() == second.ump());
94}
95
96inline void copy_sidecars(const MidiBuffer& input, MidiBuffer& output,
97 MidiUtilityProcessReport& report) noexcept {
98 if (!copy_midi_sidecars(input, output)) {
99 ++report.dropped;
100 report.complete = false;
101 }
102}
103
104inline bool copy_sysex_sidecar(const MidiBuffer& input, MidiBuffer& output) noexcept {
105 bool copied_all = !midi_block_has_drops(input);
106 for (const auto& sx : input.sysex()) {
107 const bool copied = sx.data.empty() ? output.add_sysex({}, sx.sample_offset, sx.timestamp)
108 : output.add_sysex_copy(sx.data.data(), sx.data.size(),
109 sx.sample_offset, sx.timestamp);
110 copied_all = copied && copied_all;
111 }
112 return copied_all;
113}
114
115inline bool emit_ump(UmpBuffer* output, const UmpEvent& event) noexcept {
116 return output != nullptr && output->add(event);
117}
118
119inline bool is_channel_voice(const UmpPacket& packet) noexcept {
120 return packet.message_type() == UmpMessageType::Midi1ChannelVoice ||
121 packet.message_type() == UmpMessageType::Midi2ChannelVoice;
122}
123
124inline bool is_note_addressed(const UmpPacket& packet) noexcept {
125 if (!is_channel_voice(packet))
126 return false;
127 const auto status = static_cast<std::uint8_t>(packet.status() & 0xf0);
128 if (packet.message_type() == UmpMessageType::Midi1ChannelVoice)
129 return status == 0x80 || status == 0x90 || status == 0xa0;
130 return status == 0x00 || status == 0x10 || status == 0x60 || status == 0x80 || status == 0x90 ||
131 status == 0xa0 || status == 0xf0;
132}
133
134inline UmpPacket with_channel(UmpPacket packet, std::uint8_t channel) noexcept {
135 packet.words[0] = (packet.words[0] & ~(std::uint32_t{0x0f} << 16)) |
136 (static_cast<std::uint32_t>(channel & 0x0f) << 16);
137 return packet;
138}
139
140inline MidiEvent at(MidiEvent event, std::int32_t sample_offset) noexcept {
141 event.sample_offset = sample_offset;
142 return event;
143}
144
145inline MidiEvent with_channel(const MidiEvent& event, std::uint8_t channel) noexcept {
146 const auto* bytes = event.data();
147 const auto status = static_cast<std::uint8_t>((bytes[0] & 0xf0) | (channel & 0x0f));
148 MidiEvent result{choc::midi::ShortMessage(status, event.size() > 1 ? bytes[1] : 0,
149 event.size() > 2 ? bytes[2] : 0),
150 event.sample_offset, event.timestamp};
151 return result;
152}
153
154inline bool is_channel_voice(const MidiEvent& event) noexcept {
155 return event.size() != 0 && event.data()[0] >= 0x80 && event.data()[0] < 0xf0;
156}
157
158inline bool is_note_addressed(const MidiEvent& event) noexcept {
159 if (!is_channel_voice(event))
160 return false;
161 const auto status = static_cast<std::uint8_t>(event.data()[0] & 0xf0);
162 return status == 0x80 || status == 0x90 || status == 0xa0;
163}
164
165inline int nearest_scale_note(int note, const music::Scale& scale) noexcept {
166 int best = std::clamp(note, 0, 127);
167 int best_distance = std::numeric_limits<int>::max();
168 for (int candidate = 0; candidate <= 127; ++candidate) {
169 if (!scale.contains(static_cast<music::PitchClass>(candidate % 12)))
170 continue;
171 const int distance = std::abs(candidate - note);
172 if (distance < best_distance || (distance == best_distance && candidate < best)) {
173 best = candidate;
174 best_distance = distance;
175 }
176 }
177 return best;
178}
179
180} // namespace utility_detail
181
182} // namespace pulp::midi
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
bool realtime_capacity_limited() const
Definition buffer.hpp:151
bool add(const MidiEvent &event)
Append a MIDI event to the buffer.
Definition buffer.hpp:98
A time-ordered list of UMP events for one audio block.
Definition ump_buffer.hpp:41
Definition pitch.hpp:266
MPE (MIDI Polyphonic Expression) voice tracker.
Definition utility_contract.hpp:53
MidiEvent at(MidiEvent event, std::int32_t sample_offset) noexcept
Definition utility_contract.hpp:140
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 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
int nearest_scale_note(int note, const music::Scale &scale) noexcept
Definition utility_contract.hpp:165
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 ble_midi.hpp:86
void clear_midi_block(MidiBuffer &block) noexcept
Definition block_ops.hpp:26
MidiUtilityTransportRequirement
Definition utility_contract.hpp:30
@ MonotonicSamples
Definition utility_contract.hpp:32
@ None
Definition utility_contract.hpp:31
@ FlushOnDiscontinuity
Definition utility_contract.hpp:33
MidiUtilityOverflowPolicy
Definition utility_contract.hpp:19
@ RetainReleaseDebt
Definition utility_contract.hpp:22
@ DropUnstarted
Definition utility_contract.hpp:20
@ FailOpenBalanced
Definition utility_contract.hpp:21
bool copy_midi_sidecars(const MidiBuffer &src, MidiBuffer &dst) noexcept
Definition block_ops.hpp:45
@ Midi2ChannelVoice
Definition ump.hpp:27
@ Midi1ChannelVoice
Definition ump.hpp:25
MidiUtilitySameSampleOrder
Definition utility_contract.hpp:25
@ ReleaseBeforeAttack
Definition utility_contract.hpp:27
@ InputStable
Definition utility_contract.hpp:26
bool midi_block_has_drops(const MidiBuffer &block) noexcept
Definition block_ops.hpp:34
PitchClass
Definition pitch.hpp:19
Timestamped MIDI event combining a choc::midi::ShortMessage with timing info.
Definition message.hpp:32
Definition utility_contract.hpp:36
MidiUtilityTransportRequirement transport
Definition utility_contract.hpp:41
std::size_t maximum_event_amplification
Definition utility_contract.hpp:37
bool requires_reserved_capacity_limited_output
Definition utility_contract.hpp:42
MidiUtilitySameSampleOrder same_sample_order
Definition utility_contract.hpp:40
bool requires_distinct_input_output
Definition utility_contract.hpp:43
MidiUtilityOverflowPolicy overflow
Definition utility_contract.hpp:39
std::size_t state_capacity
Definition utility_contract.hpp:38
Definition utility_contract.hpp:46
bool complete
Definition utility_contract.hpp:50
std::size_t emitted
Definition utility_contract.hpp:47
std::size_t dropped
Definition utility_contract.hpp:48
std::size_t deferred
Definition utility_contract.hpp:49
One UMP packet with a block-relative sample offset.
Definition ump_buffer.hpp:25
Definition ump.hpp:84