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;
79 std::vector<NoteIn> to_start;
80 std::vector<NoteIn> running_notes;
88 Note operator()() {
return {base_note, base_vel}; }
89 Note operator()(ossia::impulse) {
return {base_note, base_vel}; }
91 Note operator()(
const T&)
93 return {base_note, base_vel};
95 Note operator()(
float note) {
return {(uint8_t)note, base_vel}; }
96 Note operator()(
char note) {
return {(uint8_t)note, base_vel}; }
97 Note operator()(
int note) {
return {(uint8_t)note, base_vel}; }
98 Note operator()(
int note,
int vel) {
return {(uint8_t)note, (uint8_t)vel}; }
99 Note operator()(
int note,
float vel) {
return {(uint8_t)note, (uint8_t)(vel * 127.f)}; }
100 Note operator()(
const std::vector<ossia::value>& v)
107 return operator()(ossia::convert<int>(v[0]));
109 int note = ossia::convert<int>(v[0]);
110 switch(v[1].get_type())
112 case ossia::val_type::FLOAT:
113 return operator()(note, *v[1].v.target<
float>());
114 case ossia::val_type::INT:
115 return operator()(note, *v[1].v.target<
int>());
117 return operator()(note, ossia::convert<int>(v[1]));
121 return operator()(ossia::convert<int>(v[0]), ossia::convert<int>(v[1]));
124 template <std::
size_t N>
125 Note operator()(
const std::array<float, N>& v)
127 static_assert(N >= 2);
128 return operator()(v[0], v[1]);
132 static constexpr uint8_t midi_clamp(
int num) {
return (uint8_t)ossia::clamp(num, 0, 127); }
134 using tick = halp::tick_flicks;
135 void operator()(
const tick& tk)
153#define STOP_AT_NEXT_SAMPLE_FOR_ZERO_END_QUANT 1
155 const auto& start = inputs.start_quant.value;
157 const auto& end = inputs.end_quant.value;
158 const auto& shiftnote = inputs.shift_note.value;
159 const auto& base_note = midi_clamp(inputs.basenote.value);
160 const auto& base_vel = midi_clamp(inputs.basevel.value);
161 const auto& rand_note = inputs.note_random.value;
162 const auto& rand_vel = inputs.vel_random.value;
163 const auto& chan = inputs.channel.value;
165 for(
auto& in : inputs.port.value->get_data())
167 auto note = in.value.apply(val_visitor{*
this, base_note, base_vel});
170 note.pitch = std::clamp(
171 (
int)note.pitch + (
int)rnd::rand(-rand_note, rand_note), 0, 127);
175 = std::clamp((
int)note.vel + (
int)rnd::rand(-rand_vel, rand_vel), 0, 127);
177 note.pitch = ossia::clamp((
int)note.pitch + shiftnote, 0, 127);
178 note.vel = ossia::clamp((
int)note.vel, 0, 127);
184 outputs.midi.note_on(chan, note.pitch, note.vel).timestamp = in.timestamp;
187 this->running_notes.push_back(
188 {note, tk.position_in_frames + in.timestamp,
true});
193#if STOP_AT_NEXT_SAMPLE_FOR_ZERO_END_QUANT
194 outputs.midi.note_off(chan, note.pitch, note.vel).timestamp = in.timestamp;
205 this->to_start.push_back({note, {}});
211 outputs.midi.note_off(chan, note.pitch, note.vel).timestamp = in.timestamp;
214 std::optional<int> start_quant_date{};
216 if(!to_start.empty())
220 for(
auto [date, q] : tk.get_quantification_date(4. * start))
222 start_all_notes(tk.position_in_frames + date, date, chan, end);
223 start_quant_date = date;
229 start_all_notes(tk.position_in_frames, 0, chan, end);
230 start_quant_date = 0;
236 for(
auto [date, q] : tk.get_quantification_date(4. * end))
238 if(!start_quant_date || date > start_quant_date)
240 stop_notes(date, chan);
248#if STOP_AT_NEXT_SAMPLE_FOR_ZERO_END_QUANT
254 void start_all_notes(
255 ossia::physical_time abs_date, ossia::physical_time date_phys,
int chan,
258 for(
auto& note : this->to_start)
260 outputs.midi.note_on(chan, note.note.pitch, note.note.vel).timestamp = date_phys;
264 this->running_notes.push_back({{note.note}, abs_date});
270#if STOP_AT_NEXT_SAMPLE_FOR_ZERO_END_QUANT
271 outputs.midi.note_off(chan, note.note.pitch, note.note.vel).timestamp
276 this->to_start.clear();
279 void stop_notes(ossia::physical_time date_phys,
int chan)
281 for(
auto it = this->running_notes.begin(); it != this->running_notes.end();)
286 it = this->running_notes.erase(it);
287 outputs.midi.note_off(chan, note.note.pitch, note.note.vel).timestamp