Zrythm v2.0.0-DEV
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-2024 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#ifndef __AUDIO_MIDI_H__
32#define __AUDIO_MIDI_H__
33
34#include "zrythm-config.h"
35
36#include "utils/logger.h"
37#include "utils/types.h"
38
44
45namespace zrythm::utils::midi
46{
47
48/* see http://www.onicos.com/staff/iz/formats/midi-event.html */
49static constexpr uint8_t MIDI_CH1_NOTE_ON = 0x90;
50static constexpr uint8_t MIDI_CH1_NOTE_OFF = 0x80;
52static constexpr uint8_t MIDI_CH1_POLY_AFTERTOUCH = 0xA0;
53static constexpr uint8_t MIDI_CH1_CTRL_CHANGE = 0xB0;
54static constexpr uint8_t MIDI_CH1_PROG_CHANGE = 0xC0;
56static constexpr uint8_t MIDI_CH1_CHAN_AFTERTOUCH = 0xD0;
57static constexpr uint8_t MIDI_CH1_PITCH_WHEEL_RANGE = 0xE0;
58static constexpr uint8_t MIDI_ALL_NOTES_OFF = 0x7B;
59static constexpr uint8_t MIDI_ALL_SOUND_OFF = 0x78;
60static constexpr uint8_t MIDI_SYSTEM_MESSAGE = 0xF0;
61static constexpr uint8_t MIDI_SONG_POSITION = 0xF2;
62static constexpr uint8_t MIDI_CLOCK_START = 0xFA;
63static constexpr uint8_t MIDI_CLOCK_CONTINUE = 0xFB;
64static constexpr uint8_t MIDI_CLOCK_BEAT = 0xF8;
65static constexpr uint8_t MIDI_CLOCK_STOP = 0xFC;
66static constexpr uint8_t MIDI_META_EVENT = 0xFF;
67
68constexpr size_t
69midi_max_sysex_size ()
70{
71 return std::numeric_limits<std::uint16_t>::max ();
72}
73
77[[gnu::const]]
78std::string_view
80
88void
90
95std::optional<std::string>
96midi_ctrl_change_get_description (std::span<const midi_byte_t> ctrl_change);
97
105std::optional<int>
106midi_ctrl_change_get_channel (std::span<const midi_byte_t> ctrl_change);
107
112int
113midi_get_msg_length (const uint8_t status_byte);
114
119static inline float
120midi_note_number_to_frequency (const uint8_t note)
121{
122 return 440.f * powf (2.f, ((float) note - 69.f) * (1.f / 12.f));
123}
124
125static inline uint8_t
126midi_frequency_to_note_number (const float freq)
127{
128 return (uint8_t) round (
129 69.f + (12.f / logf (2.f)) * logf (freq * (1.0f / 440.f)));
130}
131
138static inline uint8_t
139midi_get_chromatic_scale_index (const uint8_t note)
140{
141 return note % 12;
142}
143
149static inline uint8_t
150midi_get_octave_number (const uint8_t note)
151{
152 const uint8_t octave_for_middle_c = 3;
153 return note / 12 + (uint8_t) (octave_for_middle_c - 5);
154}
155
160static inline bool
161midi_is_short_message_type (
162 std::span<const midi_byte_t> short_msg,
163 const midi_byte_t type)
164{
165 return (short_msg[0] & 0xf0) == type;
166}
167
168static inline midi_byte_t
169midi_get_note_number (std::span<const midi_byte_t> short_msg)
170{
171 return short_msg[1];
172}
173
174static inline midi_byte_t
175midi_get_velocity (std::span<const midi_byte_t> short_msg)
176{
177 return short_msg[2];
178}
179
183[[gnu::const]]
184std::string_view
186
187std::string
188midi_get_note_name_with_octave (std::span<const midi_byte_t> short_msg);
189
190static inline bool
191midi_is_note_on (std::span<const midi_byte_t> short_msg)
192{
193 return midi_is_short_message_type (short_msg, MIDI_CH1_NOTE_ON)
194 && midi_get_velocity (short_msg) != 0;
195}
196
197static inline bool
198midi_is_note_off (std::span<const midi_byte_t> short_msg)
199{
200 return midi_is_short_message_type (short_msg, MIDI_CH1_NOTE_OFF)
201 || (midi_is_short_message_type (short_msg, MIDI_CH1_NOTE_ON) && midi_get_velocity (short_msg) == 0);
202}
203
204static inline bool
205midi_is_program_change (std::span<const midi_byte_t> short_msg)
206{
207 return midi_is_short_message_type (short_msg, MIDI_CH1_PROG_CHANGE);
208}
209
210static inline midi_byte_t
211midi_get_program_change_number (std::span<const midi_byte_t> short_msg)
212{
213 return short_msg[1];
214}
215
216static inline bool
217midi_is_pitch_wheel (std::span<const midi_byte_t> short_msg)
218{
219 return midi_is_short_message_type (short_msg, MIDI_CH1_PITCH_WHEEL_RANGE);
220}
221
222static inline bool
223midi_is_aftertouch (std::span<const midi_byte_t> short_msg)
224{
225 return midi_is_short_message_type (short_msg, MIDI_CH1_POLY_AFTERTOUCH);
226}
227
228static inline bool
229midi_is_channel_pressure (std::span<const midi_byte_t> short_msg)
230{
231 return midi_is_short_message_type (short_msg, MIDI_CH1_CHAN_AFTERTOUCH);
232}
233
234static inline bool
235midi_is_controller (std::span<const midi_byte_t> short_msg)
236{
237 return midi_is_short_message_type (short_msg, MIDI_CH1_CTRL_CHANGE);
238}
239
248static inline uint32_t
249midi_get_14_bit_value (std::span<const midi_byte_t> short_msg)
250{
251 return short_msg[1] | ((uint32_t) short_msg[2] << 7);
252}
253
254static inline midi_byte_t
255midi_get_channel_0_to_15 (std::span<const midi_byte_t> short_msg)
256{
257 return short_msg[0] & 0x0f;
258}
259
260static inline midi_byte_t
261midi_get_channel_1_to_16 (std::span<const midi_byte_t> short_msg)
262{
263 return midi_get_channel_0_to_15 (short_msg) + 1u;
264}
265
266static inline uint32_t
267midi_get_pitchwheel_value (std::span<const midi_byte_t> short_msg)
268{
269 return midi_get_14_bit_value (short_msg);
270}
271
272static inline midi_byte_t
273midi_get_aftertouch_value (std::span<const midi_byte_t> short_msg)
274{
275 return short_msg[2];
276}
277
278static inline midi_byte_t
279midi_get_channel_pressure_value (std::span<const midi_byte_t> short_msg)
280{
281 return short_msg[1];
282}
283
284static inline midi_byte_t
285midi_get_controller_number (std::span<const midi_byte_t> short_msg)
286{
287 return short_msg[1];
288}
289
290static inline midi_byte_t
291midi_get_controller_value (std::span<const midi_byte_t> short_msg)
292{
293 return short_msg[2];
294}
295
296static inline bool
297midi_is_all_notes_off (std::span<const midi_byte_t> short_msg)
298{
299 return midi_is_controller (short_msg)
300 && midi_get_controller_number (short_msg) == 123;
301}
302
303static inline bool
304midi_is_all_sound_off (std::span<const midi_byte_t> short_msg)
305{
306 return midi_is_controller (short_msg)
307 && midi_get_controller_number (short_msg) == 120;
308}
309
310static inline bool
311midi_is_quarter_frame (std::span<const midi_byte_t> short_msg)
312{
313 return short_msg[0] == 0xf1;
314}
315
316static inline bool
317midi_is_clock (std::span<const midi_byte_t> short_msg)
318{
319 return short_msg[0] == 0xf8;
320}
321
322static inline bool
323midi_is_start (std::span<const midi_byte_t> short_msg)
324{
325 return short_msg[0] == 0xfa;
326}
327
328static inline bool
329midi_is_continue (std::span<const midi_byte_t> short_msg)
330{
331 return short_msg[0] == 0xfb;
332}
333
334static inline bool
335midi_is_stop (std::span<const midi_byte_t> short_msg)
336{
337 return short_msg[0] == 0xfc;
338}
339
340static inline bool
341midi_is_active_sense (std::span<const midi_byte_t> short_msg)
342{
343 return short_msg[0] == 0xfe;
344}
345
346static inline bool
347midi_is_song_position_pointer (std::span<const midi_byte_t> short_msg)
348{
349 return short_msg[0] == 0xf2;
350}
351
352static inline uint32_t
353midi_get_song_position_pointer_value (std::span<const midi_byte_t> short_msg)
354{
355 return midi_get_14_bit_value (short_msg);
356}
357
358std::string
359midi_get_hex_str (std::span<const midi_byte_t> msg);
360
361std::string
362midi_print_to_str (std::span<const midi_byte_t> msg);
363
364void
365midi_print (std::span<const midi_byte_t> msg);
366
367static inline bool
368midi_is_short_msg (std::span<const midi_byte_t> msg)
369{
370 assert (!msg.empty ());
371
372 if (msg.size () > 3)
373 return false;
374
375 return msg[0] != MIDI_SYSTEM_MESSAGE && msg[0] != MIDI_META_EVENT;
376}
377
378static inline bool
379midi_is_sysex (std::span<const midi_byte_t> msg)
380{
381 return msg.size () > 1 && msg[0] == MIDI_SYSTEM_MESSAGE;
382}
383
384static inline bool
385midi_is_meta_event (std::span<const midi_byte_t> msg)
386{
387 return msg.size () > 2 && msg[0] == MIDI_META_EVENT;
388}
389
390static inline bool
391midi_is_short_msg_meta_event (std::span<const midi_byte_t> short_msg)
392{
393 return midi_is_meta_event (short_msg);
394}
395
396static inline bool
397midi_is_meta_event_of_type (
398 std::span<const midi_byte_t> msg,
399 const midi_byte_t type)
400{
401 return msg.size () > 2 && msg[1] == type && msg[0] == MIDI_META_EVENT;
402}
403
404static inline midi_byte_t
405midi_get_meta_event_type (std::span<const midi_byte_t> msg)
406{
407 assert (midi_is_meta_event (msg));
408 return msg[1];
409}
410
411std::string
412midi_get_meta_event_type_name (midi_byte_t type);
413
425static inline size_t
426midi_get_meta_event_data (
427 const midi_byte_t ** data,
428 const midi_byte_t * msg,
429 const size_t msg_sz)
430{
431 assert (midi_is_meta_event (std::span (msg, msg_sz)));
432
433 /* malformed data */
434 if (msg_sz < 4)
435 return 0;
436
437 size_t content_len = 0;
438 size_t len_bytes = 0;
439 for (size_t i = 2; i < msg_sz; i++)
440 {
441 const midi_byte_t byte = msg[i];
442 len_bytes++;
443 content_len = (content_len << 7) | (byte & 0x7fu);
444
445 if (byte < 0x80)
446 break;
447
448 if (len_bytes == 4 || len_bytes + 2 == msg_sz)
449 return 0; /* malformed data */
450 }
451
452 size_t content_start = len_bytes + 2;
453
454 if (content_start + content_len > msg_sz)
455 return 0; /* malformed data */
456
457 *data = &msg[content_start];
458 return content_len;
459}
460
461} // namespace zrythm::utils::midi
462
466
467#endif
uint8_t midi_byte_t
MIDI byte.
Definition types.h:55
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.
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.