Support opening legacy osci-render projects and fix some bugs

pull/170/head
James Ball 2023-07-28 13:55:44 +01:00
rodzic 40c0086bf4
commit b845d150ff
7 zmienionych plików z 337 dodań i 28 usunięć

Wyświetl plik

@ -0,0 +1,275 @@
#include "PluginProcessor.h"
void OscirenderAudioProcessor::openLegacyProject(const juce::XmlElement* xml) {
juce::SpinLock::ScopedLockType lock1(parsersLock);
juce::SpinLock::ScopedLockType lock2(effectsLock);
if (xml != nullptr && xml->hasTagName("project")) {
auto slidersXml = xml->getChildByName("sliders");
if (slidersXml != nullptr) {
for (auto sliderXml : slidersXml->getChildIterator()) {
auto id = sliderXml->getTagName();
auto valueXml = sliderXml->getChildByName("value");
auto minXml = sliderXml->getChildByName("min");
auto maxXml = sliderXml->getChildByName("max");
double value = valueXml != nullptr ? valueXml->getAllSubText().getDoubleValue() : 0.0;
double min = minXml != nullptr ? minXml->getAllSubText().getDoubleValue() : 0.0;
double max = maxXml != nullptr ? maxXml->getAllSubText().getDoubleValue() : 0.0;
value = valueFromLegacy(value, id);
min = valueFromLegacy(min, id);
max = valueFromLegacy(max, id);
auto pair = effectFromLegacyId(id, true);
auto effect = pair.first;
auto parameter = pair.second;
if (effect != nullptr && parameter != nullptr) {
if (id != "volume" && id != "threshold") {
parameter->min = min;
parameter->max = max;
}
parameter->setUnnormalisedValueNotifyingHost(value);
}
}
}
auto translationXml = xml->getChildByName("translation");
if (translationXml != nullptr) {
auto xXml = translationXml->getChildByName("x");
auto yXml = translationXml->getChildByName("y");
auto translateEffect = getEffect("translateX");
if (translateEffect != nullptr) {
auto x = translateEffect->getParameter("translateX");
if (x != nullptr && xXml != nullptr) {
x->setUnnormalisedValueNotifyingHost(xXml->getAllSubText().getDoubleValue());
}
auto y = translateEffect->getParameter("translateY");
if (y != nullptr && yXml != nullptr) {
y->setUnnormalisedValueNotifyingHost(yXml->getAllSubText().getDoubleValue());
}
}
}
auto perspectiveFixedRotateXml = xml->getChildByName("perspectiveFixedRotate");
if (perspectiveFixedRotateXml != nullptr) {
auto xXml = perspectiveFixedRotateXml->getChildByName("x");
auto yXml = perspectiveFixedRotateXml->getChildByName("y");
auto zXml = perspectiveFixedRotateXml->getChildByName("z");
auto x = getBooleanParameter("perspectiveFixedRotateX");
auto y = getBooleanParameter("perspectiveFixedRotateY");
auto z = getBooleanParameter("perspectiveFixedRotateZ");
if (x != nullptr && xXml != nullptr) {
x->setBoolValueNotifyingHost(xXml->getAllSubText() == "true");
}
if (y != nullptr && yXml != nullptr) {
y->setBoolValueNotifyingHost(yXml->getAllSubText() == "true");
}
if (z != nullptr && zXml != nullptr) {
z->setBoolValueNotifyingHost(zXml->getAllSubText() == "true");
}
}
auto objectFixedRotate = xml->getChildByName("objectFixedRotate");
if (objectFixedRotate != nullptr) {
auto xXml = objectFixedRotate->getChildByName("x");
auto yXml = objectFixedRotate->getChildByName("y");
auto zXml = objectFixedRotate->getChildByName("z");
auto x = getBooleanParameter("objFixedRotateX");
auto y = getBooleanParameter("objFixedRotateY");
auto z = getBooleanParameter("objFixedRotateZ");
if (x != nullptr && xXml != nullptr) {
x->setBoolValueNotifyingHost(xXml->getAllSubText() == "true");
}
if (y != nullptr && yXml != nullptr) {
y->setBoolValueNotifyingHost(yXml->getAllSubText() == "true");
}
if (z != nullptr && zXml != nullptr) {
z->setBoolValueNotifyingHost(zXml->getAllSubText() == "true");
}
}
auto checkBoxesXml = xml->getChildByName("checkBoxes");
if (checkBoxesXml != nullptr) {
for (auto checkBoxXml : checkBoxesXml->getChildIterator()) {
auto id = checkBoxXml->getTagName();
auto selectedXml = checkBoxXml->getChildByName("selected");
auto animationXml = checkBoxXml->getChildByName("animation");
auto animationSpeedXml = checkBoxXml->getChildByName("animationSpeed");
bool selected = selectedXml != nullptr ? selectedXml->getAllSubText() == "true" : false;
LfoType lfoType = animationXml != nullptr ? lfoTypeFromLegacyAnimationType(animationXml->getAllSubText()) : LfoType::Static;
double lfoRate = animationSpeedXml != nullptr ? animationSpeedXml->getAllSubText().getDoubleValue() : 1.0;
auto pair = effectFromLegacyId(id, true);
auto effect = pair.first;
auto parameter = pair.second;
if (effect != nullptr && parameter != nullptr && parameter->lfo != nullptr && parameter->lfoRate != nullptr) {
if (effect->enabled != nullptr && effect->getId() == parameter->paramID) {
effect->enabled->setBoolValueNotifyingHost(selected);
}
parameter->lfo->setUnnormalisedValueNotifyingHost((int) lfoType);
parameter->lfoRate->setUnnormalisedValueNotifyingHost(lfoRate);
}
}
}
updateEffectPrecedence();
auto perspectiveFunction = xml->getChildByName("depthFunction");
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 nameXml = fileXml->getChildByName("name");
auto dataXml = fileXml->getChildByName("data");
auto fileName = nameXml != nullptr ? nameXml->getAllSubText() : "";
auto data = dataXml != nullptr ? dataXml->getAllSubText() : "";
auto stream = juce::MemoryOutputStream();
juce::Base64::convertFromBase64(stream, data);
auto fileBlock = std::make_shared<juce::MemoryBlock>(stream.getData(), stream.getDataSize());
addFile(fileName, fileBlock);
}
}
auto frameSourceXml = xml->getChildByName("frameSource");
if (frameSourceXml != nullptr) {
changeCurrentFile(frameSourceXml->getAllSubText().getIntValue());
}
broadcaster.sendChangeMessage();
}
}
// gets the effect from the legacy id and optionally updates the precedence
std::pair<std::shared_ptr<Effect>, EffectParameter*> OscirenderAudioProcessor::effectFromLegacyId(const juce::String& id, bool updatePrecedence) {
auto effectId = id;
juce::String paramId = "";
int precedence = -1;
if (id == "vectorCancelling") {
precedence = 0;
} else if (id == "bitCrush") {
precedence = 1;
} else if (id == "verticalDistort") {
precedence = 2;
effectId = "distortY";
} else if (id == "horizontalDistort") {
precedence = 3;
effectId = "distortX";
} else if (id == "wobble") {
precedence = 4;
} else if (id == "smoothing") {
precedence = 100;
} else if (id == "rotateSpeed3d") {
precedence = 52;
effectId = "perspectiveStrength";
paramId = "perspectiveRotateSpeed";
} else if (id == "zPos") {
precedence = 52;
effectId = "perspectiveStrength";
paramId = "perspectiveZPos";
} else if (id == "imageRotateX") {
precedence = 52;
effectId = "perspectiveStrength";
paramId = "perspectiveRotateX";
} else if (id == "imageRotateY") {
precedence = 52;
effectId = "perspectiveStrength";
paramId = "perspectiveRotateY";
} else if (id == "imageRotateZ") {
precedence = 52;
effectId = "perspectiveStrength";
paramId = "perspectiveRotateZ";
} else if (id == "depthScale") {
precedence = 52;
effectId = "perspectiveStrength";
} else if (id == "translationScale") {
// this doesn't exist in the new version
} else if (id == "translationSpeed") {
// this doesn't exist in the new version
} else if (id == "rotateSpeed") {
precedence = 50;
effectId = "2DRotateSpeed";
} else if (id == "visibility") {
// this doesn't exist in the new version
} else if (id == "delayDecay") {
precedence = 101;
} else if (id == "delayEchoLength") {
precedence = 101;
effectId = "delayDecay";
paramId = "delayLength";
} else if (id == "bulge") {
precedence = 53;
} else if (id == "focalLength") {
effectId = "objFocalLength";
} else if (id == "objectXRotate") {
effectId = "objRotateX";
} else if (id == "objectYRotate") {
effectId = "objRotateY";
} else if (id == "objectZRotate") {
effectId = "objRotateZ";
} else if (id == "objectRotateSpeed") {
effectId = "objRotateSpeed";
} else if (id == "octave") {
// this doesn't exist in the new version
} else if (id == "micVolume") {
// this doesn't exist in the new version
} else if (id == "brightness") {
// this doesn't exist in the new version
}
paramId = paramId == "" ? effectId : paramId;
auto effect = getEffect(effectId);
if (effect == nullptr) {
return std::make_pair(nullptr, nullptr);
} else {
if (updatePrecedence) {
effect->setPrecedence(precedence);
}
auto parameter = effect->getParameter(paramId);
return std::make_pair(effect, parameter);
}
}
LfoType OscirenderAudioProcessor::lfoTypeFromLegacyAnimationType(const juce::String& type) {
if (type == "Static") {
return LfoType::Static;
} else if (type == "Sine") {
return LfoType::Sine;
} else if (type == "Square") {
return LfoType::Square;
} else if (type == "Seesaw") {
return LfoType::Seesaw;
} else if (type == "Linear") {
return LfoType::Triangle;
} else if (type == "Forward") {
return LfoType::Sawtooth;
} else if (type == "Reverse") {
return LfoType::ReverseSawtooth;
} else {
return LfoType::Static;
}
}
double OscirenderAudioProcessor::valueFromLegacy(double value, const juce::String& id) {
if (id == "volume") {
return value / 3.0;
} else if (id == "frequency") {
return std::pow(12000.0, value);
}
return value;
}

