Zrythm v2.0.0-alpha.1
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
midi_note.h
1// SPDX-FileCopyrightText: © 2018-2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include <cstdint>
7
8#include "structure/arrangement/arranger_object.h"
9#include "utils/midi.h"
10
11#include <nlohmann/json_fwd.hpp>
12
13namespace zrythm::structure::arrangement
14{
15
16template <typename T>
18
22class MidiNote : public ArrangerObject
23{
24 Q_OBJECT
25 Q_PROPERTY (int pitch READ pitch WRITE setPitch NOTIFY pitchChanged)
26 Q_PROPERTY (
27 int velocity READ velocity WRITE setVelocity NOTIFY velocityChanged)
28 Q_PROPERTY (
29 int midiChannel READ midiChannel WRITE setMidiChannel NOTIFY
30 midiChannelChanged)
31 QML_ELEMENT
32 QML_UNCREATABLE ("")
33
34public:
35 MidiNote (const dsp::TempoMap &tempo_map, QObject * parent = nullptr);
36 Q_DISABLE_COPY_MOVE (MidiNote)
37 ~MidiNote () override;
38
39 static constexpr midi_byte_t DEFAULT_VELOCITY = 90;
40
41 enum class Notation : std::uint8_t
42 {
43 Musical,
44 Pitch,
45 };
46
47 // ========================================================================
48 // QML Interface
49 // ========================================================================
50
51 int pitch () const { return static_cast<int> (pitch_); }
52 void setPitch (int ipitch)
53 {
54 ipitch = std::clamp (ipitch, 0, 127);
55 const auto pitch = static_cast<midi_byte_t> (ipitch);
56 if (pitch_ != pitch)
57 {
58 pitch_ = pitch;
59 Q_EMIT pitchChanged (ipitch);
60 }
61 }
62 /**
63 * Shifts the pitch by @p delta amount.
64 */
65 Q_INVOKABLE void shift_pitch (int delta)
66 {
67 setPitch (static_cast<int> (pitch_) + delta);
68 }
69 Q_SIGNAL void pitchChanged (int ipitch);
70 Q_INVOKABLE QString pitchAsRichText () const
71 {
72 return pitch_to_string (pitch_, Notation::Musical, true).to_qstring ();
73 }
74
75 int velocity () const { return static_cast<int> (velocity_); }
76 void setVelocity (int ivelocity)
77 {
78 ivelocity = std::clamp (ivelocity, 0, 127);
79 const auto velocity = static_cast<midi_byte_t> (ivelocity);
80 if (velocity_ != velocity)
81 {
82 velocity_ = velocity;
83 Q_EMIT velocityChanged (ivelocity);
84 }
85 }
86 Q_SIGNAL void velocityChanged (int ivelocity);
87
88 int midiChannel () const { return static_cast<int> (midi_channel_); }
89 void setMidiChannel (int ichannel)
90 {
91 ichannel = std::clamp (ichannel, 0, 15);
92 const auto channel = static_cast<uint8_t> (ichannel);
93 if (midi_channel_ != channel)
94 {
95 midi_channel_ = channel;
96 Q_EMIT midiChannelChanged (ichannel);
97 }
98 }
99 Q_SIGNAL void midiChannelChanged (int ichannel);
100
101 // ========================================================================
102
108 static utils::Utf8String
109 pitch_to_string (uint8_t pitch, Notation notation, bool rich_text);
110
111private:
113 * Returns the note starting first, or nullptr.
114 */
115 template <RangeOfMidiNotePointers Range>
116 friend MidiNote * get_first_midi_note (const Range &range)
117 {
118 auto it = std::ranges::min_element (range, [] (const auto &a, const auto &b) {
119 return a->position ()->ticks () < b->position ()->ticks ();
120 });
121 return (it != range.end ()) ? *it : nullptr;
122 }
123
125 * Gets last midi note
126 */
127 template <RangeOfMidiNotePointers Range>
128 friend MidiNote * get_last_midi_note (const Range &range)
129 {
130 // take the reverse because max_element returns the first match, and we want
131 // to return the last match in the container in this case
132 const auto range_to_test = range | std::views::reverse;
133 auto it = std::ranges::max_element (
134 range_to_test, [] (const auto &a, const auto &b) {
135 return a->bounds ()->get_end_position_samples (false)
136 < b->bounds ()->get_end_position_samples (false);
137 });
138 return (it != range_to_test.end ()) ? *it : nullptr;
139 }
140
142 * Returns the minimum and maximum pitches in the given range.
143 */
144 template <RangeOfMidiNotePointers Range>
145 friend auto get_pitch_range (const Range &range)
146 -> std::optional<std::pair<midi_byte_t, midi_byte_t>>
147 {
148 auto [min_it, max_it] =
149 std::ranges::minmax_element (range, [] (const auto &a, const auto &b) {
150 return a->pitch () < b->pitch ();
151 });
152 if (min_it == range.end () || max_it == range.end ())
153 return std::nullopt;
154 return std::make_pair ((*min_it)->pitch (), (*max_it)->pitch ());
155 }
156
157 friend void init_from (
158 MidiNote &obj,
159 const MidiNote &other,
160 utils::ObjectCloneType clone_type);
161
162 static constexpr auto kVelocityKey = "velocity"sv;
163 static constexpr auto kPitchKey = "pitch"sv;
164 static constexpr auto kChannelKey = "midiChannel"sv;
165 friend void to_json (nlohmann::json &j, const MidiNote &note);
166 friend void from_json (const nlohmann::json &j, MidiNote &note);
167
168private:
170 std::uint8_t velocity_{ DEFAULT_VELOCITY };
171
173 std::uint8_t pitch_{ 60 };
174
176 std::uint8_t midi_channel_{ 0 };
177
178 BOOST_DESCRIBE_CLASS (
179 MidiNote,
181 (),
182 (),
183 (velocity_, pitch_, midi_channel_))
184};
185
186}
ArrangerObject(Type type, const dsp::TempoMap &tempo_map, ArrangerObjectFeatures features, QObject *parent=nullptr) noexcept
Construct a new ArrangerObject.
A MIDI note inside a Region shown in the piano roll.
Definition midi_note.h:23
friend auto get_pitch_range(const Range &range) -> std::optional< std::pair< midi_byte_t, midi_byte_t > >
Returns the minimum and maximum pitches in the given range.
Definition midi_note.h:142
Q_INVOKABLE void shift_pitch(int delta)
Shifts the pitch by delta amount.
Definition midi_note.h:62
static utils::Utf8String pitch_to_string(uint8_t pitch, Notation notation, bool rich_text)
Gets the MIDI note's value as a string (eg "C#4").
friend MidiNote * get_first_midi_note(const Range &range)
Returns the note starting first, or nullptr.
Definition midi_note.h:113
friend MidiNote * get_last_midi_note(const Range &range)
Gets last midi note.
Definition midi_note.h:125
Lightweight UTF-8 string wrapper with safe conversions.
Definition utf8_string.h:37
MIDI utils.
std::uint8_t midi_byte_t
MIDI byte.
Definition midi.h:43