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>
19 namespace vst3
20 {
21 
22 class param_queue final : public Steinberg::Vst::IParamValueQueue
23 {
24 public:
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 
69 class param_changes final : public Steinberg::Vst::IParameterChanges
70 {
71 public:
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 
96 class vst_node_base : public ossia::graph_node
97 {
98 public:
99  struct PluginHandle
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 
110  ~PluginHandle()
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 
130 protected:
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 
207  struct vst_control
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 
216 public:
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  // FIXME see note in vst_node_base::setupTimeInfo
441  time_info.projectTimeSamples = tk.start_date_to_physical(st.modelToSamples());
442 
443  time_info.systemTime = st.currentDate() - st.startDate();
444  time_info.continousTimeSamples = time_info.projectTimeSamples; // TODO
445 
446  time_info.projectTimeMusic = tk.musical_start_position;
447  time_info.barPositionMusic = tk.musical_start_last_bar;
448  time_info.cycleStartMusic = 0.;
449  time_info.cycleEndMusic = 0.;
450 
451  time_info.tempo = tk.tempo;
452  time_info.timeSigNumerator = tk.signature.upper;
453  time_info.timeSigDenominator = tk.signature.lower;
454 
455  // time_info.chord = ....;
456 
457  time_info.smpteOffsetSubframes = 0;
458  time_info.frameRate = {};
459  time_info.samplesToNextClock = 0;
460  time_info.state = F::kPlaying | F::kSystemTimeValid | F::kContTimeValid
461  | F::kProjectTimeMusicValid | F::kBarPositionValid | F::kTempoValid
462  | F::kTimeSigValid;
463  }
464 
465  Steinberg::Vst::ProcessData m_vstData;
466  ossia::small_vector<Steinberg::Vst::AudioBusBuffers, 1> m_vstInput;
467  ossia::small_vector<Steinberg::Vst::AudioBusBuffers, 1> m_vstOutput;
468 
469  Steinberg::Vst::ProcessContext m_context;
470  param_changes m_inputChanges;
471  param_changes m_outputChanges;
472  Steinberg::Vst::EventList m_inputEvents;
473  Steinberg::Vst::EventList m_outputEvents;
474 };
475 
476 template <bool UseDouble>
477 class vst_node final : public vst_node_base
478 {
479 public:
480  vst_node(Plugin dat, int sampleRate)
481  : vst_node_base{std::move(dat)}
482  {
483  if constexpr(UseDouble)
484  m_vstData.symbolicSampleSize = Steinberg::Vst::kSample64;
485  else
486  m_vstData.symbolicSampleSize = Steinberg::Vst::kSample32;
487  }
488 
489  ~vst_node() { }
490 
491  std::string label() const noexcept override { return "VST3"; }
492 
493  void all_notes_off(int bus) noexcept
494  {
495  bool ok = false;
496  if(auto it = this->fx.midi_controls.find({bus, Steinberg::Vst::kCtrlAllNotesOff});
497  it != this->fx.midi_controls.end())
498  {
499  Steinberg::Vst::ParamID pid = it->second;
500  if(auto queue_it = this->queue_map.find(pid); queue_it != this->queue_map.end())
501  {
502  auto& queue = this->m_inputChanges.queues[queue_it->second];
503  queue.data.push_back({0, 1.});
504  queue.lastValue = 1.;
505  ok = true;
506  }
507  }
508 
509  if(auto it = this->fx.midi_controls.find({bus, Steinberg::Vst::kCtrlAllSoundsOff});
510  it != this->fx.midi_controls.end())
511  {
512  Steinberg::Vst::ParamID pid = it->second;
513  if(auto queue_it = this->queue_map.find(pid); queue_it != this->queue_map.end())
514  {
515  auto& queue = this->m_inputChanges.queues[queue_it->second];
516  queue.data.push_back({0, 1.});
517  queue.lastValue = 1.;
518  ok = true;
519  }
520  }
521 
522  if(!ok)
523  {
524  // Send manual note off events
525  for(int k = 0; k <= 16; k++)
526  for(int i = 0; i <= 127; i++)
527  {
528  using VstEvent = Steinberg::Vst::Event;
529  VstEvent e;
530  e.busIndex = bus;
531  e.sampleOffset = 0;
532  e.ppqPosition = 0; // FIXME
533  e.sampleOffset = 0;
534  e.type = VstEvent::kNoteOffEvent;
535  e.noteOff.channel = k;
536  e.noteOff.pitch = i;
537  e.noteOff.velocity = 0;
538  e.noteOff.noteId = -1;
539  e.noteOff.tuning = 0.f;
540  m_inputEvents.addEvent(e);
541  }
542  }
543  }
544 
545  void all_notes_off() noexcept override
546  {
547  if(m_totalEventIns == 0)
548  return;
549 
550  m_inputEvents.clear();
551 
552  // Put messages into each MIDI in's event queues
553  {
554  for(int i = 0; i < m_totalEventIns; i++)
555  {
556  all_notes_off(i++);
557  }
558  }
559 
560  // Run a process cycle
561  {
562  constexpr int samples = 64;
563  Steinberg::Vst::ProcessData dat;
564  memcpy(&dat, &m_vstData, sizeof(m_vstData));
565  dat.inputEvents = &m_inputEvents;
566  dat.numSamples = samples;
567 
568  {
569  double** input{};
570  double** output{};
571 
572  // Copy inputs
573  if(m_totalAudioIns > 0)
574  {
575  input = (double**)alloca(sizeof(double*) * m_totalAudioIns);
576 
577  for(int k = 0; k < m_totalAudioIns; k++)
578  {
579  input[k] = (double*)alloca(sizeof(double) * samples);
580  memset(input[k], 0, sizeof(double) * samples);
581  }
582 
583  for(std::size_t i = 0; i < m_audioInputChannels.size(); i++)
584  {
585  Steinberg::Vst::AudioBusBuffers& vst_in = dat.inputs[i];
586  vst_in.channelBuffers64 = input;
587  vst_in.silenceFlags = ~0ULL;
588  }
589  }
590 
591  // Prepare outputs
592  if(m_totalAudioOuts > 0)
593  {
594  output = (double**)alloca(sizeof(double*) * m_totalAudioOuts);
595  for(int k = 0; k < m_totalAudioOuts; k++)
596  {
597  output[k] = (double*)alloca(sizeof(double) * samples);
598  memset(output[k], 0, sizeof(double) * samples);
599  }
600 
601  for(std::size_t i = 0; i < m_audioOutputChannels.size(); i++)
602  {
603  Steinberg::Vst::AudioBusBuffers& vst_out = dat.outputs[i];
604  vst_out.channelBuffers64 = output;
605  vst_out.silenceFlags = ~0ULL;
606  }
607  }
608 
609  fx.processor->process(dat);
610  }
611  }
612  }
613 
614  void run(const ossia::token_request& tk, ossia::exec_state_facade st) noexcept override
615  {
616  if(!muted() && tk.date > tk.prev_date)
617  {
618  const auto [tick_start, samples] = st.timings(tk);
619  this->setControls();
620  this->setupTimeInfo(tk, st);
621 
622  this->dispatchMidi();
623 
624  if constexpr(UseDouble)
625  {
626  processDouble(samples);
627  }
628  else
629  {
630  processFloat(samples);
631  }
632 
633  this->readbackMidi();
634  }
635  }
636 
637  void processFloat(std::size_t samples)
638  {
639  // In the float case we have temporary buffers for conversion
640  if constexpr(!UseDouble)
641  {
642  // Prepare buffers
643  if(m_totalAudioIns > 0 || m_totalAudioOuts > 0)
644  {
645  float_v.resize(std::max(m_totalAudioIns, m_totalAudioOuts));
646  for(auto& v : float_v)
647  v.resize(samples);
648 
649  float** input{};
650  float** output{};
651 
652  // Copy inputs
653  if(m_totalAudioIns > 0)
654  {
655  input = (float**)alloca(sizeof(float*) * m_totalAudioIns);
656  int channel_k = 0;
657  int float_k = 0;
658 
659  for(std::size_t i = 0; i < m_audioInputChannels.size(); i++)
660  {
661  const int numChannels = m_audioInputChannels[i];
662  auto& port = *m_inlets[i]->template target<ossia::audio_port>();
663  auto& ip = preparePort(port, numChannels, samples);
664 
665  Steinberg::Vst::AudioBusBuffers& vst_in = m_vstInput[i];
666  vst_in.channelBuffers32 = input + channel_k;
667  vst_in.silenceFlags = 0;
668 
669  for(int k = 0; k < numChannels; k++)
670  {
671  std::copy_n(
672  ip[k].data(), std::min(samples, ip[k].size()),
673  float_v[float_k].data());
674  input[channel_k] = float_v[float_k].data();
675  channel_k++;
676  float_k++;
677  }
678  }
679  }
680 
681  // Prepare outputs
682  if(m_totalAudioOuts > 0)
683  {
684  // copy audio data
685  output = (float**)alloca(sizeof(float*) * m_totalAudioOuts);
686 
687  int channel_k = 0;
688  int float_k = 0;
689  for(std::size_t i = 0; i < m_audioOutputChannels.size(); i++)
690  {
691  const int numChannels = m_audioOutputChannels[i];
692  auto& port = *m_outlets[i]->template target<ossia::audio_port>();
693  preparePort(port, numChannels, samples);
694 
695  Steinberg::Vst::AudioBusBuffers& vst_out = m_vstOutput[i];
696  vst_out.channelBuffers32 = output + channel_k;
697  vst_out.silenceFlags = 0;
698  for(int k = 0; k < numChannels; k++)
699  {
700  output[channel_k] = float_v[float_k].data();
701  channel_k++;
702  float_k++;
703  }
704  }
705  }
706  }
707 
708  // Run the process
709  {
710  m_vstData.numSamples = samples;
711 
712  fx.processor->process(m_vstData);
713  }
714 
715  // Copy the float outputs to the audio outlet buffer
716  if(m_totalAudioOuts > 0)
717  {
718  int float_k = 0;
719  for(std::size_t i = 0; i < m_audioOutputChannels.size(); i++)
720  {
721  const int numChannels = m_audioOutputChannels[i];
722  ossia::audio_port& port = *m_outlets[i]->template target<ossia::audio_port>();
723  for(int k = 0; k < numChannels; k++)
724  {
725  auto& audio_out = port.channel(k);
726  std::copy_n(float_v[float_k].data(), samples, audio_out.data());
727  }
728  }
729  }
730  }
731  }
732 
733  void processDouble(std::size_t samples)
734  {
735  // In the double case we use directly the buffers that are part of the
736  // input & output ports
737  if constexpr(UseDouble)
738  {
739  double** input{};
740  double** output{};
741 
742  // Copy inputs
743  if(m_totalAudioIns > 0)
744  {
745  input = (double**)alloca(sizeof(double*) * m_totalAudioIns);
746 
747  int channel_k = 0;
748  for(std::size_t i = 0; i < m_audioInputChannels.size(); i++)
749  {
750  const int numChannels = m_audioInputChannels[i];
751  auto& port = *m_inlets[i]->template target<ossia::audio_port>();
752  auto& ip = preparePort(port, numChannels, samples);
753 
754  Steinberg::Vst::AudioBusBuffers& vst_in = m_vstInput[i];
755  vst_in.channelBuffers64 = input + channel_k;
756  vst_in.silenceFlags = 0;
757  for(int k = 0; k < numChannels; k++)
758  {
759  input[channel_k++] = ip[k].data();
760  }
761  }
762  }
763 
764  // Prepare outputs
765  if(m_totalAudioOuts > 0)
766  {
767  output = (double**)alloca(sizeof(double*) * m_totalAudioOuts);
768  int channel_k = 0;
769  for(std::size_t i = 0; i < m_audioOutputChannels.size(); i++)
770  {
771  const int numChannels = m_audioOutputChannels[i];
772  auto& port = *m_outlets[i]->template target<ossia::audio_port>();
773  auto& op = preparePort(port, numChannels, samples);
774 
775  Steinberg::Vst::AudioBusBuffers& vst_out = m_vstOutput[i];
776  vst_out.channelBuffers64 = output + channel_k;
777  vst_out.silenceFlags = 0;
778  for(int k = 0; k < numChannels; k++)
779  {
780  output[channel_k++] = op[k].data();
781  }
782  }
783  }
784 
785  // Run process
786  {
787  m_vstData.numSamples = samples;
788 
789  fx.processor->process(m_vstData);
790  }
791  }
792  }
793 
794  struct dummy_t
795  {
796  };
797  std::conditional_t<!UseDouble, std::vector<ossia::float_vector>, dummy_t> float_v;
798 };
799 
800 template <bool b1, typename... Args>
801 auto make_vst_fx(Args&... args)
802 {
803  return ossia::make_node<vst_node<b1>>(args...);
804 }
805 }
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:478
Definition: Plugin.hpp:37
Definition: score-plugin-vst3/Vst3/Node.hpp:795
Definition: score-plugin-vst3/Vst3/Node.hpp:100
Definition: score-plugin-vst3/Vst3/Node.hpp:208