Wyświetl plik

@ -34,11 +34,12 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
{
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
addCodeEditor(-1);
for (int i = 0; i < audioProcessor.numFiles(); i++) {
addCodeEditor(i);
}
fileUpdated(audioProcessor.getCurrentFileName());
initialiseCodeEditors();
}
{
juce::MessageManagerLock lock;
audioProcessor.broadcaster.addChangeListener(this);
}
setSize(1100, 750);
@ -46,7 +47,22 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
setResizeLimits(500, 400, 999999, 999999);
}
OscirenderAudioProcessorEditor::~OscirenderAudioProcessorEditor() {}
OscirenderAudioProcessorEditor::~OscirenderAudioProcessorEditor() {
juce::MessageManagerLock lock;
audioProcessor.broadcaster.removeChangeListener(this);
}
// parsersLock must be held
void OscirenderAudioProcessorEditor::initialiseCodeEditors() {
codeDocuments.clear();
codeEditors.clear();
// -1 is the perspective function
addCodeEditor(-1);
for (int i = 0; i < audioProcessor.numFiles(); i++) {
addCodeEditor(i);
}
fileUpdated(audioProcessor.getCurrentFileName());
}
void OscirenderAudioProcessorEditor::paint(juce::Graphics& g) {
g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
@ -101,7 +117,6 @@ void OscirenderAudioProcessorEditor::resized() {
obj.setBounds(altEffectsSection);
}
effects.setBounds(effectsSection);
}
void OscirenderAudioProcessorEditor::addCodeEditor(int index) {
@ -187,6 +202,11 @@ void OscirenderAudioProcessorEditor::handleAsyncUpdate() {
resized();
}
void OscirenderAudioProcessorEditor::changeListenerCallback(juce::ChangeBroadcaster* source) {
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
initialiseCodeEditors();
}
void OscirenderAudioProcessorEditor::editPerspectiveFunction(bool enable) {
editingPerspective = enable;
juce::SpinLock::ScopedLockType lock1(audioProcessor.parsersLock);

Wyświetl plik

@ -10,7 +10,7 @@
#include "components/MainMenuBarModel.h"
class OscirenderAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::CodeDocument::Listener, public juce::AsyncUpdater {
class OscirenderAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::CodeDocument::Listener, public juce::AsyncUpdater, public juce::ChangeListener {
public:
OscirenderAudioProcessorEditor(OscirenderAudioProcessor&);
~OscirenderAudioProcessorEditor() override;
@ -18,10 +18,12 @@ public:
void paint(juce::Graphics&) override;
void resized() override;
void initialiseCodeEditors();
void addCodeEditor(int index);
void removeCodeEditor(int index);
void fileUpdated(juce::String fileName);
void handleAsyncUpdate() override;
void changeListenerCallback(juce::ChangeBroadcaster* source) override;
void editPerspectiveFunction(bool enabled);

Wyświetl plik

@ -93,10 +93,12 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
toggleableEffects.push_back(traceMax);
toggleableEffects.push_back(traceMin);
for (auto& effect : toggleableEffects) {
for (int i = 0; i < toggleableEffects.size(); i++) {
auto effect = toggleableEffects[i];
effect->markEnableable(false);
addParameter(effect->enabled);
effect->enabled->setValueNotifyingHost(false);
effect->setPrecedence(i);
}
permanentEffects.push_back(frequencyEffect);
@ -631,12 +633,27 @@ void OscirenderAudioProcessor::getStateInformation(juce::MemoryBlock& destData)
}
void OscirenderAudioProcessor::setStateInformation(const void* data, int sizeInBytes) {
juce::SpinLock::ScopedLockType lock1(parsersLock);
juce::SpinLock::ScopedLockType lock2(effectsLock);
std::unique_ptr<juce::XmlElement> xml;
std::unique_ptr<juce::XmlElement> xml(getXmlFromBinary(data, sizeInBytes));
const uint32_t magicXmlNumber = 0x21324356;
if (sizeInBytes > 8 && juce::ByteOrder::littleEndianInt(data) == magicXmlNumber) {
// this is a binary xml format
xml = getXmlFromBinary(data, sizeInBytes);
} else {
// this is a text xml format
xml = juce::XmlDocument::parse(juce::String((const char*)data, sizeInBytes));
}
if (xml.get() != nullptr && xml->hasTagName("project")) {
auto versionXml = xml->getChildByName("version");
if (versionXml != nullptr && versionXml->getAllSubText().startsWith("v1.")) {
openLegacyProject(xml.get());
return;
}
juce::SpinLock::ScopedLockType lock1(parsersLock);
juce::SpinLock::ScopedLockType lock2(effectsLock);
auto effectsXml = xml->getChildByName("effects");
if (effectsXml != nullptr) {
for (auto effectXml : effectsXml->getChildIterator()) {

Wyświetl plik

@ -32,11 +32,9 @@ class OscirenderAudioProcessor : public juce::AudioProcessor
, public FrameConsumer
{
public:
//==============================================================================
OscirenderAudioProcessor();
~OscirenderAudioProcessor() override;
//==============================================================================
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
void releaseResources() override;
@ -46,28 +44,22 @@ public:
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
//==============================================================================
juce::AudioProcessorEditor* createEditor() override;
bool hasEditor() const override;
//==============================================================================
const juce::String getName() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
bool isMidiEffect() const override;
double getTailLengthSeconds() const override;
//==============================================================================
int getNumPrograms() override;
int getCurrentProgram() override;
void setCurrentProgram (int index) override;
const juce::String getProgramName (int index) override;
void changeProgramName (int index, const juce::String& newName) override;
//==============================================================================
void getStateInformation (juce::MemoryBlock& destData) override;
void setStateInformation (const void* data, int sizeInBytes) override;
void setCurrentProgram(int index) override;
const juce::String getProgramName(int index) override;
void changeProgramName(int index, const juce::String& newName) override;
void getStateInformation(juce::MemoryBlock& destData) override;
void setStateInformation(const void* data, int sizeInBytes) override;
std::atomic<double> currentSampleRate = 0.0;
@ -257,6 +249,10 @@ private:
void updateObjValues();
std::shared_ptr<Effect> getEffect(juce::String id);
BooleanParameter* getBooleanParameter(juce::String id);
void openLegacyProject(const juce::XmlElement* xml);
std::pair<std::shared_ptr<Effect>, EffectParameter*> effectFromLegacyId(const juce::String& id, bool updatePrecedence = false);
LfoType lfoTypeFromLegacyAnimationType(const juce::String& type);
double valueFromLegacy(double value, const juce::String& id);
const double MIN_LENGTH_INCREMENT = 0.000001;

Wyświetl plik

@ -46,9 +46,6 @@ Vector2 PerspectiveEffect::apply(int index, Vector2 input, const std::vector<dou
auto z = 0.0;
{
// TODO: Instead of evaluating the script every time, we could evaluate it
// once at the start for all values of x and y and then interpolate between
// the results.
juce::SpinLock::ScopedLockType lock(codeLock);
if (!defaultScript) {
parser->setVariable("x", x);

Wyświetl plik

@ -299,6 +299,8 @@
<FILE id="eAqAle" name="IXWebSocketVersion.h" compile="0" resource="0"
file="Source/ixwebsocket/IXWebSocketVersion.h"/>
</GROUP>
<FILE id="uyOdTl" name="LegacyProject.cpp" compile="1" resource="0"
file="Source/LegacyProject.cpp"/>
<GROUP id="{75F6236A-68A5-85DA-EDAE-23D1621601DB}" name="lua">
<FILE id="X5i9iw" name="lapi.c" compile="1" resource="0" file="Source/lua/lapi.c"/>
<FILE id="J62WSE" name="lapi.h" compile="0" resource="0" file="Source/lua/lapi.h"/>