2 #include <Engine/Node/SimpleApi.hpp>
4 #include <ossia/detail/flat_map.hpp>
9 static void duplicate_vector(T& vec)
11 const int N = vec.size();
13 for(
int i = 0; i < N; i++)
14 vec.push_back(vec[i]);
22 static const constexpr
auto prettyName =
"Arpeggiator";
23 static const constexpr
auto objectKey =
"Arpeggiator";
24 static const constexpr
auto category =
"Midi";
25 static const constexpr
auto author =
"ossia score";
26 static const constexpr
auto tags = std::array<const char*, 0>{};
27 static const constexpr
auto kind = Process::ProcessCategory::MidiEffect;
28 static const constexpr
auto description =
"Arpeggiator";
29 static const uuid_constexpr
auto uuid
30 = make_uuid(
"0b98c7cd-f831-468f-81e3-706d6a97d705");
32 static const constexpr midi_in midi_ins[]{
"in"};
33 static const constexpr midi_out midi_outs[]{
"out"};
34 static const constexpr
auto controls = tuplet::make_tuple(
39 using byte =
unsigned char;
40 using chord = ossia::small_vector<std::pair<byte, byte>, 5>;
44 ossia::flat_map<byte, byte> notes;
45 ossia::small_vector<chord, 10> arpeggio;
46 std::array<int8_t, 128> in_flight{};
48 float previous_octave{};
49 int previous_arpeggio{};
55 switch(previous_arpeggio)
62 std::reverse(arpeggio.begin(), arpeggio.end());
66 duplicate_vector(arpeggio);
67 std::reverse(arpeggio.begin() + notes.size(), arpeggio.end());
71 duplicate_vector(arpeggio);
72 std::reverse(arpeggio.begin(), arpeggio.begin() + notes.size());
77 for(std::pair note : notes)
79 arpeggio[0].push_back(note);
84 const std::size_t orig_size = arpeggio.size();
87 for(
int i = 1; i < previous_octave; i++)
89 octavize(orig_size, i);
91 for(
int i = 1; i < previous_octave; i++)
93 octavize(orig_size, -i);
97 void arpeggiate(
int size_mult)
100 arpeggio.reserve(notes.size() * size_mult);
101 for(std::pair note : notes)
103 arpeggio.push_back(chord{note});
107 void octavize(std::size_t orig_size,
int i)
109 for(std::size_t j = 0; j < orig_size; j++)
111 auto copy = arpeggio[j];
112 for(
auto it = copy.begin(); it != copy.end();)
115 int res = note.first + 12 * i;
116 if(res >= 0.f && res <= 127.f)
127 arpeggio.push_back(std::move(copy));
132 using control_policy = ossia::safe_nodes::precise_tick;
134 run(
const ossia::midi_port& midi,
int arpeggio,
float octave,
int quantif,
135 ossia::midi_port& out, ossia::token_request tk, ossia::exec_state_facade st,
139 auto msgs = midi.messages;
140 self.previous_octave = octave;
141 self.previous_arpeggio = arpeggio;
146 for(
auto& note : msgs)
148 if(note.get_message_type() == libremidi::message_type::NOTE_ON)
150 self.notes.insert({note.bytes[1], note.bytes[2]});
152 else if(note.get_message_type() == libremidi::message_type::NOTE_OFF)
154 self.notes.erase(note.bytes[1]);
160 const bool mustUpdateArpeggio = msgs.size() > 0 || octave !=
self.previous_octave
161 || arpeggio !=
self.previous_arpeggio;
162 if(mustUpdateArpeggio)
167 if(
self.arpeggio.empty())
169 const auto [tick_start, d] = st.timings(tk);
170 for(
int k = 0; k < 128; k++)
172 while(
self.in_flight[k] > 0)
174 out.note_off(1, k, 0).timestamp = tick_start;
181 if(
self.index >=
self.arpeggio.size())
185 if(
auto date = tk.get_physical_quantification_date(quantif, st.modelToSamples()))
189 for(
int k = 0; k < 128; k++)
191 while(
self.in_flight[k] > 0)
193 out.note_off(1, k, 0).timestamp = *date;
199 auto& chord =
self.arpeggio[
self.index];
201 for(
auto& note : chord)
203 self.in_flight[note.first]++;
204 out.note_on(1, note.first, note.second).timestamp = *date;
208 self.index = (
self.index + 1) % (
self.arpeggio.size());
Utilities for OSSIA data structures.
Definition: DeviceInterface.hpp:33
Definition: score-lib-process/Control/Widgets.hpp:178
Definition: Arpeggiator.hpp:19