Projects are now fully saveable and loadable from a DAW

pull/170/head
James Ball 2023-07-25 14:09:21 +01:00
rodzic c75a036048
commit 8bdbe2aac4
10 zmienionych plików z 228 dodań i 51 usunięć

Wyświetl plik

@ -16,15 +16,6 @@ EffectsComponent::EffectsComponent(OscirenderAudioProcessor& p, OscirenderAudioP
audioProcessor.frequencyEffect->setValue(frequency.slider.getValue()); audioProcessor.frequencyEffect->setValue(frequency.slider.getValue());
}; };
{
juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock);
for (int i = 0; i < audioProcessor.toggleableEffects.size(); i++) {
auto effect = audioProcessor.toggleableEffects[i];
effect->setValue(effect->getValue());
itemData.data.push_back(effect);
}
}
/*addBtn.setButtonText("Add Item..."); /*addBtn.setButtonText("Add Item...");
addBtn.onClick = [this]() addBtn.onClick = [this]()
{ {
@ -33,12 +24,18 @@ EffectsComponent::EffectsComponent(OscirenderAudioProcessor& p, OscirenderAudioP
}; };
addAndMakeVisible(addBtn);*/ addAndMakeVisible(addBtn);*/
{
juce::MessageManagerLock lock;
audioProcessor.broadcaster.addChangeListener(this);
}
listBox.setModel(&listBoxModel); listBox.setModel(&listBoxModel);
addAndMakeVisible(listBox); addAndMakeVisible(listBox);
} }
EffectsComponent::~EffectsComponent() { EffectsComponent::~EffectsComponent() {
juce::MessageManagerLock lock;
audioProcessor.broadcaster.removeChangeListener(this);
} }
void EffectsComponent::resized() { void EffectsComponent::resized() {
@ -48,3 +45,8 @@ void EffectsComponent::resized() {
area.removeFromTop(6); area.removeFromTop(6);
listBox.setBounds(area); listBox.setBounds(area);
} }
void EffectsComponent::changeListenerCallback(juce::ChangeBroadcaster* source) {
itemData.resetData();
listBox.updateContent();
}

Wyświetl plik

@ -7,12 +7,13 @@
#include "components/EffectsListComponent.h" #include "components/EffectsListComponent.h"
class OscirenderAudioProcessorEditor; class OscirenderAudioProcessorEditor;
class EffectsComponent : public juce::GroupComponent { class EffectsComponent : public juce::GroupComponent, public juce::ChangeListener {
public: public:
EffectsComponent(OscirenderAudioProcessor&, OscirenderAudioProcessorEditor&); EffectsComponent(OscirenderAudioProcessor&, OscirenderAudioProcessorEditor&);
~EffectsComponent() override; ~EffectsComponent() override;
void resized() override; void resized() override;
void changeListenerCallback(juce::ChangeBroadcaster* source) override;
private: private:
OscirenderAudioProcessor& audioProcessor; OscirenderAudioProcessor& audioProcessor;

Wyświetl plik

@ -125,12 +125,16 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
} }
} }
addParameter(fixedRotateX); booleanParameters.push_back(fixedRotateX);
addParameter(fixedRotateY); booleanParameters.push_back(fixedRotateY);
addParameter(fixedRotateZ); booleanParameters.push_back(fixedRotateZ);
addParameter(perspectiveEffect->fixedRotateX); booleanParameters.push_back(perspectiveEffect->fixedRotateX);
addParameter(perspectiveEffect->fixedRotateY); booleanParameters.push_back(perspectiveEffect->fixedRotateY);
addParameter(perspectiveEffect->fixedRotateZ); booleanParameters.push_back(perspectiveEffect->fixedRotateZ);
for (auto parameter : booleanParameters) {
addParameter(parameter);
}
} }
OscirenderAudioProcessor::~OscirenderAudioProcessor() {} OscirenderAudioProcessor::~OscirenderAudioProcessor() {}
@ -257,6 +261,26 @@ void OscirenderAudioProcessor::updateObjValues() {
rotateSpeed->apply(); rotateSpeed->apply();
} }
// effectsLock should be held when calling this
std::shared_ptr<Effect> OscirenderAudioProcessor::getEffect(juce::String id) {
for (auto& effect : allEffects) {
if (effect->getId() == id) {
return effect;
}
}
return nullptr;
}
// effectsLock should be held when calling this
BooleanParameter* OscirenderAudioProcessor::getBooleanParameter(juce::String id) {
for (auto& parameter : booleanParameters) {
if (parameter->paramID == id) {
return parameter;
}
}
return nullptr;
}
// effectsLock MUST be held when calling this // effectsLock MUST be held when calling this
void OscirenderAudioProcessor::updateEffectPrecedence() { void OscirenderAudioProcessor::updateEffectPrecedence() {
auto sortFunc = [](std::shared_ptr<Effect> a, std::shared_ptr<Effect> b) { auto sortFunc = [](std::shared_ptr<Effect> a, std::shared_ptr<Effect> b) {
@ -294,15 +318,28 @@ void OscirenderAudioProcessor::addFile(juce::String fileName, const char* data,
openFile(fileBlocks.size() - 1); openFile(fileBlocks.size() - 1);
} }
// parsersLock AND effectsLock must be locked before calling this function
void OscirenderAudioProcessor::addFile(juce::String fileName, std::shared_ptr<juce::MemoryBlock> data) {
fileBlocks.push_back(data);
fileNames.push_back(fileName);
parsers.push_back(std::make_unique<FileParser>());
openFile(fileBlocks.size() - 1);
}
// parsersLock AND effectsLock must be locked before calling this function // parsersLock AND effectsLock must be locked before calling this function
void OscirenderAudioProcessor::removeFile(int index) { void OscirenderAudioProcessor::removeFile(int index) {
if (index < 0 || index >= fileBlocks.size()) { if (index < 0 || index >= fileBlocks.size()) {
return; return;
} }
changeCurrentFile(index - 1);
fileBlocks.erase(fileBlocks.begin() + index); fileBlocks.erase(fileBlocks.begin() + index);
fileNames.erase(fileNames.begin() + index); fileNames.erase(fileNames.begin() + index);
parsers.erase(parsers.begin() + index); parsers.erase(parsers.begin() + index);
auto newFileIndex = index;
if (newFileIndex >= fileBlocks.size()) {
newFileIndex = fileBlocks.size() - 1;
}
changeCurrentFile(newFileIndex);
} }
int OscirenderAudioProcessor::numFiles() { int OscirenderAudioProcessor::numFiles() {
@ -572,6 +609,13 @@ void OscirenderAudioProcessor::getStateInformation(juce::MemoryBlock& destData)
for (auto effect : allEffects) { for (auto effect : allEffects) {
effect->save(effectsXml->createNewChildElement("effect")); effect->save(effectsXml->createNewChildElement("effect"));
} }
auto booleanParametersXml = xml->createNewChildElement("booleanParameters");
for (auto parameter : booleanParameters) {
auto parameterXml = booleanParametersXml->createNewChildElement("parameter");
parameter->save(parameterXml);
}
auto perspectiveFunction = xml->createNewChildElement("perspectiveFunction"); auto perspectiveFunction = xml->createNewChildElement("perspectiveFunction");
perspectiveFunction->addTextElement(juce::Base64::toBase64(perspectiveEffect->getCode())); perspectiveFunction->addTextElement(juce::Base64::toBase64(perspectiveEffect->getCode()));
auto filesXml = xml->createNewChildElement("files"); auto filesXml = xml->createNewChildElement("files");
@ -588,8 +632,58 @@ void OscirenderAudioProcessor::getStateInformation(juce::MemoryBlock& destData)
} }
void OscirenderAudioProcessor::setStateInformation(const void* data, int sizeInBytes) { void OscirenderAudioProcessor::setStateInformation(const void* data, int sizeInBytes) {
// You should use this method to restore your parameters from this memory block, juce::SpinLock::ScopedLockType lock1(parsersLock);
// whose contents will have been created by the getStateInformation() call. juce::SpinLock::ScopedLockType lock2(effectsLock);
std::unique_ptr<juce::XmlElement> xml(getXmlFromBinary(data, sizeInBytes));
if (xml.get() != nullptr && xml->hasTagName("project")) {
auto effectsXml = xml->getChildByName("effects");
if (effectsXml != nullptr) {
for (auto effectXml : effectsXml->getChildIterator()) {
auto effect = getEffect(effectXml->getStringAttribute("id"));
if (effect != nullptr) {
effect->load(effectXml);
}
}
}
updateEffectPrecedence();
auto booleanParametersXml = xml->getChildByName("booleanParameters");
if (booleanParametersXml != nullptr) {
for (auto parameterXml : booleanParametersXml->getChildIterator()) {
auto parameter = getBooleanParameter(parameterXml->getStringAttribute("id"));
if (parameter != nullptr) {
parameter->load(parameterXml);
}
}
}
auto perspectiveFunction = xml->getChildByName("perspectiveFunction");
if (perspectiveFunction != nullptr) {
auto stream = juce::MemoryOutputStream();
juce::Base64::convertFromBase64(stream, perspectiveFunction->getAllSubText());
perspectiveEffect->updateCode(stream.toString());
}
// close all files
auto numFiles = fileBlocks.size();
for (int i = 0; i < numFiles; i++) {
removeFile(0);
}
auto filesXml = xml->getChildByName("files");
if (filesXml != nullptr) {
for (auto fileXml : filesXml->getChildIterator()) {
auto fileName = fileXml->getStringAttribute("name");
auto stream = juce::MemoryOutputStream();
juce::Base64::convertFromBase64(stream, fileXml->getAllSubText());
auto fileBlock = std::make_shared<juce::MemoryBlock>(stream.getData(), stream.getDataSize());
addFile(fileName, fileBlock);
}
}
changeCurrentFile(xml->getIntAttribute("currentFile", -1));
broadcaster.sendChangeMessage();
}
} }
//============================================================================== //==============================================================================

Wyświetl plik

@ -175,6 +175,8 @@ public:
std::vector<juce::String> fileNames; std::vector<juce::String> fileNames;
std::atomic<int> currentFile = -1; std::atomic<int> currentFile = -1;
juce::ChangeBroadcaster broadcaster;
FrameProducer producer = FrameProducer(*this, std::make_shared<FileParser>()); FrameProducer producer = FrameProducer(*this, std::make_shared<FileParser>());
BufferProducer audioProducer; BufferProducer audioProducer;
@ -188,6 +190,7 @@ public:
void updateFileBlock(int index, std::shared_ptr<juce::MemoryBlock> block); void updateFileBlock(int index, std::shared_ptr<juce::MemoryBlock> block);
void addFile(juce::File file); void addFile(juce::File file);
void addFile(juce::String fileName, const char* data, const int size); void addFile(juce::String fileName, const char* data, const int size);
void addFile(juce::String fileName, std::shared_ptr<juce::MemoryBlock> data);
void removeFile(int index); void removeFile(int index);
int numFiles(); int numFiles();
void changeCurrentFile(int index); void changeCurrentFile(int index);
@ -214,6 +217,7 @@ private:
double lengthIncrement = 0.0; double lengthIncrement = 0.0;
bool invalidateFrameBuffer = false; bool invalidateFrameBuffer = false;
std::vector<BooleanParameter*> booleanParameters;
std::vector<std::shared_ptr<Effect>> allEffects; std::vector<std::shared_ptr<Effect>> allEffects;
std::vector<std::shared_ptr<Effect>> permanentEffects; std::vector<std::shared_ptr<Effect>> permanentEffects;
@ -247,6 +251,8 @@ private:
void openFile(int index); void openFile(int index);
void updateLuaValues(); void updateLuaValues();
void updateObjValues(); void updateObjValues();
std::shared_ptr<Effect> getEffect(juce::String id);
BooleanParameter* getBooleanParameter(juce::String id);
const double MIN_LENGTH_INCREMENT = 0.000001; const double MIN_LENGTH_INCREMENT = 0.000001;

Wyświetl plik

@ -2,20 +2,9 @@
#include "../shape/Vector2.h" #include "../shape/Vector2.h"
#include <JuceHeader.h> #include <JuceHeader.h>
class BooleanParameter : public juce::AudioProcessorParameter { class BooleanParameter : public juce::AudioProcessorParameterWithID {
public: public:
juce::String name; BooleanParameter(juce::String name, juce::String id, bool value) : AudioProcessorParameterWithID(id, name), value(value) {}
juce::String id;
BooleanParameter(juce::String name, juce::String id, bool value) : name(name), id(id), value(value) {}
// COPY CONSTRUCTOR SHOULD ONLY BE USED BEFORE
// THE OBJECT IS USED IN MULTIPLE THREADS
BooleanParameter(const BooleanParameter& other) {
name = other.name;
id = other.id;
value.store(other.value.load());
}
juce::String getName(int maximumStringLength) const override { juce::String getName(int maximumStringLength) const override {
return name.substring(0, maximumStringLength); return name.substring(0, maximumStringLength);
@ -86,6 +75,15 @@ public:
return juce::AudioProcessorParameter::genericParameter; return juce::AudioProcessorParameter::genericParameter;
} }
void save(juce::XmlElement* xml) {
xml->setAttribute("id", paramID);
xml->setAttribute("value", value.load());
}
void load(juce::XmlElement* xml) {
setBoolValueNotifyingHost(xml->getBoolAttribute("value", getDefaultValue()));
}
private: private:
std::atomic<bool> value = false; std::atomic<bool> value = false;
}; };

Wyświetl plik

@ -151,9 +151,39 @@ juce::String Effect::getName() {
void Effect::save(juce::XmlElement* xml) { void Effect::save(juce::XmlElement* xml) {
if (enabled != nullptr) { if (enabled != nullptr) {
xml->setAttribute("enabled", enabled->getBoolValue()); auto enabledXml = xml->createNewChildElement("enabled");
enabled->save(enabledXml);
} }
xml->setAttribute("id", getId());
xml->setAttribute("precedence", precedence);
for (auto parameter : parameters) { for (auto parameter : parameters) {
parameter->save(xml->createNewChildElement("parameter")); parameter->save(xml->createNewChildElement("parameter"));
} }
} }
void Effect::load(juce::XmlElement* xml) {
if (enabled != nullptr) {
auto enabledXml = xml->getChildByName("enabled");
if (enabledXml != nullptr) {
enabled->load(enabledXml);
}
}
if (xml->hasAttribute("precedence")) {
setPrecedence(xml->getIntAttribute("precedence"));
}
for (auto parameterXml : xml->getChildIterator()) {
auto parameter = getParameter(parameterXml->getStringAttribute("id"));
if (parameter != nullptr) {
parameter->load(parameterXml);
}
}
}
EffectParameter* Effect::getParameter(juce::String id) {
for (auto parameter : parameters) {
if (parameter->paramID == id) {
return parameter;
}
}
return nullptr;
}

Wyświetl plik

@ -28,6 +28,8 @@ public:
juce::String getId(); juce::String getId();
juce::String getName(); juce::String getName();
void save(juce::XmlElement* xml); void save(juce::XmlElement* xml);
void load(juce::XmlElement* xml);
EffectParameter* getParameter(juce::String id);
std::vector<EffectParameter*> parameters; std::vector<EffectParameter*> parameters;
BooleanParameter* enabled; BooleanParameter* enabled;

Wyświetl plik

@ -104,6 +104,22 @@ public:
xml->setAttribute("step", step.load()); xml->setAttribute("step", step.load());
} }
// opt to not change any values if not found
void load(juce::XmlElement* xml) {
if (xml->hasAttribute("value")) {
value = xml->getDoubleAttribute("value");
}
if (xml->hasAttribute("min")) {
min = xml->getDoubleAttribute("min");
}
if (xml->hasAttribute("max")) {
max = xml->getDoubleAttribute("max");
}
if (xml->hasAttribute("step")) {
step = xml->getDoubleAttribute("step");
}
}
private: private:
// value is not necessarily in the range [min, max] so effect applications may need to clip to a valid range // value is not necessarily in the range [min, max] so effect applications may need to clip to a valid range
std::atomic<float> value = 0.0; std::atomic<float> value = 0.0;
@ -247,30 +263,36 @@ public:
} }
float getValueForText(const juce::String& text) const override { float getValueForText(const juce::String& text) const override {
int unnormalisedValue;
if (text == "Static") { if (text == "Static") {
return (int)LfoType::Static; unnormalisedValue = (int)LfoType::Static;
} else if (text == "Sine") { } else if (text == "Sine") {
return (int)LfoType::Sine; unnormalisedValue = (int)LfoType::Sine;
} else if (text == "Square") { } else if (text == "Square") {
return (int)LfoType::Square; unnormalisedValue = (int)LfoType::Square;
} else if (text == "Seesaw") { } else if (text == "Seesaw") {
return (int)LfoType::Seesaw; unnormalisedValue = (int)LfoType::Seesaw;
} else if (text == "Triangle") { } else if (text == "Triangle") {
return (int)LfoType::Triangle; unnormalisedValue = (int)LfoType::Triangle;
} else if (text == "Sawtooth") { } else if (text == "Sawtooth") {
return (int)LfoType::Sawtooth; unnormalisedValue = (int)LfoType::Sawtooth;
} else if (text == "Reverse Sawtooth") { } else if (text == "Reverse Sawtooth") {
return (int)LfoType::ReverseSawtooth; unnormalisedValue = (int)LfoType::ReverseSawtooth;
} else if (text == "Noise") { } else if (text == "Noise") {
return (int)LfoType::Noise; unnormalisedValue = (int)LfoType::Noise;
} else { } else {
return (int)LfoType::Static; unnormalisedValue = (int)LfoType::Static;
} }
return getNormalisedValue(unnormalisedValue);
} }
void save(juce::XmlElement* xml) { void save(juce::XmlElement* xml) {
xml->setAttribute("lfo", getText(getValue(), 100)); xml->setAttribute("lfo", getText(getValue(), 100));
} }
void load(juce::XmlElement* xml) {
setValueNotifyingHost(getValueForText(xml->getStringAttribute("lfo")));
}
}; };
class EffectParameter : public FloatParameter { class EffectParameter : public FloatParameter {
@ -309,5 +331,15 @@ public:
} }
} }
void load(juce::XmlElement* xml) {
FloatParameter::load(xml);
auto lfoXml = xml->getChildByName("lfo");
if (lfoXml != nullptr) {
lfo->load(lfoXml);
lfoRate->load(lfoXml);
}
}
EffectParameter(juce::String name, juce::String id, float value, float min, float max, float step = 0.001, bool smoothValueChange = true) : FloatParameter(name, id, value, min, max, step), smoothValueChange(smoothValueChange) {} EffectParameter(juce::String name, juce::String id, float value, float min, float max, float step = 0.001, bool smoothValueChange = true) : FloatParameter(name, id, value, min, max, step), smoothValueChange(smoothValueChange) {}
}; };

