From 0eada20587f29262b698c838988e5b4ff814b7d6 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 20 Nov 2021 17:00:39 +0100 Subject: [PATCH] Rewrite the unordered player to accomodate multiple queues per parameters Now, there is a hard limit on the number of queues treated (as before), but there may be duplicates, and overflowing queues will be ignored --- plugins/vst/OrderedEventProcessor.h | 44 +---- plugins/vst/OrderedEventProcessor.hpp | 268 ++++++-------------------- plugins/vst/SfizzVstController.cpp | 2 +- 3 files changed, 71 insertions(+), 243 deletions(-) diff --git a/plugins/vst/OrderedEventProcessor.h b/plugins/vst/OrderedEventProcessor.h index aa49c26df..4bc01f174 100644 --- a/plugins/vst/OrderedEventProcessor.h +++ b/plugins/vst/OrderedEventProcessor.h @@ -21,45 +21,17 @@ class OrderedEventProcessor { void processUnorderedEvents(int32 numSamples, Vst::IParameterChanges* pcs, Vst::IEventList* evs); 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 struct Cell { - Cell* 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[]> queues_; - Cell* queueList_ = nullptr; - - struct ParameterAndValue { + int32 paramCount_ { 0 }; + int32 subdivSize_ { 0 }; + struct SubdivChange { + SubdivChange(int32 offset, Vst::ParamID id, Vst::ParamValue value) + : offset(offset), id(std::move(id)), value(std::move(value)) {} + int32 offset; Vst::ParamID id {}; Vst::ParamValue value {}; }; - - std::unique_ptr pointsBySample_; - std::unique_ptr numPointsBySample_; - - int32 eventIndex_ = 0; - int32 eventCount_ = 0; - bool haveCurrentEvent_ = false; - Vst::Event currentEvent_ {}; + std::vector subdivChanges_; + std::vector queuePositions_; }; #include "OrderedEventProcessor.hpp" diff --git a/plugins/vst/OrderedEventProcessor.hpp b/plugins/vst/OrderedEventProcessor.hpp index 316eaf569..c423d25bf 100644 --- a/plugins/vst/OrderedEventProcessor.hpp +++ b/plugins/vst/OrderedEventProcessor.hpp @@ -14,234 +14,90 @@ void OrderedEventProcessor::initializeEventProcessor(const Vst::ProcessSetup& { paramCount_ = paramCount; subdivSize_ = subdivSize; - queues_.reset(new Cell[paramCount]); - pointsBySample_.reset(new ParameterAndValue[paramCount * subdivSize]); - numPointsBySample_.reset(new uint32[subdivSize]); + subdivChanges_.reserve(subdivSize * paramCount); + queuePositions_.reserve(paramCount); } template void OrderedEventProcessor::processUnorderedEvents(int32 numSamples, Vst::IParameterChanges* pcs, Vst::IEventList* evs) { + if (!pcs || !evs) + return; + + R& receiver = *static_cast(this); 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); -} - -template -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* head = nullptr; - Cell* 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* cell = &queues[id]; - cell->next = head; - cell->value.queue = vq; - cell->value.pointIndex = 0; - cell->value.pointCount = vq->getPointCount(); - head = cell; - } - } + if (subdivSize == 0) + return; + + int32 eventIdx = 0; + int32 eventCount = evs->getEventCount(); + Vst::Event event; + bool hasEvent = false; + if (eventCount > 0) { + evs->getEvent(eventIdx++, event); + hasEvent = true; } - queueList_ = head; + // Assume that there would be as many parameter changes as there are parameters, but some hosts are nice + // and send multiple queues per parameter. Clamp the number of considered parameter queues to the number of parameters. + queuePositions_.clear(); + const int32 parameterCount = pcs->getParameterCount(); + const int32 queueCapacity = static_cast(queuePositions_.capacity()); + assert(queueCapacity >= parameterCount); + const int32 consideredQueueCount = std::min(queueCapacity, parameterCount); + queuePositions_.resize(consideredQueueCount, 0); - // position ourselves to the start of the event list - eventIndex_ = 0; - eventCount_ = evs ? evs->getEventCount() : 0; - haveCurrentEvent_ = eventCount_ > 0 && - evs->getEvent(eventIndex_, currentEvent_) == kResultTrue; -} - -template -void OrderedEventProcessor::processSubdiv(Vst::IEventList* evs, int32 firstOffset, int32 lastOffset) -{ - sortSubdiv(firstOffset, lastOffset); - playSubdiv(evs, firstOffset, lastOffset); -} - -template -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* head = queueList_; - Cell* queues = queues_.get(); - const int32 paramCount = paramCount_; - ParameterAndValue* pointsBySample = pointsBySample_.get(); - uint32* numPointsBySample = numPointsBySample_.get(); - - std::fill_n(numPointsBySample, lastOffset - firstOffset + 1, 0); - - Cell* cur = head; - Cell* 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; + while (sampleIndex < numSamples || subdivNumber == 0) { + const int32 subdivCurrentSize = std::min(numSamples - sampleIndex, subdivSize); + const int32 lastOffset = sampleIndex + subdivSize; + + // Queue all changes for the subdiv + subdivChanges_.clear(); + for (int32 qIdx = 0; qIdx < consideredQueueCount; ++qIdx) { + auto* vq = pcs->getParameterData(qIdx); + if (!vq) continue; - } - - if (sampleOffset > lastOffset) - break; - // ensure that offsets never go back - if (sampleOffset < previousOffset) - sampleOffset = previousOffset; + int32 offset; + Vst::ParamID id = vq->getParameterId(); + Vst::ParamValue value; - int32 listSize = numPointsBySample[sampleOffset - firstOffset]; - ParameterAndValue* listItems = &pointsBySample[paramCount * (sampleOffset - firstOffset)]; + int32& queuePosition = queuePositions_[qIdx]; + while (queuePosition < vq->getPointCount()) { + vq->getPoint(queuePosition, offset, value); + if (offset > lastOffset) + break; - 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; + subdivChanges_.emplace_back(offset, id, value); + queuePosition++; } - - 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; + // Sort the changes + std::sort(subdivChanges_.begin(), subdivChanges_.end(), [] (const SubdivChange& lhs, const SubdivChange& rhs) { + return lhs.offset < rhs.offset; + }); + + // Play the parameter changes, interleaving events as needed in between + for (const auto& change: subdivChanges_) { + while (hasEvent && event.sampleOffset < change.offset) { + receiver.playOrderedEvent(event); + hasEvent = (eventIdx < eventCount); + evs->getEvent(eventIdx++, event); } + receiver.playOrderedParameter(change.offset, change.id, change.value); } - cur = cur->next; - } - - queueList_ = head; -} - -template -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. - - R& receiver = *static_cast(this); - - 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]; - receiver.playOrderedParameter(sampleOffset, item.id, item.value); - } - } -} - -template -void OrderedEventProcessor::playRemainder(int32 sampleOffset, Vst::IEventList* evs) -{ - // play any remaining events and parameters in arbitrary order, - // disregarding their respective offsets - R& receiver = *static_cast(this); - - int32 eventIndex = eventIndex_; - int32 eventCount = eventCount_; - bool haveCurrentEvent = haveCurrentEvent_; - while (haveCurrentEvent) { - currentEvent_.sampleOffset = std::min(sampleOffset, currentEvent_.sampleOffset); - receiver.playOrderedEvent(currentEvent_); - haveCurrentEvent = false; - while (!haveCurrentEvent && ++eventIndex < eventCount) - haveCurrentEvent = evs->getEvent(eventIndex, currentEvent_) == kResultTrue; + sampleIndex += subdivCurrentSize; + ++subdivNumber; } - Cell* queues = queues_.get(); - for (Cell* 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) - receiver.playOrderedParameter(sampleOffset, id, value); - } + // Play the remaining events + while (hasEvent) { + receiver.playOrderedEvent(event); + hasEvent = (eventIdx < eventCount); + evs->getEvent(eventIdx++, event); } } - -template -void OrderedEventProcessor::playEventsUpTo(Vst::IEventList* evs, int32 sampleOffset) -{ - R& receiver = *static_cast(this); - - int32 index = eventIndex_; - int32 count = eventCount_; - bool haveCurrentEvent = haveCurrentEvent_; - - while (haveCurrentEvent && currentEvent_.sampleOffset <= sampleOffset) { - receiver.playOrderedEvent(currentEvent_); - haveCurrentEvent = false; - while (!haveCurrentEvent && ++index < count) - haveCurrentEvent = evs->getEvent(index, currentEvent_) == kResultTrue; - } - - eventIndex_ = index; - haveCurrentEvent_ = haveCurrentEvent; -} diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index 3dab25185..2aeb4d532 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -316,7 +316,7 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) /// if (!strcmp(id, SfzUpdate::getFClassID())) { if (!sfzUpdate_->convertFromMessage(*message)) { - assert(false); + // assert(false); return kResultFalse; }