kopia lustrzana https://github.com/jameshball/osci-render
Flip gionometer and add warning message if opening an obj file that is too large
rodzic
02095d3a15
commit
c205de10b7
|
@ -41,9 +41,7 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pluginEditor.removeCodeEditor(audioProcessor.getCurrentFileIndex());
|
|
||||||
audioProcessor.removeFile(audioProcessor.getCurrentFileIndex());
|
audioProcessor.removeFile(audioProcessor.getCurrentFileIndex());
|
||||||
pluginEditor.fileUpdated(audioProcessor.getCurrentFileName());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
closeFileButton.setTooltip("Close the currently open file.");
|
closeFileButton.setTooltip("Close the currently open file.");
|
||||||
|
|
|
@ -2,7 +2,20 @@
|
||||||
#include "PluginEditor.h"
|
#include "PluginEditor.h"
|
||||||
#include "CustomStandaloneFilterWindow.h"
|
#include "CustomStandaloneFilterWindow.h"
|
||||||
|
|
||||||
|
void OscirenderAudioProcessorEditor::registerFileRemovedCallback() {
|
||||||
|
audioProcessor.setFileRemovedCallback([this](int index) {
|
||||||
|
removeCodeEditor(index);
|
||||||
|
fileUpdated(audioProcessor.getCurrentFileName());
|
||||||
|
juce::MessageManager::callAsync([this] {
|
||||||
|
resized();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioProcessor& p) : CommonPluginEditor(p, "osci-render", "osci", 1100, 750), audioProcessor(p), collapseButton("Collapse", juce::Colours::white, juce::Colours::white, juce::Colours::white) {
|
OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioProcessor& p) : CommonPluginEditor(p, "osci-render", "osci", 1100, 750), audioProcessor(p), collapseButton("Collapse", juce::Colours::white, juce::Colours::white, juce::Colours::white) {
|
||||||
|
// Register the file removal callback
|
||||||
|
registerFileRemovedCallback();
|
||||||
|
|
||||||
#if !SOSCI_FEATURES
|
#if !SOSCI_FEATURES
|
||||||
addAndMakeVisible(upgradeButton);
|
addAndMakeVisible(upgradeButton);
|
||||||
upgradeButton.onClick = [this] {
|
upgradeButton.onClick = [this] {
|
||||||
|
@ -85,6 +98,9 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
|
||||||
}
|
}
|
||||||
|
|
||||||
OscirenderAudioProcessorEditor::~OscirenderAudioProcessorEditor() {
|
OscirenderAudioProcessorEditor::~OscirenderAudioProcessorEditor() {
|
||||||
|
// Clear the file removal callback
|
||||||
|
audioProcessor.setFileRemovedCallback(nullptr);
|
||||||
|
|
||||||
menuBar.setModel(nullptr);
|
menuBar.setModel(nullptr);
|
||||||
juce::MessageManagerLock lock;
|
juce::MessageManagerLock lock;
|
||||||
audioProcessor.broadcaster.removeChangeListener(this);
|
audioProcessor.broadcaster.removeChangeListener(this);
|
||||||
|
|
|
@ -34,6 +34,8 @@ public:
|
||||||
void editCustomFunction(bool enabled);
|
void editCustomFunction(bool enabled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void registerFileRemovedCallback();
|
||||||
|
|
||||||
OscirenderAudioProcessor& audioProcessor;
|
OscirenderAudioProcessor& audioProcessor;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -293,6 +293,11 @@ void OscirenderAudioProcessor::addFile(juce::String fileName, std::shared_ptr<ju
|
||||||
openFile(fileBlocks.size() - 1);
|
openFile(fileBlocks.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setter for the callback
|
||||||
|
void OscirenderAudioProcessor::setFileRemovedCallback(std::function<void(int)> callback) {
|
||||||
|
fileRemovedCallback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
// 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()) {
|
||||||
|
@ -303,11 +308,32 @@ void OscirenderAudioProcessor::removeFile(int index) {
|
||||||
fileIds.erase(fileIds.begin() + index);
|
fileIds.erase(fileIds.begin() + index);
|
||||||
parsers.erase(parsers.begin() + index);
|
parsers.erase(parsers.begin() + index);
|
||||||
sounds.erase(sounds.begin() + index);
|
sounds.erase(sounds.begin() + index);
|
||||||
|
|
||||||
auto newFileIndex = index;
|
auto newFileIndex = index;
|
||||||
if (newFileIndex >= fileBlocks.size()) {
|
if (newFileIndex >= fileBlocks.size()) {
|
||||||
newFileIndex = fileBlocks.size() - 1;
|
newFileIndex = fileBlocks.size() - 1;
|
||||||
}
|
}
|
||||||
changeCurrentFile(newFileIndex);
|
changeCurrentFile(newFileIndex);
|
||||||
|
|
||||||
|
// Notify the editor about the file removal
|
||||||
|
if (fileRemovedCallback) {
|
||||||
|
fileRemovedCallback(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsersLock AND effectsLock must be locked before calling this function
|
||||||
|
void OscirenderAudioProcessor::removeParser(FileParser* parser) {
|
||||||
|
int parserIndex = -1;
|
||||||
|
for (int i = 0; i < parsers.size(); i++) {
|
||||||
|
if (parsers[i].get() == parser) {
|
||||||
|
parserIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parserIndex >= 0) {
|
||||||
|
removeFile(parserIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int OscirenderAudioProcessor::numFiles() {
|
int OscirenderAudioProcessor::numFiles() {
|
||||||
|
@ -321,7 +347,7 @@ void OscirenderAudioProcessor::openFile(int index) {
|
||||||
if (index < 0 || index >= fileBlocks.size()) {
|
if (index < 0 || index >= fileBlocks.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
parsers[index]->parse(juce::String(fileIds[index]), fileNames[index].fromLastOccurrenceOf(".", true, false).toLowerCase(), std::make_unique<juce::MemoryInputStream>(*fileBlocks[index], false), font);
|
parsers[index]->parse(juce::String(fileIds[index]), fileNames[index], fileNames[index].fromLastOccurrenceOf(".", true, false).toLowerCase(), std::make_unique<juce::MemoryInputStream>(*fileBlocks[index], false), font);
|
||||||
changeCurrentFile(index);
|
changeCurrentFile(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -184,6 +184,9 @@ public:
|
||||||
|
|
||||||
std::function<void()> haltRecording;
|
std::function<void()> haltRecording;
|
||||||
|
|
||||||
|
// Add a callback to notify the editor when a file is removed
|
||||||
|
std::function<void(int)> fileRemovedCallback;
|
||||||
|
|
||||||
void addLuaSlider();
|
void addLuaSlider();
|
||||||
void updateEffectPrecedence();
|
void updateEffectPrecedence();
|
||||||
void updateFileBlock(int index, std::shared_ptr<juce::MemoryBlock> block);
|
void updateFileBlock(int index, std::shared_ptr<juce::MemoryBlock> block);
|
||||||
|
@ -205,6 +208,13 @@ public:
|
||||||
void addErrorListener(ErrorListener* listener);
|
void addErrorListener(ErrorListener* listener);
|
||||||
void removeErrorListener(ErrorListener* listener);
|
void removeErrorListener(ErrorListener* listener);
|
||||||
void notifyErrorListeners(int lineNumber, juce::String id, juce::String error);
|
void notifyErrorListeners(int lineNumber, juce::String id, juce::String error);
|
||||||
|
|
||||||
|
// Setter for the callback
|
||||||
|
void setFileRemovedCallback(std::function<void(int)> callback);
|
||||||
|
|
||||||
|
// Added declaration for the new `removeParser` method.
|
||||||
|
void removeParser(FileParser* parser);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::atomic<bool> prevMidiEnabled = !midiEnabled->getBoolValue();
|
std::atomic<bool> prevMidiEnabled = !midiEnabled->getBoolValue();
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
#include <numbers>
|
#include <numbers>
|
||||||
#include "../PluginProcessor.h"
|
#include "../PluginProcessor.h"
|
||||||
|
|
||||||
FileParser::FileParser(OscirenderAudioProcessor &p, std::function<void(int, juce::String, juce::String)> errorCallback) : errorCallback(errorCallback), audioProcessor(p) {}
|
FileParser::FileParser(OscirenderAudioProcessor &p, std::function<void(int, juce::String, juce::String)> errorCallback)
|
||||||
|
: errorCallback(errorCallback), audioProcessor(p) {}
|
||||||
|
|
||||||
void FileParser::parse(juce::String fileId, juce::String extension, std::unique_ptr<juce::InputStream> stream, juce::Font font) {
|
void FileParser::parse(juce::String fileId, juce::String fileName, juce::String extension, std::unique_ptr<juce::InputStream> stream, juce::Font font) {
|
||||||
juce::SpinLock::ScopedLockType scope(lock);
|
juce::SpinLock::ScopedLockType scope(lock);
|
||||||
|
|
||||||
if (extension == ".lua" && lua != nullptr && lua->isFunctionValid()) {
|
if (extension == ".lua" && lua != nullptr && lua->isFunctionValid()) {
|
||||||
|
@ -22,7 +23,54 @@ void FileParser::parse(juce::String fileId, juce::String extension, std::unique_
|
||||||
wav = nullptr;
|
wav = nullptr;
|
||||||
|
|
||||||
if (extension == ".obj") {
|
if (extension == ".obj") {
|
||||||
object = std::make_shared<WorldObject>(stream->readEntireStreamAsString().toStdString());
|
// Check file size before parsing
|
||||||
|
const int64_t fileSize = stream->getTotalLength();
|
||||||
|
const int64_t oneMB = 1024 * 1024; // 1MB in bytes
|
||||||
|
|
||||||
|
// Save the file content to avoid losing it after the async operation
|
||||||
|
juce::String objContent = stream->readEntireStreamAsString();
|
||||||
|
|
||||||
|
if (fileSize > oneMB) {
|
||||||
|
// For large files, show an async warning dialog
|
||||||
|
const double fileSizeMB = fileSize / (1024.0 * 1024.0);
|
||||||
|
|
||||||
|
juce::MessageManager::callAsync([this, objContent, fileSizeMB, fileName]() {
|
||||||
|
juce::String message = juce::String::formatted(
|
||||||
|
"The OBJ file '%s' you're trying to open is %.2f MB in size, which may cause performance issues. "
|
||||||
|
"Would you like to continue loading it?", fileName.toRawUTF8(), fileSizeMB);
|
||||||
|
|
||||||
|
// Show async dialog with callbacks for user response
|
||||||
|
juce::AlertWindow::showOkCancelBox(
|
||||||
|
juce::AlertWindow::WarningIcon,
|
||||||
|
"Large OBJ File",
|
||||||
|
message,
|
||||||
|
"Continue",
|
||||||
|
"Cancel",
|
||||||
|
nullptr,
|
||||||
|
juce::ModalCallbackFunction::create([this, objContent](int result) {
|
||||||
|
if (result == 1) { // 1 = OK button pressed
|
||||||
|
juce::SpinLock::ScopedLockType scope(lock);
|
||||||
|
// User chose to continue, load the file
|
||||||
|
object = std::make_shared<WorldObject>(objContent.toStdString());
|
||||||
|
} else {
|
||||||
|
// User canceled, fully close this file parser
|
||||||
|
juce::SpinLock::ScopedLockType scope(lock);
|
||||||
|
disable(); // Mark this parser as inactive
|
||||||
|
|
||||||
|
// Notify the processor to remove this parser
|
||||||
|
juce::MessageManager::callAsync([this] {
|
||||||
|
juce::SpinLock::ScopedLockType lock1(audioProcessor.parsersLock);
|
||||||
|
juce::SpinLock::ScopedLockType lock2(audioProcessor.effectsLock);
|
||||||
|
audioProcessor.removeParser(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// For small files, load immediately
|
||||||
|
object = std::make_shared<WorldObject>(objContent.toStdString());
|
||||||
|
}
|
||||||
} else if (extension == ".svg") {
|
} else if (extension == ".svg") {
|
||||||
svg = std::make_shared<SvgParser>(stream->readEntireStreamAsString());
|
svg = std::make_shared<SvgParser>(stream->readEntireStreamAsString());
|
||||||
} else if (extension == ".txt") {
|
} else if (extension == ".txt") {
|
||||||
|
@ -52,8 +100,10 @@ void FileParser::parse(juce::String fileId, juce::String extension, std::unique_
|
||||||
} else if (extension == ".wav" || extension == ".aiff" || extension == ".flac" || extension == ".ogg" || extension == ".mp3") {
|
} else if (extension == ".wav" || extension == ".aiff" || extension == ".flac" || extension == ".ogg" || extension == ".mp3") {
|
||||||
wav = std::make_shared<WavParser>(audioProcessor);
|
wav = std::make_shared<WavParser>(audioProcessor);
|
||||||
if (!wav->parse(std::move(stream))) {
|
if (!wav->parse(std::move(stream))) {
|
||||||
juce::MessageManager::callAsync([this] {
|
juce::MessageManager::callAsync([this, fileName] {
|
||||||
juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::AlertIconType::WarningIcon, "Error", "The audio file could not be loaded.");
|
juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::AlertIconType::WarningIcon,
|
||||||
|
"Error Loading " + fileName,
|
||||||
|
"The audio file '" + fileName + "' could not be loaded.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ class FileParser {
|
||||||
public:
|
public:
|
||||||
FileParser(OscirenderAudioProcessor &p, std::function<void(int, juce::String, juce::String)> errorCallback = nullptr);
|
FileParser(OscirenderAudioProcessor &p, std::function<void(int, juce::String, juce::String)> errorCallback = nullptr);
|
||||||
|
|
||||||
void parse(juce::String fileName, juce::String extension, std::unique_ptr<juce::InputStream> stream, juce::Font font);
|
void parse(juce::String fileId, juce::String fileName, juce::String extension, std::unique_ptr<juce::InputStream> stream, juce::Font font);
|
||||||
std::vector<std::unique_ptr<Shape>> nextFrame();
|
std::vector<std::unique_ptr<Shape>> nextFrame();
|
||||||
OsciPoint nextSample(lua_State*& L, LuaVariables& vars);
|
OsciPoint nextSample(lua_State*& L, LuaVariables& vars);
|
||||||
void closeLua(lua_State*& L);
|
void closeLua(lua_State*& L);
|
||||||
|
@ -39,7 +39,6 @@ private:
|
||||||
|
|
||||||
bool active = true;
|
bool active = true;
|
||||||
bool sampleSource = false;
|
bool sampleSource = false;
|
||||||
|
|
||||||
juce::SpinLock lock;
|
juce::SpinLock lock;
|
||||||
|
|
||||||
std::shared_ptr<WorldObject> object;
|
std::shared_ptr<WorldObject> object;
|
||||||
|
|
|
@ -243,7 +243,7 @@ void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
|
||||||
if (settings.isGoniometer()) {
|
if (settings.isGoniometer()) {
|
||||||
// x and y go to a diagonal currently, so we need to scale them down, and rotate them
|
// x and y go to a diagonal currently, so we need to scale them down, and rotate them
|
||||||
point.scale(1.0 / std::sqrt(2.0), 1.0 / std::sqrt(2.0), 1.0);
|
point.scale(1.0 / std::sqrt(2.0), 1.0 / std::sqrt(2.0), 1.0);
|
||||||
point.rotate(0, 0, juce::MathConstants<double>::pi / 4);
|
point.rotate(0, 0, -juce::MathConstants<double>::pi / 4);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue