Merge pull request #865 from jpcima/vst-defered
Make VST updates defered
This commit is contained in:
commit
eba974cfe1
6 changed files with 128 additions and 210 deletions
|
|
@ -21,8 +21,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
|
|||
Steinberg::UpdateHandler::instance();
|
||||
|
||||
// create update objects
|
||||
oscUpdate_ = Steinberg::owned(new OSCUpdate);
|
||||
noteUpdate_ = Steinberg::owned(new NoteUpdate);
|
||||
queuedUpdates_ = Steinberg::owned(new QueuedUpdates);
|
||||
sfzUpdate_ = Steinberg::owned(new SfzUpdate);
|
||||
sfzDescriptionUpdate_ = Steinberg::owned(new SfzDescriptionUpdate);
|
||||
scalaUpdate_ = Steinberg::owned(new ScalaUpdate);
|
||||
|
|
@ -289,10 +288,10 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message)
|
|||
if (result != kResultTrue)
|
||||
return result;
|
||||
|
||||
// this is a synchronous send, because the update object gets reused
|
||||
oscUpdate_->setMessage(data, size, false);
|
||||
oscUpdate_->changed();
|
||||
oscUpdate_->clear();
|
||||
IPtr<OSCUpdate> update = Steinberg::owned(
|
||||
new OSCUpdate(reinterpret_cast<const uint8*>(data), size));
|
||||
queuedUpdates_->enqueue(update);
|
||||
queuedUpdates_->deferUpdate();
|
||||
}
|
||||
else if (!strcmp(id, "NoteEvents")) {
|
||||
const void* data = nullptr;
|
||||
|
|
@ -303,10 +302,10 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message)
|
|||
const std::pair<uint32_t, float>*>(data);
|
||||
uint32 numEvents = size / sizeof(events[0]);
|
||||
|
||||
// this is a synchronous send, because the update object gets reused
|
||||
noteUpdate_->setEvents(events, numEvents, false);
|
||||
noteUpdate_->changed();
|
||||
noteUpdate_->clear();
|
||||
IPtr<NoteUpdate> update = Steinberg::owned(
|
||||
new NoteUpdate(events, numEvents));
|
||||
queuedUpdates_->enqueue(update);
|
||||
queuedUpdates_->deferUpdate();
|
||||
}
|
||||
else if (!strcmp(id, "Automate")) {
|
||||
const void* data = nullptr;
|
||||
|
|
@ -343,20 +342,17 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name)
|
|||
if (name != Vst::ViewType::kEditor)
|
||||
return nullptr;
|
||||
|
||||
std::vector<FObject*> continuousUpdates;
|
||||
continuousUpdates.push_back(sfzUpdate_);
|
||||
continuousUpdates.push_back(sfzDescriptionUpdate_);
|
||||
continuousUpdates.push_back(scalaUpdate_);
|
||||
continuousUpdates.push_back(playStateUpdate_);
|
||||
std::vector<FObject*> updates;
|
||||
updates.push_back(queuedUpdates_);
|
||||
updates.push_back(sfzUpdate_);
|
||||
updates.push_back(sfzDescriptionUpdate_);
|
||||
updates.push_back(scalaUpdate_);
|
||||
updates.push_back(playStateUpdate_);
|
||||
for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i)
|
||||
continuousUpdates.push_back(parameters.getParameterByIndex(i));
|
||||
|
||||
std::vector<FObject*> triggerUpdates;
|
||||
triggerUpdates.push_back(oscUpdate_);
|
||||
triggerUpdates.push_back(noteUpdate_);
|
||||
updates.push_back(parameters.getParameterByIndex(i));
|
||||
|
||||
IPtr<SfizzVstEditor> editor = Steinberg::owned(
|
||||
new SfizzVstEditor(this, absl::MakeSpan(continuousUpdates), absl::MakeSpan(triggerUpdates)));
|
||||
new SfizzVstEditor(this, absl::MakeSpan(updates)));
|
||||
|
||||
editor->remember();
|
||||
return editor;
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ public:
|
|||
REFCOUNT_METHODS(Vst::EditController)
|
||||
|
||||
protected:
|
||||
Steinberg::IPtr<OSCUpdate> oscUpdate_;
|
||||
Steinberg::IPtr<NoteUpdate> noteUpdate_;
|
||||
Steinberg::IPtr<QueuedUpdates> queuedUpdates_;
|
||||
Steinberg::IPtr<SfzUpdate> sfzUpdate_;
|
||||
Steinberg::IPtr<SfzDescriptionUpdate> sfzDescriptionUpdate_;
|
||||
Steinberg::IPtr<ScalaUpdate> scalaUpdate_;
|
||||
|
|
|
|||
|
|
@ -30,14 +30,10 @@ enum {
|
|||
kNoteEventQueueSize = 8192,
|
||||
};
|
||||
|
||||
SfizzVstEditor::SfizzVstEditor(
|
||||
SfizzVstController* controller,
|
||||
absl::Span<FObject*> continuousUpdates,
|
||||
absl::Span<FObject*> triggerUpdates)
|
||||
SfizzVstEditor::SfizzVstEditor(SfizzVstController* controller, absl::Span<FObject*> updates)
|
||||
: VSTGUIEditor(controller, &sfizzUiViewRect),
|
||||
oscTemp_(new uint8_t[kOscTempSize]),
|
||||
continuousUpdates_(continuousUpdates.begin(), continuousUpdates.end()),
|
||||
triggerUpdates_(triggerUpdates.begin(), triggerUpdates.end())
|
||||
updates_(updates.begin(), updates.end())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -69,19 +65,6 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
|
|||
editor_.reset(editor);
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(oscQueueMutex_);
|
||||
OscByteVec* queue = new OscByteVec;
|
||||
oscQueue_.reset(queue);
|
||||
queue->reserve(kOscQueueSize);
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
|
||||
NoteEventsVec* queue = new NoteEventsVec;
|
||||
noteEventQueue_.reset(queue);
|
||||
queue->reserve(kNoteEventQueueSize);
|
||||
}
|
||||
|
||||
if (!frame->open(parent, platformType, config)) {
|
||||
fprintf(stderr, "[sfizz] error opening frame\n");
|
||||
return false;
|
||||
|
|
@ -89,9 +72,7 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
|
|||
|
||||
editor->open(*frame);
|
||||
|
||||
for (FObject* update : continuousUpdates_)
|
||||
update->addDependent(this);
|
||||
for (FObject* update : triggerUpdates_)
|
||||
for (FObject* update : updates_)
|
||||
update->addDependent(this);
|
||||
|
||||
threadChecker_ = Vst::ThreadChecker::create();
|
||||
|
|
@ -100,7 +81,7 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
|
|||
|
||||
Steinberg::IdleUpdateHandler::start();
|
||||
|
||||
for (FObject* update : continuousUpdates_)
|
||||
for (FObject* update : updates_)
|
||||
update->deferUpdate();
|
||||
|
||||
// let the editor know about plugin format
|
||||
|
|
@ -138,9 +119,7 @@ void PLUGIN_API SfizzVstEditor::close()
|
|||
if (frame) {
|
||||
Steinberg::IdleUpdateHandler::stop();
|
||||
|
||||
for (FObject* update : continuousUpdates_)
|
||||
update->removeDependent(this);
|
||||
for (FObject* update : triggerUpdates_)
|
||||
for (FObject* update : updates_)
|
||||
update->removeDependent(this);
|
||||
|
||||
if (editor_)
|
||||
|
|
@ -152,15 +131,6 @@ void PLUGIN_API SfizzVstEditor::close()
|
|||
this->frame = nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(oscQueueMutex_);
|
||||
oscQueue_.reset();
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
|
||||
noteEventQueue_.reset();
|
||||
}
|
||||
|
||||
updateEditorIsOpenParameter();
|
||||
}
|
||||
|
||||
|
|
@ -195,8 +165,6 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message)
|
|||
#endif
|
||||
|
||||
if (message == CVSTGUITimer::kMsgTimer) {
|
||||
processOscQueue();
|
||||
processNoteEventQueue();
|
||||
processParameterUpdates();
|
||||
updateEditorIsOpenParameter(); // Note(jpc) for Reaper, it can fail at open time
|
||||
}
|
||||
|
|
@ -206,34 +174,51 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message)
|
|||
|
||||
void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
|
||||
{
|
||||
if (OSCUpdate* update = FCast<OSCUpdate>(changedUnknown)) {
|
||||
// this update is synchronous: may happen from non-UI thread
|
||||
uint32 size = update->size();
|
||||
if (size > 0) {
|
||||
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(update->data());
|
||||
std::lock_guard<std::mutex> lock(oscQueueMutex_);
|
||||
if (OscByteVec* queue = oscQueue_.get())
|
||||
std::copy(bytes, bytes + size, std::back_inserter(*queue));
|
||||
}
|
||||
if (processUpdate(changedUnknown, message))
|
||||
return;
|
||||
|
||||
Vst::VSTGUIEditor::update(changedUnknown, message);
|
||||
}
|
||||
|
||||
bool SfizzVstEditor::processUpdate(FUnknown* changedUnknown, int32 message)
|
||||
{
|
||||
if (QueuedUpdates* update = FCast<QueuedUpdates>(changedUnknown)) {
|
||||
for (FObject* queuedUpdate : update->getUpdates(this))
|
||||
processUpdate(queuedUpdate, message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (OSCUpdate* update = FCast<OSCUpdate>(changedUnknown)) {
|
||||
const uint8* oscData = update->data();
|
||||
uint32 oscSize = update->size();
|
||||
|
||||
const char* path;
|
||||
const char* sig;
|
||||
const sfizz_arg_t* args;
|
||||
uint8_t buffer[1024];
|
||||
|
||||
uint32_t msgSize;
|
||||
while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) {
|
||||
uiReceiveMessage(path, sig, args);
|
||||
oscData += msgSize;
|
||||
oscSize -= msgSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NoteUpdate* update = FCast<NoteUpdate>(changedUnknown)) {
|
||||
// this update is synchronous: may happen from non-UI thread
|
||||
const NoteUpdate::Item* events = update->events();
|
||||
uint32 count = update->count();
|
||||
if (count > 0) {
|
||||
const auto* events = update->events();
|
||||
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
|
||||
if (NoteEventsVec* queue = noteEventQueue_.get())
|
||||
std::copy(events, events + count, std::back_inserter(*queue));
|
||||
}
|
||||
return;
|
||||
for (uint32 i = 0; i < count; ++i)
|
||||
uiReceiveValue(editIdForKey(events[i].first), events[i].second);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SfzUpdate* update = FCast<SfzUpdate>(changedUnknown)) {
|
||||
const std::string path = update->getPath();
|
||||
uiReceiveValue(EditId::SfzFile, path);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SfzDescriptionUpdate* update = FCast<SfzDescriptionUpdate>(changedUnknown)) {
|
||||
|
|
@ -268,19 +253,19 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
|
|||
uiReceiveValue(editIdForCCLabel(int(cc)), desc.ccLabel[cc]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ScalaUpdate* update = FCast<ScalaUpdate>(changedUnknown)) {
|
||||
const std::string path = update->getPath();
|
||||
uiReceiveValue(EditId::ScalaFile, path);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (PlayStateUpdate* update = FCast<PlayStateUpdate>(changedUnknown)) {
|
||||
const SfizzPlayState playState = update->getState();
|
||||
uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Vst::RangeParameter* param = Steinberg::FCast<Vst::RangeParameter>(changedUnknown)) {
|
||||
|
|
@ -297,50 +282,10 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
|
|||
std::lock_guard<std::mutex> lock(parametersToUpdateMutex_);
|
||||
parametersToUpdate_.insert(id);
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
Vst::VSTGUIEditor::update(changedUnknown, message);
|
||||
}
|
||||
|
||||
void SfizzVstEditor::processOscQueue()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(oscQueueMutex_);
|
||||
|
||||
OscByteVec* queue = oscQueue_.get();
|
||||
if (!queue)
|
||||
return;
|
||||
|
||||
const uint8_t* oscData = queue->data();
|
||||
size_t oscSize = queue->size();
|
||||
|
||||
const char* path;
|
||||
const char* sig;
|
||||
const sfizz_arg_t* args;
|
||||
uint8_t buffer[1024];
|
||||
|
||||
uint32_t msgSize;
|
||||
while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) {
|
||||
uiReceiveMessage(path, sig, args);
|
||||
oscData += msgSize;
|
||||
oscSize -= msgSize;
|
||||
}
|
||||
|
||||
queue->clear();
|
||||
}
|
||||
|
||||
void SfizzVstEditor::processNoteEventQueue()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
|
||||
|
||||
NoteEventsVec* queue = noteEventQueue_.get();
|
||||
if (!queue)
|
||||
return;
|
||||
|
||||
for (std::pair<uint32, float> event : *queue)
|
||||
uiReceiveValue(editIdForKey(event.first), event.second);
|
||||
|
||||
queue->clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
void SfizzVstEditor::processParameterUpdates()
|
||||
|
|
|
|||
|
|
@ -25,10 +25,7 @@ class SfizzVstEditor : public Vst::VSTGUIEditor,
|
|||
public:
|
||||
using Self = SfizzVstEditor;
|
||||
|
||||
SfizzVstEditor(
|
||||
SfizzVstController* controller,
|
||||
absl::Span<FObject*> continuousUpdates,
|
||||
absl::Span<FObject*> triggerUpdates);
|
||||
SfizzVstEditor(SfizzVstController* controller, absl::Span<FObject*> updates);
|
||||
~SfizzVstEditor();
|
||||
|
||||
bool PLUGIN_API open(void* parent, const VSTGUI::PlatformType& platformType) override;
|
||||
|
|
@ -48,8 +45,7 @@ public:
|
|||
|
||||
//
|
||||
private:
|
||||
void processOscQueue();
|
||||
void processNoteEventQueue();
|
||||
bool processUpdate(FUnknown* changedUnknown, int32 message);
|
||||
void processParameterUpdates();
|
||||
void updateParameter(Vst::Parameter* parameterToUpdate);
|
||||
|
||||
|
|
@ -76,18 +72,8 @@ private:
|
|||
// messaging
|
||||
std::unique_ptr<uint8[]> oscTemp_;
|
||||
|
||||
// editor state
|
||||
// note: might be updated from a non-UI thread
|
||||
typedef std::vector<uint8_t> OscByteVec;
|
||||
std::unique_ptr<OscByteVec> oscQueue_;
|
||||
std::mutex oscQueueMutex_;
|
||||
typedef std::vector<std::pair<uint32, float>> NoteEventsVec;
|
||||
std::unique_ptr<NoteEventsVec> noteEventQueue_;
|
||||
std::mutex noteEventQueueMutex_;
|
||||
|
||||
// subscribed updates
|
||||
std::vector<IPtr<FObject>> continuousUpdates_;
|
||||
std::vector<IPtr<FObject>> triggerUpdates_;
|
||||
std::vector<IPtr<FObject>> updates_;
|
||||
|
||||
// thread safety
|
||||
std::unique_ptr<Vst::ThreadChecker> threadChecker_;
|
||||
|
|
|
|||
|
|
@ -8,61 +8,51 @@
|
|||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
OSCUpdate::~OSCUpdate()
|
||||
void QueuedUpdates::enqueue(IPtr<FObject> update)
|
||||
{
|
||||
clear();
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (std::pair<IDependent* const, List>& item : updates_)
|
||||
item.second.push_back(update);
|
||||
}
|
||||
|
||||
void OSCUpdate::clear()
|
||||
auto QueuedUpdates::getUpdates(IDependent* dep) -> List
|
||||
{
|
||||
if (allocated_)
|
||||
delete[] reinterpret_cast<const uint8_t*>(data_);
|
||||
data_ = nullptr;
|
||||
size_ = 0;
|
||||
allocated_ = false;
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
List list;
|
||||
auto it = updates_.find(dep);
|
||||
if (it != updates_.end())
|
||||
std::swap(list, it->second);
|
||||
return list;
|
||||
}
|
||||
|
||||
void OSCUpdate::setMessage(const void* data, uint32_t size, bool copy)
|
||||
void QueuedUpdates::addDependent(IDependent* dep)
|
||||
{
|
||||
clear();
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
FObject::addDependent(dep);
|
||||
updates_.emplace(dep, List());
|
||||
}
|
||||
|
||||
if (copy) {
|
||||
uint8_t *buffer = new uint8_t[size];
|
||||
std::memcpy(buffer, data, size);
|
||||
data = buffer;
|
||||
}
|
||||
|
||||
data_ = data;
|
||||
size_ = size;
|
||||
allocated_ = copy;
|
||||
void QueuedUpdates::removeDependent(IDependent* dep)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
FObject::removeDependent(dep);
|
||||
updates_.erase(dep);
|
||||
}
|
||||
|
||||
///
|
||||
NoteUpdate::~NoteUpdate()
|
||||
OSCUpdate::OSCUpdate(const uint8* data, uint32 size)
|
||||
{
|
||||
clear();
|
||||
uint8* copy = new uint8[size];
|
||||
std::copy_n(data, size, copy);
|
||||
data_.reset(copy);
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
void NoteUpdate::clear()
|
||||
///
|
||||
NoteUpdate::NoteUpdate(const Item* items, uint32 count)
|
||||
{
|
||||
if (allocated_)
|
||||
delete[] events_;
|
||||
events_ = nullptr;
|
||||
count_ = 0;
|
||||
allocated_ = false;
|
||||
}
|
||||
|
||||
void NoteUpdate::setEvents(const std::pair<uint32_t, float>* events, uint32_t count, bool copy)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (copy) {
|
||||
auto *buffer = new std::pair<uint32_t, float>[count];
|
||||
std::copy_n(events, count, buffer);
|
||||
events = buffer;
|
||||
}
|
||||
|
||||
events_ = events;
|
||||
Item* copy = new Item[count];
|
||||
std::copy_n(items, count, copy);
|
||||
events_.reset(copy);
|
||||
count_ = count;
|
||||
allocated_ = copy;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,62 +8,64 @@
|
|||
#include "SfizzVstState.h"
|
||||
#include <base/source/fobject.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief Update which notifies a FIFO queue of one-time updates
|
||||
*/
|
||||
class QueuedUpdates : public Steinberg::FObject {
|
||||
public:
|
||||
using List = std::vector<IPtr<FObject>>;
|
||||
|
||||
void enqueue(IPtr<FObject> update);
|
||||
List getUpdates(IDependent* dep);
|
||||
|
||||
void addDependent(IDependent* dep) override;
|
||||
void removeDependent(IDependent* dep) override;
|
||||
|
||||
OBJ_METHODS(QueuedUpdates, FObject)
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::map<IDependent*, List> updates_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which notifies a single OSC message
|
||||
* Is is supposed to be used synchronously.
|
||||
* (ie. FObject::changed or UpdateHandler::triggerUpdates)
|
||||
*/
|
||||
class OSCUpdate : public Steinberg::FObject {
|
||||
public:
|
||||
OSCUpdate() = default;
|
||||
~OSCUpdate();
|
||||
void clear();
|
||||
void setMessage(const void* data, uint32_t size, bool copy);
|
||||
|
||||
const void* data() const noexcept { return data_; }
|
||||
const uint32_t size() const noexcept { return size_; }
|
||||
OSCUpdate(const uint8* data, uint32 size);
|
||||
const uint8* data() const noexcept { return data_.get(); }
|
||||
uint32_t size() const noexcept { return size_; }
|
||||
|
||||
OBJ_METHODS(OSCUpdate, FObject)
|
||||
|
||||
private:
|
||||
const void* data_ = nullptr;
|
||||
uint32_t size_ = 0;
|
||||
bool allocated_ = false;
|
||||
|
||||
private:
|
||||
OSCUpdate(const OSCUpdate&) = delete;
|
||||
OSCUpdate& operator=(const OSCUpdate&) = delete;
|
||||
std::unique_ptr<uint8[]> data_;
|
||||
uint32 size_ = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which notifies one or more note on/off events
|
||||
* Is is supposed to be used synchronously.
|
||||
* (ie. FObject::changed or UpdateHandler::triggerUpdates)
|
||||
*/
|
||||
class NoteUpdate : public Steinberg::FObject {
|
||||
public:
|
||||
NoteUpdate() = default;
|
||||
~NoteUpdate();
|
||||
void clear();
|
||||
void setEvents(const std::pair<uint32_t, float>* events, uint32_t count, bool copy);
|
||||
using Item = std::pair<uint32_t, float>;
|
||||
|
||||
const std::pair<uint32_t, float>* events() const noexcept { return events_; }
|
||||
NoteUpdate(const Item* items, uint32 count);
|
||||
const Item* events() const noexcept { return events_.get(); }
|
||||
const uint32_t count() const noexcept { return count_; }
|
||||
|
||||
OBJ_METHODS(NoteUpdate, FObject)
|
||||
|
||||
private:
|
||||
const std::pair<uint32_t, float>* events_ = nullptr;
|
||||
std::unique_ptr<Item[]> events_;
|
||||
uint32_t count_ = 0;
|
||||
bool allocated_ = false;
|
||||
|
||||
private:
|
||||
NoteUpdate(const NoteUpdate&) = delete;
|
||||
NoteUpdate& operator=(const NoteUpdate&) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue