25 ossia::midi_outlet out;
26 ossia::value_outlet accent_out;
27 ossia::value_outlet slide_out;
29 ossia::flat_set<uint8_t> in_flight;
36 uint8_t in_flight_channel{1};
37 bool release_pending{};
42 in_flight.reserve(32);
43 m_outlets.push_back(&out);
44 m_outlets.push_back(&accent_out);
45 m_outlets.push_back(&slide_out);
48 std::string label()
const noexcept override {
return "pattern_node"; }
50 bool legato(
int note)
const noexcept
52 for(
const Lane& lane : pattern.lanes)
53 if(lane.note == note && ossia::valid_index(current, lane.pattern))
54 return lane.pattern[current] == Note::Legato;
58 void release_all(int64_t timestamp)
noexcept
60 auto& mess = out.target<ossia::midi_port>()->messages;
61 for(uint8_t note : in_flight)
63 mess.push_back(libremidi::from_midi1::note_off(in_flight_channel, note, 0));
64 mess.back().timestamp = timestamp;
69 void set_channel(uint8_t c)
noexcept
75 release_pending =
true;
78 void run(
const ossia::token_request& tk, ossia::exec_state_facade st)
noexcept override
80 using namespace ossia;
82 const double samplesratio = st.modelToSamples();
87 const double speed = tk.speed != 0. ? std::abs(tk.speed) : 1.;
92 const int64_t tick_start
93 = tk.start_sample >= 0
94 ? int64_t(tk.start_sample)
95 : int64_t(std::floor(tk.offset.impl * samplesratio / speed));
100 release_all(tick_start);
105 if(tk.model_read_duration() == 0_tv)
108 if(tk.end_discontinuous)
114 release_all(tick_start);
120 release_all(tick_start);
121 in_flight_channel = channel;
122 release_pending =
false;
125 if(pattern.length <= 0)
136 const bool rewinding = tk.backward();
137 for(
const auto& q : tk.get_quantification_dates(pattern.division))
143 const int64_t date = tick_start + tk.physical_position(q.position, samplesratio);
144 play_step(date, rewinding);
148 void play_step(int64_t date,
bool rewinding =
false)
noexcept
154 current = (current + pattern.length - 1) % pattern.length;
156 auto& mess = out.target<ossia::midi_port>()->messages;
158 for(
auto it = in_flight.begin(); it != in_flight.end();)
163 mess.push_back(libremidi::from_midi1::note_off(in_flight_channel, note, 0));
164 mess.back().timestamp = date;
165 it = in_flight.erase(it);
173 in_flight_channel = channel;
175 for(
Lane& lane : pattern.lanes)
177 if(lane.note <= 127 && ossia::valid_index(current, lane.pattern))
179 switch(lane.pattern[current])
182 mess.push_back(libremidi::from_midi1::note_on(channel, lane.note, 100));
183 mess.back().timestamp = date;
184 in_flight.insert(lane.note);
187 if(!in_flight.contains(lane.note))
189 mess.push_back(libremidi::from_midi1::note_on(channel, lane.note, 100));
190 mess.back().timestamp = date;
191 in_flight.insert(lane.note);
195 if(in_flight.contains(lane.note))
198 libremidi::from_midi1::note_off(in_flight_channel, lane.note, 0));
199 mess.back().timestamp = date;
200 in_flight.erase(lane.note);
207 for(
Lane& lane : pattern.lanes)
209 if(ossia::valid_index(current, lane.pattern))
213 if(lane.pattern[current] != Note::Rest)
214 accent_out->write_value(1., date);
216 accent_out->write_value(0., date);
218 else if(lane.note == 254)
220 if(lane.pattern[current] != Note::Rest)
221 slide_out->write_value(1., date);
223 slide_out->write_value(0., date);
229 current = (current + 1) % pattern.length;
234 void all_notes_off()
noexcept override { mustStop =
true; }