Allow multiple files to be opened, allow files to be closed, and use j and k to change file

pull/170/head
James Ball 2023-03-30 21:09:53 +01:00
rodzic 0ff71379e4
commit ac67c1bec6
9 zmienionych plików z 146 dodań i 51 usunięć

Wyświetl plik

@ -7,30 +7,56 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
setText("Main Settings"); setText("Main Settings");
addAndMakeVisible(fileButton); addAndMakeVisible(fileButton);
fileButton.setButtonText("Choose File(s)");
fileButton.setButtonText("Choose File");
fileButton.onClick = [this] { fileButton.onClick = [this] {
chooser = std::make_unique<juce::FileChooser>("Open", juce::File::getSpecialLocation(juce::File::userHomeDirectory), "*.obj;*.svg;*.lua;*.txt"); chooser = std::make_unique<juce::FileChooser>("Open", juce::File::getSpecialLocation(juce::File::userHomeDirectory), "*.obj;*.svg;*.lua;*.txt");
auto flags = juce::FileBrowserComponent::openMode; auto flags = juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectMultipleItems;
chooser->launchAsync(flags, [this](const juce::FileChooser& chooser) { chooser->launchAsync(flags, [this](const juce::FileChooser& chooser) {
if (chooser.getURLResult().isLocalFile()) { for (auto& url : chooser.getURLResults()) {
auto file = chooser.getResult(); if (url.isLocalFile()) {
audioProcessor.addFile(file); auto file = url.getLocalFile();
pluginEditor.updateCodeEditor(); audioProcessor.addFile(file);
}
} }
pluginEditor.updateCodeEditor();
updateFileLabel();
}); });
}; };
addAndMakeVisible(closeFileButton);
closeFileButton.setButtonText("Close File");
closeFileButton.onClick = [this] {
audioProcessor.removeFile(audioProcessor.getCurrentFileIndex());
pluginEditor.updateCodeEditor();
updateFileLabel();
};
addAndMakeVisible(fileLabel);
updateFileLabel();
} }
MainComponent::~MainComponent() { MainComponent::~MainComponent() {
} }
void MainComponent::resized() { void MainComponent::updateFileLabel() {
auto xPadding = 10; if (audioProcessor.getCurrentFileIndex() == -1) {
auto yPadding = 20; fileLabel.setText("No file open", juce::dontSendNotification);
auto buttonWidth = 100; return;
auto buttonHeight = 40; }
fileButton.setBounds(xPadding, yPadding, buttonWidth, buttonHeight);
fileLabel.setText(audioProcessor.getCurrentFile().getFileName(), juce::dontSendNotification);
}
void MainComponent::resized() {
auto baseYPadding = 10;
auto xPadding = 10;
auto yPadding = 10;
auto buttonWidth = 120;
auto buttonHeight = 40;
fileButton.setBounds(xPadding, baseYPadding + yPadding, buttonWidth, buttonHeight);
closeFileButton.setBounds(xPadding, baseYPadding + yPadding + buttonHeight + yPadding, buttonWidth, buttonHeight);
fileLabel.setBounds(xPadding + buttonWidth + xPadding, baseYPadding + yPadding, getWidth() - xPadding - buttonWidth - xPadding, buttonHeight);
} }

Wyświetl plik

@ -13,13 +13,16 @@ public:
~MainComponent() override; ~MainComponent() override;
void resized() override; void resized() override;
private: private:
OscirenderAudioProcessor& audioProcessor; OscirenderAudioProcessor& audioProcessor;
OscirenderAudioProcessorEditor& pluginEditor; OscirenderAudioProcessorEditor& pluginEditor;
std::unique_ptr<juce::FileChooser> chooser; std::unique_ptr<juce::FileChooser> chooser;
juce::TextButton fileButton; juce::TextButton fileButton;
juce::TextButton closeFileButton;
juce::Label fileLabel;
void updateFileLabel();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent)
}; };

Wyświetl plik

