kopia lustrzana https://github.com/jameshball/osci-render
Refactor LuaParser and make it abort running Lua if taking too long
rodzic
faaa049b9d
commit
2e23c485b6
|
@ -30,7 +30,7 @@ OscirenderLookAndFeel::OscirenderLookAndFeel() {
|
|||
|
||||
// combo box
|
||||
setColour(juce::ComboBox::backgroundColourId, Colours::veryDark);
|
||||
setColour(juce::ComboBox::outlineColourId, juce::Colours::white);
|
||||
setColour(juce::ComboBox::outlineColourId, Colours::veryDark);
|
||||
setColour(juce::ComboBox::arrowColourId, juce::Colours::white);
|
||||
|
||||
// text box
|
||||
|
@ -92,10 +92,10 @@ void OscirenderLookAndFeel::drawComboBox(juce::Graphics& g, int width, int heigh
|
|||
juce::Rectangle<int> boxBounds{0, 0, width, height};
|
||||
|
||||
g.setColour(box.findColour(juce::ComboBox::backgroundColourId));
|
||||
g.fillRect(boxBounds.toFloat());
|
||||
g.fillRoundedRectangle(boxBounds.toFloat(), RECT_RADIUS);
|
||||
|
||||
g.setColour(box.findColour(juce::ComboBox::outlineColourId).withAlpha(box.isEnabled() ? 1.0f : 0.5f));
|
||||
g.drawRect(boxBounds.toFloat(), 1.0f);
|
||||
g.drawRoundedRectangle(boxBounds.toFloat(), RECT_RADIUS, 1.0f);
|
||||
|
||||
juce::Rectangle<int> arrowZone{width - 15, 0, 10, height};
|
||||
juce::Path path;
|
||||
|
@ -164,8 +164,14 @@ void OscirenderLookAndFeel::drawGroupComponentOutline(juce::Graphics& g, int wid
|
|||
|
||||
auto alpha = group.isEnabled() ? 1.0f : 0.5f;
|
||||
|
||||
juce::Path background;
|
||||
background.addRoundedRectangle(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), RECT_RADIUS, RECT_RADIUS);
|
||||
auto ds = juce::DropShadow(juce::Colours::black, 3, juce::Point<int>(0, 0));
|
||||
ds.drawForPath(g, background);
|
||||
|
||||
g.setColour(group.findColour(groupComponentBackgroundColourId).withMultipliedAlpha(alpha));
|
||||
g.fillRect(bounds);
|
||||
g.fillPath(background);
|
||||
|
||||
|
||||
auto header = bounds.removeFromTop(2 * textH);
|
||||
|
||||
|
@ -192,8 +198,13 @@ void OscirenderLookAndFeel::drawLinearSlider(juce::Graphics& g, int x, int y, in
|
|||
|
||||
auto thumbWidth = getSliderThumbRadius(slider);
|
||||
|
||||
juce::Path thumb;
|
||||
thumb.addEllipse(juce::Rectangle<float>(static_cast<float>(thumbWidth), static_cast<float>(thumbWidth)).withCentre(point));
|
||||
juce::DropShadow ds(juce::Colours::black, 1, { 0, 1 });
|
||||
|
||||
ds.drawForPath(g, thumb);
|
||||
g.setColour(slider.findColour(sliderThumbOutlineColourId).withAlpha(slider.isEnabled() ? 1.0f : 0.5f));
|
||||
g.drawEllipse(juce::Rectangle<float>(static_cast<float>(thumbWidth), static_cast<float>(thumbWidth)).withCentre(point), 1.0f);
|
||||
g.strokePath(thumb, juce::PathStrokeType(1.0f));
|
||||
}
|
||||
|
||||
void OscirenderLookAndFeel::drawButtonBackground(juce::Graphics& g, juce::Button& button, const juce::Colour& backgroundColour, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) {
|
||||
|
|
|
@ -69,6 +69,8 @@ class OscirenderLookAndFeel : public juce::LookAndFeel_V4 {
|
|||
public:
|
||||
OscirenderLookAndFeel();
|
||||
|
||||
static const int RECT_RADIUS = 5;
|
||||
|
||||
void drawComboBox(juce::Graphics& g, int width, int height, bool, int, int, int, int, juce::ComboBox& box) override;
|
||||
void positionComboBoxText(juce::ComboBox& box, juce::Label& label) override;
|
||||
void drawTickBox(juce::Graphics& g, juce::Component& component,
|
||||
|
|
|
@ -152,24 +152,24 @@ void OscirenderAudioProcessorEditor::paint(juce::Graphics& g) {
|
|||
|
||||
auto ds = juce::DropShadow(juce::Colours::black, 5, juce::Point<int>(0, 0));
|
||||
|
||||
if (!usingNativeMenuBar) {
|
||||
// add drop shadow to the menu bar
|
||||
ds.drawForRectangle(g, menuBar.getBounds());
|
||||
}
|
||||
// if (!usingNativeMenuBar) {
|
||||
// // add drop shadow to the menu bar
|
||||
// ds.drawForRectangle(g, menuBar.getBounds());
|
||||
// }
|
||||
|
||||
for (int i = 0; i < codeEditors.size(); i++) {
|
||||
if (codeEditors[i]->isVisible()) {
|
||||
ds.drawForRectangle(g, codeEditors[i]->getBounds());
|
||||
}
|
||||
}
|
||||
|
||||
if (lua.isVisible()) {
|
||||
ds.drawForRectangle(g, lua.getBounds());
|
||||
}
|
||||
//for (int i = 0; i < codeEditors.size(); i++) {
|
||||
// if (codeEditors[i]->isVisible()) {
|
||||
// ds.drawForRectangle(g, codeEditors[i]->getBounds());
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//if (lua.isVisible()) {
|
||||
// ds.drawForRectangle(g, lua.getBounds());
|
||||
//}
|
||||
|
||||
if (console.isVisible()) {
|
||||
ds.drawForRectangle(g, console.getBounds());
|
||||
}
|
||||
// if (console.isVisible()) {
|
||||
// ds.drawForRectangle(g, console.getBounds());
|
||||
// }
|
||||
}
|
||||
|
||||
void OscirenderAudioProcessorEditor::resized() {
|
||||
|
|
|
@ -97,13 +97,13 @@ void SettingsComponent::mouseDown(const juce::MouseEvent& event) {
|
|||
|
||||
void SettingsComponent::paint(juce::Graphics& g) {
|
||||
// add drop shadow to each component
|
||||
auto dc = juce::DropShadow(juce::Colours::black, 5, juce::Point<int>(0, 0));
|
||||
dc.drawForRectangle(g, main.getBounds());
|
||||
dc.drawForRectangle(g, effects.getBounds());
|
||||
dc.drawForRectangle(g, midi.getBounds());
|
||||
dc.drawForRectangle(g, perspective.getBounds());
|
||||
//auto dc = juce::DropShadow(juce::Colours::black, 5, juce::Point<int>(0, 0));
|
||||
//dc.drawForRectangle(g, main.getBounds());
|
||||
//dc.drawForRectangle(g, effects.getBounds());
|
||||
//dc.drawForRectangle(g, midi.getBounds());
|
||||
//dc.drawForRectangle(g, perspective.getBounds());
|
||||
|
||||
if (txt.isVisible()) {
|
||||
dc.drawForRectangle(g, txt.getBounds());
|
||||
}
|
||||
//if (txt.isVisible()) {
|
||||
// dc.drawForRectangle(g, txt.getBounds());
|
||||
//}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,23 @@
|
|||
std::function<void(const std::string&)> LuaParser::onPrint;
|
||||
std::function<void()> LuaParser::onClear;
|
||||
|
||||
void LuaParser::maximumInstructionsReached(lua_State* L, lua_Debug* D) {
|
||||
lua_getstack(L, 1, D);
|
||||
lua_getinfo(L, "l", D);
|
||||
|
||||
std::string msg = std::to_string(D->currentline) + ": Maximum instructions reached! You may have an infinite loop.";
|
||||
lua_pushstring(L, msg.c_str());
|
||||
lua_error(L);
|
||||
}
|
||||
|
||||
void LuaParser::setMaximumInstructions(lua_State*& L, int count) {
|
||||
lua_sethook(L, LuaParser::maximumInstructionsReached, LUA_MASKCOUNT, count);
|
||||
}
|
||||
|
||||
void LuaParser::resetMaximumInstructions(lua_State*& L) {
|
||||
lua_sethook(L, LuaParser::maximumInstructionsReached, 0, 0);
|
||||
}
|
||||
|
||||
static int luaPrint(lua_State* L) {
|
||||
int nargs = lua_gettop(L);
|
||||
|
||||
|
@ -81,16 +98,71 @@ void LuaParser::parse(lua_State*& L) {
|
|||
const char* error = lua_tostring(L, -1);
|
||||
reportError(error);
|
||||
lua_pop(L, 1);
|
||||
functionRef = -1;
|
||||
usingFallbackScript = true;
|
||||
if (script != fallbackScript) {
|
||||
reset(L, fallbackScript);
|
||||
}
|
||||
revertToFallback(L);
|
||||
} else {
|
||||
functionRef = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
}
|
||||
|
||||
void LuaParser::setGlobalVariable(lua_State*& L, const char* name, double value) {
|
||||
lua_pushnumber(L, value);
|
||||
lua_setglobal(L, name);
|
||||
}
|
||||
|
||||
void LuaParser::setGlobalVariable(lua_State*& L, const char* name, int value) {
|
||||
lua_pushnumber(L, value);
|
||||
lua_setglobal(L, name);
|
||||
}
|
||||
|
||||
void LuaParser::setGlobalVariables(lua_State*& L, LuaVariables& vars) {
|
||||
setGlobalVariable(L, "step", vars.step);
|
||||
setGlobalVariable(L, "sample_rate", vars.sampleRate);
|
||||
setGlobalVariable(L, "frequency", vars.frequency);
|
||||
setGlobalVariable(L, "phase", vars.phase);
|
||||
|
||||
for (int i = 0; i < NUM_SLIDERS; i++) {
|
||||
setGlobalVariable(L, SLIDER_NAMES[i], vars.sliders[i]);
|
||||
}
|
||||
|
||||
if (vars.isEffect) {
|
||||
setGlobalVariable(L, "x", vars.x);
|
||||
setGlobalVariable(L, "y", vars.y);
|
||||
setGlobalVariable(L, "z", vars.z);
|
||||
}
|
||||
}
|
||||
|
||||
void LuaParser::incrementVars(LuaVariables& vars) {
|
||||
vars.step++;
|
||||
vars.phase += 2 * std::numbers::pi * vars.frequency / vars.sampleRate;
|
||||
if (vars.phase > 2 * std::numbers::pi) {
|
||||
vars.phase -= 2 * std::numbers::pi;
|
||||
}
|
||||
}
|
||||
|
||||
void LuaParser::clearStack(lua_State*& L) {
|
||||
lua_settop(L, 0);
|
||||
}
|
||||
|
||||
void LuaParser::revertToFallback(lua_State*& L) {
|
||||
functionRef = -1;
|
||||
usingFallbackScript = true;
|
||||
if (script != fallbackScript) {
|
||||
reset(L, fallbackScript);
|
||||
}
|
||||
}
|
||||
|
||||
void LuaParser::readTable(lua_State*& L, std::vector<float>& values) {
|
||||
auto length = lua_rawlen(L, -1);
|
||||
|
||||
for (int i = 1; i <= length; i++) {
|
||||
lua_pushinteger(L, i);
|
||||
lua_gettable(L, -2);
|
||||
float value = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
values.push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
// only the audio thread runs this fuction
|
||||
std::vector<float> LuaParser::run(lua_State*& L, LuaVariables& vars) {
|
||||
// if we haven't seen this state before, reset it
|
||||
|
@ -102,77 +174,37 @@ std::vector<float> LuaParser::run(lua_State*& L, LuaVariables& vars) {
|
|||
|
||||
std::vector<float> values;
|
||||
|
||||
lua_pushnumber(L, vars.step);
|
||||
lua_setglobal(L, "step");
|
||||
|
||||
lua_pushnumber(L, vars.sampleRate);
|
||||
lua_setglobal(L, "sample_rate");
|
||||
|
||||
lua_pushnumber(L, vars.frequency);
|
||||
lua_setglobal(L, "frequency");
|
||||
|
||||
lua_pushnumber(L, vars.phase);
|
||||
lua_setglobal(L, "phase");
|
||||
|
||||
for (int i = 0; i < NUM_SLIDERS; i++) {
|
||||
lua_pushnumber(L, vars.sliders[i]);
|
||||
lua_setglobal(L, SLIDER_NAMES[i]);
|
||||
}
|
||||
|
||||
if (vars.isEffect) {
|
||||
lua_pushnumber(L, vars.x);
|
||||
lua_setglobal(L, "x");
|
||||
|
||||
lua_pushnumber(L, vars.y);
|
||||
lua_setglobal(L, "y");
|
||||
|
||||
lua_pushnumber(L, vars.z);
|
||||
lua_setglobal(L, "z");
|
||||
}
|
||||
setGlobalVariables(L, vars);
|
||||
|
||||
// Get the function from the registry
|
||||
lua_geti(L, LUA_REGISTRYINDEX, functionRef);
|
||||
|
||||
setMaximumInstructions(L, 1000000);
|
||||
|
||||
if (lua_isfunction(L, -1)) {
|
||||
const int ret = lua_pcall(L, 0, LUA_MULTRET, 0);
|
||||
if (ret != LUA_OK) {
|
||||
const char* error = lua_tostring(L, -1);
|
||||
reportError(error);
|
||||
functionRef = -1;
|
||||
usingFallbackScript = true;
|
||||
if (script != fallbackScript) {
|
||||
reset(L, fallbackScript);
|
||||
}
|
||||
} else if (lua_istable(L, -1)) {
|
||||
auto length = lua_rawlen(L, -1);
|
||||
|
||||
for (int i = 1; i <= length; i++) {
|
||||
lua_pushinteger(L, i);
|
||||
lua_gettable(L, -2);
|
||||
float value = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
values.push_back(value);
|
||||
revertToFallback(L);
|
||||
} else {
|
||||
if (lua_istable(L, -1)) {
|
||||
readTable(L, values);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
functionRef = -1;
|
||||
usingFallbackScript = true;
|
||||
if (script != fallbackScript) {
|
||||
reset(L, fallbackScript);
|
||||
}
|
||||
revertToFallback(L);
|
||||
}
|
||||
|
||||
resetMaximumInstructions(L);
|
||||
|
||||
if (functionRef != -1 && !usingFallbackScript) {
|
||||
resetErrors();
|
||||
}
|
||||
|
||||
// clear stack
|
||||
lua_settop(L, 0);
|
||||
|
||||
vars.step++;
|
||||
vars.phase += 2 * std::numbers::pi * vars.frequency / vars.sampleRate;
|
||||
if (vars.phase > 2 * std::numbers::pi) {
|
||||
vars.phase -= 2 * std::numbers::pi;
|
||||
}
|
||||
clearStack(L);
|
||||
|
||||
incrementVars(vars);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ struct LuaVariables {
|
|||
};
|
||||
|
||||
struct lua_State;
|
||||
struct lua_Debug;
|
||||
class LuaParser {
|
||||
public:
|
||||
LuaParser(juce::String fileName, juce::String script, std::function<void(int, juce::String, juce::String)> errorCallback, juce::String fallbackScript = "return { 0.0, 0.0 }");
|
||||
|
@ -73,15 +74,25 @@ public:
|
|||
static std::function<void()> onClear;
|
||||
|
||||
private:
|
||||
static void maximumInstructionsReached(lua_State* L, lua_Debug* D);
|
||||
|
||||
void reset(lua_State*& L, juce::String script);
|
||||
void reportError(const char* error);
|
||||
void parse(lua_State*& L);
|
||||
void setGlobalVariable(lua_State*& L, const char* name, double value);
|
||||
void setGlobalVariable(lua_State*& L, const char* name, int value);
|
||||
void setGlobalVariables(lua_State*& L, LuaVariables& vars);
|
||||
void incrementVars(LuaVariables& vars);
|
||||
void clearStack(lua_State*& L);
|
||||
void revertToFallback(lua_State*& L);
|
||||
void readTable(lua_State*& L, std::vector<float>& values);
|
||||
void setMaximumInstructions(lua_State*& L, int count);
|
||||
void resetMaximumInstructions(lua_State*& L);
|
||||
|
||||
int functionRef = -1;
|
||||
bool usingFallbackScript = false;
|
||||
juce::String script;
|
||||
juce::String fallbackScript;
|
||||
std::atomic<bool> updateVariables = false;
|
||||
std::function<void(int, juce::String, juce::String)> errorCallback;
|
||||
juce::String fileName;
|
||||
std::vector<lua_State*> seenStates;
|
||||
|
|
Ładowanie…
Reference in New Issue