Zrythm v2.0.0-alpha.1
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
midi_event.h
1// SPDX-FileCopyrightText: © 2018-2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include <algorithm>
7#include <cstdint>
8#include <cstring>
9#include <functional>
10
11#include "utils/midi.h"
12#include "utils/units.h"
13
14#include <crill/spin_mutex.h>
15#include <juce_audio_basics/juce_audio_basics.h>
16
17namespace zrythm::dsp
18{
19class ChordDescriptor;
20
22constexpr int MAX_MIDI_EVENTS = 2560;
23
27struct MidiEvent final
28{
29public:
30 // Rule of 0
31 MidiEvent () = default;
32 MidiEvent (
33 midi_byte_t byte1,
34 midi_byte_t byte2,
35 midi_byte_t byte3,
36 units::sample_u32_t time)
37 : raw_buffer_sz_ (3), time_ (time)
38 {
39 raw_buffer_[0] = byte1;
40 raw_buffer_[1] = byte2;
41 raw_buffer_[2] = byte3;
42 }
43
44 void set_velocity (midi_byte_t vel);
45
46 void print () const;
47
48public:
50 std::array<midi_byte_t, 3> raw_buffer_{ 0, 0, 0 };
51
52 uint_fast8_t raw_buffer_sz_{};
53
55 units::sample_u32_t time_;
56
58 std::int64_t systime_{};
59};
60
61inline bool
62operator== (const MidiEvent &lhs, const MidiEvent &rhs)
63{
64 return lhs.time_ == rhs.time_ && lhs.raw_buffer_sz_ == rhs.raw_buffer_sz_
65 && lhs.raw_buffer_ == rhs.raw_buffer_;
66}
67
73class MidiEventVector final
74{
75public:
76 MidiEventVector () { events_.reserve (MAX_MIDI_EVENTS); }
77
78 using ChordDescriptor = dsp::ChordDescriptor;
79
80 // Iterator types
81 using Iterator = std::vector<MidiEvent>::iterator;
82 using ConstIterator = std::vector<MidiEvent>::const_iterator;
83
84 // Iterator methods
85 Iterator begin ()
86 {
87 const std::lock_guard<crill::spin_mutex> lock (lock_);
88 return events_.begin ();
89 }
90
91 Iterator end ()
92 {
93 const std::lock_guard<crill::spin_mutex> lock (lock_);
94 return events_.end ();
95 }
96
97 void erase (Iterator it, Iterator it_end)
98 {
99 const std::lock_guard<crill::spin_mutex> lock (lock_);
100 events_.erase (it, it_end);
101 }
102
103 ConstIterator begin () const
104 {
105 const std::lock_guard<crill::spin_mutex> lock (lock_);
106 return events_.begin ();
107 }
108
109 ConstIterator end () const
110 {
111 const std::lock_guard<crill::spin_mutex> lock (lock_);
112 return events_.end ();
113 }
114
115 void push_back (const MidiEvent &ev)
116 {
117 const std::lock_guard<crill::spin_mutex> lock (lock_);
118 events_.push_back (ev);
119 }
120
121 void push_back (const std::vector<MidiEvent> &events)
122 {
123 const std::lock_guard<crill::spin_mutex> lock (lock_);
124 events_.insert (events_.end (), events.begin (), events.end ());
125 }
126
127 MidiEvent pop_front ()
128 {
129 const std::lock_guard<crill::spin_mutex> lock (lock_);
130 MidiEvent ev = events_.front ();
131 events_.erase (events_.begin ());
132 return ev;
133 }
134
135 MidiEvent pop_back ()
136 {
137 const std::lock_guard<crill::spin_mutex> lock (lock_);
138 MidiEvent ev = events_.back ();
139 events_.pop_back ();
140 return ev;
141 }
142
143 void clear ()
144 {
145 const std::lock_guard<crill::spin_mutex> lock (lock_);
146 events_.clear ();
147 }
148
149 size_t size () const
150 {
151 const std::lock_guard<crill::spin_mutex> lock (lock_);
152 return events_.size ();
153 }
154
155 MidiEvent front () const
156 {
157 const std::lock_guard<crill::spin_mutex> lock (lock_);
158 return events_.front ();
159 }
160
161 MidiEvent back () const
162 {
163 const std::lock_guard<crill::spin_mutex> lock (lock_);
164 return events_.back ();
165 }
166
167 MidiEvent at (size_t index) const
168 {
169 const std::lock_guard<crill::spin_mutex> lock (lock_);
170 return events_.at (index);
171 }
172
173 void swap (MidiEventVector &other)
174 {
175 const std::lock_guard<crill::spin_mutex> lock (lock_);
176 events_.swap (other.events_);
177 }
178
179 void remove_if (std::function<bool (const MidiEvent &)> predicate)
180 {
181 const std::lock_guard<crill::spin_mutex> lock (lock_);
182 // std::erase_if (events_, std::move (predicate)); // fails to build on
183 // Xcode 26
184 auto it =
185 std::remove_if (events_.begin (), events_.end (), std::move (predicate));
186 events_.erase (it, events_.end ());
187 }
188
192 void remove (const MidiEvent &event)
193 {
194 remove_if ([&event] (const MidiEvent &e) { return e == event; });
195 }
196
197 template <typename Func> void foreach_event (Func &&func) const
198 {
199 const std::lock_guard<crill::spin_mutex> lock (lock_);
200 std::ranges::for_each (events_, std::forward<Func> (func));
201 }
202
203 size_t capacity () const
204 {
205 const std::lock_guard<crill::spin_mutex> lock (lock_);
206 return events_.capacity ();
207 }
208
209 void reserve (size_t new_capacity)
210 {
211 const std::lock_guard<crill::spin_mutex> lock (lock_);
212 events_.reserve (new_capacity);
213 }
214
215 void print () const;
216
224 [[gnu::hot]] void append_w_filter (
225 const MidiEventVector &src,
226 std::optional<std::array<bool, 16>> channels,
227 units::sample_u32_t local_offset,
228 units::sample_u32_t nframes);
229
236 void append (
237 const MidiEventVector &src,
238 units::sample_u32_t local_offset,
239 units::sample_u32_t nframes);
240
241 using NotePitchToChordDescriptorFunc =
242 std::function<const ChordDescriptor *(midi_byte_t)>;
243
251 const MidiEventVector &src,
252 NotePitchToChordDescriptorFunc note_number_to_chord_descriptor,
253 midi_byte_t velocity_to_use,
254 units::sample_u32_t local_offset,
255 units::sample_u32_t nframes);
256
264 midi_byte_t channel,
265 midi_byte_t note_pitch,
266 midi_byte_t velocity,
267 units::sample_u32_t time);
268
273 const ChordDescriptor &descr,
274 midi_byte_t channel,
275 midi_byte_t velocity,
276 units::sample_u32_t time);
277
282 const ChordDescriptor &descr,
283 midi_byte_t channel,
284 units::sample_u32_t time);
285
292 midi_byte_t channel,
293 midi_byte_t volume,
294 units::sample_u32_t time);
295
296#if 0
303 bool has_note_on (bool check_main, bool check_queued);
304#endif
305
306 bool has_any () const { return !empty (); }
307 bool empty () const { return size () == 0; }
308
315 void
316 add_event_from_buf (units::sample_u32_t time, midi_byte_t * buf, int buf_size);
317
325 midi_byte_t channel,
326 midi_byte_t note_pitch,
327 units::sample_u32_t time);
328
335 midi_byte_t channel,
336 midi_byte_t controller,
337 midi_byte_t control,
338 units::sample_u32_t time);
339
345 void add_song_pos (int64_t total_sixteenths, units::sample_u32_t time);
346
347 void add_raw (const uint8_t * buf, size_t buf_sz, units::sample_u32_t time);
348
349 void add_simple (
350 midi_byte_t byte1,
351 midi_byte_t byte2,
352 midi_byte_t byte3,
353 units::sample_u32_t time)
354 {
355 push_back (MidiEvent (byte1, byte2, byte3, time));
356 }
357
365 midi_byte_t channel,
366 uint32_t pitchbend,
367 units::sample_u32_t time);
368
369 void add_channel_pressure (
370 midi_byte_t channel,
371 midi_byte_t value,
372 units::sample_u32_t time);
373
378 midi_byte_t channel,
379 units::sample_u32_t time,
380 bool with_lock);
381
385 void panic_without_lock (units::sample_u32_t time)
386 {
387 for (midi_byte_t i = 1; i < 17; i++)
388 {
389 add_all_notes_off (i, time, false);
390 }
391 }
392
396 void panic ();
397
408 juce::MidiMessageSequence &sequence,
409 bool update_matched_pairs) const;
410
415
416#if 0
420 bool check_for_note_on (int note);
421
426 bool delete_note_on (int note);
427#endif
428
432 void sort ();
433
438 void set_channel (midi_byte_t channel);
439
440 void delete_event (const MidiEvent * ev);
441
442private:
443 std::vector<MidiEvent> events_;
444
446 mutable crill::spin_mutex lock_;
447};
448
454class MidiEvents final
455{
456public:
463 void dequeue (units::sample_u32_t local_offset, units::sample_u32_t nframes);
464
465public:
468
477};
478
479} // namespace zrythm::dsp
A ChordDescriptor describes a chord and is not linked to any specific object by itself.
A lock-free thread-safe vector of MidiEvents.
Definition midi_event.h:74
void panic()
Must only be called from the UI thread.
void set_channel(midi_byte_t channel)
Sets the given MIDI channel on all applicable MIDI events.
void add_all_notes_off(midi_byte_t channel, units::sample_u32_t time, bool with_lock)
Queues MIDI note off to event queue.
void sort()
Sorts the MidiEvents by time.
void add_song_pos(int64_t total_sixteenths, units::sample_u32_t time)
Adds a song position event to the queue.
void add_pitchbend(midi_byte_t channel, uint32_t pitchbend, units::sample_u32_t time)
Adds a control event to the given MidiEvents.
void remove(const MidiEvent &event)
Removes all events that match event.
Definition midi_event.h:192
void append(const MidiEventVector &src, units::sample_u32_t local_offset, units::sample_u32_t nframes)
Appends the events from src.
void transform_chord_and_append(const MidiEventVector &src, NotePitchToChordDescriptorFunc note_number_to_chord_descriptor, midi_byte_t velocity_to_use, units::sample_u32_t local_offset, units::sample_u32_t nframes)
Transforms the given MIDI input to the MIDI notes of the corresponding chord.
void write_to_midi_sequence(juce::MidiMessageSequence &sequence, bool update_matched_pairs) const
Writes the events to a MIDI sequence.
void add_cc_volume(midi_byte_t channel, midi_byte_t volume, units::sample_u32_t time)
Add CC volume event.
void add_note_offs_from_chord_descr(const ChordDescriptor &descr, midi_byte_t channel, units::sample_u32_t time)
Adds a note off for each note in the chord.
void append_w_filter(const MidiEventVector &src, std::optional< std::array< bool, 16 > > channels, units::sample_u32_t local_offset, units::sample_u32_t nframes)
Appends the events from src.
void add_note_ons_from_chord_descr(const ChordDescriptor &descr, midi_byte_t channel, midi_byte_t velocity, units::sample_u32_t time)
Adds a note on for each note in the chord.
void add_note_off(midi_byte_t channel, midi_byte_t note_pitch, units::sample_u32_t time)
Adds a note off event to the given MidiEvents.
void panic_without_lock(units::sample_u32_t time)
Adds a note off message to every MIDI channel.
Definition midi_event.h:385
void add_control_change(midi_byte_t channel, midi_byte_t controller, midi_byte_t control, units::sample_u32_t time)
Adds a control event to the given MidiEvents.
void add_event_from_buf(units::sample_u32_t time, midi_byte_t *buf, int buf_size)
Parses a MidiEvent from a raw MIDI buffer.
void clear_duplicates()
Clears duplicates.
void add_note_on(midi_byte_t channel, midi_byte_t note_pitch, midi_byte_t velocity, units::sample_u32_t time)
Adds a note on event to the given MidiEvents.
Container for passing midi events through ports.
Definition midi_event.h:455
MidiEventVector active_events_
Events to use in this cycle.
Definition midi_event.h:467
void dequeue(units::sample_u32_t local_offset, units::sample_u32_t nframes)
Copies the queue contents to the original struct.
MidiEventVector queued_events_
For queueing events from the GUI or from hardware, since they run in different threads.
Definition midi_event.h:476
MIDI utils.
std::uint8_t midi_byte_t
MIDI byte.
Definition midi.h:43
Timed MIDI event.
Definition midi_event.h:28
std::array< midi_byte_t, 3 > raw_buffer_
Raw MIDI data.
Definition midi_event.h:50
std::int64_t systime_
Time using g_get_monotonic_time().
Definition midi_event.h:58
units::sample_u32_t time_
Time of the MIDI event, in frames from the start of the current cycle.
Definition midi_event.h:55