2 #include <ossia/dataflow/graph_node.hpp>
3 #include <ossia/dataflow/node_process.hpp>
4 #include <ossia/dataflow/port.hpp>
5 #include <ossia/detail/flat_multiset.hpp>
10 using midi_size_t = uint8_t;
14 time_value duration{};
17 midi_size_t velocity{};
20 struct note_comparator
22 using is_transparent = std::true_type;
23 bool operator()(
const note_data& lhs,
const note_data& rhs)
const
25 return lhs.start < rhs.start;
27 bool operator()(
const note_data& lhs, int64_t rhs)
const
29 return lhs.start.impl < rhs;
33 class midi final :
public ossia::nonowning_graph_node
35 ossia::midi_outlet midi_out;
38 using note_set = ossia::flat_multiset<note_data, note_comparator>;
39 explicit midi(int64_t notes)
41 m_outlets.push_back(&midi_out);
42 int64_t to_reserve =
std::max(notes * 1.1, 128.);
43 m_notes.reserve(to_reserve);
44 m_orig_notes.reserve(to_reserve);
45 m_playing_notes.reserve(to_reserve);
46 m_to_stop.reserve(64);
49 ~midi()
override =
default;
51 void set_channel(
int c) { m_channel = c; }
53 void add_note(note_data nd)
55 m_orig_notes.insert(nd);
56 if(nd.start > m_prev_date)
62 void remove_note(note_data nd)
64 m_orig_notes.erase(nd);
66 auto it = m_playing_notes.find(nd);
67 if(it != m_playing_notes.end())
70 m_playing_notes.erase(it);
74 void replace_notes(note_set&& notes)
76 for(
auto& note : m_playing_notes)
77 m_to_stop.insert(note);
78 m_playing_notes.clear();
81 swap(m_orig_notes, notes);
84 auto start_it = m_orig_notes.lower_bound(m_prev_date.impl);
85 if(start_it != m_orig_notes.end())
87 m_notes.tree().get_sequence_ref().assign(start_it, m_orig_notes.end());
93 requestTransport =
true;
94 m_transport_date = date;
100 m_to_stop.insert(m_playing_notes.begin(), m_playing_notes.end());
101 m_playing_notes.clear();
104 if(date < m_prev_date)
108 m_notes = m_orig_notes;
112 auto min_it = m_orig_notes.lower_bound({date});
113 auto max_it = m_orig_notes.lower_bound({m_prev_date});
115 if(min_it != m_orig_notes.end())
116 m_notes.insert(min_it, max_it);
119 for(
auto it = m_orig_notes.begin(); it != min_it; ++it)
121 if((it->start + it->duration) > date)
128 else if(date > m_prev_date)
131 auto min_it = m_notes.lower_bound({date});
132 if(min_it != m_notes.begin() && min_it != m_notes.end())
134 std::advance(min_it, -1);
135 m_notes.erase(m_notes.begin(), min_it);
143 void update_note(note_data oldNote, note_data newNote)
146 remove_note(oldNote);
150 void set_notes(note_set&& notes)
152 m_notes = std::move(notes);
153 m_orig_notes = m_notes;
155 auto max_it = m_notes.lower_bound({m_prev_date});
156 if(max_it != m_notes.begin())
157 m_notes.erase(m_notes.begin(), max_it);
161 bool requestTransport{};
165 void run(
const ossia::token_request& t, ossia::exec_state_facade e) noexcept
override
170 const ossia::token_request& t;
173 self.m_prev_date = t.date;
175 if(
self.requestTransport)
177 self.transport_impl(
self.m_transport_date);
178 self.requestTransport =
false;
183 ossia::midi_port& mp = *midi_out;
184 const auto samplesratio = e.modelToSamples();
185 const auto tick_start = t.physical_start(samplesratio);
187 for(
const note_data& note : m_to_stop)
189 mp.messages.push_back(
190 libremidi::channel_events::note_off(m_channel, note.pitch, 0));
191 mp.messages.back().timestamp = tick_start;
197 for(
auto& note : m_playing_notes)
199 mp.messages.push_back(
200 libremidi::channel_events::note_off(m_channel, note.pitch, 0));
201 mp.messages.back().timestamp = tick_start;
204 m_notes = m_orig_notes;
205 m_playing_notes.clear();
211 if(m_notes.empty() && m_playing_notes.empty())
215 auto it = m_notes.begin();
217 while(it != m_notes.end() && it->start < t.date)
220 mp.messages.push_back(
221 libremidi::channel_events::note_on(m_channel, note.pitch, note.velocity));
222 mp.messages.back().timestamp = tick_start;
223 m_playing_notes.insert(note);
224 it = m_notes.erase(it);
233 for(
auto it = m_playing_notes.begin(); it != m_playing_notes.end();)
235 note_data& note =
const_cast<note_data&
>(*it);
236 auto end_time = note.start + note.duration;
238 if(t.in_range({end_time}))
240 mp.messages.push_back(
241 libremidi::channel_events::note_off(m_channel, note.pitch, 0));
242 mp.messages.back().timestamp
243 = t.to_physical_time_in_tick(end_time, samplesratio);
245 it = m_playing_notes.erase(it);
254 auto max_it = m_notes.lower_bound({t.date});
255 for(
auto it = m_notes.begin(); it < max_it;)
257 note_data& note =
const_cast<note_data&
>(*it);
258 auto start_time = note.start;
259 if(start_time >= t.prev_date && start_time < t.date)
262 mp.messages.push_back(libremidi::channel_events::note_on(
263 m_channel, note.pitch, note.velocity));
264 mp.messages.back().timestamp
265 = t.to_physical_time_in_tick(start_time, samplesratio);
267 m_playing_notes.insert(note);
268 it = m_notes.erase(it);
269 max_it = std::lower_bound(
270 it, m_notes.end(), t.date.impl + 1, note_comparator{});
282 note_set m_orig_notes;
283 note_set m_playing_notes;
285 time_value m_prev_date{};
286 time_value m_transport_date{};
291 class midi_node_process final :
public ossia::node_process
294 using ossia::node_process::node_process;
298 midi& n = *
static_cast<midi*
>(node.get());
304 midi& n = *
static_cast<midi*
>(node.get());
305 n.request(ossia::token_request{});
constexpr OSSIA_INLINE auto max(const T a, const U b) noexcept -> typename std::conditional<(sizeof(T) > sizeof(U)), T, U >::type
max function tailored for values
Definition: math.hpp:96
The time_value class.
Definition: ossia/editor/scenario/time_value.hpp:28