2 #include <LV2/Context.hpp>
3 #include <LV2/lv2_atom_helpers.hpp>
5 #include <ossia/dataflow/fx_node.hpp>
6 #include <ossia/dataflow/port.hpp>
7 #include <ossia/detail/fmt.hpp>
8 #include <ossia/detail/pod_vector.hpp>
12 template <
typename OnExecStart,
typename OnExecFinished>
13 struct lv2_node final :
public ossia::graph_node
16 ossia::float_vector fInControls, fOutControls, fParamMin, fParamMax, fParamInit,
18 std::vector<ossia::float_vector> fCVs;
19 std::vector<AtomBuffer> m_atom_ins, m_atom_outs;
20 std::vector<ossia::small_vector<Message, 2>> m_message_for_atom_ins;
22 LilvInstance* fInstance{};
23 std::unique_ptr<uint8_t[]> timePositionBuffer{};
29 std::vector<MatchedPort> m_atom_timePosition_midi;
30 std::vector<MatchedPort> m_atom_timePosition_owned;
33 OnExecFinished on_finished;
34 lv2_node(
LV2Data dat,
int sampleRate, OnExecStart os, OnExecFinished of)
39 const std::size_t audio_in_size = data.audio_in_ports.size();
40 const std::size_t audio_out_size = data.audio_out_ports.size();
41 const std::size_t control_in_size = data.control_in_ports.size();
42 const std::size_t control_out_size = data.control_out_ports.size();
43 const std::size_t midi_in_size = data.midi_in_ports.size();
44 const std::size_t midi_out_size = data.midi_out_ports.size();
45 const std::size_t time_in_size = data.time_Position_ports.size();
46 const std::size_t cv_size = data.cv_ports.size();
47 const std::size_t other_size = data.control_other_ports.size();
48 const std::size_t num_ports = data.effect.plugin.get_num_ports();
52 m_inlets.push_back(
new ossia::audio_inlet);
54 if(audio_out_size > 0)
56 m_outlets.push_back(
new ossia::audio_outlet);
59 for(std::size_t i = 0; i < cv_size; i++)
61 m_inlets.push_back(
new ossia::audio_inlet);
64 for(std::size_t i = 0; i < midi_in_size; i++)
66 m_inlets.push_back(
new ossia::midi_inlet);
68 for(std::size_t i = 0; i < midi_out_size; i++)
70 m_outlets.push_back(
new ossia::midi_outlet);
73 for(std::size_t i = 0; i < control_in_size; i++)
75 m_inlets.push_back(
new ossia::value_inlet);
77 for(std::size_t i = 0; i < control_out_size; i++)
79 m_outlets.push_back(
new ossia::value_outlet);
82 fInControls.resize(control_in_size);
83 fOutControls.resize(control_out_size);
84 fOtherControls.resize(other_size);
86 for(std::size_t i = 0; i < cv_size; i++)
91 fParamMin.resize(num_ports);
92 fParamMax.resize(num_ports);
93 fParamInit.resize(num_ports);
95 data.effect.plugin.get_port_ranges_float(
96 fParamMin.data(), fParamMax.data(), fParamInit.data());
98 fInstance = data.effect.instance;
99 data.effect.instance = fInstance;
102 throw std::runtime_error(
"Error while creating a LV2 plug-in");
105 m_atom_ins.reserve(midi_in_size);
106 m_message_for_atom_ins.resize(midi_in_size);
107 for(std::size_t i = 0; i < midi_in_size; i++)
109 m_atom_ins.emplace_back(
110 2048, data.host.atom_chunk_id, data.host.midi_event_id,
true);
113 m_atom_outs.reserve(midi_out_size);
114 for(std::size_t i = 0; i < midi_out_size; i++)
116 m_atom_outs.emplace_back(
117 2048, data.host.atom_chunk_id, data.host.midi_event_id,
false);
122 for(std::size_t i = 0; i < time_in_size; i++)
124 auto port_index = data.time_Position_ports[i];
126 bool is_midi =
false;
127 for(std::size_t midi_port_k = 0; midi_port_k < data.midi_in_ports.size();
130 int midi_port_index = data.midi_in_ports[midi_port_k];
131 if(midi_port_index == port_index)
133 m_atom_timePosition_midi.push_back(
134 MatchedPort{port_index, &m_atom_ins[midi_port_k]});
144 256, data.host.atom_chunk_id, data.host.time_Position_id,
true);
145 m_atom_timePosition_owned.push_back(MatchedPort{port_index, abuf});
150 if(lilv_plugin_has_feature(data.effect.plugin.me, data.host.work_schedule)
151 && lilv_plugin_has_extension_data(
152 data.effect.plugin.me, data.host.work_interface))
154 data.effect.worker =
static_cast<const LV2_Worker_Interface*
>(
155 lilv_instance_get_extension_data(fInstance, LV2_WORKER__interface));
158 for(std::size_t i = 0; i < control_in_size; i++)
160 auto port_i = data.control_in_ports[i];
161 fInControls[i] = fParamInit[port_i];
164 if(!m_atom_timePosition_midi.empty() || !m_atom_timePosition_owned.empty())
167 timePositionBuffer = std::make_unique<uint8_t[]>(256);
170 lilv_instance_activate(fInstance);
173 void connect_all_ports()
175 const std::size_t control_in_size = data.control_in_ports.size();
176 const std::size_t control_out_size = data.control_out_ports.size();
177 const std::size_t midi_in_size = data.midi_in_ports.size();
178 const std::size_t midi_out_size = data.midi_out_ports.size();
179 const std::size_t cv_size = data.cv_ports.size();
180 const std::size_t other_size = data.control_other_ports.size();
182 for(std::size_t i = 0; i < control_in_size; i++)
184 lilv_instance_connect_port(fInstance, data.control_in_ports[i], &fInControls[i]);
187 for(std::size_t i = 0; i < control_out_size; i++)
189 lilv_instance_connect_port(fInstance, data.control_out_ports[i], &fOutControls[i]);
192 for(std::size_t i = 0; i < cv_size; i++)
194 lilv_instance_connect_port(fInstance, data.cv_ports[i], fCVs[i].data());
197 for(std::size_t i = 0; i < other_size; i++)
199 lilv_instance_connect_port(
200 fInstance, data.control_other_ports[i], &fOtherControls[i]);
203 for(std::size_t i = 0; i < midi_in_size; i++)
205 lilv_instance_connect_port(
206 fInstance, data.midi_in_ports[i], &m_atom_ins[i].buf->atoms);
209 for(std::size_t i = 0; i < midi_out_size; i++)
211 auto& out_p = data.midi_out_ports[i];
212 auto atoms = &m_atom_outs[i].buf->atoms;
213 lilv_instance_connect_port(fInstance, out_p, atoms);
216 for(
auto& [index, port] : m_atom_timePosition_owned)
218 lilv_instance_connect_port(fInstance, index, &port->buf->atoms);
222 void all_notes_off() noexcept
override
227 [[nodiscard]] std::string label() const noexcept
override
229 return fmt::format(
"lv2 ({})", data.effect.plugin.get_name().as_string());
233 const std::size_t audio_in_size = data.audio_in_ports.size();
234 const std::size_t cv_size = data.cv_ports.size();
235 const std::size_t midi_in_size = data.midi_in_ports.size();
236 const std::size_t control_in_size = data.control_in_ports.size();
242 int first_midi_idx = (audio_in_size > 0 ? 1 : 0) + cv_size;
243 for(std::size_t i = 0; i < m_atom_ins.size(); i++)
245 ossia::midi_port& ossia_port
246 = this->m_inlets[i + first_midi_idx]->template cast<ossia::midi_port>();
247 auto& lv2_port = m_atom_ins[i];
251 for(
const Message& msg : this->m_message_for_atom_ins[i])
253 auto atom = (LV2_Atom*)msg.body.data();
254 auto atom_data = (
const uint8_t*)LV2_ATOM_BODY(atom);
255 it.write(0, 0, atom->type, atom->size, atom_data);
259 for(
const libremidi::message& msg : ossia_port.messages)
262 msg.timestamp, 0, data.host.midi_event_id, msg.bytes.size(),
267 if(this->m_atom_timePosition_midi.size() != 0)
269 const LV2_Atom* atom = (
const LV2_Atom*)timePositionBuffer.get();
272 it.write(0, 0, atom->type, atom->size, (
const uint8_t*)LV2_ATOM_BODY(atom));
277 for(
auto& [port, atoms] : m_atom_timePosition_owned)
279 auto& lv2_port = atoms;
282 const LV2_Atom* atom = (
const LV2_Atom*)timePositionBuffer.get();
285 it.write(0, 0, atom->type, atom->size, (
const uint8_t*)LV2_ATOM_BODY(atom));
290 auto control_start = (audio_in_size > 0 ? 1 : 0) + midi_in_size + cv_size;
291 for(std::size_t i = control_start; i < control_in_size; i++)
293 auto& in = m_inlets[i]->template cast<ossia::value_port>().get_data();
297 if(
auto f = in.back().value.template target<float>())
299 fInControls[i - control_start] = *f;
305 void updateTime(
const ossia::token_request& tk, ossia::exec_state_facade st)
308 auto& forge = host.forge;
309 uint8_t* buffer = timePositionBuffer.get();
310 lv2_atom_forge_set_buffer(&forge, buffer, 256);
311 LV2_Atom_Forge_Frame frame;
312 lv2_atom_forge_object(&forge, &frame, 0, host.time_Position_id);
314 lv2_atom_forge_key(&forge, host.time_frame_id);
315 lv2_atom_forge_long(&forge, this->m_processed_frames);
317 lv2_atom_forge_key(&forge, host.time_framesPerSecond_id);
318 lv2_atom_forge_long(&forge, st.sampleRate());
320 lv2_atom_forge_key(&forge, host.time_speed_id);
321 lv2_atom_forge_float(&forge, tk.speed);
323 lv2_atom_forge_key(&forge, host.time_bar_id);
324 lv2_atom_forge_long(&forge, tk.musical_start_last_bar / 4.);
326 lv2_atom_forge_key(&forge, host.time_beat_id);
327 lv2_atom_forge_double(&forge, tk.musical_start_position);
329 auto barBeat = float(tk.musical_start_position - tk.musical_start_last_bar);
330 lv2_atom_forge_key(&forge, host.time_barBeat_id);
331 lv2_atom_forge_float(&forge, barBeat);
333 lv2_atom_forge_key(&forge, host.time_beatUnit_id);
334 lv2_atom_forge_int(&forge, 4);
336 lv2_atom_forge_key(&forge, host.time_beatsPerBar_id);
337 lv2_atom_forge_float(
338 &forge, 4 *
double(tk.signature.upper) /
double(tk.signature.lower));
340 lv2_atom_forge_key(&forge, host.time_beatsPerMinute_id);
341 lv2_atom_forge_float(&forge, tk.tempo);
343 lv2_atom_forge_pop(&forge, &frame);
346 void postProcess(int64_t offset)
348 if(data.effect.worker && data.effect.worker->work_response)
350 std::vector<char> vec;
351 while(data.effect.worker_datas.try_dequeue(vec))
353 data.effect.worker->work_response(
354 data.effect.instance->lv2_handle, vec.size(), vec.data());
358 if(data.effect.worker && data.effect.worker->end_run)
360 data.effect.worker->end_run(data.effect.instance->lv2_handle);
363 const std::size_t audio_out_size = data.audio_out_ports.size();
364 const std::size_t midi_out_size = data.midi_out_ports.size();
365 const std::size_t control_out_size = data.control_out_ports.size();
369 int first_midi_idx = (audio_out_size > 0 ? 1 : 0);
370 for(std::size_t i = 0; i < m_atom_outs.size(); i++)
372 ossia::midi_port& ossia_port
373 = this->m_outlets[i + first_midi_idx]->template cast<ossia::midi_port>();
377 LV2_ATOM_SEQUENCE_FOREACH(&lv2_port.buf->atoms, ev)
379 if(ev->body.type == host.midi_event_id)
381 libremidi::message msg;
382 msg.timestamp = ev->time.frames;
383 msg.bytes.resize(ev->body.size);
385 auto bytes = (uint8_t*)LV2_ATOM_BODY(&ev->body);
386 for(std::size_t i = 0; i < ev->body.size; i++)
388 msg.bytes[i] = bytes[i];
390 ossia_port.messages.push_back(std::move(msg));
399 auto control_start = (audio_out_size > 0 ? 1 : 0) + midi_out_size;
400 for(std::size_t i = control_start; i < control_out_size; i++)
402 auto& out = m_outlets[i]->template cast<ossia::value_port>();
404 out.write_value(fOutControls[i - control_start], offset);
412 port.buf->reset(
true);
414 for(
auto& [port, atoms] : m_atom_timePosition_owned)
416 atoms->buf->reset(
true);
420 port.buf->reset(
false);
423 for(
auto& mqueue : m_message_for_atom_ins)
429 lilv_instance_deactivate(fInstance);
430 if(!m_atom_timePosition_owned.empty())
431 for(
auto [port, atoms] : m_atom_timePosition_owned)
435 void run(
const ossia::token_request& tk, ossia::exec_state_facade st) noexcept
override
437 if(tk.date > tk.prev_date)
439 data.host.current = &data.effect;
440 if(!data.time_Position_ports.empty())
445 const auto [tick_start, samples] = st.timings(tk);
446 const auto audio_ins = data.audio_in_ports.size();
447 const auto audio_outs = data.audio_out_ports.size();
448 ossia::small_vector<ossia::float_vector, 2> in_vec;
449 in_vec.resize(audio_ins);
450 ossia::small_vector<ossia::float_vector, 2> out_vec;
451 out_vec.resize(audio_outs);
456 const auto& audio_in = m_inlets[0]->template cast<ossia::audio_port>();
457 for(std::size_t i = 0; i < audio_ins; i++)
459 in_vec[i].resize(samples);
460 if(audio_in.channels() > i)
462 for(std::size_t j = 0;
463 j < std::min(std::size_t(samples), audio_in.channel(i).size()); j++)
465 in_vec[i][j] = (float)audio_in.channel(i)[j];
468 lilv_instance_connect_port(
469 fInstance, data.audio_in_ports[i], in_vec[i].data());
475 for(std::size_t i = 0; i < audio_outs; i++)
477 out_vec[i].resize(samples);
478 lilv_instance_connect_port(
479 fInstance, data.audio_out_ports[i], out_vec[i].data());
483 lilv_instance_run(fInstance, samples);
487 auto& audio_out =
static_cast<ossia::audio_outlet*
>(m_outlets[0])->data;
488 audio_out.set_channels(audio_outs);
489 for(std::size_t i = 0; i < audio_outs; i++)
491 audio_out.channel(i).clear();
492 audio_out.channel(i).reserve(samples);
493 for(int64_t j = 0; j < samples; j++)
495 audio_out.channel(i).push_back((
double)out_vec[i][j]);
500 postProcess(tk.physical_start(st.modelToSamples()));
Definition: lv2_atom_helpers.hpp:195
Definition: lv2_atom_helpers.hpp:99
Definition: Context.hpp:49
Definition: Context.hpp:207
Definition: score-plugin-lv2/LV2/Node.hpp:25
Definition: score-plugin-lv2/LV2/Node.hpp:14
Definition: score-plugin-lv2/LV2/EffectModel.cpp:659