2 #include <Fx/Types.hpp>
4 #include <halp/controls.hpp>
5 #include <halp/meta.hpp>
6 #include <halp/midi.hpp>
14 halp_meta(name,
"Midi quantify")
15 halp_meta(c_name,
"Quantifier")
16 halp_meta(category,
"Midi")
17 halp_meta(manual_url,
"https://ossia.io/score-docs/processes/midi-utilities.html#quantifier")
18 halp_meta(author,
"ossia score")
19 halp_meta(description,
"Quantifies a MIDI input")
20 halp_meta(uuid,
"b8e2e5ad-17e4-43de-8d79-660a29d5c4f4")
24 halp::midi_bus<
"in", libremidi::message> midi;
26 halp::hslider_f32<
"Tightness", halp::range{0.f, 1.f, 0.8f}> tightness;
28 halp::hslider_f32<
"Tempo", halp::range{20., 300., 120.}> tempo;
48 std::vector<NoteIn> to_start;
49 std::vector<NoteIn> running_notes;
53 void prepare(halp::setup s) { setup = s; }
55 using tick = halp::tick_flicks;
56 void sequence(Note new_note, int64_t date)
58 for(
auto note : to_start)
59 if(note.note.pitch == new_note.pitch)
61 for(
auto note : running_notes)
62 if(note.note.pitch == new_note.pitch)
64 to_start.push_back(NoteIn{new_note, date});
66 void operator()(
const tick& tk)
68 double start_q_ratio = inputs.start_quant.value;
69 double precision = inputs.tightness.value;
70 double duration = inputs.duration.value;
71 double tempo = inputs.tempo.value;
74 const auto whole_dur = 240.f / tempo;
75 const auto whole_samples = whole_dur * setup.rate;
77 for(
const libremidi::message& in : inputs.midi)
80 if(!in.is_note_on_or_off())
82 outputs.midi.push_back(in);
86 Note note{in[1], in[2], (uint8_t)in.get_channel()};
88 if(in.get_message_type() == libremidi::message_type::NOTE_ON && note.vel != 0)
90 if(start_q_ratio == 0.f)
92 auto no = libremidi::channel_events::note_on(note.chan, note.pitch, note.vel);
93 no.timestamp = in.timestamp;
95 outputs.midi.push_back(no);
98 auto end = tk.position_in_frames + (int64_t)no.timestamp
99 + (int64_t)(whole_samples * duration);
100 this->running_notes.push_back({note, end});
102 else if(duration == 0.f)
105 auto noff = libremidi::channel_events::note_off(note.chan, note.pitch, note.vel);
106 noff.timestamp = no.timestamp;
107 outputs.midi.push_back(noff);
114 const auto start_q = whole_samples * start_q_ratio;
115 auto perf_date = int64_t(
116 start_q * int64_t(1 + (tk.position_in_frames + in.timestamp) / start_q));
119 int64_t actual_date = perf_date;
121 sequence(note, actual_date);
127 auto noff = libremidi::channel_events::note_off(note.chan, note.pitch, note.vel);
128 noff.timestamp = in.timestamp;
129 outputs.midi.push_back(noff);
131 for(
auto it = running_notes.begin(); it != running_notes.end();)
132 if(it->note.pitch == note.pitch)
133 it = running_notes.erase(it);
141 for(
auto it = this->to_start.begin(); it != this->to_start.end();)
145 if(note.date >= tk.position_in_frames
146 && note.date < (tk.position_in_frames + tk.frames))
148 auto no = libremidi::channel_events::note_on(
149 note.note.chan, note.note.pitch, note.note.vel);
150 no.timestamp = note.date - tk.position_in_frames;
151 outputs.midi.push_back(no);
155 auto end = note.date + (int64_t)(whole_samples * duration);
156 this->running_notes.push_back({note.note, end});
158 else if(duration == 0.f)
161 auto noff = libremidi::channel_events::note_off(note.note.chan, note.note.pitch, note.note.vel);
162 noff.timestamp = no.timestamp;
163 outputs.midi.push_back(noff);
166 it = this->to_start.erase(it);
174 for(
auto it = this->running_notes.begin(); it != this->running_notes.end();)
177 if(note.date >= tk.position_in_frames
178 && note.date < (tk.position_in_frames + tk.frames))
180 auto noff = libremidi::channel_events::note_off(note.note.chan, note.note.pitch, note.note.vel);
181 noff.timestamp = note.date - tk.position_in_frames;
182 outputs.midi.push_back(noff);
183 it = this->running_notes.erase(it);
Definition: Quantifier.hpp:37
Definition: Quantifier.hpp:44
Definition: Quantifier.hpp:13