Loading...
Searching...
No Matches
score-plugin-vst3/Vst3/Node.hpp
1#pragma once
2#include <Process/Dataflow/TimeSignature.hpp>
3
4#include <Vst3/EffectModel.hpp>
5
6#include <ossia/dataflow/fx_node.hpp>
7#include <ossia/dataflow/graph_node.hpp>
8#include <ossia/dataflow/port.hpp>
9#include <ossia/detail/logger.hpp>
10#include <ossia/detail/math.hpp>
11#include <ossia/detail/pod_vector.hpp>
12#include <ossia/detail/ssize.hpp>
13#include <ossia/editor/scenario/time_signature.hpp>
14
15#include <libremidi/ump_events.hpp>
16#include <pluginterfaces/vst/ivstmidicontrollers.h>
17
18#include <public.sdk/source/vst/hosting/eventlist.h>
19#include <public.sdk/source/vst/hosting/parameterchanges.h>
20namespace vst3
21{
22
23class param_queue final : public Steinberg::Vst::IParamValueQueue
24{
25public:
26 explicit param_queue(Steinberg::Vst::ParamID id)
27 : id{id}
28 {
29 }
30 ~param_queue() { }
31
32 Steinberg::Vst::ParamID id{};
33 ossia::small_vector<std::pair<int32_t, Steinberg::Vst::ParamValue>, 1> data;
34 Steinberg::Vst::ParamValue lastValue{};
35
36 Steinberg::tresult queryInterface(const Steinberg::TUID _iid, void** obj) override
37 {
38 return Steinberg::kResultOk;
39 }
40 Steinberg::uint32 addRef() override { return 1; }
41 Steinberg::uint32 release() override { return 1; }
42
43 Steinberg::Vst::ParamID getParameterId() override { return id; }
44 Steinberg::int32 getPointCount() override { return data.size(); }
45 Steinberg::tresult getPoint(
46 Steinberg::int32 index, Steinberg::int32& sampleOffset,
47 Steinberg::Vst::ParamValue& value) override
48 {
49 if(ossia::valid_index(index, data))
50 std::tie(sampleOffset, value) = data[index];
51 else if(index == -1)
52 {
53 sampleOffset = 0;
54 value = lastValue;
55 }
56
57 return Steinberg::kResultOk;
58 }
59
60 Steinberg::tresult addPoint(
61 Steinberg::int32 sampleOffset, Steinberg::Vst::ParamValue value,
62 Steinberg::int32& index) override
63 {
64 index = data.size();
65 data.emplace_back(sampleOffset, value);
66 return Steinberg::kResultOk;
67 }
68};
69
70class param_changes final : public Steinberg::Vst::IParameterChanges
71{
72public:
73 std::vector<param_queue> queues;
74 Steinberg::tresult queryInterface(const Steinberg::TUID _iid, void** obj) override
75 {
76 return Steinberg::kResultOk;
77 }
78 Steinberg::uint32 addRef() override { return 1; }
79 Steinberg::uint32 release() override { return 1; }
80
81 Steinberg::int32 getParameterCount() override { return queues.size(); }
82
83 param_queue* getParameterData(Steinberg::int32 index) override
84 {
85 return &queues[index];
86 }
87
88 param_queue* addParameterData(
89 const Steinberg::Vst::ParamID& id, Steinberg::int32& index /*out*/) override
90 {
91 index = queues.size();
92 queues.emplace_back(id);
93 return &queues.back();
94 }
95};
96
97class vst_node_base : public ossia::graph_node
98{
99public:
101 {
102 explicit PluginHandle(const Plugin& p)
103 : component{p.component}
104 , processor{p.processor}
105 , midi_controls{p.midiControls}
106 {
107 component->addRef();
108 processor->addRef();
109 }
110
112 {
113 // qDebug() << processor->release();
114 // qDebug() << component->release();
115 }
116
117 Steinberg::Vst::IComponent* component{};
118 Steinberg::Vst::IAudioProcessor* processor{};
119 MIDIControls midi_controls;
120 };
121
122 PluginHandle fx;
123 // Each element is the amount of channels in a given in/out port
124 ossia::small_pod_vector<int, 2> m_audioInputChannels{};
125 ossia::small_pod_vector<int, 2> m_audioOutputChannels{};
126 int m_totalAudioIns{};
127 int m_totalAudioOuts{};
128 int m_totalEventIns{};
129 int m_totalEventOuts{};
130
131protected:
132 explicit vst_node_base(const Plugin& ptr)
133 : fx{std::move(ptr)}
134 {
135 this->set_not_fp_safe();
136 m_inlets.reserve(10);
137 controls.reserve(10);
138
139 struct vis
140 {
141 vst_node_base& self;
142 void audioIn(const Steinberg::Vst::BusInfo& bus, int idx)
143 {
144 self.m_inlets.push_back(new ossia::audio_inlet);
145 self.m_audioInputChannels.push_back(bus.channelCount);
146 self.m_totalAudioIns += bus.channelCount;
147 }
148 void eventIn(const Steinberg::Vst::BusInfo& bus, int idx)
149 {
150 self.m_inlets.push_back(new ossia::midi_inlet);
151 self.m_totalEventIns++;
152 }
153 void audioOut(const Steinberg::Vst::BusInfo& bus, int idx)
154 {
155 self.m_outlets.push_back(new ossia::audio_outlet);
156 self.m_audioOutputChannels.push_back(bus.channelCount);
157 self.m_totalAudioOuts += bus.channelCount;
158 }
159 void eventOut(const Steinberg::Vst::BusInfo& bus, int idx)
160 {
161 self.m_outlets.push_back(new ossia::midi_outlet);
162 self.m_totalEventOuts++;
163 }
164 };
165
166 forEachBus(vis{*this}, *fx.component);
167
168 if(auto err = fx.processor->setProcessing(true);
169 err != Steinberg::kResultOk && err != Steinberg::kNotImplemented)
170 {
171 ossia::logger().warn("Couldn't set VST3 processing: {}", err);
172 }
173
174 m_vstData.processMode = Steinberg::Vst::ProcessModes::kRealtime;
175 m_vstData.numInputs = m_audioInputChannels.size();
176 m_vstData.numOutputs = m_audioOutputChannels.size();
177 m_vstInput.resize(m_audioInputChannels.size());
178 m_vstOutput.resize(m_audioOutputChannels.size());
179 for(std::size_t i = 0; i < m_audioInputChannels.size(); i++)
180 {
181 m_vstInput[i].numChannels = m_audioInputChannels[i];
182 }
183 for(std::size_t i = 0; i < m_audioOutputChannels.size(); i++)
184 {
185 m_vstOutput[i].numChannels = m_audioOutputChannels[i];
186 }
187
188 m_vstData.inputs = m_vstInput.data();
189 m_vstData.outputs = m_vstOutput.data();
190 m_vstData.inputParameterChanges = &m_inputChanges;
191 m_vstData.outputParameterChanges = &m_outputChanges;
192 m_vstData.inputEvents = &m_inputEvents;
193 m_vstData.outputEvents = &m_outputEvents;
194 m_vstData.processContext = &m_context;
195
196 m_inputEvents.setMaxSize(17 * 128 * m_totalEventIns);
197 m_outputEvents.setMaxSize(128 * m_totalEventOuts);
198 }
199
200 ~vst_node_base()
201 {
202 if(auto err = fx.processor->setProcessing(false);
203 err != Steinberg::kResultOk && err != Steinberg::kNotImplemented)
204 {
205 ossia::logger().warn("Couldn't set VST3 processing: {}", err);
206 }
207 }
208
210 {
211 Steinberg::Vst::ParamID idx{};
212 std::size_t queue_idx{};
213 ossia::value_port* port{};
214 };
215
216 ossia::hash_map<Steinberg::Vst::ParamID, std::size_t> queue_map;
217
218public:
219 ossia::small_vector<vst_control, 16> controls;
220
221 std::size_t add_control(ossia::value_inlet* inlet, Steinberg::Vst::ParamID id, float v)
222 {
223 (**inlet).domain = ossia::domain_base<float>{0.f, 1.f};
224 (**inlet).type = ossia::val_type::FLOAT;
225
226 // FIXME this allocates a lot :[
227 auto queue_idx = this->m_inputChanges.queues.size();
228 this->m_inputChanges.queues.emplace_back(id);
229 this->m_inputChanges.queues.back().lastValue = v;
230
231 queue_map[id] = queue_idx;
232 controls.push_back({id, queue_idx, inlet->target<ossia::value_port>()});
233 root_inputs().push_back(std::move(inlet));
234 return queue_idx;
235 }
236
237 // Used when a control is changed from the ui.
238 void set_control(std::size_t queue_idx, float value)
239 {
240 auto& queue = this->m_inputChanges.queues[queue_idx];
241 queue.lastValue = value;
242 queue.data.clear();
243 queue.data.emplace_back(0, value);
244 }
245
246 void setControls()
247 {
248 for(vst_control& p : controls)
249 {
250 const auto& vec = p.port->get_data();
251 if(vec.empty())
252 continue;
253 if(auto t = last(vec).target<float>())
254 {
255 double value = ossia::clamp<double>((double)*t, 0., 1.);
256 auto& queue = m_inputChanges.queues[p.queue_idx];
257 queue.data.clear();
258 queue.data.emplace_back(0, value);
259 queue.lastValue = value;
260 }
261 }
262 }
263
264 void dispatchMidi()
265 {
266 m_inputEvents.clear();
267 m_outputEvents.clear();
268
269 int k = 0;
270 int audioBusCount = std::ssize(m_audioInputChannels);
271 for(int i = audioBusCount; i < audioBusCount + m_totalEventIns; i++)
272 {
273 dispatchMidi(*m_inlets[i]->template target<ossia::midi_port>(), k++);
274 }
275 m_vstData.inputEvents
276 = (m_inputEvents.getEventCount() > 0) ? &m_inputEvents : nullptr;
277 m_vstData.outputEvents = &m_outputEvents;
278 }
279
280 void dispatchMidi(ossia::midi_port& port, int index)
281 {
282 // copy midi data
283 auto& ip = port.messages;
284 if(ip.empty())
285 return;
286
287 using VstEvent = Steinberg::Vst::Event;
288 VstEvent e;
289 e.busIndex = index;
290 e.sampleOffset = 0;
291 e.ppqPosition = 0; // FIXME
292 for(const libremidi::ump& mess : ip)
293 {
294 if(mess.get_type() != libremidi::midi2::message_type::MIDI_2_CHANNEL)
295 continue;
296 e.sampleOffset = mess.timestamp;
297 switch(libremidi::message_type(mess.get_status_code()))
298 {
299 case libremidi::message_type::NOTE_ON: {
300 auto [channel, note, value] = libremidi::as_01::note_off(mess);
301
302 if(value > 0)
303 {
304 e.type = VstEvent::kNoteOnEvent;
305 e.noteOn.channel = channel; // FIXME 0 or 1-based?
306 e.noteOn.pitch = note;
307 e.noteOn.velocity = value;
308 e.noteOn.noteId = -1;
309 e.noteOn.tuning = 0.f;
310 m_inputEvents.addEvent(e);
311 }
312 else
313 {
314 e.type = VstEvent::kNoteOffEvent;
315 e.noteOff.channel = mess.get_channel();
316 e.noteOff.pitch = note;
317 e.noteOff.velocity = 0;
318 e.noteOff.noteId = -1;
319 e.noteOff.tuning = 0.f;
320 m_inputEvents.addEvent(e);
321 }
322 break;
323 }
324 case libremidi::message_type::NOTE_OFF: {
325 auto [channel, note, value] = libremidi::as_01::note_off(mess);
326 e.type = VstEvent::kNoteOffEvent;
327 e.noteOff.channel = channel;
328 e.noteOff.pitch = note;
329 e.noteOff.velocity = value;
330 e.noteOff.noteId = -1;
331 e.noteOff.tuning = 0.f;
332 m_inputEvents.addEvent(e);
333 break;
334 }
335 case libremidi::message_type::POLY_PRESSURE: {
336 auto [channel, note, value] = libremidi::as_01::poly_pressure(mess);
337 e.type = VstEvent::kPolyPressureEvent;
338 e.polyPressure.channel = channel;
339 e.polyPressure.pitch = note;
340 e.polyPressure.pressure = value;
341 e.polyPressure.noteId = -1;
342 m_inputEvents.addEvent(e);
343 break;
344 }
345
346 case libremidi::message_type::PITCH_BEND: {
347 if(auto it = this->fx.midi_controls.find({index, Steinberg::Vst::kPitchBend});
348 it != this->fx.midi_controls.end())
349 {
350 auto [channel, value] = libremidi::as_01::pitch_bend(mess);
351 Steinberg::Vst::ParamID pid = it->second;
352 if(auto queue_it = this->queue_map.find(pid);
353 queue_it != this->queue_map.end())
354 {
355 auto& queue = this->m_inputChanges.queues[queue_it->second];
356 queue.data.push_back({e.sampleOffset, value});
357 queue.lastValue = value;
358 }
359 }
360 }
361
362 case libremidi::message_type::AFTERTOUCH: {
363 if(auto it = this->fx.midi_controls.find({index, Steinberg::Vst::kAfterTouch});
364 it != this->fx.midi_controls.end())
365 {
366 auto [channel, value] = libremidi::as_01::aftertouch(mess);
367 Steinberg::Vst::ParamID pid = it->second;
368 if(auto queue_it = this->queue_map.find(pid);
369 queue_it != this->queue_map.end())
370 {
371 auto& queue = this->m_inputChanges.queues[queue_it->second];
372 queue.data.push_back({e.sampleOffset, value});
373 queue.lastValue = value;
374 }
375 }
376 }
377 default:
378 break;
379 }
380 }
381 }
382
383 void readbackMidi()
384 {
385 using VstEvent = Steinberg::Vst::Event;
386
387 const int audioBusCount = std::ssize(m_audioOutputChannels);
388 const int N = m_outputEvents.getEventCount();
389
390 for(int i = 0; i < N; i++)
391 {
392 auto event_p = m_outputEvents.getEventByIndex(i);
393 if(!event_p)
394 continue;
395 VstEvent& e = *event_p;
396
397 int bus = e.busIndex;
398 auto& port = *m_outlets[bus + audioBusCount]->template target<ossia::midi_port>();
399
400 libremidi::ump mess;
401
402 switch(e.type)
403 {
404 case VstEvent::kNoteOnEvent: {
405 if(e.noteOn.velocity > 0.f)
406 mess = libremidi::from_01::note_on(
407 e.noteOn.channel, e.noteOn.pitch, e.noteOn.velocity);
408 else
409 mess = libremidi::from_01::note_off(e.noteOn.channel, e.noteOn.pitch, 0.);
410 break;
411 }
412 case VstEvent::kNoteOffEvent: {
413 mess = libremidi::from_01::note_off(
414 e.noteOff.channel, e.noteOff.pitch, e.noteOff.velocity);
415 break;
416 }
417 case VstEvent::kPolyPressureEvent: {
418 mess = libremidi::from_01::poly_pressure(
419 e.noteOff.channel, e.polyPressure.pitch, e.polyPressure.pressure);
420 break;
421 }
422 default:
423 break;
424 }
425
426 mess.timestamp = e.sampleOffset;
427 port.messages.push_back(std::move(mess));
428 }
429 }
430
431 auto& preparePort(ossia::audio_port& port, int numChannels, std::size_t samples)
432 {
433 port.set_channels(numChannels);
434
435 for(auto& i : port)
436 i.resize(samples);
437 return port.get();
438 }
439
440 void setupTimeInfo(const ossia::token_request& tk, ossia::exec_state_facade st)
441 {
442 using namespace Steinberg::Vst;
443 using F = ProcessContext;
444 Steinberg::Vst::ProcessContext& time_info = this->m_context;
445 time_info.sampleRate = st.sampleRate();
446
447 time_info.projectTimeSamples = this->m_processed_frames;
448
449 time_info.systemTime = st.currentDate() - st.startDate();
450 time_info.continousTimeSamples = this->m_processed_frames; // TODO
451
452 time_info.projectTimeMusic = tk.musical_start_position;
453 time_info.barPositionMusic = tk.musical_start_last_bar;
454 time_info.cycleStartMusic = 0.;
455 time_info.cycleEndMusic = 0.;
456
457 time_info.tempo = tk.tempo;
458 time_info.timeSigNumerator = tk.signature.upper;
459 time_info.timeSigDenominator = tk.signature.lower;
460
461 // time_info.chord = ....;
462
463 time_info.smpteOffsetSubframes = 0;
464 time_info.frameRate = {};
465 time_info.samplesToNextClock = 0;
466 time_info.state = F::kPlaying | F::kSystemTimeValid | F::kContTimeValid
467 | F::kProjectTimeMusicValid | F::kBarPositionValid | F::kTempoValid
468 | F::kTimeSigValid;
469 }
470
471 Steinberg::Vst::ProcessData m_vstData;
472 ossia::small_vector<Steinberg::Vst::AudioBusBuffers, 1> m_vstInput;
473 ossia::small_vector<Steinberg::Vst::AudioBusBuffers, 1> m_vstOutput;
474
475 Steinberg::Vst::ProcessContext m_context;
476 param_changes m_inputChanges;
477 param_changes m_outputChanges;
478 Steinberg::Vst::EventList m_inputEvents;
479 Steinberg::Vst::EventList m_outputEvents;
480};
481
482template <bool UseDouble>
483class vst_node final : public vst_node_base
484{
485public:
486 vst_node(Plugin dat, int sampleRate)
487 : vst_node_base{std::move(dat)}
488 {
489 if constexpr(UseDouble)
490 m_vstData.symbolicSampleSize = Steinberg::Vst::kSample64;
491 else
492 m_vstData.symbolicSampleSize = Steinberg::Vst::kSample32;
493 }
494
495 ~vst_node() { }
496
497 std::string label() const noexcept override { return "VST3"; }
498
499 void all_notes_off(int bus) noexcept
500 {
501 bool ok = false;
502 if(auto it = this->fx.midi_controls.find({bus, Steinberg::Vst::kCtrlAllNotesOff});
503 it != this->fx.midi_controls.end())
504 {
505 Steinberg::Vst::ParamID pid = it->second;
506 if(auto queue_it = this->queue_map.find(pid); queue_it != this->queue_map.end())
507 {
508 auto& queue = this->m_inputChanges.queues[queue_it->second];
509 queue.data.push_back({0, 1.});
510 queue.lastValue = 1.;
511 ok = true;
512 }
513 }
514
515 if(auto it = this->fx.midi_controls.find({bus, Steinberg::Vst::kCtrlAllSoundsOff});
516 it != this->fx.midi_controls.end())
517 {
518 Steinberg::Vst::ParamID pid = it->second;
519 if(auto queue_it = this->queue_map.find(pid); queue_it != this->queue_map.end())
520 {
521 auto& queue = this->m_inputChanges.queues[queue_it->second];
522 queue.data.push_back({0, 1.});
523 queue.lastValue = 1.;
524 ok = true;
525 }
526 }
527
528 if(!ok)
529 {
530 // Send manual note off events
531 for(int k = 0; k <= 16; k++)
532 for(int i = 0; i <= 127; i++)
533 {
534 using VstEvent = Steinberg::Vst::Event;
535 VstEvent e;
536 e.busIndex = bus;
537 e.sampleOffset = 0;
538 e.ppqPosition = 0; // FIXME
539 e.sampleOffset = 0;
540 e.type = VstEvent::kNoteOffEvent;
541 e.noteOff.channel = k;
542 e.noteOff.pitch = i;
543 e.noteOff.velocity = 0;
544 e.noteOff.noteId = -1;
545 e.noteOff.tuning = 0.f;
546 m_inputEvents.addEvent(e);
547 }
548 }
549 }
550
551 void all_notes_off() noexcept override
552 {
553 if(m_totalEventIns == 0)
554 return;
555
556 m_inputEvents.clear();
557
558 // Put messages into each MIDI in's event queues
559 {
560 for(int i = 0; i < m_totalEventIns; i++)
561 {
562 all_notes_off(i++);
563 }
564 }
565
566 // Run a process cycle
567 {
568 constexpr int samples = 64;
569 Steinberg::Vst::ProcessData dat;
570 memcpy(&dat, &m_vstData, sizeof(m_vstData));
571 dat.inputEvents = &m_inputEvents;
572 dat.numSamples = samples;
573
574 {
575 double** input{};
576 double** output{};
577
578 // Copy inputs
579 if(m_totalAudioIns > 0)
580 {
581 input = (double**)alloca(sizeof(double*) * m_totalAudioIns);
582
583 for(int k = 0; k < m_totalAudioIns; k++)
584 {
585 input[k] = (double*)alloca(sizeof(double) * samples);
586 memset(input[k], 0, sizeof(double) * samples);
587 }
588
589 for(std::size_t i = 0; i < m_audioInputChannels.size(); i++)
590 {
591 Steinberg::Vst::AudioBusBuffers& vst_in = dat.inputs[i];
592 vst_in.channelBuffers64 = input;
593 vst_in.silenceFlags = ~0ULL;
594 }
595 }
596
597 // Prepare outputs
598 if(m_totalAudioOuts > 0)
599 {
600 output = (double**)alloca(sizeof(double*) * m_totalAudioOuts);
601 for(int k = 0; k < m_totalAudioOuts; k++)
602 {
603 output[k] = (double*)alloca(sizeof(double) * samples);
604 memset(output[k], 0, sizeof(double) * samples);
605 }
606
607 for(std::size_t i = 0; i < m_audioOutputChannels.size(); i++)
608 {
609 Steinberg::Vst::AudioBusBuffers& vst_out = dat.outputs[i];
610 vst_out.channelBuffers64 = output;
611 vst_out.silenceFlags = ~0ULL;
612 }
613 }
614
615 fx.processor->process(dat);
616 }
617 }
618 }
619
620 void run(const ossia::token_request& tk, ossia::exec_state_facade st) noexcept override
621 {
622 if(!muted() && tk.date > tk.prev_date)
623 {
624 const auto [tick_start, samples] = st.timings(tk);
625 this->setControls();
626 this->setupTimeInfo(tk, st);
627
628 this->dispatchMidi();
629
630 if constexpr(UseDouble)
631 {
632 processDouble(samples);
633 }
634 else
635 {
636 processFloat(samples);
637 }
638
639 this->readbackMidi();
640 }
641 }
642
643 void processFloat(std::size_t samples)
644 {
645 // In the float case we have temporary buffers for conversion
646 if constexpr(!UseDouble)
647 {
648 // Prepare buffers
649 if(m_totalAudioIns > 0 || m_totalAudioOuts > 0)
650 {
651 float_v.resize(std::max(m_totalAudioIns, m_totalAudioOuts));
652 for(auto& v : float_v)
653 v.resize(samples);
654
655 float** input{};
656 float** output{};
657
658 // Copy inputs
659 if(m_totalAudioIns > 0)
660 {
661 input = (float**)alloca(sizeof(float*) * m_totalAudioIns);
662 int channel_k = 0;
663 int float_k = 0;
664
665 for(std::size_t i = 0; i < m_audioInputChannels.size(); i++)
666 {
667 const int numChannels = m_audioInputChannels[i];
668 auto& port = *m_inlets[i]->template target<ossia::audio_port>();
669 auto& ip = preparePort(port, numChannels, samples);
670
671 Steinberg::Vst::AudioBusBuffers& vst_in = m_vstInput[i];
672 vst_in.channelBuffers32 = input + channel_k;
673 vst_in.silenceFlags = 0;
674
675 for(int k = 0; k < numChannels; k++)
676 {
677 std::copy_n(
678 ip[k].data(), std::min(samples, ip[k].size()),
679 float_v[float_k].data());
680 input[channel_k] = float_v[float_k].data();
681 channel_k++;
682 float_k++;
683 }
684 }
685 }
686
687 // Prepare outputs
688 if(m_totalAudioOuts > 0)
689 {
690 // copy audio data
691 output = (float**)alloca(sizeof(float*) * m_totalAudioOuts);
692
693 int channel_k = 0;
694 int float_k = 0;
695 for(std::size_t i = 0; i < m_audioOutputChannels.size(); i++)
696 {
697 const int numChannels = m_audioOutputChannels[i];
698 auto& port = *m_outlets[i]->template target<ossia::audio_port>();
699 preparePort(port, numChannels, samples);
700
701 Steinberg::Vst::AudioBusBuffers& vst_out = m_vstOutput[i];
702 vst_out.channelBuffers32 = output + channel_k;
703 vst_out.silenceFlags = 0;
704 for(int k = 0; k < numChannels; k++)
705 {
706 output[channel_k] = float_v[float_k].data();
707 channel_k++;
708 float_k++;
709 }
710 }
711 }
712 }
713
714 // Run the process
715 {
716 m_vstData.numSamples = samples;
717
718 fx.processor->process(m_vstData);
719 }
720
721 // Copy the float outputs to the audio outlet buffer
722 if(m_totalAudioOuts > 0)
723 {
724 int float_k = 0;
725 for(std::size_t i = 0; i < m_audioOutputChannels.size(); i++)
726 {
727 const int numChannels = m_audioOutputChannels[i];
728 ossia::audio_port& port = *m_outlets[i]->template target<ossia::audio_port>();
729 for(int k = 0; k < numChannels; k++)
730 {
731 auto& audio_out = port.channel(k);
732 std::copy_n(float_v[float_k].data(), samples, audio_out.data());
733 }
734 }
735 }
736 }
737 }
738
739 void processDouble(std::size_t samples)
740 {
741 // In the double case we use directly the buffers that are part of the
742 // input & output ports
743 if constexpr(UseDouble)
744 {
745 double** input{};
746 double** output{};
747
748 // Copy inputs
749 if(m_totalAudioIns > 0)
750 {
751 input = (double**)alloca(sizeof(double*) * m_totalAudioIns);
752
753 int channel_k = 0;
754 for(std::size_t i = 0; i < m_audioInputChannels.size(); i++)
755 {
756 const int numChannels = m_audioInputChannels[i];
757 auto& port = *m_inlets[i]->template target<ossia::audio_port>();
758 auto& ip = preparePort(port, numChannels, samples);
759
760 Steinberg::Vst::AudioBusBuffers& vst_in = m_vstInput[i];
761 vst_in.channelBuffers64 = input + channel_k;
762 vst_in.silenceFlags = 0;
763 for(int k = 0; k < numChannels; k++)
764 {
765 input[channel_k++] = ip[k].data();
766 }
767 }
768 }
769
770 // Prepare outputs
771 if(m_totalAudioOuts > 0)
772 {
773 output = (double**)alloca(sizeof(double*) * m_totalAudioOuts);
774 int channel_k = 0;
775 for(std::size_t i = 0; i < m_audioOutputChannels.size(); i++)
776 {
777 const int numChannels = m_audioOutputChannels[i];
778 auto& port = *m_outlets[i]->template target<ossia::audio_port>();
779 auto& op = preparePort(port, numChannels, samples);
780
781 Steinberg::Vst::AudioBusBuffers& vst_out = m_vstOutput[i];
782 vst_out.channelBuffers64 = output + channel_k;
783 vst_out.silenceFlags = 0;
784 for(int k = 0; k < numChannels; k++)
785 {
786 output[channel_k++] = op[k].data();
787 }
788 }
789 }
790
791 // Run process
792 {
793 m_vstData.numSamples = samples;
794
795 fx.processor->process(m_vstData);
796 }
797 }
798 }
799
800 struct dummy_t
801 {
802 };
803 std::conditional_t<!UseDouble, std::vector<ossia::float_vector>, dummy_t> float_v;
804};
805
806template <bool b1, typename... Args>
807auto make_vst_fx(Args&... args)
808{
809 return ossia::make_node<vst_node<b1>>(args...);
810}
811}
Definition score-plugin-vst3/Vst3/Node.hpp:71
Definition score-plugin-vst3/Vst3/Node.hpp:24
Definition score-plugin-vst3/Vst3/Node.hpp:98
Definition score-plugin-vst3/Vst3/Node.hpp:484
STL namespace.
Definition Plugin.hpp:43
Definition score-plugin-vst3/Vst3/Node.hpp:801
Definition score-plugin-vst3/Vst3/Node.hpp:101
Definition score-plugin-vst3/Vst3/Node.hpp:210