2 #include <Fx/Types.hpp>
4 #include <ossia/dataflow/value_port.hpp>
5 #include <ossia/detail/math.hpp>
7 #include <boost/pfr.hpp>
9 #include <halp/controls.hpp>
10 #include <halp/meta.hpp>
11 #include <halp/midi.hpp>
12 #include <libremidi/message.hpp>
13 #include <rnd/random.hpp>
17 operator<<(std::ostream& s, decltype(halp::tick_musical::signature) sig)
19 return s << sig.num <<
" / " << sig.denom;
27 halp_meta(name,
"Pulse to Midi")
28 halp_meta(c_name,
"VelToNote")
29 halp_meta(category,
"Midi")
30 halp_meta(author,
"ossia score")
31 halp_meta(manual_url,
"https://ossia.io/score-docs/processes/midi-utilities.html#pulse-to-note")
34 "Converts a message into MIDI.\n"
35 "If the input is an impulse, the output will be the default pitch "
36 "at the default velocity.\n"
37 "If the input is a single integer in [0; 127], the output will be "
38 "the relevant note at the default velocity"
39 "If the input is an array of two values between [0; 127], the "
40 "output will be the relevant note.")
42 halp_meta(uuid,
"2c6493c3-5449-4e52-ae04-9aee3be5fb6a")
51 halp::hslider_f32<
"Tightness", halp::range{0.f, 1.f, 0.8f}> tightness;
53 midi_spinbox<
"Default pitch"> basenote;
54 midi_spinbox<
"Default vel."> basevel;
55 octave_slider<
"Pitch shift", -5, 5> shift_note;
56 octave_slider<
"Pitch random", 0, 2> note_random;
57 octave_slider<
"Vel. random", 0, 2> vel_random;
58 midi_channel<
"Channel"> channel;
78 std::vector<NoteIn> to_start;
79 std::vector<NoteIn> running_notes;
87 Note operator()() {
return {base_note, base_vel}; }
88 Note operator()(ossia::impulse) {
return {base_note, base_vel}; }
90 Note operator()(
const T&)
92 return {base_note, base_vel};
94 Note operator()(
float note) {
return {(uint8_t)note, base_vel}; }
95 Note operator()(
char note) {
return {(uint8_t)note, base_vel}; }
96 Note operator()(
int note) {
return {(uint8_t)note, base_vel}; }
97 Note operator()(
int note,
int vel) {
return {(uint8_t)note, (uint8_t)vel}; }
98 Note operator()(
int note,
float vel) {
return {(uint8_t)note, (uint8_t)(vel * 127.f)}; }
99 Note operator()(
const std::vector<ossia::value>& v)
106 return operator()(ossia::convert<int>(v[0]));
108 int note = ossia::convert<int>(v[0]);
109 switch(v[1].get_type())
111 case ossia::val_type::FLOAT:
112 return operator()(note, *v[1].v.target<
float>());
113 case ossia::val_type::INT:
114 return operator()(note, *v[1].v.target<
int>());
116 return operator()(note, ossia::convert<int>(v[1]));
120 return operator()(ossia::convert<int>(v[0]), ossia::convert<int>(v[1]));
123 template <std::
size_t N>
124 Note operator()(
const std::array<float, N>& v)
126 static_assert(N >= 2);
127 return operator()(v[0], v[1]);
131 static constexpr uint8_t midi_clamp(
int num) {
return (uint8_t)ossia::clamp(num, 0, 127); }
133 using tick = halp::tick_flicks;
134 void operator()(
const tick& tk)
152 #define STOP_AT_NEXT_SAMPLE_FOR_ZERO_END_QUANT 1
154 const auto& start = inputs.start_quant.value;
156 const auto& end = inputs.end_quant.value;
157 const auto& shiftnote = inputs.shift_note.value;
158 const auto& base_note = midi_clamp(inputs.basenote.value);
159 const auto& base_vel = midi_clamp(inputs.basevel.value);
160 const auto& rand_note = inputs.note_random.value;
161 const auto& rand_vel = inputs.vel_random.value;
162 const auto& chan = inputs.channel.value;
164 for(
auto& in : inputs.port.value->get_data())
166 auto note = in.value.apply(val_visitor{*
this, base_note, base_vel});
169 note.pitch += rnd::rand(-rand_note, rand_note);
171 note.vel += rnd::rand(-rand_vel, rand_vel);
173 note.pitch = ossia::clamp((
int)note.pitch + shiftnote, 0, 127);
174 note.vel = ossia::clamp((
int)note.vel, 0, 127);
180 outputs.midi.note_on(chan, note.pitch, note.vel).timestamp = in.timestamp;
183 this->running_notes.push_back({note, tk.position_in_frames + in.timestamp});
188 #if STOP_AT_NEXT_SAMPLE_FOR_ZERO_END_QUANT
189 outputs.midi.note_off(chan, note.pitch, note.vel).timestamp = in.timestamp;
197 this->to_start.push_back({note, {}});
203 outputs.midi.note_off(chan, note.pitch, note.vel).timestamp = in.timestamp;
206 std::optional<int> start_quant_date{};
208 if(!to_start.empty())
212 for(
auto [date, q] : tk.get_quantification_date(4. * start))
214 start_all_notes(tk.position_in_frames + date, date, chan, end);
215 start_quant_date = date;
221 start_all_notes(tk.position_in_frames, 0, chan, end);
222 start_quant_date = 0;
228 for(
auto [date, q] : tk.get_quantification_date(4. * end))
230 if(!start_quant_date || date > start_quant_date)
232 stop_notes(date, chan);
240 #if STOP_AT_NEXT_SAMPLE_FOR_ZERO_END_QUANT
246 void start_all_notes(
247 ossia::physical_time abs_date, ossia::physical_time date_phys,
int chan,
250 for(
auto& note : this->to_start)
252 outputs.midi.note_on(chan, note.note.pitch, note.note.vel).timestamp = date_phys;
256 this->running_notes.push_back({{note.note}, abs_date});
262 #if STOP_AT_NEXT_SAMPLE_FOR_ZERO_END_QUANT
263 outputs.midi.note_off(chan, note.note.pitch, note.note.vel).timestamp
268 this->to_start.clear();
271 void stop_notes(ossia::physical_time date_phys,
int chan)
273 for(
auto& note : this->running_notes)
275 outputs.midi.note_off(chan, note.note.pitch, note.note.vel).timestamp = date_phys;
277 this->running_notes.clear();
Definition: VelToNote.hpp:67
Definition: VelToNote.hpp:74
Definition: VelToNote.hpp:82
Definition: VelToNote.hpp:26