Sound Byte Libs 0.5.1-121-g3358a44
C++ firmware library for audio applications on 32-bit ARM Cortex-M processors
Loading...
Searching...
No Matches
midi_converter.hpp
Go to the documentation of this file.
1// sbl/dsp/widgets/midi_converter.hpp — MIDI-to-CV converter widget
2//
3// Converts MIDI events (method calls) into normalized float signal outputs.
4// The Eurorack analog: a MIDI-to-CV module (Yarns, Mutant Brain, 0-Coast).
5//
6// Input side — method calls matching MidiParser's event delivery:
7// note_on(note, velocity), note_off(note), cc(number, value),
8// pitch_bend(value), aftertouch(value)
9//
10// Output side — readable float signal values:
11// pitch() [0,1] note mapped to configurable range (default 24–120)
12// gate() 0.0 off, velocity/127.0 on (gate strength)
13// velocity() [0,1] last note-on velocity, held until next note-on
14// pressure() [0,1] channel aftertouch
15// bend() [-1,1] pitch bend, bipolar
16// cc(N) [0,1] CC value by enum or raw index
17//
18// Monophonic, last-note priority. See FDP-063 for design rationale.
19//
20// Usage:
21// sbl::dsp::widgets::MidiConverter mc;
22// mc.note_on(60, 100);
23// float p = mc.pitch(); // note 60 mapped to [0,1]
24// float g = mc.gate(); // 100/127.0 ≈ 0.787
25// mc.cc(1, 64);
26// float mod = mc.cc(sbl::dsp::widgets::Cc::CC1_ModWheel); // 64/127.0 ≈ 0.504
27
28#ifndef SBL_DSP_WIDGETS_MIDI_CONVERTER_HPP_
29#define SBL_DSP_WIDGETS_MIDI_CONVERTER_HPP_
30
31#include <algorithm>
32#include <cstdint>
33
34namespace sbl::dsp::widgets {
35
36/// Common MIDI CC numbers. Values match the MIDI spec 1:1.
37enum class Cc : uint8_t {
38 CC1_ModWheel = 1,
42 CC7_Volume = 7,
43 CC10_Pan = 10,
44 CC11_Expression = 11,
45 CC64_Sustain = 64,
47 CC71_Resonance = 71,
48 CC72_Release = 72,
49 CC73_Attack = 73,
50 CC74_Brightness = 74,
51 CC75_Decay = 75,
54 CC91_ReverbSend = 91,
55 CC93_ChorusSend = 93,
56};
57
59public:
60 // --- Input: MIDI events ---
61
62 /// Note on. Sets pitch, gate (with velocity strength), and velocity.
63 void note_on(uint8_t note, uint8_t vel) {
64 current_note_ = note;
65 float v = static_cast<float>(vel) / 127.0f;
66 gate_ = v;
67 velocity_ = v;
68 pitch_ = note_to_pitch(note);
69 }
70
71 /// Note off. Clears gate only if note matches current note (last-note priority).
72 /// Pitch and velocity are held.
73 void note_off(uint8_t note) {
74 if (note == current_note_) {
75 gate_ = 0.0f;
76 }
77 }
78
79 /// CC value. Stores normalized value for the given CC number.
80 void cc(uint8_t number, uint8_t value) {
81 cc_[number] = static_cast<float>(value) / 127.0f;
82 }
83
84 /// Pitch bend. Raw MIDI value: -8192 to +8191.
85 void pitch_bend(int16_t value) {
86 if (value >= 0) {
87 bend_ = static_cast<float>(value) / 8191.0f;
88 } else {
89 bend_ = static_cast<float>(value) / 8192.0f;
90 }
91 }
92
93 /// Channel aftertouch.
94 void aftertouch(uint8_t value) {
95 pressure_ = static_cast<float>(value) / 127.0f;
96 }
97
98 // --- Output: normalized signal values ---
99
100 /// Pitch as [0,1] mapped to the configured note range.
101 float pitch() const { return pitch_; }
102
103 /// Gate state. 0.0 = off, (0,1] = on with velocity strength.
104 float gate() const { return gate_; }
105
106 /// Last note-on velocity as [0,1]. Held until next note-on.
107 float velocity() const { return velocity_; }
108
109 /// Channel aftertouch as [0,1].
110 float pressure() const { return pressure_; }
111
112 /// Pitch bend as [-1,1].
113 float bend() const { return bend_; }
114
115 /// CC value by enum. Returns [0,1].
116 float cc(Cc cc_num) const {
117 return cc_[static_cast<uint8_t>(cc_num)];
118 }
119
120 /// CC value by raw index. Returns [0,1].
121 float cc(uint8_t cc_num) const {
122 return cc_[cc_num];
123 }
124
125 // --- Configuration ---
126
127 /// Set the note range for pitch normalization. Default: 24–120.
128 void set_pitch_range(uint8_t min_note, uint8_t max_note) {
129 min_note_ = min_note;
130 max_note_ = max_note;
131 }
132
133 /// Reset all state to initial values.
134 void reset() {
135 pitch_ = 0.0f;
136 gate_ = 0.0f;
137 velocity_ = 0.0f;
138 pressure_ = 0.0f;
139 bend_ = 0.0f;
140 current_note_ = 0;
141 for (auto& v : cc_) v = 0.0f;
142 }
143
144private:
145 float note_to_pitch(uint8_t note) const {
146 float range = static_cast<float>(max_note_ - min_note_);
147 float normalized = (static_cast<float>(note) - static_cast<float>(min_note_)) / range;
148 return std::clamp(normalized, 0.0f, 1.0f);
149 }
150
151 float pitch_ = 0.0f;
152 float gate_ = 0.0f;
153 float velocity_ = 0.0f;
154 float pressure_ = 0.0f;
155 float bend_ = 0.0f;
156 uint8_t current_note_ = 0;
157 uint8_t min_note_ = 24;
158 uint8_t max_note_ = 120;
159 float cc_[128] = {};
160};
161
162} // namespace sbl::dsp::widgets
163
164#endif // SBL_DSP_WIDGETS_MIDI_CONVERTER_HPP_
void set_pitch_range(uint8_t min_note, uint8_t max_note)
Set the note range for pitch normalization. Default: 24–120.
float pressure() const
Channel aftertouch as [0,1].
float gate() const
Gate state. 0.0 = off, (0,1] = on with velocity strength.
float bend() const
Pitch bend as [-1,1].
void aftertouch(uint8_t value)
Channel aftertouch.
float cc(uint8_t cc_num) const
CC value by raw index. Returns [0,1].
void note_on(uint8_t note, uint8_t vel)
Note on. Sets pitch, gate (with velocity strength), and velocity.
void pitch_bend(int16_t value)
Pitch bend. Raw MIDI value: -8192 to +8191.
float velocity() const
Last note-on velocity as [0,1]. Held until next note-on.
float pitch() const
Pitch as [0,1] mapped to the configured note range.
void reset()
Reset all state to initial values.
float cc(Cc cc_num) const
CC value by enum. Returns [0,1].
void cc(uint8_t number, uint8_t value)
CC value. Stores normalized value for the given CC number.
Black boxes with _su ports.
Definition channel.hpp:49
Cc
Common MIDI CC numbers. Values match the MIDI spec 1:1.