Zrythm v2.0.0-DEV
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
10#include "utils/types.h"
11
12#include <crill/spin_mutex.h>
13
14namespace juce
15{
16class MidiMessageSequence;
17}
18
19namespace zrythm::dsp
20{
21class ChordDescriptor;
22
24constexpr int MAX_MIDI_EVENTS = 2560;
25
29struct MidiEvent final
30{
31public:
32 // Rule of 0
33 MidiEvent () = default;
34 MidiEvent (
35 midi_byte_t byte1,
36 midi_byte_t byte2,
37 midi_byte_t byte3,
38 midi_time_t time)
39 : raw_buffer_sz_ (3), time_ (time)
40 {
41 raw_buffer_[0] = byte1;
42 raw_buffer_[1] = byte2;
43 raw_buffer_[2] = byte3;
44 }
45
46 void set_velocity (midi_byte_t vel);
47
48 void print () const;
49
50public:
52 std::array<midi_byte_t, 3> raw_buffer_{ 0, 0, 0 };
53
54 uint_fast8_t raw_buffer_sz_ = 0;
55
58
60 std::int64_t systime_ = 0;
61};
62
63inline bool
64operator== (const MidiEvent &lhs, const MidiEvent &rhs)
65{
66 return lhs.time_ == rhs.time_ && lhs.raw_buffer_[0] == rhs.raw_buffer_[0]
67 && lhs.raw_buffer_[1] == rhs.raw_buffer_[1]
68 && lhs.raw_buffer_[2] == rhs.raw_buffer_[2]
69 && lhs.raw_buffer_sz_ == rhs.raw_buffer_sz_;
70}
71
77class MidiEventVector final
78{
79public:
80 MidiEventVector () { events_.reserve (MAX_MIDI_EVENTS); }
81
82 using ChordDescriptor = dsp::ChordDescriptor;
83
84 // Iterator types
85 using Iterator = std::vector<MidiEvent>::iterator;
86 using ConstIterator = std::vector<MidiEvent>::const_iterator;
87
88 // Iterator methods
89 Iterator begin ()
90 {
91 const std::lock_guard<crill::spin_mutex> lock (lock_);
92 return events_.begin ();
93 }
94
95 Iterator end ()
96 {
97 const std::lock_guard<crill::spin_mutex> lock (lock_);
98 return events_.end ();
99 }
100
101 void erase (Iterator it, Iterator it_end)
102 {
103 const std::lock_guard<crill::spin_mutex> lock (lock_);
104 events_.erase (it, it_end);
105 }
106
107 ConstIterator begin () const
108 {
109 const std::lock_guard<crill::spin_mutex> lock (lock_);
110 return events_.begin ();
111 }
112
113 ConstIterator end () const
114 {
115 const std::lock_guard<crill::spin_mutex> lock (lock_);
116 return events_.end ();
117 }
118
119 void push_back (const MidiEvent &ev)
120 {
121 const std::lock_guard<crill::spin_mutex> lock (lock_);
122 events_.push_back (ev);
123 }
124
125 void push_back (const std::vector<MidiEvent> &events)
126 {
127 const std::lock_guard<crill::spin_mutex> lock (lock_);
128 events_.insert (events_.end (), events.begin (), events.end ());
129 }
130
131 MidiEvent pop_front ()
132 {
133 const std::lock_guard<crill::spin_mutex> lock (lock_);
134 MidiEvent ev = events_.front ();
135 events_.erase (events_.begin ());
136 return ev;
137 }
138
139 MidiEvent pop_back ()
140 {
141 const std::lock_guard<crill::spin_mutex> lock (lock_);
142 MidiEvent ev = events_.back ();
143 events_.pop_back ();
144 return ev;
145 }
146
147 void clear ()
148 {
149 const std::lock_guard<crill::spin_mutex> lock (lock_);
150 events_.clear ();
151 }
152
153 size_t size () const
154 {
155 const std::lock_guard<crill::spin_mutex> lock (lock_);
156 return events_.size ();
157 }
158
159 MidiEvent front () const
160 {
161 const std::lock_guard<crill::spin_mutex> lock (lock_);
162 return events_.front ();
163 }
164
165 MidiEvent back () const
166 {
167 const std::lock_guard<crill::spin_mutex> lock (lock_);
168 return events_.back ();
169 }
170
171 MidiEvent at (size_t index) const
172 {
173 const std::lock_guard<crill::spin_mutex> lock (lock_);
174 return events_.at (index);
175 }
176
177 void swap (MidiEventVector &other)
178 {
179 const std::lock_guard<crill::spin_mutex> lock (lock_);
180 events_.swap (other.events_);
181 }
182
183 void remove_if (std::function<bool (const MidiEvent &)> predicate)
184 {
185 const std::lock_guard<crill::spin_mutex> lock (lock_);
186 // std::erase_if (events_, std::move (predicate)); // fails to build on
187 // Xcode 26
188 auto it =
189 std::remove_if (events_.begin (), events_.end (), std::move (predicate));
190 events_.erase (it, events_.end ());
191 }
192
196 void remove (const MidiEvent &event)
197 {
198 remove_if ([&event] (const MidiEvent &e) { return e == event; });
199 }
200
201 void foreach_event (std::function<void (const MidiEvent &)> func) const
202 {
203 const std::lock_guard<crill::spin_mutex> lock (lock_);
204 std::ranges::for_each (events_, func);
205 }
206
207 size_t capacity () const
208 {
209 const std::lock_guard<crill::spin_mutex> lock (lock_);
210 return events_.capacity ();
211 }
212
213 void print () const;
214
222 [[gnu::hot]] void append_w_filter (
223 const MidiEventVector &src,
224 std::optional<std::array<bool, 16>> channels,
225 nframes_t local_offset,
226 nframes_t nframes);
227
234 void
235 append (const MidiEventVector &src, nframes_t local_offset, nframes_t nframes);
236
237 using NotePitchToChordDescriptorFunc =
238 std::function<const ChordDescriptor *(midi_byte_t)>;
239
247 const MidiEventVector &src,
248 NotePitchToChordDescriptorFunc note_number_to_chord_descriptor,
249 midi_byte_t velocity_to_use,
250 nframes_t local_offset,
251 nframes_t nframes);
252
260 midi_byte_t channel,
261 midi_byte_t note_pitch,
262 midi_byte_t velocity,
263 midi_time_t time);
264
269 const ChordDescriptor &descr,
270 midi_byte_t channel,
271 midi_byte_t velocity,
272 midi_time_t time);
273
278 const ChordDescriptor &descr,
279 midi_byte_t channel,
280 midi_time_t time);
281
287 void
289
290#if 0
297 bool has_note_on (bool check_main, bool check_queued);
298#endif
299
300 bool has_any () const { return !empty (); }
301 bool empty () const { return size () == 0; }
302
309 void add_event_from_buf (midi_time_t time, midi_byte_t * buf, int buf_size);
310
317 void
318 add_note_off (midi_byte_t channel, midi_byte_t note_pitch, midi_time_t time);
319
326 midi_byte_t channel,
327 midi_byte_t controller,
328 midi_byte_t control,
329 midi_time_t time);
330
336 void add_song_pos (int64_t total_sixteenths, midi_time_t time);
337
338 void add_raw (const uint8_t * buf, size_t buf_sz, midi_time_t time);
339
340 void add_simple (
341 midi_byte_t byte1,
342 midi_byte_t byte2,
343 midi_byte_t byte3,
344 midi_time_t time)
345 {
346 push_back (MidiEvent (byte1, byte2, byte3, time));
347 }
348
355 void
356 add_pitchbend (midi_byte_t channel, uint32_t pitchbend, midi_time_t time);
357
358 void
359 add_channel_pressure (midi_byte_t channel, midi_byte_t value, midi_time_t time);
360
364 void
365 add_all_notes_off (midi_byte_t channel, midi_time_t time, bool with_lock);
366
371 {
372 for (midi_byte_t i = 1; i < 17; i++)
373 {
374 add_all_notes_off (i, time, false);
375 }
376 }
377
381 void panic ();
382
393 juce::MidiMessageSequence &sequence,
394 bool update_matched_pairs) const;
395
400
401#if 0
405 bool check_for_note_on (int note);
406
411 bool delete_note_on (int note);
412#endif
413
417 void sort ();
418
423 void set_channel (midi_byte_t channel);
424
425 void delete_event (const MidiEvent * ev);
426
427private:
428 std::vector<MidiEvent> events_;
429
431 mutable crill::spin_mutex lock_;
432};
433
439class MidiEvents final
440{
441public:
448 void dequeue (nframes_t local_offset, nframes_t nframes);
449
450public:
453
462};
463
464} // 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:78
void panic()
Must only be called from the UI thread.
void add_all_notes_off(midi_byte_t channel, midi_time_t time, bool with_lock)
Queues MIDI note off to event queue.
void set_channel(midi_byte_t channel)
Sets the given MIDI channel on all applicable MIDI events.
void sort()
Sorts the MidiEvents by time.
void add_note_offs_from_chord_descr(const ChordDescriptor &descr, midi_byte_t channel, midi_time_t time)
Adds a note off for each note in the chord.
void add_song_pos(int64_t total_sixteenths, midi_time_t time)
Adds a song position event to the queue.
void add_note_ons_from_chord_descr(const ChordDescriptor &descr, midi_byte_t channel, midi_byte_t velocity, midi_time_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, midi_time_t time)
Adds a note off event to the given MidiEvents.
void append_w_filter(const MidiEventVector &src, std::optional< std::array< bool, 16 > > channels, nframes_t local_offset, nframes_t nframes)
Appends the events from src.
void remove(const MidiEvent &event)
Removes all events that match event.
Definition midi_event.h:196
void add_cc_volume(midi_byte_t channel, midi_byte_t volume, midi_time_t time)
Add CC volume event.
void add_note_on(midi_byte_t channel, midi_byte_t note_pitch, midi_byte_t velocity, midi_time_t time)
Adds a note on event to the given MidiEvents.
void panic_without_lock(midi_time_t time)
Adds a note off message to every MIDI channel.
Definition midi_event.h:370
void add_pitchbend(midi_byte_t channel, uint32_t pitchbend, midi_time_t time)
Adds a control event to the given MidiEvents.
void write_to_midi_sequence(juce::MidiMessageSequence &sequence, bool update_matched_pairs) const
Writes the events to a MIDI sequence.
void add_control_change(midi_byte_t channel, midi_byte_t controller, midi_byte_t control, midi_time_t time)
Adds a control event to the given MidiEvents.
void append(const MidiEventVector &src, nframes_t local_offset, nframes_t nframes)
Appends the events from src.
void add_event_from_buf(midi_time_t time, midi_byte_t *buf, int buf_size)
Parses a MidiEvent from a raw MIDI buffer.
void clear_duplicates()
Clears duplicates.
void transform_chord_and_append(const MidiEventVector &src, NotePitchToChordDescriptorFunc note_number_to_chord_descriptor, midi_byte_t velocity_to_use, nframes_t local_offset, nframes_t nframes)
Transforms the given MIDI input to the MIDI notes of the corresponding chord.
Container for passing midi events through ports.
Definition midi_event.h:440
MidiEventVector active_events_
Events to use in this cycle.
Definition midi_event.h:452
void dequeue(nframes_t local_offset, nframes_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:461
uint32_t nframes_t
Frame count.
Definition types.h:58
uint32_t midi_time_t
MIDI time in global frames.
Definition types.h:61
uint8_t midi_byte_t
MIDI byte.
Definition types.h:55
Timed MIDI event.
Definition midi_event.h:30
midi_time_t time_
Time of the MIDI event, in frames from the start of the current cycle.
Definition midi_event.h:57
std::array< midi_byte_t, 3 > raw_buffer_
Raw MIDI data.
Definition midi_event.h:52
std::int64_t systime_
Time using g_get_monotonic_time().
Definition midi_event.h:60