Zrythm v2.0.0-alpha.1
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
midi.h
Go to the documentation of this file.
1// clang-format off
2// SPDX-FileCopyrightText: © 2018-2026 Alexandros Theodotou <alex@zrythm.org>
3// SPDX-License-Identifier: LicenseRef-ZrythmLicense
4/*
5 * This file incorporates work covered by the following copyright and
6 * permission notice:
7 *
8 * ---
9 *
10 * CHOC is (C)2021 Tracktion Corporation, and is offered under the terms of the ISC license:
11 *
12 * Permission to use, copy, modify, and/or distribute this software for any purpose with or
13 * without fee is hereby granted, provided that the above copyright notice and this permission
14 * notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
15 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
16 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
19 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 *
21 * ---
22 */
23// clang-format on
24
30
31#pragma once
32
33#include <cassert>
34#include <cmath>
35#include <cstddef>
36#include <cstdint>
37#include <limits>
38#include <optional>
39#include <span>
40#include <string>
41
43using midi_byte_t = std::uint8_t;
44
45namespace zrythm::utils::midi
46{
47/* see http://www.onicos.com/staff/iz/formats/midi-event.html */
48static constexpr uint8_t MIDI_CH1_NOTE_ON = 0x90;
49static constexpr uint8_t MIDI_CH1_NOTE_OFF = 0x80;
51static constexpr uint8_t MIDI_CH1_POLY_AFTERTOUCH = 0xA0;
52static constexpr uint8_t MIDI_CH1_CTRL_CHANGE = 0xB0;
53static constexpr uint8_t MIDI_CH1_PROG_CHANGE = 0xC0;
55static constexpr uint8_t MIDI_CH1_CHAN_AFTERTOUCH = 0xD0;
56static constexpr uint8_t MIDI_CH1_PITCH_WHEEL_RANGE = 0xE0;
57static constexpr uint8_t MIDI_ALL_NOTES_OFF = 0x7B;
58static constexpr uint8_t MIDI_ALL_SOUND_OFF = 0x78;
59static constexpr uint8_t MIDI_SYSTEM_MESSAGE = 0xF0;
60static constexpr uint8_t MIDI_STATUS_MASK = 0xF0;
61static constexpr uint8_t MIDI_CHANNEL_MASK = 0x0F;
62static constexpr uint8_t MIDI_DATA_MASK = 0x7F;
63static constexpr uint8_t MIDI_SONG_POSITION = 0xF2;
64static constexpr uint8_t MIDI_CLOCK_START = 0xFA;
65static constexpr uint8_t MIDI_CLOCK_CONTINUE = 0xFB;
66static constexpr uint8_t MIDI_CLOCK_BEAT = 0xF8;
67static constexpr uint8_t MIDI_CLOCK_STOP = 0xFC;
68static constexpr uint8_t MIDI_META_EVENT = 0xFF;
69
70constexpr size_t
71midi_max_sysex_size ()
72{
73 return std::numeric_limits<std::uint16_t>::max ();
74}
75
79[[gnu::const]]
80std::string_view
82
90void
92
97std::optional<std::string>
98midi_ctrl_change_get_description (std::span<const midi_byte_t> ctrl_change);
99
107std::optional<int>
108midi_ctrl_change_get_channel (std::span<const midi_byte_t> ctrl_change);
109
114int
115midi_get_msg_length (const uint8_t status_byte);
116
121static inline float
122midi_note_number_to_frequency (const uint8_t note)
123{
124 return 440.f * powf (2.f, ((float) note - 69.f) * (1.f / 12.f));
125}
126
127static inline uint8_t
128midi_frequency_to_note_number (const float freq)
129{
130 return (uint8_t) round (
131 69.f + (12.f / logf (2.f)) * logf (freq * (1.0f / 440.f)));
132}
133
140static inline uint8_t
141midi_get_chromatic_scale_index (const uint8_t note)
142{
143 return note % 12;
144}
145
151static inline uint8_t
152midi_get_octave_number (const uint8_t note)
153{
154 const uint8_t octave_for_middle_c = 3;
155 return note / 12 + (uint8_t) (octave_for_middle_c - 5);
156}
157
162static inline bool
163midi_is_short_message_type (
164 std::span<const midi_byte_t> short_msg,
165 const midi_byte_t type)
166{
167 return (short_msg[0] & 0xf0) == type;
168}
169
170static inline midi_byte_t
171midi_get_note_number (std::span<const midi_byte_t> short_msg)
172{
173 return short_msg[1];
174}
175
176static inline midi_byte_t
177midi_get_velocity (std::span<const midi_byte_t> short_msg)
178{
179 return short_msg[2];
180}
181
185[[gnu::const]]
186std::string_view
188
189std::string
190midi_get_note_name_with_octave (std::span<const midi_byte_t> short_msg);
191
192static inline bool
193midi_is_note_on (std::span<const midi_byte_t> short_msg)
194{
195 return midi_is_short_message_type (short_msg, MIDI_CH1_NOTE_ON)
196 && midi_get_velocity (short_msg) != 0;
197}
198
199static inline bool
200midi_is_note_off (std::span<const midi_byte_t> short_msg)
201{
202 return midi_is_short_message_type (short_msg, MIDI_CH1_NOTE_OFF)
203 || (midi_is_short_message_type (short_msg, MIDI_CH1_NOTE_ON) && midi_get_velocity (short_msg) == 0);
204}
205
206static inline bool
207midi_is_program_change (std::span<const midi_byte_t> short_msg)
208{
209 return midi_is_short_message_type (short_msg, MIDI_CH1_PROG_CHANGE);
210}
211
212static inline midi_byte_t
213midi_get_program_change_number (std::span<const midi_byte_t> short_msg)
214{
215 return short_msg[1];
216}
217
218static inline bool
219midi_is_pitch_wheel (std::span<const midi_byte_t> short_msg)
220{
221 return midi_is_short_message_type (short_msg, MIDI_CH1_PITCH_WHEEL_RANGE);
222}
223
224static inline bool
225midi_is_aftertouch (std::span<const midi_byte_t> short_msg)
226{
227 return midi_is_short_message_type (short_msg, MIDI_CH1_POLY_AFTERTOUCH);
228}
229
230static inline bool
231midi_is_channel_pressure (std::span<const midi_byte_t> short_msg)
232{
233 return midi_is_short_message_type (short_msg, MIDI_CH1_CHAN_AFTERTOUCH);
234}
235
236static inline bool
237midi_is_controller (std::span<const midi_byte_t> short_msg)
238{
239 return midi_is_short_message_type (short_msg, MIDI_CH1_CTRL_CHANGE);
240}
241
250static inline uint32_t
251midi_get_14_bit_value (std::span<const midi_byte_t> short_msg)
252{
253 return short_msg[1] | ((uint32_t) short_msg[2] << 7);
254}
255
256static inline midi_byte_t
257midi_get_channel_0_to_15 (std::span<const midi_byte_t> short_msg)
258{
259 return short_msg[0] & 0x0f;
260}
261
262static inline midi_byte_t
263midi_get_channel_1_to_16 (std::span<const midi_byte_t> short_msg)
264{
265 return midi_get_channel_0_to_15 (short_msg) + 1u;
266}
267
268static inline uint32_t
269midi_get_pitchwheel_value (std::span<const midi_byte_t> short_msg)
270{
271 return midi_get_14_bit_value (short_msg);
272}
273
274static inline midi_byte_t
275midi_get_aftertouch_value (std::span<const midi_byte_t> short_msg)
276{
277 return short_msg[2];
278}
279
280static inline midi_byte_t
281midi_get_channel_pressure_value (std::span<const midi_byte_t> short_msg)
282{
283 return short_msg[1];
284}
285
286static inline midi_byte_t
287midi_get_controller_number (std::span<const midi_byte_t> short_msg)
288{
289 return short_msg[1];
290}
291
292static inline midi_byte_t
293midi_get_controller_value (std::span<const midi_byte_t> short_msg)
294{
295 return short_msg[2];
296}
297
298static inline bool
299midi_is_all_notes_off (std::span<const midi_byte_t> short_msg)
300{
301 return midi_is_controller (short_msg)
302 && midi_get_controller_number (short_msg) == 123;
303}
304
305static inline bool
306midi_is_all_sound_off (std::span<const midi_byte_t> short_msg)
307{
308 return midi_is_controller (short_msg)
309 && midi_get_controller_number (short_msg) == 120;
310}
311
312static inline bool
313midi_is_quarter_frame (std::span<const midi_byte_t> short_msg)
314{
315 return short_msg[0] == 0xf1;
316}
317
318static inline bool
319midi_is_clock (std::span<const midi_byte_t> short_msg)
320{
321 return short_msg[0] == 0xf8;
322}
323
324static inline bool
325midi_is_start (std::span<const midi_byte_t> short_msg)
326{
327 return short_msg[0] == 0xfa;
328}
329
330static inline bool
331midi_is_continue (std::span<const midi_byte_t> short_msg)
332{
333 return short_msg[0] == 0xfb;
334}
335
336static inline bool
337midi_is_stop (std::span<const midi_byte_t> short_msg)
338{
339 return short_msg[0] == 0xfc;
340}
341
342static inline bool
343midi_is_active_sense (std::span<const midi_byte_t> short_msg)
344{
345 return short_msg[0] == 0xfe;
346}
347
348static inline bool
349midi_is_song_position_pointer (std::span<const midi_byte_t> short_msg)
350{
351 return short_msg[0] == 0xf2;
352}
353
354static inline uint32_t
355midi_get_song_position_pointer_value (std::span<const midi_byte_t> short_msg)
356{
357 return midi_get_14_bit_value (short_msg);
358}
359
360std::string
361midi_get_hex_str (std::span<const midi_byte_t> msg);
362
363std::string
364midi_print_to_str (std::span<const midi_byte_t> msg);
365
366void
367midi_print (std::span<const midi_byte_t> msg);
368
369static inline bool
370midi_is_short_msg (std::span<const midi_byte_t> msg)
371{
372 assert (!msg.empty ());
373
374 if (msg.size () > 3)
375 return false;
376
377 return msg[0] != MIDI_SYSTEM_MESSAGE && msg[0] != MIDI_META_EVENT;
378}
379
380static inline bool
381midi_is_sysex (std::span<const midi_byte_t> msg)
382{
383 return msg.size () > 1 && msg[0] == MIDI_SYSTEM_MESSAGE;
384}
385
386static inline bool
387midi_is_meta_event (std::span<const midi_byte_t> msg)
388{
389 return msg.size () > 2 && msg[0] == MIDI_META_EVENT;
390}
391
392static inline bool
393midi_is_short_msg_meta_event (std::span<const midi_byte_t> short_msg)
394{
395 return midi_is_meta_event (short_msg);
396}
397
398static inline bool
399midi_is_meta_event_of_type (
400 std::span<const midi_byte_t> msg,
401 const midi_byte_t type)
402{
403 return msg.size () > 2 && msg[1] == type && msg[0] == MIDI_META_EVENT;
404}
405
406static inline midi_byte_t
407midi_get_meta_event_type (std::span<const midi_byte_t> msg)
408{
409 assert (midi_is_meta_event (msg));
410 return msg[1];
411}
412
413std::string
414midi_get_meta_event_type_name (midi_byte_t type);
415
427static inline size_t
428midi_get_meta_event_data (
429 const midi_byte_t ** data,
430 const midi_byte_t * msg,
431 const size_t msg_sz)
432{
433 assert (midi_is_meta_event (std::span (msg, msg_sz)));
434
435 /* malformed data */
436 if (msg_sz < 4)
437 return 0;
438
439 size_t content_len = 0;
440 size_t len_bytes = 0;
441 for (size_t i = 2; i < msg_sz; i++)
442 {
443 const midi_byte_t byte = msg[i];
444 len_bytes++;
445 content_len = (content_len << 7) | (byte & 0x7fu);
446
447 if (byte < 0x80)
448 break;
449
450 if (len_bytes == 4 || len_bytes + 2 == msg_sz)
451 return 0; /* malformed data */
452 }
453
454 size_t content_start = len_bytes + 2;
455
456 if (content_start + content_len > msg_sz)
457 return 0; /* malformed data */
458
459 *data = &msg[content_start];
460 return content_len;
461}
462
463} // namespace zrythm::utils::midi
std::optional< std::string > midi_ctrl_change_get_description(std::span< const midi_byte_t > ctrl_change)
Returns a string representation of the given control change event, if control change.
std::string_view midi_get_note_name(midi_byte_t note)
Returns the note name (eg, "C") for a value between 0 and 127.
int midi_get_msg_length(const uint8_t status_byte)
Returns the length of the MIDI message based on the status byte.
std::string_view midi_get_controller_name(midi_byte_t cc)
Return the name of the given cc (0-127).
std::optional< int > midi_ctrl_change_get_channel(std::span< const midi_byte_t > ctrl_change)
Returns the MIDI channel of the given control change event, if control change.
std::uint8_t midi_byte_t
MIDI byte.
Definition midi.h:43
void midi_get_bytes_from_combined(uint32_t val, midi_byte_t *lsb, midi_byte_t *msb)
Used for MIDI controls whose values are split between MSB/LSB.