26 std::shared_ptr<AEffectWrapper> fx{};
32 this->set_not_fp_safe();
41 ossia::value_port* port{};
45 int32_t opcode, int32_t index = 0, intptr_t value = 0,
void* ptr =
nullptr,
48 fx->dispatch(opcode, index, value, ptr, opt);
52 ossia::small_vector<vst_control, 16> controls;
63 ||
m_processThread.load(std::memory_order_relaxed) != std::this_thread::get_id())
66 const auto push = [&](
const libremidi::ump& u) {
70 for(
int i = 0; i < events->numEvents; i++)
72 const VstEvent* e = events->events[i];
75 const int64_t date = m_blockStart + std::max(0, e->deltaFrames);
76 if(e->type == kVstMidiType)
78 const auto* m =
reinterpret_cast<const VstMidiEvent*
>(e);
79 const auto* bytes =
reinterpret_cast<const unsigned char*
>(m->midiData);
80 m_toUmp.convert_packets(bytes, midiMessageSize(bytes[0]), date, push);
82 else if(e->type == kVstSysExType)
84 const auto* s =
reinterpret_cast<const VstMidiSysexEvent*
>(e);
85 if(s->sysexDump && s->dumpBytes > 0)
86 m_toUmp.convert_packets(
87 reinterpret_cast<const unsigned char*
>(s->sysexDump),
88 std::size_t(s->dumpBytes), date, push);
93 static std::size_t midiMessageSize(
unsigned char status)
noexcept
120 int64_t m_blockStart{};
121 libremidi::midi1_to_midi2 m_toUmp;
127 for(vst_control& p : controls)
129 const auto& vec = p.port->get_data();
132 auto t = ossia::convert<float>(last(vec));
134 p.value = ossia::clamp<float>(t, 0.f, 1.f);
135 fx->fx->setParameter(fx->fx, p.idx, p.value);
140 auto& prepareInput(int64_t offset, int64_t samples)
142 const auto bs = offset + samples;
143 auto& p = *m_inlets[0]->template target<ossia::audio_port>();
148 p.channel(0).resize(bs);
149 p.channel(1).resize(bs);
154 p.channel(0).resize(bs);
155 p.channel(1).assign(p.channel(0).begin(), p.channel(0).end());
168 auto& prepareOutput(int64_t offset, int64_t samples)
170 const auto bs = offset + samples;
171 auto& p = *m_outlets[0]->template target<ossia::audio_port>();
174 chan.resize(bs, boost::container::default_init);
181 void setupTimeInfo(
const ossia::token_request& tk, ossia::exec_state_facade st)
183 auto& time_info = fx->info;
186 time_info.samplePos = tk.speed != 0. ? tk.start_date_to_physical(st.modelToSamples())
187 : this->m_transport_frames;
189 time_info.sampleRate = st.sampleRate();
190 time_info.nanoSeconds = st.currentDate() - st.startDate();
191 time_info.ppqPos = tk.musical_start_position;
192 time_info.tempo = tk.tempo;
193 time_info.barStartPos = tk.musical_start_last_bar;
194 time_info.cycleStartPos = 0.;
195 time_info.cycleEndPos = 0.;
196 time_info.timeSigNumerator = tk.signature.upper;
197 time_info.timeSigDenominator = tk.signature.lower;
198 time_info.smpteOffset = 0;
199 time_info.smpteFrameRate = 0;
200 time_info.samplesToNextClock = 0;
201 time_info.flags = kVstTransportPlaying | kVstNanosValid | kVstPpqPosValid
202 | kVstTempoValid | kVstBarsValid | kVstTimeSigValid
211 static constexpr bool synth = IsSynth;
212 VstSpeakerArrangement i_arr{};
213 VstSpeakerArrangement o_arr{};
217 std::shared_ptr<AEffectWrapper> dat,
int sampleRate,
int bs,
bool midiOutput =
false)
222 m_inlets.push_back(
new ossia::audio_inlet);
223 if constexpr(IsSynth)
224 m_inlets.push_back(
new ossia::midi_inlet);
227 m_outlets.push_back(
new ossia::audio_outlet);
235 memset(&i_arr, 0,
sizeof(i_arr));
236 memset(&o_arr, 0,
sizeof(o_arr));
237 i_arr.type = kSpeakerArrStereo;
238 i_arr.numChannels = 2;
239 i_arr.speakers[0].type = kSpeakerL;
240 i_arr.speakers[1].type = kSpeakerR;
242 o_arr.type = kSpeakerArrStereo;
243 o_arr.numChannels = 2;
244 o_arr.speakers[0].type = kSpeakerL;
245 o_arr.speakers[1].type = kSpeakerR;
246 dispatch(effSetSpeakerArrangement, 0, (intptr_t)&i_arr, (
void*)&o_arr, 0);
249 dispatch(effSetSampleRate, 0, sampleRate,
nullptr, sampleRate);
250 dispatch(effSetBlockSize, 0, bs,
nullptr, bs);
252 effSetProcessPrecision, 0,
253 UseDouble ? kVstProcessPrecision64 : kVstProcessPrecision32);
254 dispatch(effMainsChanged, 0, 1);
255 dispatch(effStartProcess);
257 fx->fx->resvd2 =
reinterpret_cast<intptr_t
>(
static_cast<vst_node_base*
>(
this));
263 fx->info.flags = (fx->info.flags & ~kVstTransportPlaying) | kVstTransportChanged;
264 dispatch(effStopProcess);
265 dispatch(effMainsChanged, 0, 0);
268 std::string label()
const noexcept override
270 char paramName[512] = {0};
271 fx->fx->dispatcher(fx->fx, effGetProductString, 0, 0, paramName, 0.0f);
275 void all_notes_off()
noexcept override
277 if constexpr(IsSynth)
281 constexpr auto sz =
sizeof(VstEvents) +
sizeof(
void*) * 32 * 2;
282 VstEvents* events = (VstEvents*)alloca(sz);
283 std::memset(events, 0, sz);
285 events->numEvents = 32;
287 VstMidiEvent ev[64] = {};
288 memset(&ev, 0,
sizeof(ev));
291 for(
int i = 0; i < 16; i++)
294 e.type = kVstMidiType;
295 e.flags = kVstMidiEventIsRealtime;
296 e.byteSize =
sizeof(VstMidiEvent);
298 e.midiData[0] = (char)(uint8_t)176 + i;
299 e.midiData[1] = (char)(uint8_t)123;
303 events->events[i] =
reinterpret_cast<VstEvent*
>(&e);
307 for(
int i = 0; i < 16; i++)
309 auto& e = ev[16 + i];
310 e.type = kVstMidiType;
311 e.flags = kVstMidiEventIsRealtime;
312 e.byteSize =
sizeof(VstMidiEvent);
314 e.midiData[0] = (char)(uint8_t)176 + i;
315 e.midiData[1] = (char)(uint8_t)121;
319 events->events[16 + i] =
reinterpret_cast<VstEvent*
>(&e);
322 dispatch(effProcessEvents, 0, 0, events, 0.f);
324 if constexpr(!UseDouble)
326 float* dummy = (
float*)alloca(
sizeof(
float) * m_bs);
327 std::fill_n(dummy, m_bs, 0.f);
330 = (
float**)alloca(
sizeof(
float*) * std::max(2, this->fx->fx->numInputs));
331 for(
int i = 0; i < this->fx->fx->numInputs; i++)
335 = (
float**)alloca(
sizeof(
float*) * std::max(2, this->fx->fx->numOutputs));
336 for(
int i = 0; i < this->fx->fx->numOutputs; i++)
339 fx->fx->processReplacing(fx->fx, input, output, m_bs);
343 double* dummy = (
double*)alloca(
sizeof(
double) * m_bs);
344 std::fill_n(dummy, m_bs, 0.);
347 = (
double**)alloca(
sizeof(
double*) * std::max(2, this->fx->fx->numInputs));
348 for(
int i = 0; i < this->fx->fx->numInputs; i++)
352 = (
double**)alloca(
sizeof(
double*) * std::max(2, this->fx->fx->numOutputs));
353 for(
int i = 0; i < this->fx->fx->numOutputs; i++)
356 fx->fx->processDoubleReplacing(fx->fx, input, output, m_bs);
369 for(
const libremidi::ump& mess :
370 static_cast<ossia::midi_inlet*
>(m_inlets[1])->data.messages)
382 template <
typename Fun>
383 void dispatchMidi(int64_t offset, int64_t samples,
bool first,
bool last, Fun&& f)
385 auto& ip =
static_cast<ossia::midi_inlet*
>(m_inlets[1])->data.messages;
386 const auto in_block = [=](
const libremidi::ump& mess) {
387 return (first || mess.timestamp >= offset)
388 && (last || mess.timestamp < offset + samples);
391 = (first ?
m_deferred.size() : 0) + ossia::count_if(ip, in_block);
399 const auto sz =
sizeof(VstEvents) +
sizeof(
void*) * n_mess * 2;
400 VstEvents* events = (VstEvents*)alloca(sz);
401 std::memset(events, 0, sz);
403 ossia::small_vector<std::pair<const libremidi::ump*, int64_t>, 16> to_send;
406 to_send.push_back({&mess, 0});
407 for(
const libremidi::ump& mess : ip)
410 {&mess, std::clamp<int64_t>(
411 mess.timestamp - offset, 0, samples > 0 ? samples - 1 : 0)});
415 ossia::small_vector<VstMidiEvent, 16> vec;
417 m_sysexArena.clear();
418 std::size_t sysex_count = 0;
420 for(
const auto& [mess_ptr, delta] : to_send)
422 VstMidiSysexEvent* sysex
423 = sysex_count < m_sysexEvents.size() ? &m_sysexEvents[sysex_count] :
nullptr;
424 VstEvent* e = toVstEvent(
425 *mess_ptr, int32_t(delta), vec[i], sysex, m_sysexArena,
m_sysexStream);
428 if(e ==
reinterpret_cast<VstEvent*
>(sysex))
430 events->events[i++] = e;
432 events->numEvents = int32_t(i);
435 dispatch(effProcessEvents, 0, 0, events, 0.f);
439 void run(
const ossia::token_request& tk, ossia::exec_state_facade st)
noexcept override
441 if(!muted() && !tk.paused())
443 const auto timings = st.timings(tk);
444 if(timings.length <= 0)
448 if constexpr(IsSynth)
454 this->setupTimeInfo(tk, st);
456 if constexpr(UseDouble)
458 if constexpr(IsSynth)
460 const int64_t end = timings.start_sample + timings.length;
461 forEachBlock(timings.start_sample, timings.length, [&](int64_t o, int64_t n) {
462 dispatchMidi(o, n, o == timings.start_sample, o + n == end, [&] {
463 processDoubleBlock(o, n);
469 processDouble(timings.start_sample, timings.length);
474 if constexpr(IsSynth)
476 const int64_t end = timings.start_sample + timings.length;
477 forEachBlock(timings.start_sample, timings.length, [&](int64_t o, int64_t n) {
478 dispatchMidi(o, n, o == timings.start_sample, o + n == end, [&] {
479 processFloatBlock(o, n);
485 processFloat(timings.start_sample, timings.length);
490 if(this->fx->fx->numOutputs == 1)
492 auto& op = m_outlets[0]->template target<ossia::audio_port>()->get();
493 op[1].assign(op[0].begin(), op[0].end());
501 template <
typename F>
504 auto& time_info = fx->info;
505 const double ppq_per_frame = time_info.sampleRate > 0 && time_info.tempo > 0
506 ? time_info.tempo / (60. * time_info.sampleRate)
508 const int64_t block = std::max(m_bs, 1);
509 m_processThread.store(std::this_thread::get_id(), std::memory_order_relaxed);
510 for(int64_t done = 0; done < samples;)
512 const int64_t n = std::min(block, samples - done);
513 m_blockStart = offset + done;
520 const double step = m_backwards ? -double(n) : double(n);
521 time_info.samplePos += step;
522 if(ppq_per_frame > 0.)
524 time_info.ppqPos += step * ppq_per_frame;
526 const double bar = time_info.timeSigDenominator > 0
527 ? 4. * time_info.timeSigNumerator
528 / time_info.timeSigDenominator
531 time_info.barStartPos
532 += std::floor((time_info.ppqPos - time_info.barStartPos) / bar) * bar;
536 m_processThread.store(std::thread::id{}, std::memory_order_relaxed);
539 void processFloat(int64_t offset, int64_t samples)
541 forEachBlock(offset, samples, [
this](int64_t o, int64_t n) { processFloatBlock(o, n); });
544 void processDouble(int64_t offset, int64_t samples)
546 forEachBlock(offset, samples, [
this](int64_t o, int64_t n) { processDoubleBlock(o, n); });
549 void processFloatBlock(int64_t offset, int64_t samples)
551 if constexpr(!UseDouble)
556 const auto max_i = std::max(2, this->fx->fx->numInputs);
557 const auto max_o = std::max(2, this->fx->fx->numOutputs);
558 const auto max_io = std::max(max_i, max_o);
560 const auto max_samples = std::max(samples, (int64_t)m_bs);
565 auto& ip = prepareInput(offset, samples);
566 SCORE_ASSERT(ip.size() >= 2);
568 auto& op = prepareOutput(offset, samples);
569 SCORE_ASSERT(op.size() >= 2);
572 float_v[0].resize(max_samples);
573 float_v[1].resize(max_samples);
575 std::copy_n(ip[0].data() + offset, samples, float_v[0].data());
576 std::copy_n(ip[1].data() + offset, samples, float_v[1].data());
578 float** io = (
float**)alloca(
sizeof(
float*) * max_io);
579 io[0] = float_v[0].data();
580 io[1] = float_v[1].data();
586 float* dummy = (
float*)alloca(
sizeof(
float) * max_samples);
587 std::fill_n(dummy, max_samples, 0.f);
589 for(
int i = 2; i < max_io; i++)
593 fx->fx->processReplacing(fx->fx, io, io, samples);
595 std::copy_n(float_v[0].data(), samples, op[0].data() + offset);
596 std::copy_n(float_v[1].data(), samples, op[1].data() + offset);
603 void processDoubleBlock(int64_t offset, int64_t samples)
605 if constexpr(UseDouble)
610 const auto max_i = std::max(2, this->fx->fx->numInputs);
611 const auto max_o = std::max(2, this->fx->fx->numOutputs);
612 const auto max_io = std::max(max_i, max_o);
615 auto& ip = prepareInput(offset, samples);
616 SCORE_ASSERT(ip.size() >= 2);
618 auto& op = prepareOutput(offset, samples);
619 SCORE_ASSERT(op.size() >= 2);
621 double** input = (
double**)alloca(
sizeof(
double*) * max_i);
622 input[0] = ip[0].data() + offset;
623 input[1] = ip[1].data() + offset;
625 double** output = (
double**)alloca(
sizeof(
double*) * max_o);
626 output[0] = op[0].data() + offset;
627 output[1] = op[1].data() + offset;
633 double* dummy = (
double*)alloca(
sizeof(
double) * m_bs);
634 std::fill_n(dummy, m_bs, 0.);
635 for(
int i = 2; i < this->fx->fx->numInputs; i++)
637 for(
int i = 2; i < this->fx->fx->numOutputs; i++)
641 fx->fx->processDoubleReplacing(fx->fx, input, output, samples);
648 std::conditional_t<!UseDouble, std::array<ossia::float_vector, 2>,
dummy_t> float_v;
658 std::array<VstMidiSysexEvent, 128> m_sysexEvents{};