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