Merge pull request #803 from jpcima/sorted-events
Ordered event processing for VST
This commit is contained in:
commit
4ecfac1dce
6 changed files with 408 additions and 148 deletions
|
|
@ -38,6 +38,8 @@ add_library(sfizz-vst3-core STATIC
|
|||
SfizzVstUpdates.h
|
||||
SfizzVstUpdates.cpp
|
||||
SfizzVstIDs.h
|
||||
OrderedEventProcessor.h
|
||||
OrderedEventProcessor.cpp
|
||||
IdleUpdateHandler.h
|
||||
X11RunLoop.h
|
||||
X11RunLoop.cpp)
|
||||
|
|
|
|||
233
plugins/vst/OrderedEventProcessor.cpp
Normal file
233
plugins/vst/OrderedEventProcessor.cpp
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "OrderedEventProcessor.h"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
||||
void OrderedEventProcessor::initializeEventProcessor(const Vst::ProcessSetup& setup, int32 paramCount, int32 subdivSize)
|
||||
{
|
||||
paramCount_ = paramCount;
|
||||
subdivSize_ = subdivSize;
|
||||
queues_.reset(new Cell<QueueStatus>[paramCount]);
|
||||
pointsBySample_.reset(new ParameterAndValue[paramCount * subdivSize]);
|
||||
numPointsBySample_.reset(new uint32[subdivSize]);
|
||||
}
|
||||
|
||||
void OrderedEventProcessor::processUnorderedEvents(int32 numSamples, Vst::IParameterChanges* pcs, Vst::IEventList* evs)
|
||||
{
|
||||
int32 sampleIndex = 0;
|
||||
int32 subdivNumber = 0;
|
||||
const int32 subdivSize = subdivSize_;
|
||||
|
||||
startProcessing(evs, pcs);
|
||||
|
||||
while (sampleIndex < numSamples || subdivNumber == 0) {
|
||||
int32 subdivCurrentSize = std::min(numSamples - sampleIndex, subdivSize);
|
||||
processSubdiv(evs, sampleIndex, sampleIndex + subdivCurrentSize - 1);
|
||||
sampleIndex += subdivCurrentSize;
|
||||
++subdivNumber;
|
||||
}
|
||||
|
||||
playRemainder(std::max(int32(0), numSamples - 1), evs);
|
||||
}
|
||||
|
||||
void OrderedEventProcessor::startProcessing(Vst::IEventList* evs, Vst::IParameterChanges* pcs)
|
||||
{
|
||||
// collect the parameter queues which have values on them
|
||||
// push them onto a work list
|
||||
Cell<QueueStatus>* head = nullptr;
|
||||
Cell<QueueStatus>* queues = queues_.get();
|
||||
|
||||
if (pcs) {
|
||||
const int32 count = pcs->getParameterCount();
|
||||
for (int32 index = count; index-- > 0; ) {
|
||||
Vst::IParamValueQueue* vq = pcs->getParameterData(index);
|
||||
if (vq) {
|
||||
// Note: expectation that hosts does not send more than one
|
||||
// queue for one parameter
|
||||
Vst::ParamID id = vq->getParameterId();
|
||||
Cell<QueueStatus>* cell = &queues[id];
|
||||
cell->next = head;
|
||||
cell->value.queue = vq;
|
||||
cell->value.pointIndex = 0;
|
||||
cell->value.pointCount = vq->getPointCount();
|
||||
head = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
queueList_ = head;
|
||||
|
||||
// position ourselves to the start of the event list
|
||||
eventIndex_ = 0;
|
||||
eventCount_ = evs ? evs->getEventCount() : 0;
|
||||
haveCurrentEvent_ = eventCount_ > 0 &&
|
||||
evs->getEvent(eventIndex_, currentEvent_) == kResultTrue;
|
||||
}
|
||||
|
||||
void OrderedEventProcessor::processSubdiv(Vst::IEventList* evs, int32 firstOffset, int32 lastOffset)
|
||||
{
|
||||
sortSubdiv(firstOffset, lastOffset);
|
||||
playSubdiv(evs, firstOffset, lastOffset);
|
||||
}
|
||||
|
||||
void OrderedEventProcessor::sortSubdiv(int32 firstOffset, int32 lastOffset)
|
||||
{
|
||||
// this collects parameter changes from the subdivision that goes from
|
||||
// first offset to last offset, included.
|
||||
|
||||
// these parameter changes are on a array of lists.
|
||||
// this 2D structure is backed by a contiguous array dimensioned for
|
||||
// the worst case.
|
||||
|
||||
// Example:
|
||||
// pointsBySample (column-major →) | numPointsBySample[sample]
|
||||
// ====================================================================
|
||||
// sample 0 [P1] [P2] [ ] [ ] | 2
|
||||
// sample 1 [P3] [ ] [ ] [ ] | 1
|
||||
// sample 2 [ ] [ ] [ ] [ ] | 0
|
||||
// sample 3 [P1] [P2] [P3] [P4] | 4
|
||||
|
||||
Cell<QueueStatus>* head = queueList_;
|
||||
Cell<QueueStatus>* queues = queues_.get();
|
||||
const int32 paramCount = paramCount_;
|
||||
ParameterAndValue* pointsBySample = pointsBySample_.get();
|
||||
uint32* numPointsBySample = numPointsBySample_.get();
|
||||
|
||||
std::fill_n(numPointsBySample, lastOffset - firstOffset + 1, 0);
|
||||
|
||||
Cell<QueueStatus>* cur = head;
|
||||
Cell<QueueStatus>* prev = nullptr;
|
||||
|
||||
while (cur) {
|
||||
Vst::IParamValueQueue* vq = cur->value.queue;
|
||||
const Vst::ParamID id = Vst::ParamID(std::distance(queues, cur));
|
||||
|
||||
int32 pointIndex = cur->value.pointIndex;
|
||||
int32 pointCount = cur->value.pointCount;
|
||||
|
||||
int32 previousOffset = firstOffset;
|
||||
while (pointIndex < pointCount) {
|
||||
int32 sampleOffset;
|
||||
Vst::ParamValue value;
|
||||
|
||||
if (vq->getPoint(pointIndex, sampleOffset, value) != kResultTrue) {
|
||||
++pointIndex;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sampleOffset > lastOffset)
|
||||
break;
|
||||
|
||||
// ensure that offsets never go back
|
||||
if (sampleOffset < previousOffset)
|
||||
sampleOffset = previousOffset;
|
||||
|
||||
int32 listSize = numPointsBySample[sampleOffset - firstOffset];
|
||||
ParameterAndValue* listItems = &pointsBySample[paramCount * (sampleOffset - firstOffset)];
|
||||
|
||||
bool isDuplicatePoint = listSize > 0 && listItems[listSize - 1].id == id;
|
||||
if (isDuplicatePoint) {
|
||||
// protect against duplicates, which might overflow the list
|
||||
listItems[listSize - 1].value = value;
|
||||
}
|
||||
else {
|
||||
assert(listSize < paramCount);
|
||||
listItems[listSize].id = id;
|
||||
listItems[listSize].value = value;
|
||||
numPointsBySample[sampleOffset - firstOffset] = ++listSize;
|
||||
}
|
||||
|
||||
previousOffset = sampleOffset;
|
||||
++pointIndex;
|
||||
}
|
||||
|
||||
if (pointIndex < pointCount) {
|
||||
cur->value.pointIndex = pointIndex;
|
||||
prev = cur;
|
||||
}
|
||||
else {
|
||||
// if no more points, take this queue off the list
|
||||
if (prev)
|
||||
prev->next = cur->next;
|
||||
else {
|
||||
head = cur->next;
|
||||
prev = nullptr;
|
||||
}
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
queueList_ = head;
|
||||
}
|
||||
|
||||
void OrderedEventProcessor::playSubdiv(Vst::IEventList* evs, int32 firstOffset, int32 lastOffset)
|
||||
{
|
||||
// go over the points in sample order, and play them intermittently with the
|
||||
// event queue according to the sample offsets.
|
||||
|
||||
const int32 paramCount = paramCount_;
|
||||
const ParameterAndValue* pointsBySample = pointsBySample_.get();
|
||||
const uint32* numPointsBySample = numPointsBySample_.get();
|
||||
|
||||
for (int32 sampleOffset = firstOffset; sampleOffset <= lastOffset; ++sampleOffset) {
|
||||
const int32 listSize = numPointsBySample[sampleOffset - firstOffset];
|
||||
const ParameterAndValue* listItems = &pointsBySample[paramCount * (sampleOffset - firstOffset)];
|
||||
playEventsUpTo(evs, sampleOffset);
|
||||
for (int32 i = 0; i < listSize; ++i) {
|
||||
const ParameterAndValue item = listItems[i];
|
||||
playOrderedParameter(sampleOffset, item.id, item.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OrderedEventProcessor::playRemainder(int32 sampleOffset, Vst::IEventList* evs)
|
||||
{
|
||||
// play any remaining events and parameters in arbitrary order,
|
||||
// disregarding their respective offsets
|
||||
|
||||
int32 eventIndex = eventIndex_;
|
||||
int32 eventCount = eventCount_;
|
||||
bool haveCurrentEvent = haveCurrentEvent_;
|
||||
while (haveCurrentEvent) {
|
||||
currentEvent_.sampleOffset = std::min(sampleOffset, currentEvent_.sampleOffset);
|
||||
playOrderedEvent(currentEvent_);
|
||||
haveCurrentEvent = false;
|
||||
while (!haveCurrentEvent && ++eventIndex < eventCount)
|
||||
haveCurrentEvent = evs->getEvent(eventIndex, currentEvent_) == kResultTrue;
|
||||
}
|
||||
|
||||
Cell<QueueStatus>* queues = queues_.get();
|
||||
for (Cell<QueueStatus>* cur = queueList_; cur; cur = cur->next) {
|
||||
for (int32 i = cur->value.pointIndex, n = cur->value.pointCount; i < n; ++i) {
|
||||
Vst::IParamValueQueue* vq = cur->value.queue;
|
||||
const Vst::ParamID id = Vst::ParamID(std::distance(queues, cur));
|
||||
int32 unusedSampleOffset;
|
||||
Vst::ParamValue value;
|
||||
if (vq->getPoint(i, unusedSampleOffset, value) == kResultTrue)
|
||||
playOrderedParameter(sampleOffset, id, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OrderedEventProcessor::playEventsUpTo(Vst::IEventList* evs, int32 sampleOffset)
|
||||
{
|
||||
int32 index = eventIndex_;
|
||||
int32 count = eventCount_;
|
||||
bool haveCurrentEvent = haveCurrentEvent_;
|
||||
|
||||
while (haveCurrentEvent && currentEvent_.sampleOffset <= sampleOffset) {
|
||||
playOrderedEvent(currentEvent_);
|
||||
haveCurrentEvent = false;
|
||||
while (!haveCurrentEvent && ++index < count)
|
||||
haveCurrentEvent = evs->getEvent(index, currentEvent_) == kResultTrue;
|
||||
}
|
||||
|
||||
eventIndex_ = index;
|
||||
haveCurrentEvent_ = haveCurrentEvent;
|
||||
}
|
||||
66
plugins/vst/OrderedEventProcessor.h
Normal file
66
plugins/vst/OrderedEventProcessor.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#pragma once
|
||||
#include "pluginterfaces/vst/ivstevents.h"
|
||||
#include "pluginterfaces/vst/ivstparameterchanges.h"
|
||||
#include "pluginterfaces/vst/ivstaudioprocessor.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace Steinberg;
|
||||
|
||||
class OrderedEventProcessor {
|
||||
public:
|
||||
virtual ~OrderedEventProcessor() {}
|
||||
|
||||
void initializeEventProcessor(const Vst::ProcessSetup& setup, int32 paramCount, int32 subdivSize = 128);
|
||||
void processUnorderedEvents(int32 numSamples, Vst::IParameterChanges* pcs, Vst::IEventList* evs);
|
||||
|
||||
protected:
|
||||
virtual void playOrderedParameter(int32 sampleOffset, Vst::ParamID id, Vst::ParamValue value) = 0;
|
||||
virtual void playOrderedEvent(const Vst::Event& event) = 0;
|
||||
|
||||
private:
|
||||
void startProcessing(Vst::IEventList* evs, Vst::IParameterChanges* pcs);
|
||||
void processSubdiv(Vst::IEventList* evs, int32 firstOffset, int32 lastOffset);
|
||||
|
||||
void sortSubdiv(int32 firstOffset, int32 lastOffset);
|
||||
void playSubdiv(Vst::IEventList* evs, int32 firstOffset, int32 lastOffset);
|
||||
|
||||
void playRemainder(int32 sampleOffset, Vst::IEventList* evs);
|
||||
|
||||
void playEventsUpTo(Vst::IEventList* evs, int32 sampleOffset);
|
||||
|
||||
private:
|
||||
template <class T> struct Cell {
|
||||
Cell<T>* next = nullptr;
|
||||
T value {};
|
||||
};
|
||||
|
||||
struct QueueStatus {
|
||||
Vst::IParamValueQueue* queue = nullptr;
|
||||
int32 pointIndex = 0;
|
||||
int32 pointCount = 0;
|
||||
};
|
||||
|
||||
int32 paramCount_ = 0;
|
||||
int32 subdivSize_ = 0;
|
||||
std::unique_ptr<Cell<QueueStatus>[]> queues_;
|
||||
Cell<QueueStatus>* queueList_ = nullptr;
|
||||
|
||||
struct ParameterAndValue {
|
||||
Vst::ParamID id {};
|
||||
Vst::ParamValue value {};
|
||||
};
|
||||
|
||||
std::unique_ptr<ParameterAndValue[]> pointsBySample_;
|
||||
std::unique_ptr<uint32[]> numPointsBySample_;
|
||||
|
||||
int32 eventIndex_ = 0;
|
||||
int32 eventCount_ = 0;
|
||||
bool haveCurrentEvent_ = false;
|
||||
Vst::Event currentEvent_ {};
|
||||
};
|
||||
|
|
@ -25,6 +25,7 @@ enum {
|
|||
kPidCC0,
|
||||
kPidCCLast = kPidCC0 + sfz::config::numCCs - 1,
|
||||
/* Reserved */
|
||||
kNumParameters,
|
||||
};
|
||||
|
||||
struct SfizzRange {
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state)
|
|||
if (state) {
|
||||
synth->setSampleRate(processSetup.sampleRate);
|
||||
synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock);
|
||||
initializeEventProcessor(processSetup, kNumParameters);
|
||||
startBackgroundWork();
|
||||
} else {
|
||||
stopBackgroundWork();
|
||||
|
|
@ -241,16 +242,22 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
|||
{
|
||||
sfz::Sfizz& synth = *_synth;
|
||||
|
||||
std::unique_lock<SpinMutex> lock(_processMutex, std::defer_lock);
|
||||
if (data.processMode == Vst::kOffline)
|
||||
lock.lock();
|
||||
else
|
||||
lock.try_lock();
|
||||
|
||||
if (data.processContext)
|
||||
updateTimeInfo(*data.processContext);
|
||||
|
||||
if (Vst::IParameterChanges* pc = data.inputParameterChanges)
|
||||
processParameterChanges(*pc);
|
||||
const uint32 numFrames = data.numSamples;
|
||||
_canPerformEventsAndParameters = lock.owns_lock();
|
||||
processUnorderedEvents(numFrames, data.inputParameterChanges, data.inputEvents);
|
||||
|
||||
if (data.numOutputs < 1) // flush mode
|
||||
return kResultTrue;
|
||||
|
||||
uint32 numFrames = data.numSamples;
|
||||
constexpr uint32 numChannels = 2;
|
||||
float* outputs[numChannels];
|
||||
|
||||
|
|
@ -259,8 +266,6 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
|||
for (unsigned c = 0; c < numChannels; ++c)
|
||||
outputs[c] = data.outputs[0].channelBuffers32[c];
|
||||
|
||||
std::unique_lock<SpinMutex> lock(_processMutex, std::try_to_lock);
|
||||
|
||||
if (!lock.owns_lock()) {
|
||||
for (unsigned c = 0; c < numChannels; ++c)
|
||||
std::memset(outputs[c], 0, numFrames * sizeof(float));
|
||||
|
|
@ -275,12 +280,6 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
|||
|
||||
processMessagesFromUi();
|
||||
|
||||
if (Vst::IParameterChanges* pc = data.inputParameterChanges)
|
||||
processControllerChanges(*pc);
|
||||
|
||||
if (Vst::IEventList* events = data.inputEvents)
|
||||
processEvents(*events);
|
||||
|
||||
synth.setVolume(_state.volume);
|
||||
synth.setScalaRootKey(_state.scalaRootKey);
|
||||
synth.setTuningFrequency(_state.tuningFrequency);
|
||||
|
|
@ -336,153 +335,105 @@ void SfizzVstProcessor::updateTimeInfo(const Vst::ProcessContext& context)
|
|||
synth.playbackState(0, (context.state & context.kPlaying) != 0);
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::processParameterChanges(Vst::IParameterChanges& pc)
|
||||
void SfizzVstProcessor::playOrderedParameter(int32 sampleOffset, Vst::ParamID id, Vst::ParamValue value)
|
||||
{
|
||||
uint32 paramCount = pc.getParameterCount();
|
||||
if (!_canPerformEventsAndParameters)
|
||||
return;
|
||||
|
||||
for (uint32 paramIndex = 0; paramIndex < paramCount; ++paramIndex) {
|
||||
Vst::IParamValueQueue* vq = pc.getParameterData(paramIndex);
|
||||
if (!vq)
|
||||
continue;
|
||||
sfz::Sfizz& synth = *_synth;
|
||||
const SfizzRange range = SfizzRange::getForParameter(id);
|
||||
|
||||
const Vst::ParamID id = vq->getParameterId();
|
||||
const SfizzRange range = SfizzRange::getForParameter(id);
|
||||
|
||||
uint32 pointCount = vq->getPointCount();
|
||||
int32 sampleOffset;
|
||||
Vst::ParamValue value;
|
||||
|
||||
switch (id) {
|
||||
case kPidVolume:
|
||||
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue)
|
||||
_state.volume = range.denormalize(value);
|
||||
break;
|
||||
case kPidNumVoices:
|
||||
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) {
|
||||
int32 data = static_cast<int32>(range.denormalize(value));
|
||||
_state.numVoices = data;
|
||||
if (writeWorkerMessage(kMsgIdSetNumVoices, &data, sizeof(data)))
|
||||
_semaToWorker.post();
|
||||
}
|
||||
break;
|
||||
case kPidOversampling:
|
||||
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) {
|
||||
int32 data = static_cast<int32>(range.denormalize(value));
|
||||
_state.oversamplingLog2 = data;
|
||||
if (writeWorkerMessage(kMsgIdSetOversampling, &data, sizeof(data)))
|
||||
_semaToWorker.post();
|
||||
}
|
||||
break;
|
||||
case kPidPreloadSize:
|
||||
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) {
|
||||
int32 data = static_cast<int32>(range.denormalize(value));
|
||||
_state.preloadSize = data;
|
||||
if (writeWorkerMessage(kMsgIdSetPreloadSize, &data, sizeof(data)))
|
||||
_semaToWorker.post();
|
||||
}
|
||||
break;
|
||||
case kPidScalaRootKey:
|
||||
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue)
|
||||
_state.scalaRootKey = static_cast<int32>(range.denormalize(value));
|
||||
break;
|
||||
case kPidTuningFrequency:
|
||||
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue)
|
||||
_state.tuningFrequency = range.denormalize(value);
|
||||
break;
|
||||
case kPidStretchedTuning:
|
||||
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue)
|
||||
_state.stretchedTuning = range.denormalize(value);
|
||||
break;
|
||||
switch (id) {
|
||||
case kPidVolume:
|
||||
_state.volume = range.denormalize(value);
|
||||
break;
|
||||
case kPidNumVoices:
|
||||
{
|
||||
int32 data = static_cast<int32>(range.denormalize(value));
|
||||
_state.numVoices = data;
|
||||
if (writeWorkerMessage(kMsgIdSetNumVoices, &data, sizeof(data)))
|
||||
_semaToWorker.post();
|
||||
}
|
||||
break;
|
||||
case kPidOversampling:
|
||||
{
|
||||
int32 data = static_cast<int32>(range.denormalize(value));
|
||||
_state.oversamplingLog2 = data;
|
||||
if (writeWorkerMessage(kMsgIdSetOversampling, &data, sizeof(data)))
|
||||
_semaToWorker.post();
|
||||
}
|
||||
break;
|
||||
case kPidPreloadSize:
|
||||
{
|
||||
int32 data = static_cast<int32>(range.denormalize(value));
|
||||
_state.preloadSize = data;
|
||||
if (writeWorkerMessage(kMsgIdSetPreloadSize, &data, sizeof(data)))
|
||||
_semaToWorker.post();
|
||||
}
|
||||
break;
|
||||
case kPidScalaRootKey:
|
||||
_state.scalaRootKey = static_cast<int32>(range.denormalize(value));
|
||||
break;
|
||||
case kPidTuningFrequency:
|
||||
_state.tuningFrequency = range.denormalize(value);
|
||||
break;
|
||||
case kPidStretchedTuning:
|
||||
_state.stretchedTuning = range.denormalize(value);
|
||||
break;
|
||||
case kPidAftertouch:
|
||||
synth.aftertouch(sampleOffset, fastRound(value * 127.0));
|
||||
break;
|
||||
case kPidPitchBend:
|
||||
synth.pitchWheel(sampleOffset, fastRound(value * 16383) - 8192);
|
||||
break;
|
||||
default:
|
||||
if (id >= kPidCC0 && id <= kPidCCLast) {
|
||||
int32 ccNumber = static_cast<int32>(id - kPidCC0);
|
||||
synth.hdcc(sampleOffset, ccNumber, value);
|
||||
_state.controllers[ccNumber] = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::processControllerChanges(Vst::IParameterChanges& pc)
|
||||
void SfizzVstProcessor::playOrderedEvent(const Vst::Event& event)
|
||||
{
|
||||
if (!_canPerformEventsAndParameters)
|
||||
return;
|
||||
|
||||
sfz::Sfizz& synth = *_synth;
|
||||
uint32 paramCount = pc.getParameterCount();
|
||||
const int32 sampleOffset = event.sampleOffset;
|
||||
|
||||
for (uint32 paramIndex = 0; paramIndex < paramCount; ++paramIndex) {
|
||||
Vst::IParamValueQueue* vq = pc.getParameterData(paramIndex);
|
||||
if (!vq)
|
||||
continue;
|
||||
|
||||
Vst::ParamID id = vq->getParameterId();
|
||||
uint32 pointCount = vq->getPointCount();
|
||||
int32 sampleOffset;
|
||||
Vst::ParamValue value;
|
||||
|
||||
switch (id) {
|
||||
default:
|
||||
if (id >= kPidCC0 && id <= kPidCCLast) {
|
||||
auto ccNumber = static_cast<int>(id - kPidCC0);
|
||||
for (uint32 pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
|
||||
if (vq->getPoint(pointIndex, sampleOffset, value) == kResultTrue) {
|
||||
synth.hdcc(sampleOffset, ccNumber, value);
|
||||
_state.controllers[ccNumber] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (event.type) {
|
||||
case Vst::Event::kNoteOnEvent: {
|
||||
int pitch = event.noteOn.pitch;
|
||||
if (pitch < 0 || pitch >= 128)
|
||||
break;
|
||||
|
||||
case kPidAftertouch:
|
||||
for (uint32 pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
|
||||
if (vq->getPoint(pointIndex, sampleOffset, value) == kResultTrue)
|
||||
synth.aftertouch(sampleOffset, fastRound(value * 127.0));
|
||||
}
|
||||
break;
|
||||
|
||||
case kPidPitchBend:
|
||||
for (uint32 pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
|
||||
if (vq->getPoint(pointIndex, sampleOffset, value) == kResultTrue)
|
||||
synth.pitchWheel(sampleOffset, fastRound(value * 16383) - 8192);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::processEvents(Vst::IEventList& events)
|
||||
{
|
||||
sfz::Sfizz& synth = *_synth;
|
||||
uint32 numEvents = events.getEventCount();
|
||||
|
||||
for (uint32 i = 0; i < numEvents; i++) {
|
||||
Vst::Event e;
|
||||
if (events.getEvent(i, e) != kResultTrue)
|
||||
continue;
|
||||
|
||||
switch (e.type) {
|
||||
case Vst::Event::kNoteOnEvent: {
|
||||
int pitch = e.noteOn.pitch;
|
||||
if (pitch < 0 || pitch >= 128)
|
||||
break;
|
||||
if (e.noteOn.velocity <= 0.0f) {
|
||||
synth.noteOff(e.sampleOffset, pitch, 0);
|
||||
_noteEventsCurrentCycle[pitch] = 0.0f;
|
||||
}
|
||||
else {
|
||||
synth.noteOn(e.sampleOffset, pitch, convertVelocityFromFloat(e.noteOn.velocity));
|
||||
_noteEventsCurrentCycle[pitch] = e.noteOn.velocity;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Vst::Event::kNoteOffEvent: {
|
||||
int pitch = e.noteOn.pitch;
|
||||
if (pitch < 0 || pitch >= 128)
|
||||
break;
|
||||
synth.noteOff(e.sampleOffset, pitch, convertVelocityFromFloat(e.noteOff.velocity));
|
||||
if (event.noteOn.velocity <= 0.0f) {
|
||||
synth.noteOff(sampleOffset, pitch, 0);
|
||||
_noteEventsCurrentCycle[pitch] = 0.0f;
|
||||
}
|
||||
else {
|
||||
synth.noteOn(sampleOffset, pitch, convertVelocityFromFloat(event.noteOn.velocity));
|
||||
_noteEventsCurrentCycle[pitch] = event.noteOn.velocity;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Vst::Event::kNoteOffEvent: {
|
||||
int pitch = event.noteOn.pitch;
|
||||
if (pitch < 0 || pitch >= 128)
|
||||
break;
|
||||
}
|
||||
case Vst::Event::kPolyPressureEvent: {
|
||||
int pitch = e.polyPressure.pitch;
|
||||
if (pitch < 0 || pitch >= 128)
|
||||
break;
|
||||
synth.polyAftertouch(e.sampleOffset, pitch, convertVelocityFromFloat(e.polyPressure.pressure));
|
||||
synth.noteOff(sampleOffset, pitch, convertVelocityFromFloat(event.noteOff.velocity));
|
||||
_noteEventsCurrentCycle[pitch] = 0.0f;
|
||||
break;
|
||||
}
|
||||
case Vst::Event::kPolyPressureEvent: {
|
||||
int pitch = event.polyPressure.pitch;
|
||||
if (pitch < 0 || pitch >= 128)
|
||||
break;
|
||||
}
|
||||
}
|
||||
synth.polyAftertouch(sampleOffset, pitch, convertVelocityFromFloat(event.polyPressure.pressure));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
#include "SfizzVstState.h"
|
||||
#include "OrderedEventProcessor.h"
|
||||
#include "sfizz/RTSemaphore.h"
|
||||
#include "ring_buffer/ring_buffer.h"
|
||||
#include "public.sdk/source/vst/vstaudioeffect.h"
|
||||
|
|
@ -18,7 +19,8 @@
|
|||
|
||||
using namespace Steinberg;
|
||||
|
||||
class SfizzVstProcessor : public Vst::AudioEffect {
|
||||
class SfizzVstProcessor : public Vst::AudioEffect,
|
||||
public OrderedEventProcessor {
|
||||
public:
|
||||
SfizzVstProcessor();
|
||||
~SfizzVstProcessor();
|
||||
|
|
@ -35,9 +37,11 @@ public:
|
|||
tresult PLUGIN_API canProcessSampleSize(int32 symbolicSampleSize) override;
|
||||
tresult PLUGIN_API setActive(TBool state) override;
|
||||
tresult PLUGIN_API process(Vst::ProcessData& data) override;
|
||||
void processParameterChanges(Vst::IParameterChanges& pc);
|
||||
void processControllerChanges(Vst::IParameterChanges& pc);
|
||||
void processEvents(Vst::IEventList& events);
|
||||
|
||||
// OrderedEventProcessor
|
||||
void playOrderedParameter(int32 sampleOffset, Vst::ParamID id, Vst::ParamValue value) override;
|
||||
void playOrderedEvent(const Vst::Event& event) override;
|
||||
|
||||
void processMessagesFromUi();
|
||||
static int convertVelocityFromFloat(float x);
|
||||
|
||||
|
|
@ -57,6 +61,9 @@ private:
|
|||
SfizzVstState _state;
|
||||
float _currentStretchedTuning = 0;
|
||||
|
||||
// whether allowed to perform events (owns the processing lock)
|
||||
bool _canPerformEventsAndParameters {};
|
||||
|
||||
// client
|
||||
sfz::ClientPtr _client;
|
||||
std::unique_ptr<uint8_t[]> _oscTemp;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue