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#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 void foreach_event (std::function<void (const MidiEvent &)> func) const
198 {
199 const std::lock_guard<crill::spin_mutex> lock (lock_);
200 std::ranges::for_each (events_, 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 print () const;
210
218 [[gnu::hot]] void append_w_filter (
219 const MidiEventVector &src,
220 std::optional<std::array<bool, 16>> channels,
221 units::sample_u32_t local_offset,
222 units::sample_u32_t nframes);
223
230 void append (
231 const MidiEventVector &src,
232 units::sample_u32_t local_offset,
233 units::sample_u32_t nframes);
234
235 using NotePitchToChordDescriptorFunc =
236 std::function<const ChordDescriptor *(midi_byte_t)>;
237
245 const MidiEventVector &src,
246 NotePitchToChordDescriptorFunc note_number_to_chord_descriptor,
247 midi_byte_t velocity_to_use,
248 units::sample_u32_t local_offset,
249 units::sample_u32_t nframes);
250
258 midi_byte_t channel,
259 midi_byte_t note_pitch,
260 midi_byte_t velocity,
261 units::sample_u32_t time);
262
267 const ChordDescriptor &descr,
268 midi_byte_t channel,
269 midi_byte_t velocity,
270 units::sample_u32_t time);
271
276 const ChordDescriptor &descr,
277 midi_byte_t channel,
278 units::sample_u32_t time);
279
286 midi_byte_t channel,
287 midi_byte_t volume,
288 units::sample_u32_t time);
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
310 add_event_from_buf (units::sample_u32_t time, midi_byte_t * buf, int buf_size);
311
319 midi_byte_t channel,
320 midi_byte_t note_pitch,
321 units::sample_u32_t time);
322
329 midi_byte_t channel,
330 midi_byte_t controller,
331 midi_byte_t control,
332 units::sample_u32_t time);
333
339 void add_song_pos (int64_t total_sixteenths, units::sample_u32_t time);
340
341 void add_raw (const uint8_t * buf, size_t buf_sz, units::sample_u32_t time);
342
343 void add_simple (
344 midi_byte_t byte1,
345 midi_byte_t byte2,
346 midi_byte_t byte3,
347 units::sample_u32_t time)
348 {
349 push_back (MidiEvent (byte1, byte2, byte3, time));
350 }
351
359 midi_byte_t channel,
360 uint32_t pitchbend,
361 units::sample_u32_t time);
362
363 void add_channel_pressure (
364 midi_byte_t channel,
365 midi_byte_t value,
366 units::sample_u32_t time);
367
372 midi_byte_t channel,
373 units::sample_u32_t time,
374 bool with_lock);
375
379 void panic_without_lock (units::sample_u32_t time)
380 {
381 for (midi_byte_t i = 1; i < 17; i++)
382 {
383 add_all_notes_off (i, time, false);
384 }
385 }
386
390 void panic ();
391
402 juce::MidiMessageSequence &sequence,
403 bool update_matched_pairs) const;
404
409
410#if 0
414 bool check_for_note_on (int note);
415
420 bool delete_note_on (int note);
421#endif
422
426 void sort ();
427
432 void set_channel (midi_byte_t channel);
433
434 void delete_event (const MidiEvent * ev);
435
436private:
437 std::vector<MidiEvent> events_;
438
440 mutable crill::spin_mutex lock_;
441};
442
448class MidiEvents final
449{
450public:
457 void dequeue (units::sample_u32_t local_offset, units::sample_u32_t nframes);
458
459public:
462
471};
472
473} // 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:379
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:449
MidiEventVector active_events_
Events to use in this cycle.
Definition midi_event.h:461
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:470
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