Zrythm v2.0.0-DEV
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
10namespace zrythm::structure::arrangement
11{
12
13template <typename T>
15
19class MidiNote : public ArrangerObject
20{
21 Q_OBJECT
22 Q_PROPERTY (int pitch READ pitch WRITE setPitch NOTIFY pitchChanged)
23 Q_PROPERTY (
24 int velocity READ velocity WRITE setVelocity NOTIFY velocityChanged)
25 QML_ELEMENT
26 QML_UNCREATABLE ("")
27
28public:
29 MidiNote (const dsp::TempoMap &tempo_map, QObject * parent = nullptr);
30 Z_DISABLE_COPY_MOVE (MidiNote)
31 ~MidiNote () override;
32
33 static constexpr midi_byte_t DEFAULT_VELOCITY = 90;
34
35 enum class Notation : std::uint8_t
36 {
37 Musical,
38 Pitch,
39 };
40
41 // ========================================================================
42 // QML Interface
43 // ========================================================================
44
45 int pitch () const { return static_cast<int> (pitch_); }
46 void setPitch (int ipitch)
47 {
48 ipitch = std::clamp (ipitch, 0, 127);
49 const auto pitch = static_cast<midi_byte_t> (ipitch);
50 if (pitch_ != pitch)
51 {
52 pitch_ = pitch;
53 Q_EMIT pitchChanged (ipitch);
54 }
55 }
58 */
59 Q_INVOKABLE void shift_pitch (int delta)
60 {
61 setPitch (static_cast<int> (pitch_) + delta);
62 }
63 Q_SIGNAL void pitchChanged (int ipitch);
64 Q_INVOKABLE QString pitchAsRichText () const
65 {
66 return pitch_to_string (pitch_, Notation::Musical, true).to_qstring ();
67 }
68
69 int velocity () const { return static_cast<int> (velocity_); }
70 void setVelocity (int ivelocity)
71 {
72 ivelocity = std::clamp (ivelocity, 0, 127);
73 const auto velocity = static_cast<midi_byte_t> (ivelocity);
74 if (velocity_ != velocity)
75 {
76 velocity_ = velocity;
77 Q_EMIT velocityChanged (ivelocity);
78 }
79 }
80 Q_SIGNAL void velocityChanged (int ivelocity);
81
82 // ========================================================================
83
90 pitch_to_string (uint8_t pitch, Notation notation, bool rich_text);
91
92private:
96 template <RangeOfMidiNotePointers Range>
97 friend MidiNote * get_first_midi_note (const Range &range)
98 {
99 auto it = std::ranges::min_element (range, [] (const auto &a, const auto &b) {
100 return a->position ()->ticks () < b->position ()->ticks ();
101 });
102 return (it != range.end ()) ? *it : nullptr;
103 }
104
108 template <RangeOfMidiNotePointers Range>
109 friend MidiNote * get_last_midi_note (const Range &range)
110 {
111 // take the reverse because max_element returns the first match, and we want
112 // to return the last match in the container in this case
113 const auto range_to_test = range | std::views::reverse;
114 auto it = std::ranges::max_element (
115 range_to_test, [] (const auto &a, const auto &b) {
116 return a->bounds ()->get_end_position_samples (false)
117 < b->bounds ()->get_end_position_samples (false);
118 });
119 return (it != range_to_test.end ()) ? *it : nullptr;
120 }
121
125 template <RangeOfMidiNotePointers Range>
126 friend auto get_pitch_range (const Range &range)
127 -> std::optional<std::pair<midi_byte_t, midi_byte_t>>
128 {
129 auto [min_it, max_it] =
130 std::ranges::minmax_element (range, [] (const auto &a, const auto &b) {
131 return a->pitch () < b->pitch ();
132 });
133 if (min_it == range.end () || max_it == range.end ())
134 return std::nullopt;
135 return std::make_pair ((*min_it)->pitch (), (*max_it)->pitch ());
136 }
137
138 friend void init_from (
139 MidiNote &obj,
140 const MidiNote &other,
141 utils::ObjectCloneType clone_type);
142
143 static constexpr auto kVelocityKey = "velocity"sv;
144 static constexpr auto kPitchKey = "pitch"sv;
145 friend void to_json (nlohmann::json &j, const MidiNote &note)
146 {
147 to_json (j, static_cast<const ArrangerObject &> (note));
148 j[kVelocityKey] = note.velocity_;
149 j[kPitchKey] = note.pitch_;
150 }
151 friend void from_json (const nlohmann::json &j, MidiNote &note)
152 {
153 from_json (j, static_cast<ArrangerObject &> (note));
154 j.at (kVelocityKey).get_to (note.velocity_);
155 j.at (kPitchKey).get_to (note.pitch_);
156 }
157
158private:
160 std::uint8_t velocity_{ DEFAULT_VELOCITY };
161
163 std::uint8_t pitch_{ 60 };
164
165 BOOST_DESCRIBE_CLASS (MidiNote, (ArrangerObject), (), (), (velocity_, pitch_))
166};
167
168}
Base class for all objects in the arranger.
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:20
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:125
Q_INVOKABLE void shift_pitch(int delta)
Shifts the pitch by delta amount.
Definition midi_note.h:58
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:96
friend MidiNote * get_last_midi_note(const Range &range)
Gets last midi note.
Definition midi_note.h:108
Lightweight UTF-8 string wrapper with safe conversions.
Definition utf8_string.h:38
uint8_t midi_byte_t
MIDI byte.
Definition types.h:55
@ Musical
Musical time (ticks).
Definition types.h:169