kopia lustrzana https://github.com/jameshball/osci-render
Add recording settings
rodzic
cc3dae4267
commit
77a3271cde
|
@ -46,17 +46,27 @@ CommonPluginEditor::CommonPluginEditor(CommonAudioProcessor& p, juce::String app
|
||||||
visualiserSettingsWindow.setVisible(false);
|
visualiserSettingsWindow.setVisible(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
visualiserSettingsWindow.setResizable(false, false);
|
visualiserSettings.setLookAndFeel(&getLookAndFeel());
|
||||||
|
visualiserSettings.setSize(550, 400);
|
||||||
|
visualiserSettingsWindow.setContentNonOwned(&visualiserSettings, true);
|
||||||
|
visualiserSettingsWindow.centreWithSize(550, 400);
|
||||||
#if JUCE_WINDOWS
|
#if JUCE_WINDOWS
|
||||||
// if not standalone, use native title bar for compatibility with DAWs
|
// if not standalone, use native title bar for compatibility with DAWs
|
||||||
visualiserSettingsWindow.setUsingNativeTitleBar(processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone);
|
visualiserSettingsWindow.setUsingNativeTitleBar(processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone);
|
||||||
#elif JUCE_MAC
|
#elif JUCE_MAC
|
||||||
visualiserSettingsWindow.setUsingNativeTitleBar(true);
|
visualiserSettingsWindow.setUsingNativeTitleBar(true);
|
||||||
#endif
|
#endif
|
||||||
visualiserSettings.setLookAndFeel(&getLookAndFeel());
|
|
||||||
visualiserSettings.setSize(550, 400);
|
recordingSettings.setLookAndFeel(&getLookAndFeel());
|
||||||
visualiserSettingsWindow.setContentNonOwned(&visualiserSettings, true);
|
recordingSettings.setSize(300, 200);
|
||||||
visualiserSettingsWindow.centreWithSize(550, 400);
|
recordingSettingsWindow.setContentNonOwned(&recordingSettings, true);
|
||||||
|
recordingSettingsWindow.centreWithSize(300, 200);
|
||||||
|
#if JUCE_WINDOWS
|
||||||
|
// if not standalone, use native title bar for compatibility with DAWs
|
||||||
|
recordingSettingsWindow.setUsingNativeTitleBar(processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone);
|
||||||
|
#elif JUCE_MAC
|
||||||
|
recordingSettingsWindow.setUsingNativeTitleBar(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
menuBar.toFront(true);
|
menuBar.toFront(true);
|
||||||
|
|
||||||
|
@ -149,6 +159,11 @@ void CommonPluginEditor::openAudioSettings() {
|
||||||
standalone->showAudioSettingsDialog();
|
standalone->showAudioSettingsDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommonPluginEditor::openRecordingSettings() {
|
||||||
|
recordingSettingsWindow.setVisible(true);
|
||||||
|
recordingSettingsWindow.toFront(true);
|
||||||
|
}
|
||||||
|
|
||||||
void CommonPluginEditor::resetToDefault() {
|
void CommonPluginEditor::resetToDefault() {
|
||||||
juce::StandaloneFilterWindow* window = findParentComponentOfClass<juce::StandaloneFilterWindow>();
|
juce::StandaloneFilterWindow* window = findParentComponentOfClass<juce::StandaloneFilterWindow>();
|
||||||
if (window != nullptr) {
|
if (window != nullptr) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ public:
|
||||||
void saveProjectAs();
|
void saveProjectAs();
|
||||||
void updateTitle();
|
void updateTitle();
|
||||||
void openAudioSettings();
|
void openAudioSettings();
|
||||||
|
void openRecordingSettings();
|
||||||
void resetToDefault();
|
void resetToDefault();
|
||||||
void openVisualiserSettings();
|
void openVisualiserSettings();
|
||||||
|
|
||||||
|
@ -45,7 +46,9 @@ public:
|
||||||
|
|
||||||
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.visualiserParameters, 3);
|
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.visualiserParameters, 3);
|
||||||
SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings");
|
SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings");
|
||||||
VisualiserComponent visualiser{audioProcessor.lastOpenedDirectory, applicationFolder.getChildFile(ffmpegFileName), audioProcessor.haltRecording, audioProcessor.threadManager, visualiserSettings, nullptr, appName == "sosci"};
|
RecordingSettings recordingSettings = RecordingSettings(audioProcessor.recordingParameters);
|
||||||
|
SettingsWindow recordingSettingsWindow = SettingsWindow("Recording Settings");
|
||||||
|
VisualiserComponent visualiser{audioProcessor.lastOpenedDirectory, applicationFolder.getChildFile(ffmpegFileName), audioProcessor.haltRecording, audioProcessor.threadManager, visualiserSettings, audioProcessor.recordingParameters, nullptr, appName == "sosci"};
|
||||||
|
|
||||||
std::unique_ptr<juce::FileChooser> chooser;
|
std::unique_ptr<juce::FileChooser> chooser;
|
||||||
juce::MenuBarComponent menuBar;
|
juce::MenuBarComponent menuBar;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "concurrency/AudioBackgroundThreadManager.h"
|
#include "concurrency/AudioBackgroundThreadManager.h"
|
||||||
#include "audio/SampleRateManager.h"
|
#include "audio/SampleRateManager.h"
|
||||||
#include "visualiser/VisualiserSettings.h"
|
#include "visualiser/VisualiserSettings.h"
|
||||||
|
#include "visualiser/RecordingSettings.h"
|
||||||
#include "audio/Effect.h"
|
#include "audio/Effect.h"
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
@ -57,6 +58,7 @@ public:
|
||||||
std::atomic<double> currentSampleRate = 0.0;
|
std::atomic<double> currentSampleRate = 0.0;
|
||||||
juce::SpinLock effectsLock;
|
juce::SpinLock effectsLock;
|
||||||
VisualiserParameters visualiserParameters;
|
VisualiserParameters visualiserParameters;
|
||||||
|
RecordingParameters recordingParameters;
|
||||||
|
|
||||||
AudioBackgroundThreadManager threadManager;
|
AudioBackgroundThreadManager threadManager;
|
||||||
std::function<void()> haltRecording;
|
std::function<void()> haltRecording;
|
||||||
|
|
|
@ -297,14 +297,39 @@ void OscirenderLookAndFeel::drawMenuBarBackground(juce::Graphics& g, int width,
|
||||||
g.fillRect(r);
|
g.fillRect(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
juce::TextLayout OscirenderLookAndFeel::layoutTooltipText(const juce::String& text, juce::Colour colour) {
|
||||||
|
const float tooltipFontSize = 17.0f;
|
||||||
|
const int maxToolTipWidth = 600;
|
||||||
|
|
||||||
|
juce::AttributedString s;
|
||||||
|
s.setJustification (juce::Justification::centred);
|
||||||
|
s.append (text, juce::Font (tooltipFontSize, juce::Font::bold), colour);
|
||||||
|
|
||||||
|
juce::TextLayout tl;
|
||||||
|
tl.createLayoutWithBalancedLineLengths (s, (float) maxToolTipWidth);
|
||||||
|
return tl;
|
||||||
|
}
|
||||||
|
|
||||||
|
juce::Rectangle<int> OscirenderLookAndFeel::getTooltipBounds (const juce::String& tipText, juce::Point<int> screenPos, juce::Rectangle<int> parentArea) {
|
||||||
|
const juce::TextLayout tl (layoutTooltipText(tipText, juce::Colours::black));
|
||||||
|
|
||||||
|
auto w = (int) (tl.getWidth() + 14.0f);
|
||||||
|
auto h = (int) (tl.getHeight() + 6.0f);
|
||||||
|
|
||||||
|
return juce::Rectangle<int> (screenPos.x > parentArea.getCentreX() ? screenPos.x - (w + 12) : screenPos.x + 24,
|
||||||
|
screenPos.y > parentArea.getCentreY() ? screenPos.y - (h + 6) : screenPos.y + 6,
|
||||||
|
w, h)
|
||||||
|
.constrainedWithin (parentArea);
|
||||||
|
}
|
||||||
|
|
||||||
void OscirenderLookAndFeel::drawTooltip(juce::Graphics& g, const juce::String& text, int width, int height) {
|
void OscirenderLookAndFeel::drawTooltip(juce::Graphics& g, const juce::String& text, int width, int height) {
|
||||||
juce::Rectangle<int> bounds (width, height);
|
juce::Rectangle<int> bounds (width, height);
|
||||||
|
|
||||||
g.setColour(findColour(juce::TooltipWindow::backgroundColourId));
|
g.setColour(findColour(juce::TooltipWindow::backgroundColourId));
|
||||||
g.fillRect(bounds);
|
g.fillRect(bounds);
|
||||||
|
|
||||||
LookAndFeelHelpers::layoutTooltipText (text, findColour (juce::TooltipWindow::textColourId))
|
layoutTooltipText(text, findColour(juce::TooltipWindow::textColourId))
|
||||||
.draw (g, { static_cast<float> (width), static_cast<float> (height) });
|
.draw(g, {static_cast<float> (width), static_cast<float> (height)});
|
||||||
}
|
}
|
||||||
|
|
||||||
void OscirenderLookAndFeel::drawCornerResizer(juce::Graphics&, int w, int h, bool isMouseOver, bool isMouseDragging) {
|
void OscirenderLookAndFeel::drawCornerResizer(juce::Graphics&, int w, int h, bool isMouseOver, bool isMouseDragging) {
|
||||||
|
|
|
@ -94,6 +94,8 @@ public:
|
||||||
bool shouldDrawButtonAsHighlighted,
|
bool shouldDrawButtonAsHighlighted,
|
||||||
bool shouldDrawButtonAsDown) override;
|
bool shouldDrawButtonAsDown) override;
|
||||||
void drawMenuBarBackground(juce::Graphics& g, int width, int height, bool, juce::MenuBarComponent& menuBar) override;
|
void drawMenuBarBackground(juce::Graphics& g, int width, int height, bool, juce::MenuBarComponent& menuBar) override;
|
||||||
|
juce::TextLayout layoutTooltipText(const juce::String& text, juce::Colour colour);
|
||||||
|
juce::Rectangle<int> getTooltipBounds(const juce::String& tipText, juce::Point<int> screenPos, juce::Rectangle<int> parentArea) override;
|
||||||
juce::CodeEditorComponent::ColourScheme getDefaultColourScheme();
|
juce::CodeEditorComponent::ColourScheme getDefaultColourScheme();
|
||||||
void drawTooltip(juce::Graphics& g, const juce::String& text, int width, int height) override;
|
void drawTooltip(juce::Graphics& g, const juce::String& text, int width, int height) override;
|
||||||
void drawCornerResizer(juce::Graphics&, int w, int h, bool isMouseOver, bool isMouseDragging) override;
|
void drawCornerResizer(juce::Graphics&, int w, int h, bool isMouseOver, bool isMouseDragging) override;
|
||||||
|
|
|
@ -414,8 +414,3 @@ void OscirenderAudioProcessorEditor::mouseMove(const juce::MouseEvent& event) {
|
||||||
setMouseCursor(juce::MouseCursor::NormalCursor);
|
setMouseCursor(juce::MouseCursor::NormalCursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OscirenderAudioProcessorEditor::openAudioSettings() {
|
|
||||||
juce::StandalonePluginHolder* standalone = juce::StandalonePluginHolder::getInstance();
|
|
||||||
standalone->showAudioSettingsDialog();
|
|
||||||
}
|
|
||||||
|
|
|
@ -31,8 +31,6 @@ public:
|
||||||
|
|
||||||
void editCustomFunction(bool enabled);
|
void editCustomFunction(bool enabled);
|
||||||
|
|
||||||
void openAudioSettings();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OscirenderAudioProcessor& audioProcessor;
|
OscirenderAudioProcessor& audioProcessor;
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -659,6 +659,9 @@ void OscirenderAudioProcessor::getStateInformation(juce::MemoryBlock& destData)
|
||||||
fileXml->addTextElement(base64);
|
fileXml->addTextElement(base64);
|
||||||
}
|
}
|
||||||
xml->setAttribute("currentFile", currentFile);
|
xml->setAttribute("currentFile", currentFile);
|
||||||
|
|
||||||
|
recordingParameters.save(xml.get());
|
||||||
|
|
||||||
copyXmlToBinary(*xml, destData);
|
copyXmlToBinary(*xml, destData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -773,6 +776,8 @@ void OscirenderAudioProcessor::setStateInformation(const void* data, int sizeInB
|
||||||
}
|
}
|
||||||
changeCurrentFile(xml->getIntAttribute("currentFile", -1));
|
changeCurrentFile(xml->getIntAttribute("currentFile", -1));
|
||||||
|
|
||||||
|
recordingParameters.load(xml.get());
|
||||||
|
|
||||||
broadcaster.sendChangeMessage();
|
broadcaster.sendChangeMessage();
|
||||||
prevMidiEnabled = !midiEnabled->getBoolValue();
|
prevMidiEnabled = !midiEnabled->getBoolValue();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
#include "SosciPluginEditor.h"
|
#include "SosciPluginEditor.h"
|
||||||
#include "audio/EffectParameter.h"
|
#include "audio/EffectParameter.h"
|
||||||
|
|
||||||
SosciAudioProcessor::SosciAudioProcessor() {}
|
SosciAudioProcessor::SosciAudioProcessor() {
|
||||||
|
addAllParameters();
|
||||||
|
}
|
||||||
|
|
||||||
SosciAudioProcessor::~SosciAudioProcessor() {}
|
SosciAudioProcessor::~SosciAudioProcessor() {}
|
||||||
|
|
||||||
|
@ -77,6 +79,8 @@ void SosciAudioProcessor::getStateInformation(juce::MemoryBlock& destData) {
|
||||||
parameter->save(parameterXml);
|
parameter->save(parameterXml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recordingParameters.save(xml.get());
|
||||||
|
|
||||||
copyXmlToBinary(*xml, destData);
|
copyXmlToBinary(*xml, destData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +138,8 @@ void SosciAudioProcessor::setStateInformation(const void* data, int sizeInBytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recordingParameters.load(xml.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,8 +122,8 @@ void EffectComponent::resized() {
|
||||||
auto bounds = getLocalBounds();
|
auto bounds = getLocalBounds();
|
||||||
auto componentBounds = bounds.removeFromRight(25);
|
auto componentBounds = bounds.removeFromRight(25);
|
||||||
if (component != nullptr) {
|
if (component != nullptr) {
|
||||||
component->setBounds(componentBounds);
|
component->setBounds(componentBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sidechainEnabled) {
|
if (sidechainEnabled) {
|
||||||
sidechainButton->setBounds(bounds.removeFromRight(20));
|
sidechainButton->setBounds(bounds.removeFromRight(20));
|
||||||
|
@ -135,7 +135,9 @@ void EffectComponent::resized() {
|
||||||
lfo.setBounds(bounds.removeFromRight(drawingSmall ? 70 : 100).reduced(0, 5));
|
lfo.setBounds(bounds.removeFromRight(drawingSmall ? 70 : 100).reduced(0, 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
rangeButton.setBounds(bounds.removeFromRight(20));
|
if (rangeButton.isVisible()) {
|
||||||
|
rangeButton.setBounds(bounds.removeFromRight(20));
|
||||||
|
}
|
||||||
|
|
||||||
bounds.removeFromLeft(5);
|
bounds.removeFromLeft(5);
|
||||||
|
|
||||||
|
@ -169,6 +171,10 @@ void EffectComponent::handleAsyncUpdate() {
|
||||||
getParentComponent()->repaint();
|
getParentComponent()->repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EffectComponent::setRangeEnabled(bool enabled) {
|
||||||
|
rangeButton.setVisible(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
void EffectComponent::setComponent(std::shared_ptr<juce::Component> component) {
|
void EffectComponent::setComponent(std::shared_ptr<juce::Component> component) {
|
||||||
this->component = component;
|
this->component = component;
|
||||||
addAndMakeVisible(component.get());
|
addAndMakeVisible(component.get());
|
||||||
|
|
|
@ -16,6 +16,8 @@ public:
|
||||||
void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override;
|
void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override;
|
||||||
void handleAsyncUpdate() override;
|
void handleAsyncUpdate() override;
|
||||||
|
|
||||||
|
void setRangeEnabled(bool enabled);
|
||||||
|
|
||||||
void setComponent(std::shared_ptr<juce::Component> component);
|
void setComponent(std::shared_ptr<juce::Component> component);
|
||||||
void setSliderOnValueChange();
|
void setSliderOnValueChange();
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, Oscirend
|
||||||
});
|
});
|
||||||
|
|
||||||
addMenuItem(2, "Settings...", [this] {
|
addMenuItem(2, "Settings...", [this] {
|
||||||
//editor.openRecordingSettings();
|
editor.openRecordingSettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
addMenuItem(3, "Settings...", [this] {
|
addMenuItem(3, "Settings...", [this] {
|
||||||
|
|
|
@ -1,61 +1,44 @@
|
||||||
#include "VisualiserSettings.h"
|
#include "RecordingSettings.h"
|
||||||
#include "VisualiserComponent.h"
|
#include "VisualiserComponent.h"
|
||||||
#include "../PluginEditor.h"
|
#include "../PluginEditor.h"
|
||||||
|
|
||||||
|
|
||||||
VisualiserSettings::VisualiserSettings(VisualiserParameters& parameters, int numChannels) : parameters(parameters), numChannels(numChannels) {
|
RecordingSettings::RecordingSettings(RecordingParameters& ps) : parameters(ps) {
|
||||||
addAndMakeVisible(brightness);
|
addAndMakeVisible(quality);
|
||||||
addAndMakeVisible(intensity);
|
addAndMakeVisible(recordAudio);
|
||||||
addAndMakeVisible(persistence);
|
addAndMakeVisible(recordVideo);
|
||||||
addAndMakeVisible(hue);
|
addAndMakeVisible(compressionPreset);
|
||||||
addAndMakeVisible(saturation);
|
addAndMakeVisible(compressionPresetLabel);
|
||||||
addAndMakeVisible(focus);
|
|
||||||
addAndMakeVisible(noise);
|
quality.setSliderOnValueChange();
|
||||||
addAndMakeVisible(glow);
|
quality.setRangeEnabled(false);
|
||||||
addAndMakeVisible(smooth);
|
recordAudio.onClick = [this] {
|
||||||
addChildComponent(sweepMs);
|
if (!recordAudio.getToggleState() && !recordVideo.getToggleState()) {
|
||||||
addAndMakeVisible(graticuleToggle);
|
recordVideo.setToggleState(true, juce::NotificationType::sendNotification);
|
||||||
addAndMakeVisible(smudgeToggle);
|
}
|
||||||
addAndMakeVisible(upsamplingToggle);
|
|
||||||
addAndMakeVisible(sweepToggle);
|
|
||||||
|
|
||||||
brightness.setSliderOnValueChange();
|
|
||||||
intensity.setSliderOnValueChange();
|
|
||||||
persistence.setSliderOnValueChange();
|
|
||||||
hue.setSliderOnValueChange();
|
|
||||||
saturation.setSliderOnValueChange();
|
|
||||||
focus.setSliderOnValueChange();
|
|
||||||
noise.setSliderOnValueChange();
|
|
||||||
glow.setSliderOnValueChange();
|
|
||||||
smooth.setSliderOnValueChange();
|
|
||||||
sweepMs.setSliderOnValueChange();
|
|
||||||
|
|
||||||
sweepToggle.onClick = [this] {
|
|
||||||
sweepMs.setVisible(sweepToggle.getToggleState());
|
|
||||||
resized();
|
|
||||||
};
|
};
|
||||||
|
recordVideo.onClick = [this] {
|
||||||
|
if (!recordAudio.getToggleState() && !recordVideo.getToggleState()) {
|
||||||
|
recordAudio.setToggleState(true, juce::NotificationType::sendNotification);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
compressionPreset.onChange = [this] {
|
||||||
|
parameters.compressionPreset = parameters.compressionPresets[compressionPreset.getSelectedId() - 1];
|
||||||
|
};
|
||||||
|
compressionPreset.addItemList(parameters.compressionPresets, 1);
|
||||||
|
compressionPreset.setSelectedId(parameters.compressionPresets.indexOf(parameters.compressionPreset) + 1);
|
||||||
|
compressionPresetLabel.setTooltip("The compression preset to use when recording video. Slower presets will produce smaller files at the expense of encoding time.");
|
||||||
}
|
}
|
||||||
|
|
||||||
VisualiserSettings::~VisualiserSettings() {}
|
RecordingSettings::~RecordingSettings() {}
|
||||||
|
|
||||||
void VisualiserSettings::resized() {
|
void RecordingSettings::resized() {
|
||||||
auto area = getLocalBounds().reduced(20);
|
auto area = getLocalBounds().reduced(20);
|
||||||
double rowHeight = 30;
|
double rowHeight = 30;
|
||||||
brightness.setBounds(area.removeFromTop(rowHeight));
|
quality.setBounds(area.removeFromTop(rowHeight).expanded(6, 0));
|
||||||
intensity.setBounds(area.removeFromTop(rowHeight));
|
recordAudio.setBounds(area.removeFromTop(rowHeight));
|
||||||
persistence.setBounds(area.removeFromTop(rowHeight));
|
recordVideo.setBounds(area.removeFromTop(rowHeight));
|
||||||
hue.setBounds(area.removeFromTop(rowHeight));
|
auto row = area.removeFromTop(rowHeight);
|
||||||
saturation.setBounds(area.removeFromTop(rowHeight));
|
compressionPresetLabel.setBounds(row.removeFromLeft(140));
|
||||||
focus.setBounds(area.removeFromTop(rowHeight));
|
compressionPreset.setBounds(row.removeFromRight(80));
|
||||||
noise.setBounds(area.removeFromTop(rowHeight));
|
|
||||||
glow.setBounds(area.removeFromTop(rowHeight));
|
|
||||||
smooth.setBounds(area.removeFromTop(rowHeight));
|
|
||||||
graticuleToggle.setBounds(area.removeFromTop(rowHeight));
|
|
||||||
smudgeToggle.setBounds(area.removeFromTop(rowHeight));
|
|
||||||
upsamplingToggle.setBounds(area.removeFromTop(rowHeight));
|
|
||||||
|
|
||||||
sweepToggle.setBounds(area.removeFromTop(rowHeight));
|
|
||||||
if (sweepToggle.getToggleState()) {
|
|
||||||
sweepMs.setBounds(area.removeFromTop(rowHeight));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,187 +1,99 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VERSION_HINT 2
|
|
||||||
|
|
||||||
#include <JuceHeader.h>
|
#include <JuceHeader.h>
|
||||||
#include "../components/EffectComponent.h"
|
#include "../components/EffectComponent.h"
|
||||||
#include "../components/SvgButton.h"
|
#include "../components/SvgButton.h"
|
||||||
#include "../LookAndFeel.h"
|
#include "../LookAndFeel.h"
|
||||||
#include "../components/SwitchButton.h"
|
#include "../components/SwitchButton.h"
|
||||||
#include "../audio/SmoothEffect.h"
|
|
||||||
|
|
||||||
class VisualiserParameters {
|
#define VERSION_HINT 2
|
||||||
|
|
||||||
|
class RecordingParameters {
|
||||||
public:
|
public:
|
||||||
BooleanParameter* graticuleEnabled = new BooleanParameter("Show Graticule", "graticuleEnabled", VERSION_HINT, false, "Show the graticule or grid lines over the oscilloscope display.");
|
RecordingParameters() {
|
||||||
BooleanParameter* smudgesEnabled = new BooleanParameter("Show Smudges", "smudgesEnabled", VERSION_HINT, false, "Adds a subtle layer of dirt/smudges to the oscilloscope display to make it look more realistic.");
|
qualityParameter.disableLfo();
|
||||||
BooleanParameter* upsamplingEnabled = new BooleanParameter("Upsample Audio", "upsamplingEnabled", VERSION_HINT, true, "Upsamples the audio before visualising it to make it appear more realistic, at the expense of performance.");
|
qualityParameter.disableSidechain();
|
||||||
BooleanParameter* sweepEnabled = new BooleanParameter("Sweep", "sweepEnabled", VERSION_HINT, false, "Plots the audio signal over time, sweeping from left to right");
|
}
|
||||||
BooleanParameter* visualiserFullScreen = new BooleanParameter("Visualiser Fullscreen", "visualiserFullScreen", VERSION_HINT, false, "Makes the software visualiser fullscreen.");
|
|
||||||
|
|
||||||
std::shared_ptr<Effect> persistenceEffect = std::make_shared<Effect>(
|
EffectParameter qualityParameter = EffectParameter(
|
||||||
new EffectParameter(
|
"Quality",
|
||||||
"Persistence",
|
"Controls the quality of the recording video. 0 is the worst possible quality, and 1 is lossless.",
|
||||||
"Controls how long the light glows for on the oscilloscope display.",
|
"brightness",
|
||||||
"persistence",
|
VERSION_HINT, 0.7, 0.0, 1.0
|
||||||
VERSION_HINT, 0.5, 0, 6.0
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
std::shared_ptr<Effect> hueEffect = std::make_shared<Effect>(
|
Effect qualityEffect = Effect(&qualityParameter);
|
||||||
new EffectParameter(
|
|
||||||
"Hue",
|
BooleanParameter recordAudio = BooleanParameter("Record Audio", "recordAudio", VERSION_HINT, true, "Record audio along with the video.");
|
||||||
"Controls the hue/colour of the oscilloscope display.",
|
BooleanParameter recordVideo = BooleanParameter("Record Video", "recordVideo", VERSION_HINT, true, "Record video output of the visualiser.");
|
||||||
"hue",
|
|
||||||
VERSION_HINT, 125, 0, 359, 1
|
juce::String compressionPreset = "fast";
|
||||||
)
|
|
||||||
);
|
int getCRF() {
|
||||||
std::shared_ptr<Effect> brightnessEffect = std::make_shared<Effect>(
|
double quality = juce::jlimit(0.0, 1.0, qualityEffect.getValue());
|
||||||
new EffectParameter(
|
// mapping to 0-51 for ffmpeg's crf value
|
||||||
"Brightness",
|
return 51 * (1.0 - quality) ;
|
||||||
"Controls how bright the light glows for on the oscilloscope display.",
|
}
|
||||||
"brightness",
|
|
||||||
VERSION_HINT, 2.0, 0.0, 10.0
|
bool recordingVideo() {
|
||||||
)
|
return recordVideo.getBoolValue();
|
||||||
);
|
}
|
||||||
std::shared_ptr<Effect> intensityEffect = std::make_shared<Effect>(
|
|
||||||
new EffectParameter(
|
bool recordingAudio() {
|
||||||
"Intensity",
|
return recordAudio.getBoolValue();
|
||||||
"Controls how bright the electron beam of the oscilloscope is.",
|
}
|
||||||
"intensity",
|
|
||||||
VERSION_HINT, 5.0, 0.0, 10.0
|
juce::String getCompressionPreset() {
|
||||||
)
|
return compressionPreset;
|
||||||
);
|
}
|
||||||
std::shared_ptr<Effect> saturationEffect = std::make_shared<Effect>(
|
|
||||||
new EffectParameter(
|
void save(juce::XmlElement* xml) {
|
||||||
"Saturation",
|
auto settingsXml = xml->createNewChildElement("recordingSettings");
|
||||||
"Controls how saturated the colours are on the oscilloscope.",
|
recordAudio.save(settingsXml->createNewChildElement("recordAudio"));
|
||||||
"saturation",
|
recordVideo.save(settingsXml->createNewChildElement("recordVideo"));
|
||||||
VERSION_HINT, 1.0, 0.0, 5.0
|
settingsXml->setAttribute("compressionPreset", compressionPreset);
|
||||||
)
|
|
||||||
);
|
auto qualityXml = settingsXml->createNewChildElement("quality");
|
||||||
std::shared_ptr<Effect> focusEffect = std::make_shared<Effect>(
|
qualityEffect.save(qualityXml);
|
||||||
new EffectParameter(
|
}
|
||||||
"Focus",
|
|
||||||
"Controls how focused the electron beam of the oscilloscope is.",
|
// opt to not change any values if not found
|
||||||
"focus",
|
void load(juce::XmlElement* xml) {
|
||||||
VERSION_HINT, 1.0, 0.01, 10.0
|
if (auto* settingsXml = xml->getChildByName("recordingSettings")) {
|
||||||
)
|
if (auto* recordAudioXml = settingsXml->getChildByName("recordAudio")) {
|
||||||
);
|
recordAudio.load(recordAudioXml);
|
||||||
std::shared_ptr<Effect> noiseEffect = std::make_shared<Effect>(
|
}
|
||||||
new EffectParameter(
|
if (auto* recordVideoXml = settingsXml->getChildByName("recordVideo")) {
|
||||||
"Noise",
|
recordVideo.load(recordVideoXml);
|
||||||
"Controls how much noise/grain is added to the oscilloscope display.",
|
}
|
||||||
"noise",
|
if (settingsXml->hasAttribute("compressionPreset")) {
|
||||||
VERSION_HINT, 0.0, 0.0, 1.0
|
compressionPreset = settingsXml->getStringAttribute("compressionPreset");
|
||||||
)
|
}
|
||||||
);
|
if (auto* qualityXml = settingsXml->getChildByName("quality")) {
|
||||||
std::shared_ptr<Effect> glowEffect = std::make_shared<Effect>(
|
qualityEffect.load(qualityXml);
|
||||||
new EffectParameter(
|
}
|
||||||
"Glow",
|
}
|
||||||
"Controls how much the light glows on the oscilloscope display.",
|
}
|
||||||
"glow",
|
|
||||||
VERSION_HINT, 0.3, 0.0, 1.0
|
juce::StringArray compressionPresets = { "ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow" };
|
||||||
)
|
|
||||||
);
|
|
||||||
std::shared_ptr<Effect> smoothEffect = std::make_shared<Effect>(
|
|
||||||
std::make_shared<SmoothEffect>(),
|
|
||||||
new EffectParameter(
|
|
||||||
"Smoothing",
|
|
||||||
"This works as a low-pass frequency filter, effectively reducing the sample rate of the audio being visualised.",
|
|
||||||
"visualiserSmoothing",
|
|
||||||
VERSION_HINT, 0, 0.0, 1.0
|
|
||||||
)
|
|
||||||
);
|
|
||||||
std::shared_ptr<Effect> sweepMsEffect = std::make_shared<Effect>(
|
|
||||||
new EffectParameter(
|
|
||||||
"Sweep (ms)",
|
|
||||||
"The number of milliseconds it takes for the oscilloscope to sweep from left to right.",
|
|
||||||
"sweepMs",
|
|
||||||
VERSION_HINT, 0.3, 0.0, 1.0
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Effect>> effects = {persistenceEffect, hueEffect, brightnessEffect, intensityEffect, saturationEffect, focusEffect, noiseEffect, glowEffect, sweepMsEffect};
|
|
||||||
std::vector<BooleanParameter*> booleans = {graticuleEnabled, smudgesEnabled, upsamplingEnabled, visualiserFullScreen, sweepEnabled};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class VisualiserSettings : public juce::Component {
|
class RecordingSettings : public juce::Component {
|
||||||
public:
|
public:
|
||||||
VisualiserSettings(VisualiserParameters&, int numChannels = 2);
|
RecordingSettings(RecordingParameters&);
|
||||||
~VisualiserSettings();
|
~RecordingSettings();
|
||||||
|
|
||||||
void resized() override;
|
void resized() override;
|
||||||
|
|
||||||
double getBrightness() {
|
|
||||||
return parameters.brightnessEffect->getActualValue() - 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getIntensity() {
|
|
||||||
return parameters.intensityEffect->getActualValue() / 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getPersistence() {
|
|
||||||
return parameters.persistenceEffect->getActualValue() - 1.33;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getHue() {
|
|
||||||
return parameters.hueEffect->getActualValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
double getSaturation() {
|
|
||||||
return parameters.saturationEffect->getActualValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
double getFocus() {
|
|
||||||
return parameters.focusEffect->getActualValue() / 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getNoise() {
|
|
||||||
return parameters.noiseEffect->getActualValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
double getGlow() {
|
|
||||||
return parameters.glowEffect->getActualValue() * 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getGraticuleEnabled() {
|
|
||||||
return parameters.graticuleEnabled->getBoolValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getSmudgesEnabled() {
|
|
||||||
return parameters.smudgesEnabled->getBoolValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getUpsamplingEnabled() {
|
|
||||||
return parameters.upsamplingEnabled->getBoolValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
VisualiserParameters& parameters;
|
RecordingParameters& parameters;
|
||||||
int numChannels;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EffectComponent brightness{*parameters.brightnessEffect};
|
EffectComponent quality{parameters.qualityEffect};
|
||||||
EffectComponent intensity{*parameters.intensityEffect};
|
|
||||||
EffectComponent persistence{*parameters.persistenceEffect};
|
|
||||||
EffectComponent hue{*parameters.hueEffect};
|
|
||||||
EffectComponent saturation{*parameters.saturationEffect};
|
|
||||||
EffectComponent focus{*parameters.focusEffect};
|
|
||||||
EffectComponent noise{*parameters.noiseEffect};
|
|
||||||
EffectComponent glow{*parameters.glowEffect};
|
|
||||||
EffectComponent smooth{*parameters.smoothEffect};
|
|
||||||
EffectComponent sweepMs{*parameters.sweepMsEffect};
|
|
||||||
|
|
||||||
jux::SwitchButton graticuleToggle{parameters.graticuleEnabled};
|
|
||||||
jux::SwitchButton smudgeToggle{parameters.smudgesEnabled};
|
|
||||||
jux::SwitchButton upsamplingToggle{parameters.upsamplingEnabled};
|
|
||||||
jux::SwitchButton sweepToggle{parameters.sweepEnabled};
|
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserSettings)
|
jux::SwitchButton recordAudio{¶meters.recordAudio};
|
||||||
};
|
jux::SwitchButton recordVideo{¶meters.recordVideo};
|
||||||
|
|
||||||
class SettingsWindow : public juce::DocumentWindow {
|
juce::Label compressionPresetLabel{"Compression Speed", "Compression Speed"};
|
||||||
public:
|
juce::ComboBox compressionPreset;
|
||||||
SettingsWindow(juce::String name) : juce::DocumentWindow(name, Colours::darker, juce::DocumentWindow::TitleBarButtons::closeButton) {}
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(RecordingSettings)
|
||||||
void closeButtonPressed() override {
|
|
||||||
setVisible(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include "TexturedFragmentShader.glsl"
|
#include "TexturedFragmentShader.glsl"
|
||||||
#include "TexturedVertexShader.glsl"
|
#include "TexturedVertexShader.glsl"
|
||||||
|
|
||||||
VisualiserComponent::VisualiserComponent(juce::File& lastOpenedDirectory, juce::File ffmpegFile, std::function<void()>& haltRecording, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent, bool visualiserOnly) : lastOpenedDirectory(lastOpenedDirectory), ffmpegFile(ffmpegFile), haltRecording(haltRecording), settings(settings), threadManager(threadManager), visualiserOnly(visualiserOnly), AudioBackgroundThread("VisualiserComponent" + juce::String(parent != nullptr ? " Child" : ""), threadManager), parent(parent) {
|
VisualiserComponent::VisualiserComponent(juce::File& lastOpenedDirectory, juce::File ffmpegFile, std::function<void()>& haltRecording, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, RecordingParameters& recordingParameters, VisualiserComponent* parent, bool visualiserOnly) : lastOpenedDirectory(lastOpenedDirectory), ffmpegFile(ffmpegFile), haltRecording(haltRecording), settings(settings), recordingParameters(recordingParameters), threadManager(threadManager), visualiserOnly(visualiserOnly), AudioBackgroundThread("VisualiserComponent" + juce::String(parent != nullptr ? " Child" : ""), threadManager), parent(parent) {
|
||||||
addAndMakeVisible(ffmpegDownloader);
|
addAndMakeVisible(ffmpegDownloader);
|
||||||
|
|
||||||
ffmpegDownloader.onSuccessfulDownload = [this] {
|
ffmpegDownloader.onSuccessfulDownload = [this] {
|
||||||
|
@ -176,49 +176,87 @@ void VisualiserComponent::setRecording(bool recording) {
|
||||||
stopwatch.reset();
|
stopwatch.reset();
|
||||||
|
|
||||||
if (recording) {
|
if (recording) {
|
||||||
if (!ffmpegFile.exists()) {
|
recordingVideo = recordingParameters.recordingVideo();
|
||||||
// ask the user if they want to download ffmpeg
|
recordingAudio = recordingParameters.recordingAudio();
|
||||||
juce::MessageBoxOptions options = juce::MessageBoxOptions()
|
if (!recordingVideo && !recordingAudio) {
|
||||||
.withTitle("Recording requires FFmpeg")
|
|
||||||
.withMessage("FFmpeg is required to record video to .mp4.\n\nWould you like to download it now?")
|
|
||||||
.withButton("Yes")
|
|
||||||
.withButton("No")
|
|
||||||
.withIconType(juce::AlertWindow::QuestionIcon)
|
|
||||||
.withAssociatedComponent(this);
|
|
||||||
|
|
||||||
juce::AlertWindow::showAsync(options, [this](int result) {
|
|
||||||
if (result == 1) {
|
|
||||||
record.setEnabled(false);
|
|
||||||
ffmpegDownloader.download();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
record.setToggleState(false, juce::NotificationType::dontSendNotification);
|
record.setToggleState(false, juce::NotificationType::dontSendNotification);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tempVideoFile = std::make_unique<juce::TemporaryFile>(".mp4");
|
|
||||||
juce::String resolution = std::to_string(renderTexture.width) + "x" + std::to_string(renderTexture.height);
|
|
||||||
juce::String cmd = "\"" + ffmpegFile.getFullPathName() +
|
|
||||||
"\" -r " + juce::String(FRAME_RATE) + " -f rawvideo -pix_fmt rgba -s " + resolution + " -i - -threads 0 -preset fast -y -pix_fmt yuv420p -crf " + juce::String(21) + " -vf vflip \"" + tempVideoFile->getFile().getFullPathName() + "\"";
|
|
||||||
|
|
||||||
ffmpegProcess.start(cmd);
|
if (recordingVideo) {
|
||||||
framePixels.resize(renderTexture.width * renderTexture.height * 4);
|
if (!ffmpegFile.exists()) {
|
||||||
|
// ask the user if they want to download ffmpeg
|
||||||
|
juce::MessageBoxOptions options = juce::MessageBoxOptions()
|
||||||
|
.withTitle("Recording requires FFmpeg")
|
||||||
|
.withMessage("FFmpeg is required to record video to .mp4.\n\nWould you like to download it now?")
|
||||||
|
.withButton("Yes")
|
||||||
|
.withButton("No")
|
||||||
|
.withIconType(juce::AlertWindow::QuestionIcon)
|
||||||
|
.withAssociatedComponent(this);
|
||||||
|
|
||||||
tempAudioFile = std::make_unique<juce::TemporaryFile>(".wav");
|
juce::AlertWindow::showAsync(options, [this](int result) {
|
||||||
audioRecorder.startRecording(tempAudioFile->getFile());
|
if (result == 1) {
|
||||||
|
record.setEnabled(false);
|
||||||
|
ffmpegDownloader.download();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
record.setToggleState(false, juce::NotificationType::dontSendNotification);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tempVideoFile = std::make_unique<juce::TemporaryFile>(".mp4");
|
||||||
|
juce::String resolution = std::to_string(renderTexture.width) + "x" + std::to_string(renderTexture.height);
|
||||||
|
juce::String cmd = "\"" + ffmpegFile.getFullPathName() + "\"" +
|
||||||
|
" -r " + juce::String(FRAME_RATE) +
|
||||||
|
" -f rawvideo" +
|
||||||
|
" -pix_fmt rgba" +
|
||||||
|
" -s " + resolution +
|
||||||
|
" -i -" +
|
||||||
|
" -threads 4" +
|
||||||
|
" -preset " + recordingParameters.getCompressionPreset() +
|
||||||
|
" -y" +
|
||||||
|
" -pix_fmt yuv420p" +
|
||||||
|
" -crf " + juce::String(recordingParameters.getCRF()) +
|
||||||
|
" -vf vflip" +
|
||||||
|
" \"" + tempVideoFile->getFile().getFullPathName() + "\"";
|
||||||
|
|
||||||
|
ffmpegProcess.start(cmd);
|
||||||
|
framePixels.resize(renderTexture.width * renderTexture.height * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recordingAudio) {
|
||||||
|
tempAudioFile = std::make_unique<juce::TemporaryFile>(".wav");
|
||||||
|
audioRecorder.startRecording(tempAudioFile->getFile());
|
||||||
|
}
|
||||||
|
|
||||||
setPaused(false);
|
setPaused(false);
|
||||||
stopwatch.start();
|
stopwatch.start();
|
||||||
} else if (ffmpegProcess.isRunning()) {
|
} else if (ffmpegProcess.isRunning() || audioRecorder.isRecording()) {
|
||||||
audioRecorder.stop();
|
bool wasRecordingAudio = recordingAudio;
|
||||||
ffmpegProcess.close();
|
bool wasRecordingVideo = recordingVideo;
|
||||||
chooser = std::make_unique<juce::FileChooser>("Save recording", lastOpenedDirectory, "*.mp4");
|
recordingAudio = false;
|
||||||
|
recordingVideo = false;
|
||||||
|
|
||||||
|
juce::String extension = wasRecordingVideo ? "mp4" : "wav";
|
||||||
|
if (wasRecordingAudio) {
|
||||||
|
audioRecorder.stop();
|
||||||
|
}
|
||||||
|
if (wasRecordingVideo) {
|
||||||
|
ffmpegProcess.close();
|
||||||
|
}
|
||||||
|
chooser = std::make_unique<juce::FileChooser>("Save recording", lastOpenedDirectory, "*." + extension);
|
||||||
auto flags = juce::FileBrowserComponent::saveMode | juce::FileBrowserComponent::canSelectFiles | juce::FileBrowserComponent::warnAboutOverwriting;
|
auto flags = juce::FileBrowserComponent::saveMode | juce::FileBrowserComponent::canSelectFiles | juce::FileBrowserComponent::warnAboutOverwriting;
|
||||||
|
|
||||||
chooser->launchAsync(flags, [this](const juce::FileChooser& chooser) {
|
chooser->launchAsync(flags, [this, wasRecordingAudio, wasRecordingVideo](const juce::FileChooser& chooser) {
|
||||||
auto file = chooser.getResult();
|
auto file = chooser.getResult();
|
||||||
if (file != juce::File()) {
|
if (file != juce::File()) {
|
||||||
ffmpegProcess.start("\"" + ffmpegFile.getFullPathName() + "\" -i \"" + tempVideoFile->getFile().getFullPathName() + "\" -i \"" + tempAudioFile->getFile().getFullPathName() + "\" -c:v copy -c:a aac -y \"" + file.getFullPathName() + "\"");
|
if (wasRecordingAudio && wasRecordingVideo) {
|
||||||
ffmpegProcess.close();
|
ffmpegProcess.start("\"" + ffmpegFile.getFullPathName() + "\" -i \"" + tempVideoFile->getFile().getFullPathName() + "\" -i \"" + tempAudioFile->getFile().getFullPathName() + "\" -c:v copy -c:a aac -y \"" + file.getFullPathName() + "\"");
|
||||||
|
ffmpegProcess.close();
|
||||||
|
} else if (wasRecordingAudio) {
|
||||||
|
tempAudioFile->getFile().copyFileTo(file);
|
||||||
|
} else if (wasRecordingVideo) {
|
||||||
|
tempVideoFile->getFile().copyFileTo(file);
|
||||||
|
}
|
||||||
lastOpenedDirectory = file.getParentDirectory();
|
lastOpenedDirectory = file.getParentDirectory();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -256,7 +294,7 @@ void VisualiserComponent::resized() {
|
||||||
|
|
||||||
void VisualiserComponent::popoutWindow() {
|
void VisualiserComponent::popoutWindow() {
|
||||||
setRecording(false);
|
setRecording(false);
|
||||||
auto visualiser = new VisualiserComponent(lastOpenedDirectory, ffmpegFile, haltRecording, threadManager, settings, this);
|
auto visualiser = new VisualiserComponent(lastOpenedDirectory, ffmpegFile, haltRecording, threadManager, settings, recordingParameters, this);
|
||||||
visualiser->settings.setLookAndFeel(&getLookAndFeel());
|
visualiser->settings.setLookAndFeel(&getLookAndFeel());
|
||||||
visualiser->openSettings = openSettings;
|
visualiser->openSettings = openSettings;
|
||||||
visualiser->closeSettings = closeSettings;
|
visualiser->closeSettings = closeSettings;
|
||||||
|
@ -384,12 +422,16 @@ void VisualiserComponent::renderOpenGL() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.getToggleState()) {
|
if (record.getToggleState()) {
|
||||||
// draw frame to ffmpeg
|
if (recordingVideo) {
|
||||||
glBindTexture(GL_TEXTURE_2D, renderTexture.id);
|
// draw frame to ffmpeg
|
||||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, framePixels.data());
|
glBindTexture(GL_TEXTURE_2D, renderTexture.id);
|
||||||
ffmpegProcess.write(framePixels.data(), 4 * renderTexture.width * renderTexture.height);
|
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, framePixels.data());
|
||||||
|
ffmpegProcess.write(framePixels.data(), 4 * renderTexture.width * renderTexture.height);
|
||||||
|
}
|
||||||
|
|
||||||
audioRecorder.audioThreadCallback(xSamples, ySamples);
|
if (recordingAudio) {
|
||||||
|
audioRecorder.audioThreadCallback(xSamples, ySamples);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderingSemaphore.release();
|
renderingSemaphore.release();
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "../concurrency/AudioBackgroundThread.h"
|
#include "../concurrency/AudioBackgroundThread.h"
|
||||||
#include "../components/SvgButton.h"
|
#include "../components/SvgButton.h"
|
||||||
#include "VisualiserSettings.h"
|
#include "VisualiserSettings.h"
|
||||||
|
#include "RecordingSettings.h"
|
||||||
#include "../components/StopwatchComponent.h"
|
#include "../components/StopwatchComponent.h"
|
||||||
#include "../img/qoixx.hpp"
|
#include "../img/qoixx.hpp"
|
||||||
#include "../components/DownloaderComponent.h"
|
#include "../components/DownloaderComponent.h"
|
||||||
|
@ -31,7 +32,7 @@ struct Texture {
|
||||||
class VisualiserWindow;
|
class VisualiserWindow;
|
||||||
class VisualiserComponent : public juce::Component, public AudioBackgroundThread, public juce::MouseListener, public juce::OpenGLRenderer, public juce::AsyncUpdater {
|
class VisualiserComponent : public juce::Component, public AudioBackgroundThread, public juce::MouseListener, public juce::OpenGLRenderer, public juce::AsyncUpdater {
|
||||||
public:
|
public:
|
||||||
VisualiserComponent(juce::File& lastOpenedDirectory, juce::File ffmpegFile, std::function<void()>& haltRecording, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent = nullptr, bool visualiserOnly = false);
|
VisualiserComponent(juce::File& lastOpenedDirectory, juce::File ffmpegFile, std::function<void()>& haltRecording, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, RecordingParameters& recordingParameters, VisualiserComponent* parent = nullptr, bool visualiserOnly = false);
|
||||||
~VisualiserComponent() override;
|
~VisualiserComponent() override;
|
||||||
|
|
||||||
std::function<void()> openSettings;
|
std::function<void()> openSettings;
|
||||||
|
@ -78,6 +79,9 @@ private:
|
||||||
std::function<void(FullScreenMode)> fullScreenCallback;
|
std::function<void(FullScreenMode)> fullScreenCallback;
|
||||||
|
|
||||||
VisualiserSettings& settings;
|
VisualiserSettings& settings;
|
||||||
|
RecordingParameters& recordingParameters;
|
||||||
|
bool recordingAudio = false;
|
||||||
|
bool recordingVideo = false;
|
||||||
|
|
||||||
StopwatchComponent stopwatch;
|
StopwatchComponent stopwatch;
|
||||||
SvgButton record{"Record", BinaryData::record_svg, juce::Colours::red, juce::Colours::red.withAlpha(0.01f)};
|
SvgButton record{"Record", BinaryData::record_svg, juce::Colours::red, juce::Colours::red.withAlpha(0.01f)};
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
|
|
||||||
class VisualiserParameters {
|
class VisualiserParameters {
|
||||||
public:
|
public:
|
||||||
BooleanParameter* graticuleEnabled = new BooleanParameter("Show Graticule", "graticuleEnabled", VERSION_HINT, false, "Show the graticule or grid lines over the oscilloscope display.");
|
BooleanParameter* graticuleEnabled = new BooleanParameter("Show Graticule", "graticuleEnabled", VERSION_HINT, true, "Show the graticule or grid lines over the oscilloscope display.");
|
||||||
BooleanParameter* smudgesEnabled = new BooleanParameter("Show Smudges", "smudgesEnabled", VERSION_HINT, false, "Adds a subtle layer of dirt/smudges to the oscilloscope display to make it look more realistic.");
|
BooleanParameter* smudgesEnabled = new BooleanParameter("Show Smudges", "smudgesEnabled", VERSION_HINT, true, "Adds a subtle layer of dirt/smudges to the oscilloscope display to make it look more realistic.");
|
||||||
BooleanParameter* upsamplingEnabled = new BooleanParameter("Upsample Audio", "upsamplingEnabled", VERSION_HINT, true, "Upsamples the audio before visualising it to make it appear more realistic, at the expense of performance.");
|
BooleanParameter* upsamplingEnabled = new BooleanParameter("Upsample Audio", "upsamplingEnabled", VERSION_HINT, true, "Upsamples the audio before visualising it to make it appear more realistic, at the expense of performance.");
|
||||||
BooleanParameter* sweepEnabled = new BooleanParameter("Sweep", "sweepEnabled", VERSION_HINT, false, "Plots the audio signal over time, sweeping from left to right");
|
BooleanParameter* sweepEnabled = new BooleanParameter("Sweep", "sweepEnabled", VERSION_HINT, false, "Plots the audio signal over time, sweeping from left to right");
|
||||||
BooleanParameter* visualiserFullScreen = new BooleanParameter("Visualiser Fullscreen", "visualiserFullScreen", VERSION_HINT, false, "Makes the software visualiser fullscreen.");
|
BooleanParameter* visualiserFullScreen = new BooleanParameter("Visualiser Fullscreen", "visualiserFullScreen", VERSION_HINT, false, "Makes the software visualiser fullscreen.");
|
||||||
|
@ -179,7 +179,9 @@ private:
|
||||||
|
|
||||||
class SettingsWindow : public juce::DocumentWindow {
|
class SettingsWindow : public juce::DocumentWindow {
|
||||||
public:
|
public:
|
||||||
SettingsWindow(juce::String name) : juce::DocumentWindow(name, Colours::darker, juce::DocumentWindow::TitleBarButtons::closeButton) {}
|
SettingsWindow(juce::String name) : juce::DocumentWindow(name, Colours::darker, juce::DocumentWindow::TitleBarButtons::closeButton) {
|
||||||
|
setResizable(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
void closeButtonPressed() override {
|
void closeButtonPressed() override {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
|
|
|
@ -610,6 +610,10 @@
|
||||||
file="Source/visualiser/OutputFragmentShader.glsl" xcodeResource="0"/>
|
file="Source/visualiser/OutputFragmentShader.glsl" xcodeResource="0"/>
|
||||||
<FILE id="A0CrLF" name="OutputVertexShader.glsl" compile="0" resource="0"
|
<FILE id="A0CrLF" name="OutputVertexShader.glsl" compile="0" resource="0"
|
||||||
file="Source/visualiser/OutputVertexShader.glsl" xcodeResource="0"/>
|
file="Source/visualiser/OutputVertexShader.glsl" xcodeResource="0"/>
|
||||||
|
<FILE id="XpwLUN" name="RecordingSettings.cpp" compile="1" resource="0"
|
||||||
|
file="Source/visualiser/RecordingSettings.cpp"/>
|
||||||
|
<FILE id="EOcvBc" name="RecordingSettings.h" compile="0" resource="0"
|
||||||
|
file="Source/visualiser/RecordingSettings.h"/>
|
||||||
<FILE id="Gwupwc" name="SimpleFragmentShader.glsl" compile="0" resource="0"
|
<FILE id="Gwupwc" name="SimpleFragmentShader.glsl" compile="0" resource="0"
|
||||||
file="Source/visualiser/SimpleFragmentShader.glsl"/>
|
file="Source/visualiser/SimpleFragmentShader.glsl"/>
|
||||||
<FILE id="RX1oPv" name="SimpleVertexShader.glsl" compile="0" resource="0"
|
<FILE id="RX1oPv" name="SimpleVertexShader.glsl" compile="0" resource="0"
|
||||||
|
|
|
@ -135,6 +135,10 @@
|
||||||
file="Source/visualiser/OutputFragmentShader.glsl"/>
|
file="Source/visualiser/OutputFragmentShader.glsl"/>
|
||||||
<FILE id="df9jjf" name="OutputVertexShader.glsl" compile="0" resource="0"
|
<FILE id="df9jjf" name="OutputVertexShader.glsl" compile="0" resource="0"
|
||||||
file="Source/visualiser/OutputVertexShader.glsl"/>
|
file="Source/visualiser/OutputVertexShader.glsl"/>
|
||||||
|
<FILE id="Hw3RUp" name="RecordingSettings.cpp" compile="1" resource="0"
|
||||||
|
file="Source/visualiser/RecordingSettings.cpp"/>
|
||||||
|
<FILE id="dAhaYq" name="RecordingSettings.h" compile="0" resource="0"
|
||||||
|
file="Source/visualiser/RecordingSettings.h"/>
|
||||||
<FILE id="ePJ1g0" name="SimpleFragmentShader.glsl" compile="0" resource="0"
|
<FILE id="ePJ1g0" name="SimpleFragmentShader.glsl" compile="0" resource="0"
|
||||||
file="Source/visualiser/SimpleFragmentShader.glsl"/>
|
file="Source/visualiser/SimpleFragmentShader.glsl"/>
|
||||||
<FILE id="GFGIEu" name="SimpleVertexShader.glsl" compile="0" resource="0"
|
<FILE id="GFGIEu" name="SimpleVertexShader.glsl" compile="0" resource="0"
|
||||||
|
|
Ładowanie…
Reference in New Issue