Slightly improve Lua variable performance, and add control for MIDI voices

pull/170/head
James Ball 2023-12-21 14:14:33 +00:00
rodzic b85318a4fd
commit fd8935a589
11 zmienionych plików z 95 dodań i 12 usunięć

Wyświetl plik

@ -5,14 +5,33 @@ MidiComponent::MidiComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
setText("MIDI Settings");
addAndMakeVisible(midiToggle);
addAndMakeVisible(voicesSlider);
addAndMakeVisible(voicesLabel);
addAndMakeVisible(keyboard);
midiToggle.setToggleState(audioProcessor.midiEnabled->getBoolValue(), juce::dontSendNotification);
midiToggle.setTooltip("Enable MIDI input for the synth. If disabled, the synth will play a constant tone, as controlled by the frequency slider.");
midiToggle.onClick = [this]() {
audioProcessor.midiEnabled->setBoolValueNotifyingHost(midiToggle.getToggleState());
};
audioProcessor.midiEnabled->addListener(this);
voicesSlider.setRange(1, 16, 1);
voicesSlider.setValue(audioProcessor.voices->getValueUnnormalised(), juce::dontSendNotification);
voicesSlider.setTextBoxStyle(juce::Slider::TextBoxRight, false, 50, 20);
voicesLabel.setText("Voices", juce::dontSendNotification);
voicesLabel.attachToComponent(&voicesSlider, true);
voicesLabel.setTooltip("Number of voices for the synth to use. Larger numbers will use more CPU, and may cause audio glitches.");
voicesSlider.onValueChange = [this]() {
audioProcessor.voices->setUnnormalisedValueNotifyingHost(voicesSlider.getValue());
};
audioProcessor.voices->addListener(this);
addAndMakeVisible(envelope);
envelope.setAdsrMode(true);
envelope.setEnv(audioProcessor.adsrEnv);
@ -39,6 +58,9 @@ MidiComponent::~MidiComponent() {
audioProcessor.sustainLevel->removeListener(this);
audioProcessor.releaseTime->removeListener(this);
audioProcessor.releaseShape->removeListener(this);
audioProcessor.midiEnabled->removeListener(this);
audioProcessor.voices->removeListener(this);
}
void MidiComponent::parameterValueChanged(int parameterIndex, float newValue) {
@ -48,6 +70,9 @@ void MidiComponent::parameterValueChanged(int parameterIndex, float newValue) {
void MidiComponent::parameterGestureChanged(int parameterIndex, bool gestureIsStarting) {}
void MidiComponent::handleAsyncUpdate() {
midiToggle.setToggleState(audioProcessor.midiEnabled->getBoolValue(), juce::dontSendNotification);
voicesSlider.setValue(audioProcessor.voices->getValueUnnormalised(), juce::dontSendNotification);
Env newEnv = Env(
{
0.0,
@ -74,7 +99,10 @@ void MidiComponent::handleAsyncUpdate() {
void MidiComponent::resized() {
auto area = getLocalBounds().withTrimmedTop(20).reduced(20);
midiToggle.setBounds(area.removeFromTop(30));
auto topRow = area.removeFromTop(30);
midiToggle.setBounds(topRow.removeFromLeft(120));
topRow.removeFromLeft(80);
voicesSlider.setBounds(topRow.removeFromLeft(250));
area.removeFromTop(5);
keyboard.setBounds(area.removeFromBottom(50));
envelope.setBounds(area);

Wyświetl plik

@ -20,6 +20,8 @@ private:
OscirenderAudioProcessorEditor& pluginEditor;
juce::ToggleButton midiToggle{"Enable MIDI"};
juce::Slider voicesSlider;
juce::Label voicesLabel;
juce::MidiKeyboardComponent keyboard{audioProcessor.keyboardState, juce::MidiKeyboardComponent::horizontalKeyboard};
EnvelopeContainerComponent envelope;

Wyświetl plik

@ -157,9 +157,12 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
addParameter(releaseTime);
addParameter(releaseShape);
for (int i = 0; i < 4; i++) {
for (int i = 0; i < voices->getValueUnnormalised(); i++) {
synth.addVoice(new ShapeVoice(*this));
}
addParameter(voices);
voices->addListener(this);
synth.addSound(defaultSound);
}
@ -168,6 +171,7 @@ OscirenderAudioProcessor::~OscirenderAudioProcessor() {
for (auto& effect : luaEffects) {
effect->removeListener(0, this);
}
voices->removeListener(this);
}
const juce::String OscirenderAudioProcessor::getName() const {
@ -766,6 +770,22 @@ void OscirenderAudioProcessor::parameterValueChanged(int parameterIndex, float n
return;
}
}
if (parameterIndex == voices->getParameterIndex()) {
int numVoices = voices->getValueUnnormalised();
// if the number of voices has changed, update the synth without clearing all the voices
if (numVoices != synth.getNumVoices()) {
if (numVoices > synth.getNumVoices()) {
for (int i = synth.getNumVoices(); i < numVoices; i++) {
synth.addVoice(new ShapeVoice(*this));
}
} else {
for (int i = synth.getNumVoices() - 1; i >= numVoices; i--) {
synth.removeVoice(i);
}
}
}
}
}
void OscirenderAudioProcessor::parameterGestureChanged(int parameterIndex, bool gestureIsStarting) {}

Wyświetl plik

@ -270,6 +270,8 @@ public:
juce::MidiKeyboardState keyboardState;
IntParameter* voices = new IntParameter("Voices", "voices", VERSION_HINT, 4, 1, 16);
private:
juce::SpinLock consumerLock;
std::vector<std::shared_ptr<BufferConsumer>> consumers;

Wyświetl plik

@ -27,7 +27,7 @@ EffectComponent::EffectComponent(OscirenderAudioProcessor& p, Effect& effect) :
void EffectComponent::setupComponent() {
EffectParameter* parameter = effect.parameters[index];
label.setTooltip(parameter->description);
setTooltip(parameter->description);
label.setText(parameter->name, juce::dontSendNotification);
label.setInterceptsMouseClicks(false, false);

Wyświetl plik

@ -2,7 +2,11 @@
#include "../PluginEditor.h"
juce::StringArray MainMenuBarModel::getMenuBarNames() {
return juce::StringArray("File", "Options");
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
return juce::StringArray("File", "Options");
} else {
return juce::StringArray("File");
}
}
juce::PopupMenu MainMenuBarModel::getMenuForIndex(int topLevelMenuIndex, const juce::String& menuName) {

Wyświetl plik

@ -59,10 +59,14 @@ void LuaParser::parse(lua_State*& L) {
// only the audio thread runs this fuction
std::vector<float> LuaParser::run(lua_State*& L, const LuaVariables vars, long& step, double& phase) {
juce::SpinLock::ScopedLockType lock(variableLock);
// if we haven't seen this state before, reset it
if (std::find(seenStates.begin(), seenStates.end(), L) == seenStates.end()) {
int stateIndex = std::find(seenStates.begin(), seenStates.end(), L) - seenStates.begin();
if (stateIndex == seenStates.size()) {
reset(L, script);
seenStates.push_back(L);
staleStates.push_back(true);
}
std::vector<float> values;
@ -79,14 +83,14 @@ std::vector<float> LuaParser::run(lua_State*& L, const LuaVariables vars, long&
lua_pushnumber(L, phase);
lua_setglobal(L, "phase");
// this CANNOT run at the same time as setVariable
juce::SpinLock::ScopedTryLockType lock(variableLock);
if (lock.isLocked()) {
if (staleStates[stateIndex]) {
// update variables
for (int i = 0; i < variableNames.size(); i++) {
lua_pushnumber(L, variables[i]);
lua_setglobal(L, variableNames[i].toUTF8());
}
}
staleStates[stateIndex] = false;
}
lua_geti(L, LUA_REGISTRYINDEX, functionRef);
@ -147,15 +151,26 @@ void LuaParser::setVariable(juce::String variableName, double value) {
break;
}
}
bool changed = false;
if (index == -1) {
// add new variable
variableNames.push_back(variableName);
variables.push_back(value);
changed = true;
} else {
// update existing variable
changed = variables[index] != value;
variables[index] = value;
}
if (changed) {
// mark all states as stale
for (int i = 0; i < staleStates.size(); i++) {
staleStates[i] = true;
}
}
}
bool LuaParser::isFunctionValid() {

Wyświetl plik

@ -43,4 +43,5 @@ private:
std::function<void(int, juce::String, juce::String)> errorCallback;
juce::String fileName;
std::vector<lua_State*> seenStates;
std::vector<bool> staleStates;
};

Wyświetl plik

@ -58,10 +58,12 @@ double CircleArc::length() {
len = 0;
// TODO: Replace this, it's stupid. Do a real approximation.
int segments = 5;
Vector2 start;
Vector2 end = nextVector(0);
for (int i = 0; i < segments; i++) {
Vector2 v1 = nextVector(i / (double) segments);
Vector2 v2 = nextVector((i + 1) / (double) segments);
len += Line(v1.x, v1.y, v2.x, v2.y).length();
start = end;
end = nextVector((i + 1) / (double) segments);
len += Line::length(start.x, start.y, end.x, end.y);
}
}
return len;

Wyświetl plik

@ -38,6 +38,14 @@ void Line::translate(double x, double y) {
y2 += y;
}
double Line::length(double x1, double y1, double x2, double y2) {
// Euclidean distance approximation based on octagonal boundary
double dx = std::abs(x2 - x1);
double dy = std::abs(y2 - y1);
return 0.41 * std::min(dx, dy) + 0.941246 * std::max(dx, dy);
}
double inline Line::length() {
if (len < 0) {
// Euclidean distance approximation based on octagonal boundary

Wyświetl plik

@ -11,6 +11,7 @@ public:
void rotate(double theta) override;
void scale(double x, double y) override;
void translate(double x, double y) override;
static double length(double x1, double y1, double x2, double y2);
double length() override;
std::unique_ptr<Shape> clone() override;
std::string type() override;