41 halp_meta(name,
"Arpeggiator")
42 halp_meta(c_name,
"Arpeggiator")
43 halp_meta(category,
"Midi")
44 halp_meta(author,
"ossia score")
47 "https://ossia.io/score-docs/processes/midi-utilities.html#arpeggiator")
48 halp_meta(description,
"Arpeggiator")
49 halp_meta(uuid,
"0b98c7cd-f831-468f-81e3-706d6a97d705")
54 halp::midi_bus<
"in", libremidi::message> midi;
56 halp::hslider_i32<
"Octave", halp::irange{1, 7, 1}> octave;
57 halp::hslider_i32<
"Quantification", halp::irange{1, 32, 8}> quantification;
61 halp::midi_out_bus<
"out", libremidi::message> midi;
64 using byte =
unsigned char;
65 using chord = ossia::small_vector<std::pair<byte, byte>, 5>;
67 ossia::flat_map<byte, byte> notes;
68 ossia::small_vector<chord, 10> arpeggio;
69 std::array<int8_t, 128> in_flight{};
71 float previous_octave{};
72 int previous_arpeggio{};
78 switch(previous_arpeggio)
85 std::reverse(arpeggio.begin(), arpeggio.end());
89 duplicate_vector(arpeggio);
90 std::reverse(arpeggio.begin() + notes.size(), arpeggio.end());
94 duplicate_vector(arpeggio);
95 std::reverse(arpeggio.begin(), arpeggio.begin() + notes.size());
100 for(std::pair note : notes)
102 arpeggio[0].push_back(note);
107 const std::size_t orig_size = arpeggio.size();
110 for(
int i = 1; i < previous_octave; i++)
112 octavize(orig_size, i);
114 for(
int i = 1; i < previous_octave; i++)
116 octavize(orig_size, -i);
120 void arpeggiate(
int size_mult)
123 arpeggio.reserve(notes.size() * size_mult);
124 for(std::pair note : notes)
126 arpeggio.push_back(chord{note});
130 void octavize(std::size_t orig_size,
int i)
132 for(std::size_t j = 0; j < orig_size; j++)
134 auto copy = arpeggio[j];
135 for(
auto it = copy.begin(); it != copy.end();)
138 int res = note.first + 12 * i;
139 if(res >= 0.f && res <= 127.f)
150 arpeggio.push_back(std::move(copy));
154 using tick = halp::tick_musical;
155 void operator()(
const halp::tick_musical& tk)
159 auto& midi = this->inputs.midi;
160 auto& out = this->outputs.midi;
161 const auto& msgs = midi;
162 const int octave = inputs.octave;
163 const int arpeggio = inputs.arpeggios.value;
164 self.previous_octave = octave;
165 self.previous_arpeggio = arpeggio;
170 for(
auto& note : msgs)
172 if(note.get_message_type() == libremidi::message_type::NOTE_ON)
174 self.notes.insert({note.bytes[1], note.bytes[2]});
176 else if(note.get_message_type() == libremidi::message_type::NOTE_OFF)
178 self.notes.erase(note.bytes[1]);
184 const bool mustUpdateArpeggio = msgs.size() > 0 || octave != self.previous_octave || arpeggio != self.previous_arpeggio;
185 if(mustUpdateArpeggio)
190 if(self.arpeggio.empty())
192 for(
int k = 0; k < 128; k++)
194 while(self.in_flight[k] > 0)
196 out.note_off(1, k, 0).timestamp = 0;
203 if(self.index >= self.arpeggio.size())
208 for(
auto [date, q] : tk.get_quantification_date_with_bars(inputs.quantification.value))
213 if(date >= tk.frames)
217 for(
int k = 0; k < 128; k++)
219 while(self.in_flight[k] > 0)
221 out.note_off(1, k, 0).timestamp = date;
227 auto& chord = self.arpeggio[self.index];
229 for(
auto& note : chord)
231 self.in_flight[note.first]++;
232 out.note_on(1, note.first, note.second).timestamp = date;
236 self.index = (self.index + 1) % (self.arpeggio.size());