@ -54,6 +54,7 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
collapseButton.setShape(path, false, true, true); collapseButton.setShape(path, false, true, true);
} else { } else {
codeEditor->setVisible(true); codeEditor->setVisible(true);
updateCodeEditor();
juce::Path path; juce::Path path;
path.addTriangle(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f); path.addTriangle(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f);
collapseButton.setShape(path, false, true, true); collapseButton.setShape(path, false, true, true);
@ -93,15 +94,40 @@ void OscirenderAudioProcessorEditor::resized() {
} }
void OscirenderAudioProcessorEditor::updateCodeEditor() { void OscirenderAudioProcessorEditor::updateCodeEditor() {
codeEditor->loadContent(juce::MemoryInputStream(*audioProcessor.getFileBlock(audioProcessor.getCurrentFile()), false).readEntireStreamAsString()); if (codeEditor->isVisible() && audioProcessor.getCurrentFileIndex() != -1) {
codeEditor->loadContent(juce::MemoryInputStream(*audioProcessor.getFileBlock(audioProcessor.getCurrentFileIndex()), false).readEntireStreamAsString());
}
} }
void OscirenderAudioProcessorEditor::codeDocumentTextInserted(const juce::String& newText, int insertIndex) { void OscirenderAudioProcessorEditor::codeDocumentTextInserted(const juce::String& newText, int insertIndex) {
juce::String file = codeDocument.getAllContent(); juce::String file = codeDocument.getAllContent();
audioProcessor.updateFileBlock(audioProcessor.getCurrentFile(), std::make_shared<juce::MemoryBlock>(file.toRawUTF8(), file.getNumBytesAsUTF8() + 1)); audioProcessor.updateFileBlock(audioProcessor.getCurrentFileIndex(), std::make_shared<juce::MemoryBlock>(file.toRawUTF8(), file.getNumBytesAsUTF8() + 1));
} }
void OscirenderAudioProcessorEditor::codeDocumentTextDeleted(int startIndex, int endIndex) { void OscirenderAudioProcessorEditor::codeDocumentTextDeleted(int startIndex, int endIndex) {
juce::String file = codeDocument.getAllContent(); juce::String file = codeDocument.getAllContent();
audioProcessor.updateFileBlock(audioProcessor.getCurrentFile(), std::make_shared<juce::MemoryBlock>(file.toRawUTF8(), file.getNumBytesAsUTF8() + 1)); audioProcessor.updateFileBlock(audioProcessor.getCurrentFileIndex(), std::make_shared<juce::MemoryBlock>(file.toRawUTF8(), file.getNumBytesAsUTF8() + 1));
}
bool OscirenderAudioProcessorEditor::keyPressed(const juce::KeyPress& key) {
int numFiles = audioProcessor.numFiles();
int currentFile = audioProcessor.getCurrentFileIndex();
if (key.getTextCharacter() == 'j') {
currentFile++;
if (currentFile == numFiles) {
currentFile = 0;
}
audioProcessor.changeCurrentFile(currentFile);
updateCodeEditor();
return true;
} else if (key.getTextCharacter() == 'k') {
currentFile--;
if (currentFile < 0) {
currentFile = numFiles - 1;
}
audioProcessor.changeCurrentFile(currentFile);
updateCodeEditor();
return true;
}
return false;
} }

Wyświetl plik

@ -41,5 +41,7 @@ private:
void codeDocumentTextInserted(const juce::String& newText, int insertIndex) override; void codeDocumentTextInserted(const juce::String& newText, int insertIndex) override;
void codeDocumentTextDeleted(int startIndex, int endIndex) override; void codeDocumentTextDeleted(int startIndex, int endIndex) override;
bool keyPressed(const juce::KeyPress& key) override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscirenderAudioProcessorEditor) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscirenderAudioProcessorEditor)
}; };

Wyświetl plik

