|
|
@@ -57,7 +57,7 @@ public: |
|
|
|
void flush() override {} |
|
|
|
|
|
|
|
private: |
|
|
|
std::string buildScProcessBlockString(const ProcessBlock* block) const noexcept; |
|
|
|
const char* buildScProcessBlockString(const ProcessBlock* block) const noexcept; |
|
|
|
int getResultAsInt(const char* text) noexcept; |
|
|
|
bool isVcvPrototypeProcessBlock(const PyrSlot* slot) const noexcept; |
|
|
|
|
|
|
@@ -161,82 +161,80 @@ void SC_VcvPrototypeClient::postText(const char* str, size_t len) { |
|
|
|
_engine->display(std::string(str, len)); |
|
|
|
} |
|
|
|
|
|
|
|
// 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 = insOutsSize + otherArraysSize + overhead; |
|
|
|
static char scratchBuf[bufferSize]; |
|
|
|
|
|
|
|
std::string SC_VcvPrototypeClient::buildScProcessBlockString(const ProcessBlock* block) const noexcept { |
|
|
|
constexpr unsigned bufferSize = overhead + insOutsSize + otherArraysSize; |
|
|
|
|
|
|
|
// 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 |
|
|
|
|
|
|
|
template <typename... Ts> |
|
|
|
static void doAppend(char*& buf, int& size, const char* fmt, Ts... vals) { |
|
|
|
#pragma GCC diagnostic push |
|
|
|
#pragma GCC diagnostic ignored "-Wformat-security" |
|
|
|
auto result = std::snprintf(buf, size, fmt, vals...); |
|
|
|
#pragma GCC diagnostic pop |
|
|
|
buf += result; |
|
|
|
size -= result; |
|
|
|
} |
|
|
|
|
|
|
|
std::ostringstream builder; |
|
|
|
const char* SC_VcvPrototypeClient::buildScProcessBlockString(const ProcessBlock* block) const noexcept { |
|
|
|
auto* buf = processBlockStringScratchBuf + processBeginStringOffset - 1; |
|
|
|
int size = sizeof(processBlockStringScratchBuf) - processBeginStringOffset + 1; |
|
|
|
|
|
|
|
// TODO so expensive |
|
|
|
builder << std::fixed; // to ensure floats aren't actually treated as Integers |
|
|
|
builder << "^~vcv_process.(VcvPrototypeProcessBlock.new(" |
|
|
|
<< block->sampleRate << ',' |
|
|
|
<< block->sampleTime << ',' |
|
|
|
<< block->bufferSize << ','; |
|
|
|
// Perhaps imprudently assuming snprintf never returns a negative code |
|
|
|
doAppend(buf, size, "%.6f,%.6f,%d,", block->sampleRate, block->sampleTime, block->bufferSize); |
|
|
|
|
|
|
|
// after this, all floats are printed in float arrays, so we don't need to worry about misinterpreting |
|
|
|
builder << std::defaultfloat; |
|
|
|
auto&& appendInOutArray = [&builder](const int bufferSize, const float (&data)[NUM_ROWS][MAX_BUFFER_SIZE]) { |
|
|
|
builder << '['; |
|
|
|
auto&& appendInOutArray = [&buf, &size](const int bufferSize, const float (&data)[NUM_ROWS][MAX_BUFFER_SIZE]) { |
|
|
|
doAppend(buf, size, "["); |
|
|
|
for (int i = 0; i < NUM_ROWS; ++i) { |
|
|
|
builder << "FloatArray["; |
|
|
|
doAppend(buf, size, "FloatArray["); |
|
|
|
for (int j = 0; j < bufferSize; ++j) { |
|
|
|
builder << data[i][j]; |
|
|
|
if (j != bufferSize - 1) |
|
|
|
builder << ','; |
|
|
|
doAppend(buf, size, "%g%c", data[i][j], j == bufferSize - 1 ? ' ' : ','); |
|
|
|
} |
|
|
|
builder << ']'; |
|
|
|
if (i != NUM_ROWS - 1) |
|
|
|
builder << ','; |
|
|
|
doAppend(buf, size, "]%c", i == NUM_ROWS - 1 ? ' ' : ','); |
|
|
|
} |
|
|
|
builder << ']'; |
|
|
|
doAppend(buf, size, "],"); |
|
|
|
}; |
|
|
|
|
|
|
|
appendInOutArray(block->bufferSize, block->inputs); |
|
|
|
builder << ','; |
|
|
|
appendInOutArray(block->bufferSize, block->outputs); |
|
|
|
builder << ','; |
|
|
|
|
|
|
|
// knobs |
|
|
|
builder << "FloatArray["; |
|
|
|
for (int i = 0; i < NUM_ROWS; ++i) { |
|
|
|
builder << block->knobs[i]; |
|
|
|
if (i != NUM_ROWS - 1) |
|
|
|
builder << ','; |
|
|
|
} |
|
|
|
builder << "],"; |
|
|
|
doAppend(buf, size, "FloatArray["); |
|
|
|
for (int i = 0; i < NUM_ROWS; ++i) |
|
|
|
doAppend(buf, size, "%g%c", block->knobs[i], i == NUM_ROWS - 1 ? ' ' : ','); |
|
|
|
|
|
|
|
// switches |
|
|
|
builder << '[' << std::boolalpha; |
|
|
|
for (int i = 0; i < NUM_ROWS; ++i) { |
|
|
|
builder << block->switches[i]; |
|
|
|
if (i != NUM_ROWS - 1) |
|
|
|
builder << ','; |
|
|
|
} |
|
|
|
builder << "],"; |
|
|
|
doAppend(buf, size, "],["); |
|
|
|
for (int i = 0; i < NUM_ROWS; ++i) |
|
|
|
doAppend(buf, size, "%s%c", block->switches[i] ? "true" : "false", i == NUM_ROWS - 1 ? ' ' : ','); |
|
|
|
doAppend(buf, size, "]"); |
|
|
|
|
|
|
|
// lights, switchlights |
|
|
|
auto&& appendLightsArray = [&builder](const float (&array)[NUM_ROWS][3]) { |
|
|
|
builder << '['; |
|
|
|
auto&& appendLightsArray = [&buf, &size](const float (&array)[NUM_ROWS][3]) { |
|
|
|
doAppend(buf, size, ",["); |
|
|
|
for (int i = 0; i < NUM_ROWS; ++i) { |
|
|
|
builder << "FloatArray[" << array[i][0] << ',' << array[i][1] << ',' << array[i][2] << ']'; |
|
|
|
if (i != NUM_ROWS - 1) |
|
|
|
builder << ','; |
|
|
|
doAppend(buf, size, "FloatArray[%g,%g,%g]%c", array[i][0], array[i][1], array[i][2], |
|
|
|
i == NUM_ROWS - 1 ? ' ' : ','); |
|
|
|
} |
|
|
|
builder << ']'; |
|
|
|
doAppend(buf, size, "]"); |
|
|
|
}; |
|
|
|
|
|
|
|
appendLightsArray(block->lights); |
|
|
|
builder << ','; |
|
|
|
appendLightsArray(block->switchLights); |
|
|
|
|
|
|
|
builder << "));\n"; |
|
|
|
return builder.str(); |
|
|
|
doAppend(buf, size, "));"); |
|
|
|
|
|
|
|
// printf("%s\nWrote %lu, %u remaining", processBlockStringScratchBuf, buf - processBlockStringScratchBuf, size); |
|
|
|
|
|
|
|
return processBlockStringScratchBuf; |
|
|
|
} |
|
|
|
|
|
|
|
bool SC_VcvPrototypeClient::isVcvPrototypeProcessBlock(const PyrSlot* slot) const noexcept { |
|
|
@@ -333,8 +331,8 @@ static unsigned int timesIndex = 0; |
|
|
|
void SC_VcvPrototypeClient::evaluateProcessBlock(ProcessBlock* block) noexcept { |
|
|
|
// TODO timing test code |
|
|
|
auto start = std::chrono::high_resolution_clock::now(); |
|
|
|
auto&& string = buildScProcessBlockString(block); |
|
|
|
interpret(string.c_str()); |
|
|
|
auto* buf = buildScProcessBlockString(block); |
|
|
|
interpret(buf); |
|
|
|
readScProcessBlockResult(block); |
|
|
|
auto end = std::chrono::high_resolution_clock::now(); |
|
|
|
auto ticks = (end - start).count(); |
|
|
|