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