kopia lustrzana https://github.com/jameshball/osci-render
Improve the look of the timeline and the volume component, and show error when opening large video files, and fix several bugs
rodzic
114a7bf66f
commit
0150dcde51
|
@ -67,6 +67,10 @@ void FrameSettingsComponent::resized() {
|
|||
|
||||
auto firstColumn = area.removeFromLeft(220);
|
||||
|
||||
if (juce::JUCEApplicationBase::isStandaloneApp()) {
|
||||
timeline.setVisible(animated);
|
||||
}
|
||||
|
||||
if (animated) {
|
||||
if (juce::JUCEApplicationBase::isStandaloneApp()) {
|
||||
timeline.setBounds(timelineArea);
|
||||
|
|
|
@ -10,10 +10,10 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
|
|||
fileButton.setButtonText("Choose File(s)");
|
||||
|
||||
fileButton.onClick = [this] {
|
||||
juce::String fileFormats = "*.obj;*.svg;*.lua;*.txt;*.gpla;*.gif;*.png;*.jpg;*.jpeg;*.wav;*.aiff;*.ogg;*.flac;*.mp3";
|
||||
#if OSCI_PREMIUM
|
||||
fileFormats += ";*.mp4;*.mov";
|
||||
#endif
|
||||
juce::String fileFormats;
|
||||
for (auto& ext : audioProcessor.FILE_EXTENSIONS) {
|
||||
fileFormats += "*." + ext + ";";
|
||||
}
|
||||
chooser = std::make_unique<juce::FileChooser>("Open", audioProcessor.getLastOpenedDirectory(), fileFormats);
|
||||
auto flags = juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectMultipleItems |
|
||||
juce::FileBrowserComponent::canSelectFiles;
|
||||
|
|
|
@ -127,21 +127,10 @@ bool OscirenderAudioProcessorEditor::isInterestedInFileDrag(const juce::StringAr
|
|||
return false;
|
||||
}
|
||||
juce::File file(files[0]);
|
||||
return
|
||||
file.hasFileExtension("wav") ||
|
||||
file.hasFileExtension("aiff") ||
|
||||
file.hasFileExtension("ogg") ||
|
||||
file.hasFileExtension("flac") ||
|
||||
file.hasFileExtension("mp3") ||
|
||||
file.hasFileExtension("osci") ||
|
||||
file.hasFileExtension("txt") ||
|
||||
file.hasFileExtension("lua") ||
|
||||
file.hasFileExtension("svg") ||
|
||||
file.hasFileExtension("obj") ||
|
||||
file.hasFileExtension("gif") ||
|
||||
file.hasFileExtension("png") ||
|
||||
file.hasFileExtension("jpg") ||
|
||||
file.hasFileExtension("gpla");
|
||||
juce::String ext = file.getFileExtension().toLowerCase();
|
||||
if (std::find(audioProcessor.FILE_EXTENSIONS.begin(), audioProcessor.FILE_EXTENSIONS.end(), ext) != audioProcessor.FILE_EXTENSIONS.end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void OscirenderAudioProcessorEditor::filesDropped(const juce::StringArray& files, int x, int y) {
|
||||
|
@ -164,7 +153,18 @@ void OscirenderAudioProcessorEditor::filesDropped(const juce::StringArray& files
|
|||
|
||||
bool OscirenderAudioProcessorEditor::isBinaryFile(juce::String name) {
|
||||
name = name.toLowerCase();
|
||||
return name.endsWith(".gpla") || name.endsWith(".gif") || name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".wav") || name.endsWith(".aiff") || name.endsWith(".ogg") || name.endsWith(".mp3") || name.endsWith(".flac");
|
||||
return name.endsWith(".gpla")
|
||||
|| name.endsWith(".gif")
|
||||
|| name.endsWith(".png")
|
||||
|| name.endsWith(".jpg")
|
||||
|| name.endsWith(".jpeg")
|
||||
|| name.endsWith(".wav")
|
||||
|| name.endsWith(".aiff")
|
||||
|| name.endsWith(".ogg")
|
||||
|| name.endsWith(".mp3")
|
||||
|| name.endsWith(".flac")
|
||||
|| name.endsWith(".mp4")
|
||||
|| name.endsWith(".mov");
|
||||
}
|
||||
|
||||
// parsersLock must be held
|
||||
|
|
|
@ -213,6 +213,27 @@ public:
|
|||
// Added declaration for the new `removeParser` method.
|
||||
void removeParser(FileParser* parser);
|
||||
|
||||
const std::vector<juce::String> FILE_EXTENSIONS = {
|
||||
"obj",
|
||||
"svg",
|
||||
"lua",
|
||||
"txt",
|
||||
"gpla",
|
||||
"gif",
|
||||
"png",
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"wav",
|
||||
"aiff",
|
||||
"ogg",
|
||||
"flac",
|
||||
"mp3",
|
||||
#if OSCI_PREMIUM
|
||||
"mp4",
|
||||
"mov",
|
||||
#endif
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
std::atomic<bool> prevMidiEnabled = !midiEnabled->getBoolValue();
|
||||
|
|
|
@ -68,8 +68,8 @@ void SosciPluginEditor::resized() {
|
|||
menuBar.setBounds(area.removeFromTop(25));
|
||||
|
||||
if (juce::JUCEApplication::isStandaloneApp()) {
|
||||
auto volumeArea = area.removeFromLeft(30);
|
||||
volume.setBounds(volumeArea.withSizeKeepingCentre(volumeArea.getWidth(), juce::jmin(volumeArea.getHeight(), 300)));
|
||||
auto volumeArea = area.removeFromLeft(35);
|
||||
volume.setBounds(volumeArea.withSizeKeepingCentre(30, juce::jmin(volumeArea.getHeight(), 300)));
|
||||
}
|
||||
|
||||
auto settingsArea = area.removeFromRight(juce::jmax(juce::jmin(0.4 * getWidth(), 550.0), 350.0));
|
||||
|
|
|
@ -11,9 +11,6 @@ TimelineComponent::TimelineComponent()
|
|||
slider.setColour(juce::Slider::ColourIds::trackColourId, juce::Colours::white.withAlpha(0.8f));
|
||||
slider.setColour(juce::Slider::ColourIds::backgroundColourId, juce::Colours::white.withAlpha(0.2f));
|
||||
slider.setColour(juce::Slider::ColourIds::thumbColourId, juce::Colours::white);
|
||||
|
||||
// Add tooltips
|
||||
slider.setTooltip("Drag to seek through the timeline");
|
||||
|
||||
addChildComponent(playButton);
|
||||
addChildComponent(pauseButton);
|
||||
|
@ -24,7 +21,7 @@ TimelineComponent::TimelineComponent()
|
|||
playButton.setTooltip("Play");
|
||||
pauseButton.setTooltip("Pause");
|
||||
stopButton.setTooltip("Stop");
|
||||
repeatButton.setTooltip("Toggle repeat mode");
|
||||
repeatButton.setTooltip("Loop");
|
||||
|
||||
slider.setMouseDragSensitivity(150); // Make slider more responsive
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ public:
|
|||
g.strokePath(valueTrack, { trackWidth, juce::PathStrokeType::curved, juce::PathStrokeType::rounded });
|
||||
|
||||
// Draw thumb
|
||||
auto thumbWidth = getSliderThumbRadius(slider);
|
||||
auto thumbWidth = 10;
|
||||
g.setColour(slider.findColour(juce::Slider::thumbColourId));
|
||||
|
||||
// Draw a more modern rectangular thumb with rounded corners
|
||||
|
|
|
@ -7,6 +7,11 @@ VolumeComponent::VolumeComponent(CommonAudioProcessor& p)
|
|||
setOpaque(false);
|
||||
setShouldBeRunning(true);
|
||||
|
||||
leftVolumeSmoothed.reset(10); // Reset with 5 steps for smooth animation
|
||||
rightVolumeSmoothed.reset(10);
|
||||
leftVolumeSmoothed.setCurrentAndTargetValue(0.0f);
|
||||
rightVolumeSmoothed.setCurrentAndTargetValue(0.0f);
|
||||
|
||||
addAndMakeVisible(volumeSlider);
|
||||
volumeSlider.setSliderStyle(juce::Slider::SliderStyle::LinearVertical);
|
||||
volumeSlider.setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0);
|
||||
|
@ -18,7 +23,7 @@ VolumeComponent::VolumeComponent(CommonAudioProcessor& p)
|
|||
volumeSlider.setValue(volumeParam->getValueUnnormalised());
|
||||
volumeSlider.setDoubleClickReturnValue(true, 1.0);
|
||||
volumeSlider.setLookAndFeel(&thumbRadiusLookAndFeel);
|
||||
volumeSlider.setColour(juce::Slider::ColourIds::thumbColourId, juce::Colours::black);
|
||||
volumeSlider.setColour(juce::Slider::ColourIds::thumbColourId, juce::Colours::white);
|
||||
|
||||
addAndMakeVisible(thresholdSlider);
|
||||
thresholdSlider.setSliderStyle(juce::Slider::SliderStyle::LinearVertical);
|
||||
|
@ -45,45 +50,83 @@ VolumeComponent::VolumeComponent(CommonAudioProcessor& p)
|
|||
volumeButton.onClick = [this] {
|
||||
audioProcessor.muteParameter->setBoolValueNotifyingHost(!audioProcessor.muteParameter->getBoolValue());
|
||||
};
|
||||
|
||||
auto doc = juce::XmlDocument::parse(BinaryData::threshold_svg);
|
||||
thresholdIcon = juce::Drawable::createFromSVG(*doc);
|
||||
|
||||
addAndMakeVisible(*thresholdIcon);
|
||||
}
|
||||
|
||||
void VolumeComponent::paint(juce::Graphics& g) {
|
||||
g.setColour(juce::Colours::transparentBlack);
|
||||
g.fillAll();
|
||||
|
||||
auto r = getLocalBounds().toFloat();
|
||||
r.removeFromTop(20);
|
||||
r.removeFromRight(r.getWidth() / 2);
|
||||
r.removeFromTop(volumeSlider.getLookAndFeel().getSliderThumbRadius(volumeSlider));
|
||||
r.removeFromBottom(volumeSlider.getLookAndFeel().getSliderThumbRadius(volumeSlider));
|
||||
|
||||
g.setColour(juce::Colours::white);
|
||||
g.fillRect(r);
|
||||
|
||||
auto channelHeight = r.getHeight();
|
||||
auto leftVolumeHeight = channelHeight * leftVolume;
|
||||
auto rightVolumeHeight = channelHeight * rightVolume;
|
||||
|
||||
auto overallRect = r;
|
||||
|
||||
auto leftRect = r.removeFromLeft(r.getWidth() / 2);
|
||||
auto rightRect = r;
|
||||
auto leftRegion = leftRect;
|
||||
auto rightRegion = rightRect;
|
||||
|
||||
g.setGradientFill(juce::ColourGradient(juce::Colour(0xff00ff00), 0, leftRect.getBottom(), juce::Colours::red, 0, leftRect.getY(), false));
|
||||
g.fillRect(leftRect.removeFromBottom(leftVolumeHeight));
|
||||
g.setColour(juce::Colours::white.withAlpha(0.1f));
|
||||
g.drawRoundedRectangle(overallRect, 4.0f, 1.0f);
|
||||
|
||||
g.setGradientFill(juce::ColourGradient(juce::Colour(0xff00ff00), 0, rightRect.getBottom(), juce::Colours::red, 0, rightRect.getY(), false));
|
||||
g.fillRect(rightRect.removeFromBottom(rightVolumeHeight));
|
||||
// Enable anti-aliasing for smoother rendering
|
||||
g.setImageResamplingQuality(juce::Graphics::highResamplingQuality);
|
||||
|
||||
auto barWidth = 5.0f;
|
||||
auto drawVolumeMeter = [&](juce::Rectangle<float>& rect, float volume) {
|
||||
auto meterRect = rect.removeFromBottom(volume * channelHeight);
|
||||
auto gradient = juce::ColourGradient(
|
||||
juce::Colour(0xff00ff00), // Green
|
||||
0, overallRect.getBottom(),
|
||||
juce::Colour(0xffff0000), // Red
|
||||
0, overallRect.getY(),
|
||||
false);
|
||||
gradient.addColour(0.3, juce::Colour(0xffffe600)); // Yellow in the middle
|
||||
g.setGradientFill(gradient);
|
||||
// Draw rounded meter
|
||||
g.saveState();
|
||||
g.reduceClipRegion(meterRect.toNearestInt());
|
||||
g.fillRoundedRectangle(meterRect.reduced(1.0f), 2.0f);
|
||||
g.restoreState();
|
||||
};
|
||||
|
||||
g.setColour(juce::Colours::black);
|
||||
g.fillRect(leftRegion.getX(), leftRegion.getBottom() - (avgLeftVolume * channelHeight) - barWidth / 2, leftRegion.getWidth(), barWidth);
|
||||
g.fillRect(rightRegion.getX(), rightRegion.getBottom() - (avgRightVolume * channelHeight) - barWidth / 2, rightRegion.getWidth(), barWidth);
|
||||
drawVolumeMeter(leftRect, leftVolume);
|
||||
drawVolumeMeter(rightRect, rightVolume);
|
||||
|
||||
g.fillRect(leftRegion.getX(), rightRegion.getBottom() - (thresholdSlider.getValue() * channelHeight) - barWidth / 2, leftRegion.getWidth() + rightRegion.getWidth(), barWidth);
|
||||
// Draw smooth volume indicators
|
||||
auto drawSmoothIndicator = [&](const juce::Rectangle<float>& region, float value) {
|
||||
if (value > 0.0f) { // Only draw indicator if volume is greater than 0
|
||||
auto y = region.getBottom() - (value * channelHeight);
|
||||
auto indicatorHeight = 3.0f;
|
||||
auto indicatorRect = juce::Rectangle<float>(
|
||||
region.getX(), y - indicatorHeight / 2,
|
||||
region.getWidth(), indicatorHeight);
|
||||
|
||||
g.setColour(juce::Colours::white.withAlpha(0.8f));
|
||||
g.fillRoundedRectangle(indicatorRect, 1.5f);
|
||||
}
|
||||
};
|
||||
|
||||
drawSmoothIndicator(leftRegion, leftVolumeSmoothed.getNextValue());
|
||||
drawSmoothIndicator(rightRegion, rightVolumeSmoothed.getNextValue());
|
||||
|
||||
// Draw threshold line with modern style
|
||||
auto thresholdY = rightRegion.getBottom() - (thresholdSlider.getValue() * channelHeight);
|
||||
auto thresholdRect = juce::Rectangle<float>(
|
||||
leftRegion.getX(),
|
||||
thresholdY - 1.5f,
|
||||
leftRegion.getWidth() + rightRegion.getWidth(),
|
||||
3.0f
|
||||
);
|
||||
|
||||
g.setColour(juce::Colours::white.withAlpha(0.7f));
|
||||
g.fillRoundedRectangle(thresholdRect, 1.5f);
|
||||
}
|
||||
|
||||
void VolumeComponent::handleAsyncUpdate() {
|
||||
|
@ -108,12 +151,12 @@ void VolumeComponent::runTask(const std::vector<osci::Point>& buffer) {
|
|||
leftVolume = 0;
|
||||
rightVolume = 0;
|
||||
}
|
||||
|
||||
|
||||
this->leftVolume = leftVolume;
|
||||
this->rightVolume = rightVolume;
|
||||
|
||||
avgLeftVolume = (avgLeftVolume * 0.95) + (leftVolume * 0.05);
|
||||
avgRightVolume = (avgRightVolume * 0.95) + (rightVolume * 0.05);
|
||||
leftVolumeSmoothed.setTargetValue(leftVolume);
|
||||
rightVolumeSmoothed.setTargetValue(rightVolume);
|
||||
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
@ -128,11 +171,8 @@ void VolumeComponent::resized() {
|
|||
auto r = getLocalBounds();
|
||||
|
||||
auto iconRow = r.removeFromTop(20);
|
||||
auto volumeRect = iconRow.removeFromLeft(iconRow.getWidth() / 2);
|
||||
volumeButton.setBounds(volumeRect.expanded(3));
|
||||
thresholdIcon->setTransformToFit(iconRow.reduced(2).toFloat(), juce::RectanglePlacement::centred);
|
||||
volumeButton.setBounds(iconRow);
|
||||
|
||||
volumeSlider.setBounds(r.removeFromLeft(r.getWidth() / 2));
|
||||
auto radius = volumeSlider.getLookAndFeel().getSliderThumbRadius(volumeSlider);
|
||||
thresholdSlider.setBounds(r.reduced(0, radius / 2));
|
||||
thresholdSlider.setBounds(r.reduced(0, 5.0));
|
||||
}
|
||||
|
|
|
@ -26,32 +26,29 @@ public:
|
|||
float kx = (float) x + (float) width * 0.5f;
|
||||
float ky = sliderPos;
|
||||
|
||||
auto outlineThickness = slider.isEnabled() ? 0.8f : 0.3f;
|
||||
auto sliderRadius = (float) getSliderThumbRadius(slider);
|
||||
auto diameter = sliderRadius * 2.0f;
|
||||
auto halfThickness = outlineThickness * 0.5f;
|
||||
|
||||
auto isDownOrDragging = slider.isEnabled() && (slider.isMouseOverOrDragging() || slider.isMouseButtonDown());
|
||||
|
||||
auto knobColour = slider.findColour(juce::Slider::thumbColourId)
|
||||
.withMultipliedSaturation((slider.hasKeyboardFocus (false) || isDownOrDragging) ? 1.3f : 0.9f)
|
||||
.withMultipliedAlpha(slider.isEnabled() ? 1.0f : 0.7f);
|
||||
|
||||
y = (int) (ky - sliderRadius);
|
||||
|
||||
// draw triangle that points left
|
||||
// Create a simple arrow path inspired by the SVG
|
||||
juce::Path p;
|
||||
p.addTriangle(
|
||||
x + diameter, y,
|
||||
x + diameter, y + diameter,
|
||||
x, ky
|
||||
);
|
||||
float arrowWidth = diameter;
|
||||
float arrowHeight = diameter;
|
||||
|
||||
// Start at the point of the arrow
|
||||
p.startNewSubPath(x, y + arrowHeight * 0.5f);
|
||||
// Draw line to top-right corner
|
||||
p.lineTo(x + arrowWidth, y);
|
||||
// Draw line to bottom-right corner
|
||||
p.lineTo(x + arrowWidth, y + arrowHeight);
|
||||
// Close the path back to the point
|
||||
p.closeSubPath();
|
||||
|
||||
g.setColour(knobColour);
|
||||
g.setColour(juce::Colours::white.withAlpha(isDownOrDragging ? 1.0f : 0.9f));
|
||||
g.fillPath(p);
|
||||
|
||||
g.setColour(slider.findColour(sliderThumbOutlineColourId));
|
||||
g.strokePath(p, juce::PathStrokeType(outlineThickness));
|
||||
}
|
||||
|
||||
void drawLinearSlider(juce::Graphics& g, int x, int y, int width, int height, float sliderPos, float minSliderPos, float maxSliderPos, const juce::Slider::SliderStyle style, juce::Slider& slider) override {
|
||||
|
@ -74,22 +71,23 @@ private:
|
|||
CommonAudioProcessor& audioProcessor;
|
||||
|
||||
const int DEFAULT_SAMPLE_RATE = 192000;
|
||||
const double BUFFER_DURATION_SECS = 0.02;
|
||||
const double BUFFER_DURATION_SECS = 1.0/60.0;
|
||||
|
||||
int sampleRate = DEFAULT_SAMPLE_RATE;
|
||||
|
||||
std::atomic<float> leftVolume = 0;
|
||||
std::atomic<float> rightVolume = 0;
|
||||
std::atomic<float> avgLeftVolume = 0;
|
||||
std::atomic<float> avgRightVolume = 0;
|
||||
|
||||
ThumbRadiusLookAndFeel thumbRadiusLookAndFeel{20};
|
||||
ThumbRadiusLookAndFeel thumbRadiusLookAndFeel{12};
|
||||
juce::Slider volumeSlider;
|
||||
ThresholdLookAndFeel thresholdLookAndFeel{7};
|
||||
juce::Slider thresholdSlider;
|
||||
|
||||
SvgButton volumeButton = SvgButton("VolumeButton", BinaryData::volume_svg, juce::Colours::white, juce::Colours::red, audioProcessor.muteParameter, BinaryData::mute_svg);
|
||||
std::unique_ptr<juce::Drawable> thresholdIcon;
|
||||
|
||||
// Animation smoothing
|
||||
juce::SmoothedValue<float> leftVolumeSmoothed;
|
||||
juce::SmoothedValue<float> rightVolumeSmoothed;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VolumeComponent)
|
||||
};
|
||||
|
|
|
@ -5,6 +5,47 @@
|
|||
FileParser::FileParser(OscirenderAudioProcessor &p, std::function<void(int, juce::String, juce::String)> errorCallback)
|
||||
: errorCallback(errorCallback), audioProcessor(p) {}
|
||||
|
||||
// Helper function to show file size warning
|
||||
void FileParser::showFileSizeWarning(juce::String fileName, int64_t totalBytes, int64_t mbLimit,
|
||||
juce::String fileType, std::function<void()> callback) {
|
||||
|
||||
if (totalBytes <= mbLimit * 1024 * 1024) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
const double fileSizeMB = totalBytes / (1024.0 * 1024.0);
|
||||
juce::String message = juce::String::formatted(
|
||||
"The %s file '%s' you're trying to open is %.2f MB in size, and may time a long time to open. "
|
||||
"Would you like to continue loading it?", fileType.toRawUTF8(), fileName.toRawUTF8(), fileSizeMB);
|
||||
|
||||
juce::MessageManager::callAsync([this, message, callback]() {
|
||||
juce::AlertWindow::showOkCancelBox(
|
||||
juce::AlertWindow::WarningIcon,
|
||||
"Large File",
|
||||
message,
|
||||
"Continue",
|
||||
"Cancel",
|
||||
nullptr,
|
||||
juce::ModalCallbackFunction::create([this, callback](int result) {
|
||||
juce::SpinLock::ScopedLockType scope(lock);
|
||||
if (result == 1) { // 1 = OK button pressed
|
||||
callback();
|
||||
} else {
|
||||
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);
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -21,54 +62,13 @@ void FileParser::parse(juce::String fileId, juce::String fileName, juce::String
|
|||
wav = nullptr;
|
||||
|
||||
if (extension == ".obj") {
|
||||
// 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
|
||||
showFileSizeWarning(fileName, fileSize, 1, "OBJ", [this, objContent]() {
|
||||
object = std::make_shared<WorldObject>(objContent.toStdString());
|
||||
}
|
||||
isAnimatable = false;
|
||||
sampleSource = false;
|
||||
});
|
||||
} else if (extension == ".svg") {
|
||||
svg = std::make_shared<SvgParser>(stream->readEntireStreamAsString());
|
||||
} else if (extension == ".txt") {
|
||||
|
@ -94,16 +94,23 @@ void FileParser::parse(juce::String fileId, juce::String fileName, juce::String
|
|||
} else if (extension == ".gif" || extension == ".png" || extension == ".jpg" || extension == ".jpeg" || extension == ".mp4" || extension == ".mov") {
|
||||
juce::MemoryBlock buffer{};
|
||||
int bytesRead = stream->readIntoMemoryBlock(buffer);
|
||||
img = std::make_shared<ImageParser>(audioProcessor, extension, buffer);
|
||||
|
||||
showFileSizeWarning(fileName, bytesRead, 20, (extension == ".mp4" || extension == ".mov") ? "video" : "image",
|
||||
[this, buffer, extension]() {
|
||||
img = std::make_shared<ImageParser>(audioProcessor, extension, buffer);
|
||||
isAnimatable = extension == ".gif" || extension == ".mp4" || extension == ".mov";
|
||||
sampleSource = true;
|
||||
}
|
||||
);
|
||||
} else if (extension == ".wav" || extension == ".aiff" || extension == ".flac" || extension == ".ogg" || extension == ".mp3") {
|
||||
wav = std::make_shared<WavParser>(audioProcessor);
|
||||
if (!wav->parse(std::move(stream))) {
|
||||
juce::MessageManager::callAsync([this, fileName] {
|
||||
juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::AlertIconType::WarningIcon,
|
||||
"Error Loading " + fileName,
|
||||
"The audio file '" + fileName + "' could not be loaded.");
|
||||
});
|
||||
}
|
||||
if (!wav->parse(std::move(stream))) {
|
||||
juce::MessageManager::callAsync([this, fileName] {
|
||||
juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::AlertIconType::WarningIcon,
|
||||
"Error Loading " + fileName,
|
||||
"The audio file '" + fileName + "' could not be loaded.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isAnimatable = gpla != nullptr || (img != nullptr && (extension == ".gif" || extension == ".mp4" || extension == ".mov"));
|
||||
|
@ -111,83 +118,83 @@ void FileParser::parse(juce::String fileId, juce::String fileName, juce::String
|
|||
}
|
||||
|
||||
std::vector<std::unique_ptr<osci::Shape>> FileParser::nextFrame() {
|
||||
juce::SpinLock::ScopedLockType scope(lock);
|
||||
juce::SpinLock::ScopedLockType scope(lock);
|
||||
|
||||
if (object != nullptr) {
|
||||
return object->draw();
|
||||
} else if (svg != nullptr) {
|
||||
return svg->draw();
|
||||
} else if (text != nullptr) {
|
||||
return text->draw();
|
||||
} else if (gpla != nullptr) {
|
||||
return gpla->draw();
|
||||
}
|
||||
auto tempShapes = std::vector<std::unique_ptr<osci::Shape>>();
|
||||
// return a square
|
||||
tempShapes.push_back(std::make_unique<osci::Line>(osci::Point(-0.5, -0.5, 0), osci::Point(0.5, -0.5, 0)));
|
||||
tempShapes.push_back(std::make_unique<osci::Line>(osci::Point(0.5, -0.5, 0), osci::Point(0.5, 0.5, 0)));
|
||||
tempShapes.push_back(std::make_unique<osci::Line>(osci::Point(0.5, 0.5, 0), osci::Point(-0.5, 0.5, 0)));
|
||||
tempShapes.push_back(std::make_unique<osci::Line>(osci::Point(-0.5, 0.5, 0), osci::Point(-0.5, -0.5, 0)));
|
||||
return tempShapes;
|
||||
if (object != nullptr) {
|
||||
return object->draw();
|
||||
} else if (svg != nullptr) {
|
||||
return svg->draw();
|
||||
} else if (text != nullptr) {
|
||||
return text->draw();
|
||||
} else if (gpla != nullptr) {
|
||||
return gpla->draw();
|
||||
}
|
||||
auto tempShapes = std::vector<std::unique_ptr<osci::Shape>>();
|
||||
// return a square
|
||||
tempShapes.push_back(std::make_unique<osci::Line>(osci::Point(-0.5, -0.5, 0), osci::Point(0.5, -0.5, 0)));
|
||||
tempShapes.push_back(std::make_unique<osci::Line>(osci::Point(0.5, -0.5, 0), osci::Point(0.5, 0.5, 0)));
|
||||
tempShapes.push_back(std::make_unique<osci::Line>(osci::Point(0.5, 0.5, 0), osci::Point(-0.5, 0.5, 0)));
|
||||
tempShapes.push_back(std::make_unique<osci::Line>(osci::Point(-0.5, 0.5, 0), osci::Point(-0.5, -0.5, 0)));
|
||||
return tempShapes;
|
||||
}
|
||||
|
||||
osci::Point FileParser::nextSample(lua_State*& L, LuaVariables& vars) {
|
||||
juce::SpinLock::ScopedLockType scope(lock);
|
||||
juce::SpinLock::ScopedLockType scope(lock);
|
||||
|
||||
if (lua != nullptr) {
|
||||
auto values = lua->run(L, vars);
|
||||
if (values.size() == 2) {
|
||||
return osci::Point(values[0], values[1], 0);
|
||||
} else if (values.size() > 2) {
|
||||
return osci::Point(values[0], values[1], values[2]);
|
||||
}
|
||||
} else if (img != nullptr) {
|
||||
return img->getSample();
|
||||
} else if (wav != nullptr) {
|
||||
if (lua != nullptr) {
|
||||
auto values = lua->run(L, vars);
|
||||
if (values.size() == 2) {
|
||||
return osci::Point(values[0], values[1], 0);
|
||||
} else if (values.size() > 2) {
|
||||
return osci::Point(values[0], values[1], values[2]);
|
||||
}
|
||||
} else if (img != nullptr) {
|
||||
return img->getSample();
|
||||
} else if (wav != nullptr) {
|
||||
return wav->getSample();
|
||||
}
|
||||
|
||||
return osci::Point();
|
||||
return osci::Point();
|
||||
}
|
||||
|
||||
bool FileParser::isSample() {
|
||||
return sampleSource;
|
||||
return sampleSource;
|
||||
}
|
||||
|
||||
bool FileParser::isActive() {
|
||||
return active;
|
||||
return active;
|
||||
}
|
||||
|
||||
void FileParser::disable() {
|
||||
active = false;
|
||||
active = false;
|
||||
}
|
||||
|
||||
void FileParser::enable() {
|
||||
active = true;
|
||||
active = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<WorldObject> FileParser::getObject() {
|
||||
return object;
|
||||
return object;
|
||||
}
|
||||
|
||||
std::shared_ptr<SvgParser> FileParser::getSvg() {
|
||||
return svg;
|
||||
return svg;
|
||||
}
|
||||
|
||||
std::shared_ptr<TextParser> FileParser::getText() {
|
||||
return text;
|
||||
return text;
|
||||
}
|
||||
|
||||
std::shared_ptr<LineArtParser> FileParser::getLineArt() {
|
||||
return gpla;
|
||||
return gpla;
|
||||
}
|
||||
|
||||
std::shared_ptr<LuaParser> FileParser::getLua() {
|
||||
return lua;
|
||||
return lua;
|
||||
}
|
||||
|
||||
std::shared_ptr<ImageParser> FileParser::getImg() {
|
||||
return img;
|
||||
return img;
|
||||
}
|
||||
|
||||
std::shared_ptr<WavParser> FileParser::getWav() {
|
||||
|
|
|
@ -39,6 +39,9 @@ public:
|
|||
bool isAnimatable = false;
|
||||
|
||||
private:
|
||||
void showFileSizeWarning(juce::String fileName, int64_t totalBytes, int64_t mbLimit,
|
||||
juce::String fileType, std::function<void()> callback);
|
||||
|
||||
OscirenderAudioProcessor& audioProcessor;
|
||||
|
||||
bool active = true;
|
||||
|
|
Ładowanie…
Reference in New Issue