Wyświetl plik

@ -74,13 +74,13 @@ void EffectsListComponent::resized() {
} }
std::shared_ptr<juce::Component> EffectsListComponent::createComponent(EffectParameter* parameter) { std::shared_ptr<juce::Component> EffectsListComponent::createComponent(EffectParameter* parameter) {
if (parameter->paramID == "rotateX" || parameter->paramID == "rotateY" || parameter->paramID == "rotateZ") { if (parameter->paramID == "perspectiveRotateX" || parameter->paramID == "perspectiveRotateY" || parameter->paramID == "perspectiveRotateZ") {
BooleanParameter* toggle; BooleanParameter* toggle;
if (parameter->paramID == "rotateX") { if (parameter->paramID == "perspectiveRotateX") {
toggle = audioProcessor.perspectiveEffect->fixedRotateX; toggle = audioProcessor.perspectiveEffect->fixedRotateX;
} else if (parameter->paramID == "rotateY") { } else if (parameter->paramID == "perspectiveRotateY") {
toggle = audioProcessor.perspectiveEffect->fixedRotateY; toggle = audioProcessor.perspectiveEffect->fixedRotateY;
} else if (parameter->paramID == "rotateZ") { } else if (parameter->paramID == "perspectiveRotateZ") {
toggle = audioProcessor.perspectiveEffect->fixedRotateZ; toggle = audioProcessor.perspectiveEffect->fixedRotateZ;
} }
std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::fixed_rotate_svg, "white", "red", toggle); std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::fixed_rotate_svg, "white", "red", toggle);
@ -88,7 +88,7 @@ std::shared_ptr<juce::Component> EffectsListComponent::createComponent(EffectPar
toggle->setBoolValueNotifyingHost(!toggle->getBoolValue()); toggle->setBoolValueNotifyingHost(!toggle->getBoolValue());
}; };
return button; return button;
} else if (parameter->paramID == "depthScale") { } else if (parameter->paramID == "perspectiveStrength") {
std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::pencil_svg, "white", "red"); std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::pencil_svg, "white", "red");
std::weak_ptr<SvgButton> weakButton = button; std::weak_ptr<SvgButton> weakButton = button;
button->setEdgeIndent(5); button->setEdgeIndent(5);

Wyświetl plik

@ -14,7 +14,19 @@ struct AudioEffectListBoxItemData : public DraggableListBoxItemData
OscirenderAudioProcessor& audioProcessor; OscirenderAudioProcessor& audioProcessor;
OscirenderAudioProcessorEditor& editor; OscirenderAudioProcessorEditor& editor;
AudioEffectListBoxItemData(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), editor(editor) {} AudioEffectListBoxItemData(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), editor(editor) {
resetData();
}
void resetData() {
juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock);
data.clear();
for (int i = 0; i < audioProcessor.toggleableEffects.size(); i++) {
auto effect = audioProcessor.toggleableEffects[i];
effect->setValue(effect->getValue());
data.push_back(effect);
}
}
int getNumItems() override { int getNumItems() override {
return data.size(); return data.size();