kopia lustrzana https://github.com/jameshball/osci-render
Allow multiple files to be opened, allow files to be closed, and use j and k to change file
rodzic
0ff71379e4
commit
ac67c1bec6
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
Ładowanie…
Reference in New Issue