kopia lustrzana https://github.com/jameshball/osci-render
Use independent states for every ShapeVoice for Lua
rodzic
c17d5024cf
commit
b85318a4fd
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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"/>
|
||||
|
|
Ładowanie…
Reference in New Issue