Use independent states for every ShapeVoice for Lua

pull/170/head
James Ball 2023-12-20 23:30:20 +00:00
rodzic c17d5024cf
commit b85318a4fd
12 zmienionych plików z 95 dodań i 74 usunięć

Wyświetl plik

@ -350,7 +350,7 @@ void OscirenderAudioProcessor::addFile(juce::File file) {
fileBlocks.push_back(std::make_shared<juce::MemoryBlock>());
fileNames.push_back(file.getFileName());
fileIds.push_back(currentFileId++);
parsers.push_back(std::make_shared<FileParser>(errorCallback, variableCallback));
parsers.push_back(std::make_shared<FileParser>(errorCallback));
sounds.push_back(new ShapeSound(parsers.back()));
file.createInputStream()->readIntoMemoryBlock(*fileBlocks.back());
@ -362,7 +362,7 @@ void OscirenderAudioProcessor::addFile(juce::String fileName, const char* data,
fileBlocks.push_back(std::make_shared<juce::MemoryBlock>());
fileNames.push_back(fileName);
fileIds.push_back(currentFileId++);
parsers.push_back(std::make_shared<FileParser>(errorCallback, variableCallback));
parsers.push_back(std::make_shared<FileParser>(errorCallback));
sounds.push_back(new ShapeSound(parsers.back()));
fileBlocks.back()->append(data, size);
@ -374,7 +374,7 @@ void OscirenderAudioProcessor::addFile(juce::String fileName, std::shared_ptr<ju
fileBlocks.push_back(data);
fileNames.push_back(fileName);
fileIds.push_back(currentFileId++);
parsers.push_back(std::make_shared<FileParser>(errorCallback, variableCallback));
parsers.push_back(std::make_shared<FileParser>(errorCallback));
sounds.push_back(new ShapeSound(parsers.back()));
openFile(fileBlocks.size() - 1);

Wyświetl plik

@ -231,10 +231,7 @@ public:
std::shared_ptr<DelayEffect> delayEffect = std::make_shared<DelayEffect>();
std::function<void(int, juce::String, juce::String)> errorCallback = [this](int lineNum, juce::String fileName, juce::String error) { notifyErrorListeners(lineNum, fileName, error); };
std::function<LuaVariables()> variableCallback = [this]() {
return LuaVariables{ (int) currentSampleRate.load(), frequency.load()};
};
std::shared_ptr<PerspectiveEffect> perspectiveEffect = std::make_shared<PerspectiveEffect>(VERSION_HINT, errorCallback, variableCallback);
std::shared_ptr<PerspectiveEffect> perspectiveEffect = std::make_shared<PerspectiveEffect>(VERSION_HINT, errorCallback);
BooleanParameter* midiEnabled = new BooleanParameter("MIDI Enabled", "midiEnabled", VERSION_HINT, false);
BooleanParameter* inputEnabled = new BooleanParameter("Audio Input Enabled", "inputEnabled", VERSION_HINT, false);

Wyświetl plik

@ -4,12 +4,16 @@
const juce::String PerspectiveEffect::FILE_NAME = "6a3580b0-c5fc-4b28-a33e-e26a487f052f";
PerspectiveEffect::PerspectiveEffect(int versionHint, std::function<void(int, juce::String, juce::String)> errorCallback, std::function<LuaVariables()> variableCallback) : versionHint(versionHint), errorCallback(errorCallback), variableCallback(variableCallback) {
PerspectiveEffect::PerspectiveEffect(int versionHint, std::function<void(int, juce::String, juce::String)> errorCallback) : versionHint(versionHint), errorCallback(errorCallback) {
fixedRotateX = new BooleanParameter("Perspective Fixed Rotate X", "perspectiveFixedRotateX", versionHint, false);
fixedRotateY = new BooleanParameter("Perspective Fixed Rotate Y", "perspectiveFixedRotateY", versionHint, false);
fixedRotateZ = new BooleanParameter("Perspective Fixed Rotate Z", "perspectiveFixedRotateZ", versionHint, false);
}
PerspectiveEffect::~PerspectiveEffect() {
parser->close(L);
}
Vector2 PerspectiveEffect::apply(int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
auto effectScale = values[0];
auto depth = 1.0 + (values[1] - 0.1) * 3;
@ -49,7 +53,7 @@ Vector2 PerspectiveEffect::apply(int index, Vector2 input, const std::vector<dou
parser->setVariable("y", y);
parser->setVariable("z", z);
auto result = parser->run();
auto result = parser->run(L, LuaVariables{sampleRate, 0}, step, phase);
if (result.size() >= 3) {
x = result[0];
y = result[1];
@ -94,7 +98,7 @@ void PerspectiveEffect::updateCode(const juce::String& newCode) {
juce::SpinLock::ScopedLockType lock(codeLock);
defaultScript = newCode == DEFAULT_SCRIPT;
code = newCode;
parser = std::make_unique<LuaParser>(FILE_NAME, code, errorCallback, variableCallback);
parser = std::make_unique<LuaParser>(FILE_NAME, code, errorCallback);
}
void PerspectiveEffect::setVariable(juce::String variableName, double value) {

Wyświetl plik

@ -6,7 +6,8 @@
class PerspectiveEffect : public EffectApplication {
public:
PerspectiveEffect(int versionHint, std::function<void(int, juce::String, juce::String)> errorCallback, std::function<LuaVariables()> variableCallback);
PerspectiveEffect(int versionHint, std::function<void(int, juce::String, juce::String)> errorCallback);
~PerspectiveEffect();
// arbitrary UUID
static const juce::String FILE_NAME;
@ -25,8 +26,7 @@ private:
const juce::String DEFAULT_SCRIPT = "return { x, y, z }";
juce::String code = DEFAULT_SCRIPT;
std::function<void(int, juce::String, juce::String)> errorCallback;
std::function<LuaVariables()> variableCallback;
std::unique_ptr<LuaParser> parser = std::make_unique<LuaParser>(FILE_NAME, code, errorCallback, variableCallback);
std::unique_ptr<LuaParser> parser = std::make_unique<LuaParser>(FILE_NAME, code, errorCallback);
juce::SpinLock codeLock;
bool defaultScript = true;
@ -34,9 +34,14 @@ private:
float currentRotateX = 0;
float currentRotateY = 0;
float currentRotateZ = 0;
lua_State *L = nullptr;
int versionHint;
long step = 1;
double phase = 0;
double linearSpeedToActualSpeed(double rotateSpeed) {
double actualSpeed = (std::exp(3 * juce::jmin(10.0, std::abs(rotateSpeed))) - 1) / 50000;
if (rotateSpeed < 0) {

Wyświetl plik

@ -103,7 +103,7 @@ void ShapeVoice::renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int star
renderingSample = parser != nullptr && parser->isSample();
if (renderingSample) {
channels = parser->nextSample();
channels = parser->nextSample(L, LuaVariables{ audioProcessor.currentSampleRate, frequency }, step, phase);
} else if (currentShape < frame.size()) {
auto& shape = frame[currentShape];
double length = shape->length();

Wyświetl plik

@ -37,6 +37,10 @@ private:
bool currentlyPlaying = false;
double frequency = 1.0;
lua_State* L = nullptr;
long step = 1;
double phase = 0;
Env adsr;
double time = 0.0;
double releaseTime = 0.0;

Wyświetl plik

@ -1,15 +1,9 @@
#include "LuaParser.h"
#include "luaimport.h"
LuaParser::LuaParser(juce::String fileName, juce::String script, std::function<void(int, juce::String, juce::String)> errorCallback, std::function<LuaVariables()> variableCallback, juce::String fallbackScript) : fallbackScript(fallbackScript), errorCallback(errorCallback), variableCallback(variableCallback), fileName(fileName) {
reset(script);
}
LuaParser::LuaParser(juce::String fileName, juce::String script, std::function<void(int, juce::String, juce::String)> errorCallback, juce::String fallbackScript) : script(script), fallbackScript(fallbackScript), errorCallback(errorCallback), fileName(fileName) {}
LuaParser::~LuaParser() {
lua_close(L);
}
void LuaParser::reset(juce::String script) {
void LuaParser::reset(lua_State*& L, juce::String script) {
functionRef = -1;
if (L != nullptr) {
@ -17,11 +11,9 @@ void LuaParser::reset(juce::String script) {
}
L = luaL_newstate();
lua_atpanic(L, panic);
luaL_openlibs(L);
this->script = script;
parse();
parse(L);
}
void LuaParser::reportError(const char* errorChars) {
@ -49,7 +41,7 @@ void LuaParser::reportError(const char* errorChars) {
}
}
void LuaParser::parse() {
void LuaParser::parse(lua_State*& L) {
const int ret = luaL_loadstring(L, script.toUTF8());
if (ret != 0) {
const char* error = lua_tostring(L, -1);
@ -58,7 +50,7 @@ void LuaParser::parse() {
functionRef = -1;
usingFallbackScript = true;
if (script != fallbackScript) {
reset(fallbackScript);
reset(L, fallbackScript);
}
} else {
functionRef = luaL_ref(L, LUA_REGISTRYINDEX);
@ -66,14 +58,18 @@ void LuaParser::parse() {
}
// only the audio thread runs this fuction
std::vector<float> LuaParser::run() {
std::vector<float> LuaParser::run(lua_State*& L, const LuaVariables vars, long& step, double& phase) {
// if we haven't seen this state before, reset it
if (std::find(seenStates.begin(), seenStates.end(), L) == seenStates.end()) {
reset(L, script);
seenStates.push_back(L);
}
std::vector<float> values;
lua_pushnumber(L, step);
lua_setglobal(L, "step");
auto vars = variableCallback();
lua_pushnumber(L, vars.sampleRate);
lua_setglobal(L, "sample_rate");
@ -84,18 +80,13 @@ std::vector<float> LuaParser::run() {
lua_setglobal(L, "phase");
// this CANNOT run at the same time as setVariable
if (updateVariables) {
juce::SpinLock::ScopedTryLockType lock(variableLock);
if (lock.isLocked()) {
for (int i = 0; i < variableNames.size(); i++) {
lua_pushnumber(L, variables[i]);
lua_setglobal(L, variableNames[i].toUTF8());
}
variableNames.clear();
variables.clear();
updateVariables = false;
}
}
juce::SpinLock::ScopedTryLockType lock(variableLock);
if (lock.isLocked()) {
for (int i = 0; i < variableNames.size(); i++) {
lua_pushnumber(L, variables[i]);
lua_setglobal(L, variableNames[i].toUTF8());
}
}
lua_geti(L, LUA_REGISTRYINDEX, functionRef);
@ -107,7 +98,7 @@ std::vector<float> LuaParser::run() {
functionRef = -1;
usingFallbackScript = true;
if (script != fallbackScript) {
reset(fallbackScript);
reset(L, fallbackScript);
}
} else if (lua_istable(L, -1)) {
auto length = lua_rawlen(L, -1);
@ -124,7 +115,7 @@ std::vector<float> LuaParser::run() {
functionRef = -1;
usingFallbackScript = true;
if (script != fallbackScript) {
reset(fallbackScript);
reset(L, fallbackScript);
}
}
@ -148,9 +139,23 @@ std::vector<float> LuaParser::run() {
// many threads can run this function
void LuaParser::setVariable(juce::String variableName, double value) {
juce::SpinLock::ScopedLockType lock(variableLock);
variableNames.push_back(variableName);
variables.push_back(value);
updateVariables = true;
// find variable index
int index = -1;
for (int i = 0; i < variableNames.size(); i++) {
if (variableNames[i] == variableName) {
index = i;
break;
}
}
if (index == -1) {
// add new variable
variableNames.push_back(variableName);
variables.push_back(value);
} else {
// update existing variable
variables[index] = value;
}
}
bool LuaParser::isFunctionValid() {
@ -165,9 +170,8 @@ void LuaParser::resetErrors() {
errorCallback(-1, fileName, "");
}
int LuaParser::panic(lua_State *L) {
const char *msg = lua_tostring(L, -1);
if (msg == NULL) msg = "error object is not a string";
DBG("PANIC: unprotected error in call to Lua API (%s)\n" << msg);
return 0; /* return to Lua to abort */
}
void LuaParser::close(lua_State*& L) {
if (L != nullptr) {
lua_close(L);
}
}

Wyświetl plik

@ -11,34 +11,29 @@ public:
};
struct LuaVariables {
int sampleRate;
double sampleRate;
double frequency;
};
struct lua_State;
class LuaParser {
public:
LuaParser(juce::String fileName, juce::String script, std::function<void(int, juce::String, juce::String)> errorCallback, std::function<LuaVariables()> variableCallback, juce::String fallbackScript = "return { 0.0, 0.0 }");
~LuaParser();
LuaParser(juce::String fileName, juce::String script, std::function<void(int, juce::String, juce::String)> errorCallback, juce::String fallbackScript = "return { 0.0, 0.0 }");
std::vector<float> run();
std::vector<float> run(lua_State*& L, const LuaVariables vars, long& step, double& phase);
void setVariable(juce::String variableName, double value);
bool isFunctionValid();
juce::String getScript();
void resetErrors();
void close(lua_State*& L);
private:
void reset(juce::String script);
void reset(lua_State*& L, juce::String script);
void reportError(const char* error);
void parse();
static int panic(lua_State* L);
void parse(lua_State*& L);
int functionRef = -1;
bool usingFallbackScript = false;
long step = 1;
double phase = 0.0;
lua_State* L = nullptr;
juce::String script;
juce::String fallbackScript;
std::atomic<bool> updateVariables = false;
@ -46,6 +41,6 @@ private:
std::vector<juce::String> variableNames;
std::vector<double> variables;
std::function<void(int, juce::String, juce::String)> errorCallback;
std::function<LuaVariables()> variableCallback;
juce::String fileName;
std::vector<lua_State*> seenStates;
};

Wyświetl plik

@ -1,3 +1,6 @@
#ifndef LUAIMPORT_H
#define LUAIMPORT_H
extern "C" {
#include "lapi.c"
#include "lauxlib.c"
@ -32,4 +35,6 @@ extern "C" {
#include "lutf8lib.c"
#include "lvm.c"
#include "lzio.c"
}
}
#endif

Wyświetl plik

@ -3,7 +3,7 @@
#include "../shape/CircleArc.h"
#include <numbers>
FileParser::FileParser(std::function<void(int, juce::String, juce::String)> errorCallback, std::function<LuaVariables()> variableCallback) : errorCallback(errorCallback), variableCallback(variableCallback) {}
FileParser::FileParser(std::function<void(int, juce::String, juce::String)> errorCallback) : errorCallback(errorCallback) {}
void FileParser::parse(juce::String fileName, juce::String extension, std::unique_ptr<juce::InputStream> stream, juce::Font font) {
juce::SpinLock::ScopedLockType scope(lock);
@ -27,7 +27,7 @@ void FileParser::parse(juce::String fileName, juce::String extension, std::uniqu
} else if (extension == ".txt") {
text = std::make_shared<TextParser>(stream->readEntireStreamAsString(), font);
} else if (extension == ".lua") {
lua = std::make_shared<LuaParser>(fileName, stream->readEntireStreamAsString(), errorCallback, variableCallback, fallbackLuaScript);
lua = std::make_shared<LuaParser>(fileName, stream->readEntireStreamAsString(), errorCallback, fallbackLuaScript);
}
sampleSource = lua != nullptr;
@ -48,11 +48,11 @@ std::vector<std::unique_ptr<Shape>> FileParser::nextFrame() {
return tempShapes;
}
Vector2 FileParser::nextSample() {
Vector2 FileParser::nextSample(lua_State*& L, const LuaVariables vars, long& step, double& phase) {
juce::SpinLock::ScopedLockType scope(lock);
if (lua != nullptr) {
auto values = lua->run();
auto values = lua->run(L, vars, step, phase);
if (values.size() < 2) {
return Vector2();
}
@ -60,6 +60,12 @@ Vector2 FileParser::nextSample() {
}
}
void FileParser::closeLua(lua_State*& L) {
if (lua != nullptr) {
lua->close(L);
}
}
bool FileParser::isSample() {
return sampleSource;
}

Wyświetl plik

@ -10,11 +10,12 @@
class FileParser {
public:
FileParser(std::function<void(int, juce::String, juce::String)> errorCallback = nullptr, std::function<LuaVariables()> variableCallback = nullptr);
FileParser(std::function<void(int, juce::String, juce::String)> errorCallback = nullptr);
void parse(juce::String fileName, juce::String extension, std::unique_ptr<juce::InputStream>, juce::Font);
std::vector<std::unique_ptr<Shape>> nextFrame();
Vector2 nextSample();
Vector2 nextSample(lua_State*& L, const LuaVariables vars, long& step, double& phase);
void closeLua(lua_State*& L);
bool isSample();
bool isActive();
void disable();
@ -41,5 +42,4 @@ private:
juce::String fallbackLuaScript = "return { 0.0, 0.0 }";
std::function<void(int, juce::String, juce::String)> errorCallback;
std::function<LuaVariables()> variableCallback;
};

Wyświetl plik

@ -502,7 +502,8 @@
<VS2022 targetFolder="Builds/VisualStudio2022" smallIcon="pSc1mq" bigIcon="pSc1mq">
<CONFIGURATIONS>
<CONFIGURATION isDebug="1" name="Debug" targetName="osci-render"/>
<CONFIGURATION isDebug="0" name="Release" targetName="osci-render"/>
<CONFIGURATION isDebug="0" name="Release" targetName="osci-render" alwaysGenerateDebugSymbols="1"
debugInformationFormat="ProgramDatabase"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>