score-plugin-vst/Vst/Node.hpp
1 #pragma once
2 #include <Process/Dataflow/TimeSignature.hpp>
3 
4 #include <Vst/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/pod_vector.hpp>
10 #include <ossia/editor/scenario/time_signature.hpp>
11 namespace vst
12 {
13 
14 class vst_node_base : public ossia::graph_node
15 {
16 public:
17  std::shared_ptr<AEffectWrapper> fx{};
18 
19 protected:
20  explicit vst_node_base(std::shared_ptr<AEffectWrapper>&& ptr)
21  : fx{std::move(ptr)}
22  {
23  m_inlets.reserve(10);
24  controls.reserve(10);
25  }
26 
27  struct vst_control
28  {
29  int idx{};
30  float value{};
31  ossia::value_port* port{};
32  };
33 
34  inline void dispatch(
35  int32_t opcode, int32_t index = 0, intptr_t value = 0, void* ptr = nullptr,
36  float opt = 0.0f)
37  {
38  fx->dispatch(opcode, index, value, ptr, opt);
39  }
40 
41 public:
42  ossia::small_vector<vst_control, 16> controls;
43 
44  void setControls()
45  {
46  for(vst_control& p : controls)
47  {
48  const auto& vec = p.port->get_data();
49  if(vec.empty())
50  continue;
51  auto t = ossia::convert<float>(last(vec));
52  {
53  p.value = ossia::clamp<float>(t, 0.f, 1.f);
54  fx->fx->setParameter(fx->fx, p.idx, p.value);
55  }
56  }
57  }
58 
59  auto& prepareInput(int64_t offset, int64_t samples)
60  {
61  const auto bs = offset + samples;
62  auto& p = *m_inlets[0]->template target<ossia::audio_port>();
63  switch(p.channels())
64  {
65  case 0: {
66  p.set_channels(2);
67  p.channel(0).resize(bs);
68  p.channel(1).resize(bs);
69  break;
70  }
71  case 1: {
72  p.set_channels(2);
73  p.channel(0).resize(bs);
74  p.channel(1).assign(p.channel(0).begin(), p.channel(0).end());
75  break;
76  }
77  default: {
78  for(auto& i : p)
79  i.resize(bs);
80  break;
81  }
82  }
83 
84  return p.get();
85  }
86 
87  auto& prepareOutput(int64_t offset, int64_t samples)
88  {
89  const auto bs = offset + samples;
90  auto& p = *m_outlets[0]->template target<ossia::audio_port>();
91  p.set_channels(2);
92  for(auto& chan : p)
93  chan.resize(bs, boost::container::default_init);
94  return p.get();
95  }
96 
97  void setupTimeInfo(const ossia::token_request& tk, ossia::exec_state_facade st)
98  {
99  static const constexpr double ppq_reference = 960.;
100 
101  auto& time_info = fx->info;
102  // TODO this isn't accurate when tempo becomes slower !
103  // We need to track the actual number of audio buffers played through an interval
104  time_info.samplePos = tk.start_date_to_physical(st.modelToSamples());
105  time_info.sampleRate = st.sampleRate();
106  time_info.nanoSeconds = st.currentDate() - st.startDate();
107  time_info.ppqPos = tk.musical_start_position; // * ppq_reference;
108  time_info.tempo = tk.tempo;
109  time_info.barStartPos = tk.musical_start_last_bar; // * ppq_reference;
110  time_info.cycleStartPos = 0.;
111  time_info.cycleEndPos = 0.;
112  time_info.timeSigNumerator = tk.signature.upper;
113  time_info.timeSigDenominator = tk.signature.lower;
114  time_info.smpteOffset = 0;
115  time_info.smpteFrameRate = 0;
116  time_info.samplesToNextClock = 0;
117  time_info.flags = kVstTransportPlaying | kVstNanosValid | kVstPpqPosValid
118  | kVstTempoValid | kVstBarsValid | kVstTimeSigValid
119  | kVstClockValid;
120  }
121 };
122 
123 template <bool UseDouble, bool IsSynth>
124 class vst_node final : public vst_node_base
125 {
126 public:
127  static constexpr bool synth = IsSynth;
128  VstSpeakerArrangement i_arr{};
129  VstSpeakerArrangement o_arr{};
130  int m_bs{};
131 
132  vst_node(std::shared_ptr<AEffectWrapper> dat, int sampleRate, int bs)
133  : vst_node_base{std::move(dat)}
134  , m_bs{bs}
135  {
136  // Midi or audio input
137  m_inlets.push_back(new ossia::audio_inlet);
138  if constexpr(IsSynth)
139  m_inlets.push_back(new ossia::midi_inlet);
140 
141  // audio output
142  m_outlets.push_back(new ossia::audio_outlet);
143 
144  {
145  memset(&i_arr, 0, sizeof(i_arr));
146  memset(&o_arr, 0, sizeof(o_arr));
147  i_arr.type = kSpeakerArrStereo;
148  i_arr.numChannels = 2;
149  i_arr.speakers[0].type = kSpeakerL;
150  i_arr.speakers[1].type = kSpeakerR;
151 
152  o_arr.type = kSpeakerArrStereo;
153  o_arr.numChannels = 2;
154  o_arr.speakers[0].type = kSpeakerL;
155  o_arr.speakers[1].type = kSpeakerR;
156  dispatch(effSetSpeakerArrangement, 0, (intptr_t)&i_arr, (void*)&o_arr, 0);
157  }
158 
159  dispatch(effSetSampleRate, 0, sampleRate, nullptr, sampleRate);
160  dispatch(effSetBlockSize, 0, bs, nullptr, bs); // Generalize what's in pd
161  dispatch(
162  effSetProcessPrecision, 0,
163  UseDouble ? kVstProcessPrecision64 : kVstProcessPrecision32);
164  dispatch(effMainsChanged, 0, 1);
165  dispatch(effStartProcess);
166 
167  fx->fx->resvd2 = reinterpret_cast<intptr_t>(this);
168  }
169 
170  ~vst_node()
171  {
172  fx->fx->resvd2 = 0;
173  dispatch(effStopProcess);
174  dispatch(effMainsChanged, 0, 0);
175  }
176 
177  std::string label() const noexcept override { return ""; }
178 
179  void all_notes_off() noexcept override
180  {
181  if constexpr(IsSynth)
182  {
183  // copy midi data
184  // should be 32 but some VSTs read a bit out of bounds apparently ! so we allocate a bit more memory
185  constexpr auto sz = sizeof(VstEvents) + sizeof(void*) * 32 * 2;
186  VstEvents* events = (VstEvents*)alloca(sz);
187  std::memset(events, 0, sz);
188 
189  events->numEvents = 32;
190 
191  VstMidiEvent ev[64] = {};
192  memset(&ev, 0, sizeof(ev));
193 
194  // All notes off
195  for(int i = 0; i < 16; i++)
196  {
197  auto& e = ev[i];
198  e.type = kVstMidiType;
199  e.flags = kVstMidiEventIsRealtime;
200  e.byteSize = sizeof(VstMidiEvent);
201 
202  e.midiData[0] = (char)(uint8_t)176 + i;
203  e.midiData[1] = (char)(uint8_t)123;
204  e.midiData[2] = 0;
205  e.midiData[3] = 0;
206 
207  events->events[i] = reinterpret_cast<VstEvent*>(&e);
208  }
209 
210  // All sound off
211  for(int i = 0; i < 16; i++)
212  {
213  auto& e = ev[16 + i];
214  e.type = kVstMidiType;
215  e.flags = kVstMidiEventIsRealtime;
216  e.byteSize = sizeof(VstMidiEvent);
217 
218  e.midiData[0] = (char)(uint8_t)176 + i;
219  e.midiData[1] = (char)(uint8_t)121;
220  e.midiData[2] = 0;
221  e.midiData[3] = 0;
222 
223  events->events[16 + i] = reinterpret_cast<VstEvent*>(&e);
224  }
225 
226  dispatch(effProcessEvents, 0, 0, events, 0.f);
227 
228  if constexpr(!UseDouble)
229  {
230  float* dummy = (float*)alloca(sizeof(float) * m_bs);
231  std::fill_n(dummy, m_bs, 0.f);
232 
233  float** input
234  = (float**)alloca(sizeof(float*) * std::max(2, this->fx->fx->numInputs));
235  for(int i = 0; i < this->fx->fx->numInputs; i++)
236  input[i] = dummy;
237 
238  float** output
239  = (float**)alloca(sizeof(float*) * std::max(2, this->fx->fx->numOutputs));
240  for(int i = 0; i < this->fx->fx->numOutputs; i++)
241  output[i] = dummy;
242 
243  fx->fx->processReplacing(fx->fx, input, output, m_bs);
244  }
245  else
246  {
247  double* dummy = (double*)alloca(sizeof(double) * m_bs);
248  std::fill_n(dummy, m_bs, 0.);
249 
250  double** input
251  = (double**)alloca(sizeof(double*) * std::max(2, this->fx->fx->numInputs));
252  for(int i = 0; i < this->fx->fx->numInputs; i++)
253  input[i] = dummy;
254 
255  double** output
256  = (double**)alloca(sizeof(double*) * std::max(2, this->fx->fx->numOutputs));
257  for(int i = 0; i < this->fx->fx->numOutputs; i++)
258  output[i] = dummy;
259 
260  fx->fx->processDoubleReplacing(fx->fx, input, output, m_bs);
261  }
262  }
263  }
264 
265  // Note: the function that does the actual tick is passed as an argument
266  // since some plug-ins only store pointers to VstEvents struct,
267  // which would go out of scope if this function was just called like this.
268  template <typename Fun>
269  void dispatchMidi(int64_t offset, Fun&& f)
270  {
271  // copy midi data
272  auto& ip = static_cast<ossia::midi_inlet*>(m_inlets[1])->data.messages;
273  const auto n_mess = ip.size();
274  if(n_mess == 0)
275  {
276  f();
277  return;
278  }
279 
280  // -2 since two are already available ?
281  const auto sz = sizeof(VstEvents) + sizeof(void*) * n_mess * 2;
282  VstEvents* events = (VstEvents*)alloca(sz);
283  std::memset(events, 0, sz);
284  events->numEvents = n_mess;
285 
286  ossia::small_vector<VstMidiEvent, 16> vec;
287  vec.resize(n_mess);
288  std::size_t i = 0;
289  for(libremidi::message& mess : ip)
290  {
291  VstMidiEvent& e = vec[i];
292  std::memset(&e, 0, sizeof(VstMidiEvent));
293 
294  e.type = kVstMidiType;
295  e.byteSize = sizeof(VstMidiEvent);
296  e.deltaFrames = mess.timestamp - offset;
297  e.flags = kVstMidiEventIsRealtime;
298 
299  std::memcpy(
300  e.midiData, mess.bytes.data(), std::min(mess.bytes.size(), (std::size_t)4));
301  // for (std::size_t k = 0, N = std::min(mess.bytes.size(),
302  // (std::size_t)4); k < N; k++)
303  // e.midiData[k] = mess.bytes[k];
304 
305  events->events[i] = reinterpret_cast<VstEvent*>(&e);
306  i++;
307  }
308  dispatch(effProcessEvents, 0, 0, events, 0.f);
309  f();
310  }
311 
312  void run(const ossia::token_request& tk, ossia::exec_state_facade st) noexcept override
313  {
314  if(!muted() && tk.date > tk.prev_date)
315  {
316  const auto timings = st.timings(tk);
317  this->setControls();
318  this->setupTimeInfo(tk, st);
319 
320  if constexpr(UseDouble)
321  {
322  if constexpr(IsSynth)
323  {
324  dispatchMidi(timings.start_sample, [this, timings] {
325  processDouble(timings.start_sample, timings.length);
326  });
327  }
328  else
329  {
330  processDouble(timings.start_sample, timings.length);
331  }
332  }
333  else
334  {
335  if constexpr(IsSynth)
336  {
337  dispatchMidi(timings.start_sample, [this, timings] {
338  processFloat(timings.start_sample, timings.length);
339  });
340  }
341  else
342  {
343  processFloat(timings.start_sample, timings.length);
344  }
345  }
346 
347  // upmix mono VSTs to stereo
348  if(this->fx->fx->numOutputs == 1)
349  {
350  auto& op = m_outlets[0]->template target<ossia::audio_port>()->get();
351  op[1].assign(op[0].begin(), op[0].end());
352  }
353  }
354  }
355 
356  void processFloat(int64_t offset, int64_t samples)
357  {
358  if constexpr(!UseDouble)
359  {
360  if(samples <= 0)
361  return;
362 
363  SCORE_ASSERT(m_bs >= offset + samples);
364 
365  const auto max_i = std::max(2, this->fx->fx->numInputs);
366  const auto max_o = std::max(2, this->fx->fx->numOutputs);
367  const auto max_io = std::max(max_i, max_o);
368 
369  const auto max_samples = std::max(samples, (int64_t)m_bs); // * 16;
370 
371  //qDebug() << samples << m_bs << max_samples << offset << " ::: " << max_i << max_o << max_io;
372 
373  // prepare ossia::graph_node buffers
374  auto& ip = prepareInput(offset, samples);
375  SCORE_ASSERT(ip.size() >= 2);
376 
377  auto& op = prepareOutput(offset, samples);
378  SCORE_ASSERT(op.size() >= 2);
379 
380  // copy io
381  float_v[0].resize(max_samples);
382  float_v[1].resize(max_samples);
383 
384  std::copy_n(ip[0].data() + offset, samples, float_v[0].data());
385  std::copy_n(ip[1].data() + offset, samples, float_v[1].data());
386 
387  float** io = (float**)alloca(sizeof(float*) * max_io);
388  io[0] = float_v[0].data();
389  io[1] = float_v[1].data();
390 
391  if(max_io > 2)
392  {
393  // Note that alloca has *function* scope, not block scope so it is
394  // freed at the end
395  float* dummy = (float*)alloca(sizeof(float) * max_samples);
396  std::fill_n(dummy, max_samples, 0.f);
397 
398  for(int i = 2; i < max_io; i++)
399  io[i] = dummy;
400  }
401 
402  fx->fx->processReplacing(fx->fx, io, io, samples);
403 
404  std::copy_n(float_v[0].data(), samples, op[0].data() + offset);
405  std::copy_n(float_v[1].data(), samples, op[1].data() + offset);
406 
407  float_v[0].clear();
408  float_v[1].clear();
409  }
410  }
411 
412  void processDouble(int64_t offset, int64_t samples)
413  {
414  if constexpr(UseDouble)
415  {
416  if(samples <= 0)
417  return;
418 
419  SCORE_ASSERT(m_bs >= offset + samples);
420 
421  const auto max_i = std::max(2, this->fx->fx->numInputs);
422  const auto max_o = std::max(2, this->fx->fx->numOutputs);
423  const auto max_io = std::max(max_i, max_o);
424 
425  // copy audio data
426  auto& ip = prepareInput(offset, samples);
427  SCORE_ASSERT(ip.size() >= 2);
428 
429  auto& op = prepareOutput(offset, samples);
430  SCORE_ASSERT(op.size() >= 2);
431 
432  double** input = (double**)alloca(sizeof(double*) * max_i);
433  input[0] = ip[0].data() + offset;
434  input[1] = ip[1].data() + offset;
435 
436  double** output = (double**)alloca(sizeof(double*) * max_o);
437  output[0] = op[0].data() + offset;
438  output[1] = op[1].data() + offset;
439 
440  if(max_io > 2)
441  {
442  // Note that alloca has *function* scope, not block scope so it is
443  // freed at the end
444  double* dummy = (double*)alloca(sizeof(double) * m_bs);
445  std::fill_n(dummy, m_bs, 0.);
446  for(int i = 2; i < this->fx->fx->numInputs; i++)
447  input[i] = dummy;
448  for(int i = 2; i < this->fx->fx->numOutputs; i++)
449  output[i] = dummy;
450  }
451 
452  fx->fx->processDoubleReplacing(fx->fx, input, output, samples);
453  }
454  }
455 
456  struct dummy_t
457  {
458  };
459  std::conditional_t<!UseDouble, std::array<ossia::float_vector, 2>, dummy_t> float_v;
460 };
461 
462 template <bool b1, bool b2, typename... Args>
463 auto make_vst_fx(Args&... args)
464 {
465  return ossia::make_node<vst_node<b1, b2>>(args...);
466 }
467 }
Definition: score-plugin-vst/Vst/Node.hpp:15
Definition: score-plugin-vst/Vst/Node.hpp:125
Definition: score-plugin-vst/Vst/Node.hpp:457
Definition: score-plugin-vst/Vst/Node.hpp:28