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");
addAndMakeVisible(fileButton);
fileButton.setButtonText("Choose File");
fileButton.setButtonText("Choose File(s)");
fileButton.onClick = [this] {
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) {
if (chooser.getURLResult().isLocalFile()) {
auto file = chooser.getResult();
audioProcessor.addFile(file);
pluginEditor.updateCodeEditor();
for (auto& url : chooser.getURLResults()) {
if (url.isLocalFile()) {
auto file = url.getLocalFile();
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() {
}
void MainComponent::resized() {
auto xPadding = 10;
auto yPadding = 20;
auto buttonWidth = 100;
auto buttonHeight = 40;
fileButton.setBounds(xPadding, yPadding, buttonWidth, buttonHeight);
void MainComponent::updateFileLabel() {
if (audioProcessor.getCurrentFileIndex() == -1) {
fileLabel.setText("No file open", juce::dontSendNotification);
return;
}
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;
void resized() override;
private:
OscirenderAudioProcessor& audioProcessor;
OscirenderAudioProcessorEditor& pluginEditor;
std::unique_ptr<juce::FileChooser> chooser;
juce::TextButton fileButton;
juce::TextButton closeFileButton;
juce::Label fileLabel;
void updateFileLabel();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent)
};

Wyświetl plik

@ -54,6 +54,7 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
collapseButton.setShape(path, false, true, true);
} else {
codeEditor->setVisible(true);
updateCodeEditor();
juce::Path path;
path.addTriangle(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f);
collapseButton.setShape(path, false, true, true);
@ -93,15 +94,40 @@ void OscirenderAudioProcessorEditor::resized() {
}
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) {
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) {
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 codeDocumentTextDeleted(int startIndex, int endIndex) override;
bool keyPressed(const juce::KeyPress& key) override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscirenderAudioProcessorEditor)
};

Wyświetl plik

@ -29,10 +29,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
)
#endif
{
parsers.push_back(std::make_unique<FileParser>());
files.emplace_back();
fileBlocks.emplace_back();
producer = std::make_unique<FrameProducer>(*this, parsers[0]);
producer = std::make_unique<FrameProducer>(*this, std::make_shared<FileParser>());
producer->startThread();
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;
}
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) {
// 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>>>();
@ -282,8 +261,10 @@ void OscirenderAudioProcessor::addFile(juce::File file) {
}
void OscirenderAudioProcessor::removeFile(int index) {
openFile(index - 1);
parsers[index]->disable();
if (index < 0 || index >= fileBlocks.size()) {
return;
}
changeCurrentFile(index - 1);
fileBlocks.erase(fileBlocks.begin() + index);
files.erase(files.begin() + index);
parsers.erase(parsers.begin() + index);
@ -294,19 +275,56 @@ int OscirenderAudioProcessor::numFiles() {
}
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));
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;
}
juce::File OscirenderAudioProcessor::getCurrentFile() {
return files[currentFile];
}
std::shared_ptr<juce::MemoryBlock> OscirenderAudioProcessor::getFileBlock(int 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() {
currentShape = 0;
shapeDrawn = 0.0;
@ -318,8 +336,10 @@ void OscirenderAudioProcessor::updateFrame() {
if (scope.blockSize1 > 0) {
frame.swap(frameBuffer[scope.startIndex1]);
currentBufferIndex = frameBufferIndices[scope.startIndex1];
} else if (scope.blockSize2 > 0) {
frame.swap(frameBuffer[scope.startIndex2]);
currentBufferIndex = frameBufferIndices[scope.startIndex2];
}
frameLength = Shape::totalLength(frame);
@ -349,6 +369,17 @@ void OscirenderAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, j
auto* channelData = buffer.getArrayOfWritePointers();
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) {
updateLengthIncrement();

Wyświetl plik

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

Wyświetl plik

@ -6,5 +6,5 @@
class FrameConsumer {
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() {
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
frameSource = source;
sourceFileIndex = fileIndex;
}

Wyświetl plik

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