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>()); fileBlocks.push_back(std::make_shared<juce::MemoryBlock>());
fileNames.push_back(file.getFileName()); fileNames.push_back(file.getFileName());
fileIds.push_back(currentFileId++); 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())); sounds.push_back(new ShapeSound(parsers.back()));
file.createInputStream()->readIntoMemoryBlock(*fileBlocks.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>()); fileBlocks.push_back(std::make_shared<juce::MemoryBlock>());
fileNames.push_back(fileName); fileNames.push_back(fileName);
fileIds.push_back(currentFileId++); 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())); sounds.push_back(new ShapeSound(parsers.back()));
fileBlocks.back()->append(data, size); fileBlocks.back()->append(data, size);
@ -374,7 +374,7 @@ void OscirenderAudioProcessor::addFile(juce::String fileName, std::shared_ptr<ju
fileBlocks.push_back(data); fileBlocks.push_back(data);
fileNames.push_back(fileName); fileNames.push_back(fileName);
fileIds.push_back(currentFileId++); 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())); sounds.push_back(new ShapeSound(parsers.back()));
openFile(fileBlocks.size() - 1); openFile(fileBlocks.size() - 1);

Wyświetl plik

@ -231,10 +231,7 @@ public:
std::shared_ptr<DelayEffect> delayEffect = std::make_shared<DelayEffect>(); 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<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]() { std::shared_ptr<PerspectiveEffect> perspectiveEffect = std::make_shared<PerspectiveEffect>(VERSION_HINT, errorCallback);
return LuaVariables{ (int) currentSampleRate.load(), frequency.load()};
};
std::shared_ptr<PerspectiveEffect> perspectiveEffect = std::make_shared<PerspectiveEffect>(VERSION_HINT, errorCallback, variableCallback);
BooleanParameter* midiEnabled = new BooleanParameter("MIDI Enabled", "midiEnabled", VERSION_HINT, false); BooleanParameter* midiEnabled = new BooleanParameter("MIDI Enabled", "midiEnabled", VERSION_HINT, false);
BooleanParameter* inputEnabled = new BooleanParameter("Audio Input Enabled", "inputEnabled", 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"; 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); fixedRotateX = new BooleanParameter("Perspective Fixed Rotate X", "perspectiveFixedRotateX", versionHint, false);
fixedRotateY = new BooleanParameter("Perspective Fixed Rotate Y", "perspectiveFixedRotateY", versionHint, false); fixedRotateY = new BooleanParameter("Perspective Fixed Rotate Y", "perspectiveFixedRotateY", versionHint, false);
fixedRotateZ = new BooleanParameter("Perspective Fixed Rotate Z", "perspectiveFixedRotateZ", 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) { Vector2 PerspectiveEffect::apply(int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
auto effectScale = values[0]; auto effectScale = values[0];
auto depth = 1.0 + (values[1] - 0.1) * 3; 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("y", y);
parser->setVariable("z", z); parser->setVariable("z", z);
auto result = parser->run(); auto result = parser->run(L, LuaVariables{sampleRate, 0}, step, phase);
if (result.size() >= 3) { if (result.size() >= 3) {
x = result[0]; x = result[0];
y = result[1]; y = result[1];
@ -94,7 +98,7 @@ void PerspectiveEffect::updateCode(const juce::String& newCode) {
juce::SpinLock::ScopedLockType lock(codeLock); juce::SpinLock::ScopedLockType lock(codeLock);
defaultScript = newCode == DEFAULT_SCRIPT; defaultScript = newCode == DEFAULT_SCRIPT;
code = newCode; 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) { void PerspectiveEffect::setVariable(juce::String variableName, double value) {

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -1,3 +1,6 @@
#ifndef LUAIMPORT_H
#define LUAIMPORT_H
extern "C" { extern "C" {
#include "lapi.c" #include "lapi.c"
#include "lauxlib.c" #include "lauxlib.c"
@ -33,3 +36,5 @@ extern "C" {
#include "lvm.c" #include "lvm.c"
#include "lzio.c" #include "lzio.c"
} }
#endif

Wyświetl plik

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

Wyświetl plik

@ -10,11 +10,12 @@
class FileParser { class FileParser {
public: 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); void parse(juce::String fileName, juce::String extension, std::unique_ptr<juce::InputStream>, juce::Font);
std::vector<std::unique_ptr<Shape>> nextFrame(); 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 isSample();
bool isActive(); bool isActive();
void disable(); void disable();
@ -41,5 +42,4 @@ private:
juce::String fallbackLuaScript = "return { 0.0, 0.0 }"; juce::String fallbackLuaScript = "return { 0.0, 0.0 }";
std::function<void(int, juce::String, juce::String)> errorCallback; 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"> <VS2022 targetFolder="Builds/VisualStudio2022" smallIcon="pSc1mq" bigIcon="pSc1mq">
<CONFIGURATIONS> <CONFIGURATIONS>
<CONFIGURATION isDebug="1" name="Debug" targetName="osci-render"/> <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> </CONFIGURATIONS>
<MODULEPATHS> <MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/> <MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>