|
|
@@ -43,8 +43,8 @@ public: |
|
|
|
std::string&& command = "VcvPrototypeProcessBlock.numRows = " + std::to_string(NUM_ROWS); |
|
|
|
interpret(command.c_str()); |
|
|
|
} |
|
|
|
int getFrameDivider() noexcept { return getResultAsInt("^~vcv_frameDivider"); } |
|
|
|
int getBufferSize() noexcept { return getResultAsInt("^~vcv_bufferSize"); } |
|
|
|
int getFrameDivider() noexcept { return getInterpretResultAsInt("^~vcv_frameDivider"); } |
|
|
|
int getBufferSize() noexcept { return getInterpretResultAsInt("^~vcv_bufferSize"); } |
|
|
|
|
|
|
|
bool isOk() const noexcept { return _ok; } |
|
|
|
|
|
|
@@ -56,19 +56,21 @@ public: |
|
|
|
void flush() override {} |
|
|
|
|
|
|
|
private: |
|
|
|
// Called on unrecoverable error, will stop the plugin |
|
|
|
void fail(const std::string& msg) noexcept; |
|
|
|
|
|
|
|
const char* buildScProcessBlockString(const ProcessBlock* block) const noexcept; |
|
|
|
int getResultAsInt(const char* text) noexcept; |
|
|
|
bool isVcvPrototypeProcessBlock(const PyrSlot* slot) const noexcept; |
|
|
|
|
|
|
|
void fail(const std::string& msg) noexcept; |
|
|
|
int getInterpretResultAsInt(const char* text) noexcept; |
|
|
|
|
|
|
|
// converts top of stack back to ProcessBlock data |
|
|
|
void readScProcessBlockResult(ProcessBlock* block) noexcept; |
|
|
|
|
|
|
|
// helpers for copying SC info back into process block's arrays |
|
|
|
bool isVcvPrototypeProcessBlock(const PyrSlot* slot) const noexcept; |
|
|
|
bool copyFloatArray(const PyrSlot& inSlot, const char* context, float* outArray, int size) noexcept; |
|
|
|
template <typename Array> |
|
|
|
bool copyArrayOfFloatArrays(const PyrSlot& inSlot, const char* context, Array& array, int size) noexcept; |
|
|
|
bool copyFloatArray(const PyrSlot& inSlot, const char* context, float* outArray, int size) noexcept; |
|
|
|
|
|
|
|
SuperColliderEngine* _engine; |
|
|
|
PyrSymbol* _vcvPrototypeProcessBlockSym; |
|
|
@@ -157,27 +159,68 @@ void SC_VcvPrototypeClient::interpret(const char* text) noexcept { |
|
|
|
interpretCmdLine(); |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef SC_VCV_ENGINE_TIMING |
|
|
|
static long long int gmax = 0; |
|
|
|
static constexpr unsigned int nTimes = 1024; |
|
|
|
static long long int times[nTimes] = {}; |
|
|
|
static unsigned int timesIndex = 0; |
|
|
|
#endif |
|
|
|
|
|
|
|
void SC_VcvPrototypeClient::evaluateProcessBlock(ProcessBlock* block) noexcept { |
|
|
|
#ifdef SC_VCV_ENGINE_TIMING |
|
|
|
auto start = std::chrono::high_resolution_clock::now(); |
|
|
|
#endif |
|
|
|
auto* buf = buildScProcessBlockString(block); |
|
|
|
interpret(buf); |
|
|
|
readScProcessBlockResult(block); |
|
|
|
#ifdef SC_VCV_ENGINE_TIMING |
|
|
|
auto end = std::chrono::high_resolution_clock::now(); |
|
|
|
auto ticks = (end - start).count(); |
|
|
|
|
|
|
|
times[timesIndex] = ticks; |
|
|
|
timesIndex++; |
|
|
|
timesIndex %= nTimes; |
|
|
|
if (gmax < ticks) |
|
|
|
{ |
|
|
|
gmax = ticks; |
|
|
|
std::printf("MAX TIME %lld\n", ticks); |
|
|
|
} |
|
|
|
if (timesIndex == 0) |
|
|
|
{ |
|
|
|
std::printf("AVG TIME %lld\n", std::accumulate(std::begin(times), std::end(times), 0ull) / nTimes); |
|
|
|
} |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void SC_VcvPrototypeClient::postText(const char* str, size_t len) { |
|
|
|
// Ensure the last message logged (presumably an error) stays onscreen. |
|
|
|
if (_ok) |
|
|
|
_engine->display(std::string(str, len)); |
|
|
|
} |
|
|
|
|
|
|
|
void SC_VcvPrototypeClient::fail(const std::string& msg) noexcept { |
|
|
|
// Ensure the last messaged logged in a previous failure stays onscreen. |
|
|
|
if (_ok) |
|
|
|
_engine->display(msg); |
|
|
|
_ok = false; |
|
|
|
} |
|
|
|
|
|
|
|
// This should be well above what we ever need to represent a process block. |
|
|
|
constexpr unsigned overhead = 512; |
|
|
|
constexpr unsigned floatSize = 10; |
|
|
|
constexpr unsigned insOutsSize = MAX_BUFFER_SIZE * NUM_ROWS * 2 * floatSize; |
|
|
|
constexpr unsigned otherArraysSize = floatSize * NUM_ROWS * 8; |
|
|
|
constexpr unsigned bufferSize = overhead + insOutsSize + otherArraysSize; |
|
|
|
// Currently comes out to around 500 KB. |
|
|
|
constexpr unsigned buildPbOverhead = 1024; |
|
|
|
constexpr unsigned buildPbFloatSize = 10; |
|
|
|
constexpr unsigned buildPbInsOutsSize = MAX_BUFFER_SIZE * NUM_ROWS * 2 * buildPbFloatSize; |
|
|
|
constexpr unsigned buildPbOtherArraysSize = buildPbFloatSize * NUM_ROWS * 8; |
|
|
|
constexpr unsigned buildPbBufferSize = buildPbOverhead + buildPbInsOutsSize + buildPbOtherArraysSize; |
|
|
|
|
|
|
|
// Don't write initial string every time |
|
|
|
#define PROCESS_BEGIN_STRING "^~vcv_process.(VcvPrototypeProcessBlock.new(" |
|
|
|
static char processBlockStringScratchBuf[bufferSize] = PROCESS_BEGIN_STRING; |
|
|
|
constexpr unsigned processBeginStringOffset = sizeof(PROCESS_BEGIN_STRING); |
|
|
|
#undef PROCESS_BEGIN_STRING |
|
|
|
#define BUILD_PB_BEGIN_STRING "^~vcv_process.(VcvPrototypeProcessBlock.new(" |
|
|
|
static char buildPbStringScratchBuf[buildPbBufferSize] = BUILD_PB_BEGIN_STRING; |
|
|
|
constexpr unsigned buildPbBeginStringOffset = sizeof(BUILD_PB_BEGIN_STRING); |
|
|
|
#undef BUILD_PB_BEGIN_STRING |
|
|
|
|
|
|
|
const char* SC_VcvPrototypeClient::buildScProcessBlockString(const ProcessBlock* block) const noexcept { |
|
|
|
auto* buf = processBlockStringScratchBuf + processBeginStringOffset - 1; |
|
|
|
auto* buf = buildPbStringScratchBuf + buildPbBeginStringOffset - 1; |
|
|
|
|
|
|
|
// Perhaps imprudently assuming snprintf never returns a negative code |
|
|
|
buf += std::sprintf(buf, "%.6f,%.6f,%d,", block->sampleRate, block->sampleTime, block->bufferSize); |
|
|
@@ -223,58 +266,27 @@ const char* SC_VcvPrototypeClient::buildScProcessBlockString(const ProcessBlock* |
|
|
|
|
|
|
|
buf += std::sprintf(buf, "));"); |
|
|
|
|
|
|
|
return processBlockStringScratchBuf; |
|
|
|
} |
|
|
|
|
|
|
|
bool SC_VcvPrototypeClient::isVcvPrototypeProcessBlock(const PyrSlot* slot) const noexcept { |
|
|
|
if (NotObj(slot)) |
|
|
|
return false; |
|
|
|
|
|
|
|
auto* klass = slotRawObject(slot)->classptr; |
|
|
|
auto* klassNameSymbol = slotRawSymbol(&klass->name); |
|
|
|
return klassNameSymbol == _vcvPrototypeProcessBlockSym; |
|
|
|
return buildPbStringScratchBuf; |
|
|
|
} |
|
|
|
|
|
|
|
template <typename Array> |
|
|
|
bool SC_VcvPrototypeClient::copyArrayOfFloatArrays(const PyrSlot& inSlot, const char* context, Array& outArray, int size) noexcept |
|
|
|
{ |
|
|
|
// OUTPUTS |
|
|
|
if (!isKindOfSlot(const_cast<PyrSlot*>(&inSlot), class_array)) { |
|
|
|
fail(std::string(context) + " must be a Array"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
auto* inObj = slotRawObject(&inSlot); |
|
|
|
if (inObj->size != NUM_ROWS) { |
|
|
|
fail(std::string(context) + " must be of size " + std::to_string(NUM_ROWS)); |
|
|
|
return false; |
|
|
|
} |
|
|
|
int SC_VcvPrototypeClient::getInterpretResultAsInt(const char* text) noexcept { |
|
|
|
interpret(text); |
|
|
|
|
|
|
|
for (int i = 0; i < NUM_ROWS; ++i) { |
|
|
|
if (!copyFloatArray(inObj->slots[i], "subarray", outArray[i], size)) { |
|
|
|
return false; |
|
|
|
auto* resultSlot = &scGlobals()->result; |
|
|
|
if (IsInt(resultSlot)) { |
|
|
|
auto intResult = slotRawInt(resultSlot); |
|
|
|
if (intResult > 0) { |
|
|
|
return intResult; |
|
|
|
} else { |
|
|
|
fail(std::string("Result of '") + text + "' should be > 0"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} else { |
|
|
|
fail(std::string("Result of '") + text + "' should be Integer"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool SC_VcvPrototypeClient::copyFloatArray(const PyrSlot& inSlot, const char* context, float* outArray, int size) noexcept |
|
|
|
{ |
|
|
|
if (!isKindOfSlot(const_cast<PyrSlot*>(&inSlot), class_floatarray)) { |
|
|
|
fail(std::string(context) + " must be a FloatArray"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
auto* floatArrayObj = slotRawObject(&inSlot); |
|
|
|
if (floatArrayObj->size != size) { |
|
|
|
fail(std::string(context) + " must be of size " + std::to_string(size)); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
auto* floatArray = reinterpret_cast<const PyrFloatArray*>(floatArrayObj); |
|
|
|
auto* rawArray = static_cast<const float*>(floatArray->f); |
|
|
|
std::memcpy(outArray, rawArray, size * sizeof(float)); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
void SC_VcvPrototypeClient::readScProcessBlockResult(ProcessBlock* block) noexcept { |
|
|
|
auto* resultSlot = &scGlobals()->result; |
|
|
@@ -302,60 +314,54 @@ void SC_VcvPrototypeClient::readScProcessBlockResult(ProcessBlock* block) noexce |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
void SC_VcvPrototypeClient::fail(const std::string& msg) noexcept { |
|
|
|
_engine->display(msg); |
|
|
|
_ok = false; |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef SC_VCV_ENGINE_TIMING |
|
|
|
static long long int gmax = 0; |
|
|
|
static constexpr unsigned int nTimes = 1024; |
|
|
|
static long long int times[nTimes] = {}; |
|
|
|
static unsigned int timesIndex = 0; |
|
|
|
#endif |
|
|
|
bool SC_VcvPrototypeClient::isVcvPrototypeProcessBlock(const PyrSlot* slot) const noexcept { |
|
|
|
if (NotObj(slot)) |
|
|
|
return false; |
|
|
|
|
|
|
|
void SC_VcvPrototypeClient::evaluateProcessBlock(ProcessBlock* block) noexcept { |
|
|
|
#ifdef SC_VCV_ENGINE_TIMING |
|
|
|
auto start = std::chrono::high_resolution_clock::now(); |
|
|
|
#endif |
|
|
|
auto* buf = buildScProcessBlockString(block); |
|
|
|
interpret(buf); |
|
|
|
readScProcessBlockResult(block); |
|
|
|
#ifdef SC_VCV_ENGINE_TIMING |
|
|
|
auto end = std::chrono::high_resolution_clock::now(); |
|
|
|
auto ticks = (end - start).count(); |
|
|
|
auto* klass = slotRawObject(slot)->classptr; |
|
|
|
auto* klassNameSymbol = slotRawSymbol(&klass->name); |
|
|
|
return klassNameSymbol == _vcvPrototypeProcessBlockSym; |
|
|
|
} |
|
|
|
|
|
|
|
times[timesIndex] = ticks; |
|
|
|
timesIndex++; |
|
|
|
timesIndex %= nTimes; |
|
|
|
if (gmax < ticks) |
|
|
|
{ |
|
|
|
gmax = ticks; |
|
|
|
std::printf("MAX TIME %lld\n", ticks); |
|
|
|
bool SC_VcvPrototypeClient::copyFloatArray(const PyrSlot& inSlot, const char* context, float* outArray, int size) noexcept |
|
|
|
{ |
|
|
|
if (!isKindOfSlot(const_cast<PyrSlot*>(&inSlot), class_floatarray)) { |
|
|
|
fail(std::string(context) + " must be a FloatArray"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
if (timesIndex == 0) |
|
|
|
{ |
|
|
|
std::printf("AVG TIME %lld\n", std::accumulate(std::begin(times), std::end(times), 0ull) / nTimes); |
|
|
|
auto* floatArrayObj = slotRawObject(&inSlot); |
|
|
|
if (floatArrayObj->size != size) { |
|
|
|
fail(std::string(context) + " must be of size " + std::to_string(size)); |
|
|
|
return false; |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
auto* floatArray = reinterpret_cast<const PyrFloatArray*>(floatArrayObj); |
|
|
|
auto* rawArray = static_cast<const float*>(floatArray->f); |
|
|
|
std::memcpy(outArray, rawArray, size * sizeof(float)); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
int SC_VcvPrototypeClient::getResultAsInt(const char* text) noexcept { |
|
|
|
interpret(text); |
|
|
|
template <typename Array> |
|
|
|
bool SC_VcvPrototypeClient::copyArrayOfFloatArrays(const PyrSlot& inSlot, const char* context, Array& outArray, int size) noexcept |
|
|
|
{ |
|
|
|
// OUTPUTS |
|
|
|
if (!isKindOfSlot(const_cast<PyrSlot*>(&inSlot), class_array)) { |
|
|
|
fail(std::string(context) + " must be a Array"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
auto* inObj = slotRawObject(&inSlot); |
|
|
|
if (inObj->size != NUM_ROWS) { |
|
|
|
fail(std::string(context) + " must be of size " + std::to_string(NUM_ROWS)); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
auto* resultSlot = &scGlobals()->result; |
|
|
|
if (IsInt(resultSlot)) { |
|
|
|
auto intResult = slotRawInt(resultSlot); |
|
|
|
if (intResult > 0) { |
|
|
|
return intResult; |
|
|
|
} else { |
|
|
|
fail(std::string("Result of '") + text + "' should be > 0"); |
|
|
|
return -1; |
|
|
|
for (int i = 0; i < NUM_ROWS; ++i) { |
|
|
|
if (!copyFloatArray(inObj->slots[i], "subarray", outArray[i], size)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} else { |
|
|
|
fail(std::string("Result of '") + text + "' should be Integer"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
__attribute__((constructor(1000))) |
|
|
|