@ -29,10 +29,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
) )
#endif #endif
{ {
parsers.push_back(std::make_unique<FileParser>()); producer = std::make_unique<FrameProducer>(*this, std::make_shared<FileParser>());
files.emplace_back();
fileBlocks.emplace_back();
producer = std::make_unique<FrameProducer>(*this, parsers[0]);
producer->startThread(); producer->startThread();
allEffects.push_back(std::make_shared<Effect>(std::make_unique<BitCrushEffect>(), "Bit Crush", "bitCrush")); allEffects.push_back(std::make_shared<Effect>(std::make_unique<BitCrushEffect>(), "Bit Crush", "bitCrush"));
@ -197,24 +194,6 @@ void OscirenderAudioProcessor::updateAngleDelta() {
thetaDelta = cyclesPerSample * 2.0 * juce::MathConstants<double>::pi; thetaDelta = cyclesPerSample * 2.0 * juce::MathConstants<double>::pi;
} }
void OscirenderAudioProcessor::addFrame(std::vector<std::unique_ptr<Shape>> frame) {
const auto scope = frameFifo.write(1);
if (scope.blockSize1 > 0) {
frameBuffer[scope.startIndex1].clear();
for (auto& shape : frame) {
frameBuffer[scope.startIndex1].push_back(std::move(shape));
}
}
if (scope.blockSize2 > 0) {
frameBuffer[scope.startIndex2].clear();
for (auto& shape : frame) {
frameBuffer[scope.startIndex2].push_back(std::move(shape));
}
}
}
void OscirenderAudioProcessor::enableEffect(std::shared_ptr<Effect> effect) { void OscirenderAudioProcessor::enableEffect(std::shared_ptr<Effect> effect) {
// need to make a new vector because the old one is being iterated over in another thread // need to make a new vector because the old one is being iterated over in another thread
std::shared_ptr<std::vector<std::shared_ptr<Effect>>> newEffects = std::make_shared<std::vector<std::shared_ptr<Effect>>>(); std::shared_ptr<std::vector<std::shared_ptr<Effect>>> newEffects = std::make_shared<std::vector<std::shared_ptr<Effect>>>();
@ -282,8 +261,10 @@ void OscirenderAudioProcessor::addFile(juce::File file) {
} }
void OscirenderAudioProcessor::removeFile(int index) { void OscirenderAudioProcessor::removeFile(int index) {
openFile(index - 1); if (index < 0 || index >= fileBlocks.size()) {
parsers[index]->disable(); return;
}
changeCurrentFile(index - 1);
fileBlocks.erase(fileBlocks.begin() + index); fileBlocks.erase(fileBlocks.begin() + index);
files.erase(files.begin() + index); files.erase(files.begin() + index);
parsers.erase(parsers.begin() + index); parsers.erase(parsers.begin() + index);
@ -294,19 +275,56 @@ int OscirenderAudioProcessor::numFiles() {
} }
void OscirenderAudioProcessor::openFile(int index) { void OscirenderAudioProcessor::openFile(int index) {
currentFile = index; if (index < 0 || index >= fileBlocks.size()) {
return;
}
parsers[index]->parse(files[index].getFileExtension(), std::make_unique<juce::MemoryInputStream>(*fileBlocks[index], false)); parsers[index]->parse(files[index].getFileExtension(), std::make_unique<juce::MemoryInputStream>(*fileBlocks[index], false));
producer->setSource(parsers[index]); producer->setSource(parsers[index], index);
currentFile = index;
invalidateFrameBuffer = true;
} }
int OscirenderAudioProcessor::getCurrentFile() { void OscirenderAudioProcessor::changeCurrentFile(int index) {
if (index < 0 || index >= fileBlocks.size()) {
return;
}
producer->setSource(parsers[index], index);
currentFile = index;
invalidateFrameBuffer = true;
}
int OscirenderAudioProcessor::getCurrentFileIndex() {
return currentFile; return currentFile;
} }
juce::File OscirenderAudioProcessor::getCurrentFile() {
return files[currentFile];
}
std::shared_ptr<juce::MemoryBlock> OscirenderAudioProcessor::getFileBlock(int index) { std::shared_ptr<juce::MemoryBlock> OscirenderAudioProcessor::getFileBlock(int index) {
return fileBlocks[index]; return fileBlocks[index];
} }
void OscirenderAudioProcessor::addFrame(std::vector<std::unique_ptr<Shape>> frame, int fileIndex) {
const auto scope = frameFifo.write(1);
if (scope.blockSize1 > 0) {
frameBuffer[scope.startIndex1].clear();
for (auto& shape : frame) {
frameBuffer[scope.startIndex1].push_back(std::move(shape));
}
frameBufferIndices[scope.startIndex1] = fileIndex;
}
if (scope.blockSize2 > 0) {
frameBuffer[scope.startIndex2].clear();
for (auto& shape : frame) {
frameBuffer[scope.startIndex2].push_back(std::move(shape));
}
frameBufferIndices[scope.startIndex2] = fileIndex;
}
}
void OscirenderAudioProcessor::updateFrame() { void OscirenderAudioProcessor::updateFrame() {
currentShape = 0; currentShape = 0;
shapeDrawn = 0.0; shapeDrawn = 0.0;
@ -318,8 +336,10 @@ void OscirenderAudioProcessor::updateFrame() {
if (scope.blockSize1 > 0) { if (scope.blockSize1 > 0) {
frame.swap(frameBuffer[scope.startIndex1]); frame.swap(frameBuffer[scope.startIndex1]);
currentBufferIndex = frameBufferIndices[scope.startIndex1];
} else if (scope.blockSize2 > 0) { } else if (scope.blockSize2 > 0) {
frame.swap(frameBuffer[scope.startIndex2]); frame.swap(frameBuffer[scope.startIndex2]);
currentBufferIndex = frameBufferIndices[scope.startIndex2];
} }
frameLength = Shape::totalLength(frame); frameLength = Shape::totalLength(frame);
@ -349,6 +369,17 @@ void OscirenderAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, j
auto* channelData = buffer.getArrayOfWritePointers(); auto* channelData = buffer.getArrayOfWritePointers();
auto numSamples = buffer.getNumSamples(); auto numSamples = buffer.getNumSamples();
if (invalidateFrameBuffer) {
frameFifo.reset();
// keeps getting the next frame until the frame comes from the file that we want to render.
// this MIGHT be hacky and cause issues later down the line, but for now it works as a
// solution to get instant changing of current file when pressing j and k.
while (currentBufferIndex != currentFile) {
updateFrame();
}
invalidateFrameBuffer = false;
}
for (auto sample = 0; sample < numSamples; ++sample) { for (auto sample = 0; sample < numSamples; ++sample) {
updateLengthIncrement(); updateLengthIncrement();

Wyświetl plik

@ -74,7 +74,7 @@ public:
BitCrushEffect bitCrushEffect = BitCrushEffect(); BitCrushEffect bitCrushEffect = BitCrushEffect();
BulgeEffect bulgeEffect = BulgeEffect(); BulgeEffect bulgeEffect = BulgeEffect();
std::vector<std::shared_ptr<FileParser>> parsers; std::vector<std::shared_ptr<FileParser>> parsers;
std::vector<std::shared_ptr<juce::MemoryBlock>> fileBlocks; std::vector<std::shared_ptr<juce::MemoryBlock>> fileBlocks;
std::vector<juce::File> files; std::vector<juce::File> files;
@ -83,7 +83,7 @@ public:
std::unique_ptr<FrameProducer> producer; std::unique_ptr<FrameProducer> producer;
void updateAngleDelta(); void updateAngleDelta();
void addFrame(std::vector<std::unique_ptr<Shape>> frame) override; void addFrame(std::vector<std::unique_ptr<Shape>> frame, int fileIndex) override;
void enableEffect(std::shared_ptr<Effect> effect); void enableEffect(std::shared_ptr<Effect> effect);
void disableEffect(std::shared_ptr<Effect> effect); void disableEffect(std::shared_ptr<Effect> effect);
void updateEffectPrecedence(); void updateEffectPrecedence();
@ -92,7 +92,9 @@ public:
void removeFile(int index); void removeFile(int index);
int numFiles(); int numFiles();
void openFile(int index); void openFile(int index);
int getCurrentFile(); void changeCurrentFile(int index);
int getCurrentFileIndex();
juce::File getCurrentFile();
std::shared_ptr<juce::MemoryBlock> getFileBlock(int index); std::shared_ptr<juce::MemoryBlock> getFileBlock(int index);
private: private:
double theta = 0.0; double theta = 0.0;
@ -100,13 +102,16 @@ private:
juce::AbstractFifo frameFifo{ 10 }; juce::AbstractFifo frameFifo{ 10 };
std::vector<std::unique_ptr<Shape>> frameBuffer[10]; std::vector<std::unique_ptr<Shape>> frameBuffer[10];
int frameBufferIndices[10];
int currentShape = 0; int currentShape = 0;
std::vector<std::unique_ptr<Shape>> frame; std::vector<std::unique_ptr<Shape>> frame;
int currentBufferIndex = -1;
double frameLength; double frameLength;
double shapeDrawn = 0.0; double shapeDrawn = 0.0;
double frameDrawn = 0.0; double frameDrawn = 0.0;
double lengthIncrement = 0.0; double lengthIncrement = 0.0;
bool invalidateFrameBuffer = false;
void updateFrame(); void updateFrame();
void updateLengthIncrement(); void updateLengthIncrement();

Wyświetl plik

@ -6,5 +6,5 @@
class FrameConsumer { class FrameConsumer {
public: public:
virtual void addFrame(std::vector<std::unique_ptr<Shape>> frame) = 0; virtual void addFrame(std::vector<std::unique_ptr<Shape>> frame, int fileIndex) = 0;
}; };

Wyświetl plik

@ -9,11 +9,12 @@ FrameProducer::~FrameProducer() {
void FrameProducer::run() { void FrameProducer::run() {
while (!threadShouldExit() && frameSource->isActive()) { while (!threadShouldExit() && frameSource->isActive()) {
frameConsumer.addFrame(frameSource->next()); frameConsumer.addFrame(frameSource->next(), sourceFileIndex);
} }
} }
void FrameProducer::setSource(std::shared_ptr<FrameSource> source) { void FrameProducer::setSource(std::shared_ptr<FrameSource> source, int fileIndex) {
// TODO: should make this atomic // TODO: should make this atomic
frameSource = source; frameSource = source;
sourceFileIndex = fileIndex;
} }

Wyświetl plik

@ -10,8 +10,9 @@ public:
~FrameProducer() override; ~FrameProducer() override;
void run() override; void run() override;
void setSource(std::shared_ptr<FrameSource>); void setSource(std::shared_ptr<FrameSource>, int fileIndex);
private: private:
FrameConsumer& frameConsumer; FrameConsumer& frameConsumer;
std::shared_ptr<FrameSource> frameSource; std::shared_ptr<FrameSource> frameSource;
int sourceFileIndex = 0;
}; };