kopia lustrzana https://github.com/jameshball/osci-render
Merge pull request #278 from jameshball/develop
Merge develop into main for latest releasepull/277/head
commit
4642044a47
Plik binarny nie jest wyświetlany.
|
@ -39,12 +39,12 @@ CommonPluginEditor::CommonPluginEditor(CommonAudioProcessor& p, juce::String app
|
|||
addAndMakeVisible(visualiser);
|
||||
|
||||
visualiserSettings.setLookAndFeel(&getLookAndFeel());
|
||||
visualiserSettings.setSize(550, 550);
|
||||
visualiserSettings.setSize(550, VISUALISER_SETTINGS_HEIGHT);
|
||||
visualiserSettings.setColour(juce::ResizableWindow::backgroundColourId, Colours::dark);
|
||||
|
||||
recordingSettings.setLookAndFeel(&getLookAndFeel());
|
||||
recordingSettings.setSize(300, 200);
|
||||
recordingSettingsWindow.centreWithSize(300, 230);
|
||||
recordingSettings.setSize(350, 230);
|
||||
recordingSettingsWindow.centreWithSize(350, 260);
|
||||
#if JUCE_WINDOWS
|
||||
// if not standalone, use native title bar for compatibility with DAWs
|
||||
recordingSettingsWindow.setUsingNativeTitleBar(processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone);
|
||||
|
@ -84,6 +84,12 @@ bool CommonPluginEditor::keyPressed(const juce::KeyPress& key) {
|
|||
saveProject();
|
||||
} else if (key.getModifiers().isCommandDown() && key.getKeyCode() == 'O') {
|
||||
openProject();
|
||||
} else if (key.isKeyCode(juce::KeyPress::F11Key) && juce::JUCEApplicationBase::isStandaloneApp()) {
|
||||
// set fullscreen
|
||||
juce::StandaloneFilterWindow* window = findParentComponentOfClass<juce::StandaloneFilterWindow>();
|
||||
if (window != nullptr) {
|
||||
window->setFullScreen(!fullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
|
||||
private:
|
||||
CommonAudioProcessor& audioProcessor;
|
||||
bool fullScreen = false;
|
||||
|
||||
juce::File applicationFolder = juce::File::getSpecialLocation(juce::File::SpecialLocationType::userApplicationDataDirectory)
|
||||
#if JUCE_MAC
|
||||
|
@ -48,6 +49,8 @@ public:
|
|||
SharedTextureManager sharedTextureManager;
|
||||
#endif
|
||||
|
||||
int VISUALISER_SETTINGS_HEIGHT = 750;
|
||||
|
||||
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.visualiserParameters, 3);
|
||||
RecordingSettings recordingSettings = RecordingSettings(audioProcessor.recordingParameters);
|
||||
SettingsWindow recordingSettingsWindow = SettingsWindow("Recording Settings", recordingSettings);
|
||||
|
|
|
@ -12,10 +12,9 @@
|
|||
#include "components/AudioPlayerComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
CommonAudioProcessor::CommonAudioProcessor()
|
||||
CommonAudioProcessor::CommonAudioProcessor(const BusesProperties& busesProperties)
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
: AudioProcessor (BusesProperties().withInput("Input", juce::AudioChannelSet::namedChannelSet(3), true)
|
||||
.withOutput("Output", juce::AudioChannelSet::stereo(), true))
|
||||
: AudioProcessor(busesProperties)
|
||||
#endif
|
||||
{
|
||||
// locking isn't necessary here because we are in the constructor
|
||||
|
@ -25,7 +24,9 @@ CommonAudioProcessor::CommonAudioProcessor()
|
|||
effects.push_back(effect);
|
||||
}
|
||||
|
||||
effects.push_back(visualiserParameters.smoothEffect);
|
||||
for (auto effect : visualiserParameters.audioEffects) {
|
||||
effects.push_back(effect);
|
||||
}
|
||||
|
||||
for (auto parameter : visualiserParameters.booleans) {
|
||||
booleanParameters.push_back(parameter);
|
||||
|
|
|
@ -25,7 +25,7 @@ class CommonAudioProcessor : public juce::AudioProcessor, public SampleRateMana
|
|||
#endif
|
||||
{
|
||||
public:
|
||||
CommonAudioProcessor();
|
||||
CommonAudioProcessor(const BusesProperties& busesProperties);
|
||||
~CommonAudioProcessor() override;
|
||||
|
||||
void addAllParameters();
|
||||
|
|
|
@ -125,6 +125,7 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
|
|||
};
|
||||
|
||||
BooleanParameter* visualiserFullScreen = audioProcessor.visualiserParameters.visualiserFullScreen;
|
||||
pluginEditor.visualiser.setFullScreen(visualiserFullScreen->getBoolValue());
|
||||
|
||||
addAndMakeVisible(pluginEditor.visualiser);
|
||||
pluginEditor.visualiser.setFullScreenCallback([this, visualiserFullScreen](FullScreenMode mode) {
|
||||
|
@ -135,6 +136,8 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
|
|||
} else if (mode == FullScreenMode::MAIN_COMPONENT) {
|
||||
visualiserFullScreen->setBoolValueNotifyingHost(false);
|
||||
}
|
||||
|
||||
pluginEditor.visualiser.setFullScreen(visualiserFullScreen->getBoolValue());
|
||||
|
||||
pluginEditor.resized();
|
||||
pluginEditor.repaint();
|
||||
|
@ -229,9 +232,9 @@ void MainComponent::resized() {
|
|||
auto shiftedBounds = bounds;
|
||||
shiftedBounds.setX(topLeft.getX());
|
||||
shiftedBounds.setY(topLeft.getY());
|
||||
if (minDim < 35) {
|
||||
minDim = 35;
|
||||
}
|
||||
//if (minDim < 35) {
|
||||
// minDim = 35;
|
||||
//}
|
||||
pluginEditor.visualiser.setBounds(shiftedBounds.withSizeKeepingCentre(minDim - 25, minDim));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ void OscirenderAudioProcessorEditor::resized() {
|
|||
bool luaFileOpen = false;
|
||||
|
||||
if (ableToEditFile) {
|
||||
if (codeEditors[index]->isVisible()) {
|
||||
if (index < codeEditors.size() && codeEditors[index]->isVisible()) {
|
||||
editorVisible = true;
|
||||
|
||||
juce::Component dummy;
|
||||
|
@ -197,7 +197,9 @@ void OscirenderAudioProcessorEditor::resized() {
|
|||
|
||||
collapseButton.setVisible(ableToEditFile);
|
||||
|
||||
codeEditors[index]->setVisible(fileOpen);
|
||||
if (index < codeEditors.size()) {
|
||||
codeEditors[index]->setVisible(fileOpen);
|
||||
}
|
||||
resizerBar.setVisible(fileOpen);
|
||||
|
||||
console.setVisible(luaFileOpen);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "audio/EffectParameter.h"
|
||||
|
||||
//==============================================================================
|
||||
OscirenderAudioProcessor::OscirenderAudioProcessor() {
|
||||
OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(BusesProperties().withInput("Input", juce::AudioChannelSet::namedChannelSet(2), true).withOutput("Output", juce::AudioChannelSet::stereo(), true)) {
|
||||
// locking isn't necessary here because we are in the constructor
|
||||
|
||||
toggleableEffects.push_back(std::make_shared<Effect>(
|
||||
|
|
|
@ -68,7 +68,7 @@ public:
|
|||
"Frequency",
|
||||
"Controls how many times per second the image is drawn, thereby controlling the pitch of the sound. Lower frequencies result in more-accurately drawn images, but more flickering, and vice versa.",
|
||||
"frequency",
|
||||
VERSION_HINT, 220.0, 0.0, 12000.0, 0.1
|
||||
VERSION_HINT, 220.0, 0.0, 12000.0
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -150,8 +150,8 @@ public:
|
|||
|
||||
BooleanParameter* animateFrames = new BooleanParameter("Animate", "animateFrames", VERSION_HINT, true, "Enables animation for files that have multiple frames, such as GIFs or Line Art.");
|
||||
BooleanParameter* animationSyncBPM = new BooleanParameter("Sync To BPM", "animationSyncBPM", VERSION_HINT, false, "Synchronises the animation's framerate with the BPM of your DAW.");
|
||||
FloatParameter* animationRate = new FloatParameter("Animation Rate", "animationRate", VERSION_HINT, 30, -1000, 1000, 0.01);
|
||||
FloatParameter* animationOffset = new FloatParameter("Animation Offset", "animationOffset", VERSION_HINT, 0, -10000, 10000, 0.1);
|
||||
FloatParameter* animationRate = new FloatParameter("Animation Rate", "animationRate", VERSION_HINT, 30, -1000, 1000);
|
||||
FloatParameter* animationOffset = new FloatParameter("Animation Offset", "animationOffset", VERSION_HINT, 0, -10000, 10000);
|
||||
|
||||
BooleanParameter* invertImage = new BooleanParameter("Invert Image", "invertImage", VERSION_HINT, false, "Inverts the image so that dark pixels become light, and vice versa.");
|
||||
std::shared_ptr<Effect> imageThreshold = std::make_shared<Effect>(
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
SosciPluginEditor::SosciPluginEditor(SosciAudioProcessor& p) : CommonPluginEditor(p, "sosci", "sosci", 1180, 750), audioProcessor(p) {
|
||||
initialiseMenuBar(model);
|
||||
addAndMakeVisible(volume);
|
||||
if (juce::JUCEApplication::isStandaloneApp()) {
|
||||
addAndMakeVisible(volume);
|
||||
}
|
||||
addAndMakeVisible(visualiserSettingsWrapper);
|
||||
|
||||
BooleanParameter* visualiserFullScreen = audioProcessor.visualiserParameters.visualiserFullScreen;
|
||||
|
@ -24,9 +26,21 @@ SosciPluginEditor::SosciPluginEditor(SosciAudioProcessor& p) : CommonPluginEdito
|
|||
|
||||
resized();
|
||||
visualiserFullScreen->addListener(this);
|
||||
|
||||
if (juce::JUCEApplication::isStandaloneApp()) {
|
||||
juce::StandalonePluginHolder* standalone = juce::StandalonePluginHolder::getInstance();
|
||||
juce::AudioDeviceManager& manager = standalone->deviceManager;
|
||||
manager.addChangeListener(this);
|
||||
currentInputDevice = getInputDeviceName();
|
||||
}
|
||||
}
|
||||
|
||||
SosciPluginEditor::~SosciPluginEditor() {
|
||||
if (juce::JUCEApplication::isStandaloneApp()) {
|
||||
juce::StandalonePluginHolder* standalone = juce::StandalonePluginHolder::getInstance();
|
||||
juce::AudioDeviceManager& manager = standalone->deviceManager;
|
||||
manager.removeChangeListener(this);
|
||||
}
|
||||
audioProcessor.visualiserParameters.visualiserFullScreen->removeListener(this);
|
||||
menuBar.setModel(nullptr);
|
||||
}
|
||||
|
@ -43,11 +57,13 @@ void SosciPluginEditor::resized() {
|
|||
} else {
|
||||
menuBar.setBounds(area.removeFromTop(25));
|
||||
|
||||
auto volumeArea = area.removeFromLeft(30);
|
||||
volume.setBounds(volumeArea.withSizeKeepingCentre(volumeArea.getWidth(), juce::jmin(volumeArea.getHeight(), 300)));
|
||||
if (juce::JUCEApplication::isStandaloneApp()) {
|
||||
auto volumeArea = area.removeFromLeft(30);
|
||||
volume.setBounds(volumeArea.withSizeKeepingCentre(volumeArea.getWidth(), juce::jmin(volumeArea.getHeight(), 300)));
|
||||
}
|
||||
|
||||
auto settingsArea = area.removeFromRight(juce::jmax(juce::jmin(0.4 * getWidth(), 550.0), 350.0));
|
||||
visualiserSettings.setSize(settingsArea.getWidth(), 550);
|
||||
visualiserSettings.setSize(settingsArea.getWidth(), VISUALISER_SETTINGS_HEIGHT);
|
||||
visualiserSettingsWrapper.setBounds(settingsArea);
|
||||
|
||||
if (area.getWidth() < 10) {
|
||||
|
@ -83,8 +99,12 @@ void SosciPluginEditor::filesDropped(const juce::StringArray& files, int x, int
|
|||
|
||||
void SosciPluginEditor::visualiserFullScreenChanged() {
|
||||
bool fullScreen = audioProcessor.visualiserParameters.visualiserFullScreen->getBoolValue();
|
||||
|
||||
visualiser.setFullScreen(fullScreen);
|
||||
|
||||
volume.setVisible(!fullScreen);
|
||||
if (juce::JUCEApplication::isStandaloneApp()) {
|
||||
volume.setVisible(!fullScreen);
|
||||
}
|
||||
visualiserSettingsWrapper.setVisible(!fullScreen);
|
||||
menuBar.setVisible(!fullScreen);
|
||||
resized();
|
||||
|
@ -98,3 +118,28 @@ void SosciPluginEditor::parameterValueChanged(int parameterIndex, float newValue
|
|||
}
|
||||
|
||||
void SosciPluginEditor::parameterGestureChanged(int parameterIndex, bool gestureIsStarting) {}
|
||||
|
||||
void SosciPluginEditor::changeListenerCallback(juce::ChangeBroadcaster* source) {
|
||||
if (juce::JUCEApplication::isStandaloneApp()) {
|
||||
juce::String inputDevice = getInputDeviceName();
|
||||
if (inputDevice != currentInputDevice) {
|
||||
currentInputDevice = inputDevice;
|
||||
// switch to getting audio from input if the user changes the input device
|
||||
// because we assume they are debugging and want to hear the audio from mic
|
||||
audioProcessor.stopAudioFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
juce::String SosciPluginEditor::getInputDeviceName() {
|
||||
if (juce::StandalonePluginHolder::getInstance() == nullptr) {
|
||||
return "Unknown";
|
||||
}
|
||||
juce::StandalonePluginHolder* standalone = juce::StandalonePluginHolder::getInstance();
|
||||
juce::AudioDeviceManager& manager = standalone->deviceManager;
|
||||
auto device = manager.getCurrentAudioDevice();
|
||||
auto deviceType = manager.getCurrentDeviceTypeObject();
|
||||
int inputIndex = deviceType->getIndexOfDevice(device, true);
|
||||
auto inputName = deviceType->getDeviceNames(true)[inputIndex];
|
||||
return inputName;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "components/SosciMainMenuBarModel.h"
|
||||
#include "components/SvgButton.h"
|
||||
|
||||
class SosciPluginEditor : public CommonPluginEditor, public juce::FileDragAndDropTarget, public juce::AudioProcessorParameter::Listener {
|
||||
class SosciPluginEditor : public CommonPluginEditor, public juce::FileDragAndDropTarget, public juce::AudioProcessorParameter::Listener, public juce::ChangeListener {
|
||||
public:
|
||||
SosciPluginEditor(SosciAudioProcessor&);
|
||||
~SosciPluginEditor() override;
|
||||
|
@ -21,10 +21,15 @@ public:
|
|||
void visualiserFullScreenChanged();
|
||||
void parameterValueChanged(int parameterIndex, float newValue) override;
|
||||
void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override;
|
||||
void changeListenerCallback(juce::ChangeBroadcaster* source) override;
|
||||
|
||||
private:
|
||||
SosciAudioProcessor& audioProcessor;
|
||||
|
||||
juce::String getInputDeviceName();
|
||||
|
||||
juce::String currentInputDevice;
|
||||
|
||||
ScrollableComponent visualiserSettingsWrapper = ScrollableComponent(visualiserSettings);
|
||||
|
||||
SosciMainMenuBarModel model{*this, audioProcessor};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "SosciPluginEditor.h"
|
||||
#include "audio/EffectParameter.h"
|
||||
|
||||
SosciAudioProcessor::SosciAudioProcessor() {
|
||||
SosciAudioProcessor::SosciAudioProcessor() : CommonAudioProcessor(BusesProperties().withInput("Input", juce::AudioChannelSet::namedChannelSet(4), true).withOutput("Output", juce::AudioChannelSet::stereo(), true)) {
|
||||
// demo audio file on standalone only
|
||||
if (juce::JUCEApplicationBase::isStandaloneApp()) {
|
||||
std::unique_ptr<juce::InputStream> stream = std::make_unique<juce::MemoryInputStream>(BinaryData::sosci_flac, BinaryData::sosci_flacSize, false);
|
||||
|
@ -62,14 +62,16 @@ void SosciAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::M
|
|||
// this is the point that the visualiser will draw
|
||||
threadManager.write(point, "VisualiserComponent");
|
||||
|
||||
point.scale(volume, volume, 1.0);
|
||||
if (juce::JUCEApplication::isStandaloneApp()) {
|
||||
point.scale(volume, volume, 1.0);
|
||||
|
||||
// clip
|
||||
point.x = juce::jmax(-threshold, juce::jmin(threshold.load(), point.x));
|
||||
point.y = juce::jmax(-threshold, juce::jmin(threshold.load(), point.y));
|
||||
// clip
|
||||
point.x = juce::jmax(-threshold, juce::jmin(threshold.load(), point.x));
|
||||
point.y = juce::jmax(-threshold, juce::jmin(threshold.load(), point.y));
|
||||
|
||||
// this is the point that the volume component will draw (i.e. post scale/clipping)
|
||||
threadManager.write(point, "VolumeComponent");
|
||||
// this is the point that the volume component will draw (i.e. post scale/clipping)
|
||||
threadManager.write(point, "VolumeComponent");
|
||||
}
|
||||
|
||||
if (output.getNumChannels() > 0) {
|
||||
outputArray[0][sample] = point.x;
|
||||
|
|
|
@ -79,13 +79,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void audioThreadCallback(const std::vector<float>& left, const std::vector<float>& right) {
|
||||
juce::AudioBuffer<float> buffer(2, left.size());
|
||||
buffer.copyFrom(0, 0, left.data(), left.size());
|
||||
buffer.copyFrom(1, 0, right.data(), right.size());
|
||||
audioThreadCallback(buffer);
|
||||
}
|
||||
|
||||
void setRecordLength(double recordLength) {
|
||||
recordingLength = recordLength;
|
||||
}
|
||||
|
|
|
@ -328,9 +328,11 @@ class EffectParameter : public FloatParameter {
|
|||
public:
|
||||
std::atomic<bool> smoothValueChange = true;
|
||||
LfoTypeParameter* lfo = new LfoTypeParameter(name + " LFO", paramID + "Lfo", getVersionHint(), 1);
|
||||
FloatParameter* lfoRate = new FloatParameter(name + " LFO Rate", paramID + "LfoRate", getVersionHint(), 1.0f, 0.0f, 10000.0f, 0.01f, "Hz");
|
||||
FloatParameter* lfoRate = new FloatParameter(name + " LFO Rate", paramID + "LfoRate", getVersionHint(), 1.0f, 0.0f, 10000.0f, 0.001f, "Hz");
|
||||
BooleanParameter* sidechain = new BooleanParameter(name + " Sidechain Enabled", paramID + "Sidechain", getVersionHint(), false, "Toggles " + name + " Sidechain.");
|
||||
std::atomic<float> phase = 0.0f;
|
||||
// this is what the value will get reset to on double-click.
|
||||
std::atomic<float> defaultValue;
|
||||
juce::String description;
|
||||
|
||||
std::vector<juce::AudioProcessorParameter*> getParameters() {
|
||||
|
@ -399,5 +401,5 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
EffectParameter(juce::String name, juce::String description, juce::String id, int versionHint, float value, float min, float max, float step = 0.01, bool smoothValueChange = true) : FloatParameter(name, id, versionHint, value, min, max, step), smoothValueChange(smoothValueChange), description(description) {}
|
||||
EffectParameter(juce::String name, juce::String description, juce::String id, int versionHint, float value, float min, float max, float step = 0.0001, bool smoothValueChange = true) : FloatParameter(name, id, versionHint, value, min, max, step), smoothValueChange(smoothValueChange), description(description), defaultValue(value) {}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
#include "StereoEffect.h"
|
||||
|
||||
StereoEffect::StereoEffect() {}
|
||||
|
||||
StereoEffect::~StereoEffect() {}
|
||||
|
||||
OsciPoint StereoEffect::apply(int index, OsciPoint input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
if (this->sampleRate != sampleRate) {
|
||||
this->sampleRate = sampleRate;
|
||||
initialiseBuffer(sampleRate);
|
||||
}
|
||||
double sampleOffset = values[0].load() / 10;
|
||||
sampleOffset = juce::jlimit(0.0, 1.0, sampleOffset);
|
||||
sampleOffset *= buffer.size();
|
||||
|
||||
head++;
|
||||
if (head >= buffer.size()) {
|
||||
head = 0;
|
||||
}
|
||||
|
||||
buffer[head] = input;
|
||||
|
||||
int readHead = head - sampleOffset;
|
||||
if (readHead < 0) {
|
||||
readHead += buffer.size();
|
||||
}
|
||||
|
||||
return OsciPoint(input.x, buffer[readHead].y, input.z);
|
||||
}
|
||||
|
||||
void StereoEffect::initialiseBuffer(double sampleRate) {
|
||||
buffer.clear();
|
||||
buffer.resize(bufferLength * sampleRate);
|
||||
head = 0;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
#include "EffectApplication.h"
|
||||
#include "../shape/OsciPoint.h"
|
||||
|
||||
class StereoEffect : public EffectApplication {
|
||||
public:
|
||||
StereoEffect();
|
||||
~StereoEffect();
|
||||
|
||||
OsciPoint apply(int index, OsciPoint input, const std::vector<std::atomic<double>>& values, double sampleRate) override;
|
||||
|
||||
private:
|
||||
void initialiseBuffer(double sampleRate);
|
||||
|
||||
const double bufferLength = 0.1;
|
||||
double sampleRate = -1;
|
||||
std::vector<OsciPoint> buffer;
|
||||
int head = 0;
|
||||
};
|
|
@ -109,6 +109,9 @@ void AudioPlayerComponent::setPaused(bool paused) {
|
|||
void AudioPlayerComponent::parserChanged() {
|
||||
setup();
|
||||
repaint();
|
||||
if (onParserChanged != nullptr) {
|
||||
onParserChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayerComponent::resized() {
|
||||
|
|
|
@ -63,6 +63,9 @@ public:
|
|||
void setup();
|
||||
void parserChanged() override;
|
||||
void setPaused(bool paused);
|
||||
bool isInitialised() const { return audioProcessor.wavParser.isInitialised(); }
|
||||
|
||||
std::function<void()> onParserChanged;
|
||||
|
||||
private:
|
||||
CommonAudioProcessor& audioProcessor;
|
||||
|
|
|
@ -17,11 +17,13 @@ EffectComponent::EffectComponent(Effect& effect, int index) : effect(effect), in
|
|||
|
||||
slider.setSliderStyle(juce::Slider::LinearHorizontal);
|
||||
slider.setTextBoxStyle(juce::Slider::TextBoxRight, false, TEXT_BOX_WIDTH, slider.getTextBoxHeight());
|
||||
slider.setNumDecimalPlacesToDisplay(4);
|
||||
|
||||
lfoSlider.setSliderStyle(juce::Slider::LinearHorizontal);
|
||||
lfoSlider.setTextBoxStyle(juce::Slider::TextBoxRight, false, TEXT_BOX_WIDTH, lfoSlider.getTextBoxHeight());
|
||||
lfoSlider.setTextValueSuffix("Hz");
|
||||
lfoSlider.setColour(sliderThumbOutlineColourId, juce::Colour(0xff00ff00));
|
||||
lfoSlider.setNumDecimalPlacesToDisplay(3);
|
||||
|
||||
label.setFont(juce::Font(14.0f));
|
||||
|
||||
|
@ -59,6 +61,7 @@ void EffectComponent::setupComponent() {
|
|||
|
||||
slider.setRange(parameter->min, parameter->max, parameter->step);
|
||||
slider.setValue(parameter->getValueUnnormalised(), juce::dontSendNotification);
|
||||
slider.setDoubleClickReturnValue(true, parameter->defaultValue);
|
||||
|
||||
lfoEnabled = parameter->lfo != nullptr && parameter->lfoRate != nullptr;
|
||||
if (lfoEnabled) {
|
||||
|
@ -81,6 +84,7 @@ void EffectComponent::setupComponent() {
|
|||
lfoSlider.setRange(parameter->lfoRate->min, parameter->lfoRate->max, parameter->lfoRate->step);
|
||||
lfoSlider.setValue(parameter->lfoRate->getValueUnnormalised(), juce::dontSendNotification);
|
||||
lfoSlider.setSkewFactorFromMidPoint(parameter->lfoRate->min + 0.1 * (parameter->lfoRate->max - parameter->lfoRate->min));
|
||||
lfoSlider.setDoubleClickReturnValue(true, 1.0);
|
||||
|
||||
if (lfo.getSelectedId() == static_cast<int>(LfoType::Static)) {
|
||||
lfoSlider.setVisible(false);
|
||||
|
|
|
@ -13,6 +13,7 @@ VolumeComponent::VolumeComponent(CommonAudioProcessor& p) : AudioBackgroundThrea
|
|||
auto volumeParam = audioProcessor.volumeEffect->parameters[0];
|
||||
volumeSlider.setRange(volumeParam->min, volumeParam->max, volumeParam->step);
|
||||
volumeSlider.setValue(volumeParam->getValueUnnormalised());
|
||||
volumeSlider.setDoubleClickReturnValue(true, 1.0);
|
||||
volumeSlider.setLookAndFeel(&thumbRadiusLookAndFeel);
|
||||
volumeSlider.setColour(juce::Slider::ColourIds::thumbColourId, juce::Colours::black);
|
||||
|
||||
|
@ -25,6 +26,7 @@ VolumeComponent::VolumeComponent(CommonAudioProcessor& p) : AudioBackgroundThrea
|
|||
auto& thresholdParam = audioProcessor.thresholdEffect->parameters[0];
|
||||
thresholdSlider.setRange(thresholdParam->min, thresholdParam->max, thresholdParam->step);
|
||||
thresholdSlider.setValue(thresholdParam->getValueUnnormalised());
|
||||
thresholdSlider.setDoubleClickReturnValue(true, 1.0);
|
||||
thresholdSlider.setLookAndFeel(&thresholdLookAndFeel);
|
||||
thresholdSlider.setColour(juce::Slider::ColourIds::thumbColourId, juce::Colours::black);
|
||||
|
||||
|
|
|
@ -66,6 +66,16 @@ ImageParser::ImageParser(OscirenderAudioProcessor& p, juce::String extension, ju
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (frames.size() == 0) {
|
||||
juce::MessageManager::callAsync([this] {
|
||||
juce::AlertWindow::showMessageBoxAsync(juce::AlertWindow::AlertIconType::WarningIcon, "Invalid GIF", "The image could not be loaded. Please try optimising the GIF with https://ezgif.com/optimize.");
|
||||
});
|
||||
|
||||
width = 1;
|
||||
height = 1;
|
||||
frames.emplace_back(std::vector<uint8_t>(1));
|
||||
}
|
||||
|
||||
setFrame(0);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ RecordingSettings::RecordingSettings(RecordingParameters& ps) : parameters(ps) {
|
|||
addAndMakeVisible(recordVideo);
|
||||
addAndMakeVisible(compressionPreset);
|
||||
addAndMakeVisible(compressionPresetLabel);
|
||||
addAndMakeVisible(customSharedTextureOutputLabel);
|
||||
addAndMakeVisible(customSharedTextureOutputEditor);
|
||||
|
||||
quality.setSliderOnValueChange();
|
||||
quality.setRangeEnabled(false);
|
||||
|
@ -34,6 +36,12 @@ RecordingSettings::RecordingSettings(RecordingParameters& ps) : parameters(ps) {
|
|||
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.");
|
||||
|
||||
customSharedTextureOutputLabel.setTooltip("Custom name for when creating a new Syphon/Spout server. WARNING: You should not use the same name when running multiple servers at once!.");
|
||||
customSharedTextureOutputEditor.setText(parameters.customSharedTextureServerName);
|
||||
customSharedTextureOutputEditor.onTextChange = [this] {
|
||||
parameters.customSharedTextureServerName = customSharedTextureOutputEditor.getText();
|
||||
};
|
||||
#else
|
||||
addAndMakeVisible(recordVideoWarning);
|
||||
addAndMakeVisible(sosciLink);
|
||||
|
@ -61,8 +69,13 @@ void RecordingSettings::resized() {
|
|||
recordAudio.setBounds(area.removeFromTop(rowHeight));
|
||||
recordVideo.setBounds(area.removeFromTop(rowHeight));
|
||||
auto row = area.removeFromTop(rowHeight);
|
||||
compressionPresetLabel.setBounds(row.removeFromLeft(140));
|
||||
compressionPreset.setBounds(row.removeFromRight(80));
|
||||
compressionPresetLabel.setBounds(row.removeFromLeft(170));
|
||||
compressionPreset.setBounds(row.removeFromRight(100));
|
||||
|
||||
area.removeFromTop(5);
|
||||
row = area.removeFromTop(rowHeight);
|
||||
customSharedTextureOutputLabel.setBounds(row.removeFromLeft(170));
|
||||
customSharedTextureOutputEditor.setBounds(row.removeFromRight(100));
|
||||
#else
|
||||
recordVideoWarning.setBounds(area.removeFromTop(2 * rowHeight));
|
||||
area.removeFromTop(rowHeight / 2);
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
recordAudio.save(settingsXml->createNewChildElement("recordAudio"));
|
||||
recordVideo.save(settingsXml->createNewChildElement("recordVideo"));
|
||||
settingsXml->setAttribute("compressionPreset", compressionPreset);
|
||||
settingsXml->setAttribute("customSharedTextureServerName", customSharedTextureServerName);
|
||||
|
||||
auto qualityXml = settingsXml->createNewChildElement("quality");
|
||||
qualityEffect.save(qualityXml);
|
||||
|
@ -65,6 +66,9 @@ public:
|
|||
if (settingsXml->hasAttribute("compressionPreset")) {
|
||||
compressionPreset = settingsXml->getStringAttribute("compressionPreset");
|
||||
}
|
||||
if (settingsXml->hasAttribute("customSharedTextureServerName")) {
|
||||
customSharedTextureServerName = settingsXml->getStringAttribute("customSharedTextureServerName");
|
||||
}
|
||||
if (auto* qualityXml = settingsXml->getChildByName("quality")) {
|
||||
qualityEffect.load(qualityXml);
|
||||
}
|
||||
|
@ -72,6 +76,7 @@ public:
|
|||
}
|
||||
|
||||
juce::StringArray compressionPresets = { "ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow" };
|
||||
juce::String customSharedTextureServerName = "";
|
||||
};
|
||||
|
||||
class RecordingSettings : public juce::Component {
|
||||
|
@ -102,6 +107,13 @@ public:
|
|||
juce::String getCompressionPreset() {
|
||||
return parameters.compressionPreset;
|
||||
}
|
||||
|
||||
juce::String getCustomSharedTextureServerName() {
|
||||
if (parameters.customSharedTextureServerName.isEmpty()) {
|
||||
return "osci-render - " + juce::String(juce::Time::getCurrentTime().toMilliseconds());
|
||||
}
|
||||
return parameters.customSharedTextureServerName;
|
||||
}
|
||||
|
||||
RecordingParameters& parameters;
|
||||
|
||||
|
@ -119,6 +131,9 @@ private:
|
|||
|
||||
juce::Label compressionPresetLabel{"Compression Speed", "Compression Speed"};
|
||||
juce::ComboBox compressionPreset;
|
||||
|
||||
juce::Label customSharedTextureOutputLabel{"Custom Syphon/Spout Name", "Custom Syphon/Spout Name"};
|
||||
juce::TextEditor customSharedTextureOutputEditor{"customSharedTextureOutputEditor"};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(RecordingSettings)
|
||||
};
|
||||
|
|
|
@ -115,8 +115,24 @@ VisualiserComponent::VisualiserComponent(
|
|||
popoutWindow();
|
||||
};
|
||||
|
||||
if (visualiserOnly && juce::JUCEApplication::isStandaloneApp()) {
|
||||
addAndMakeVisible(audioInputButton);
|
||||
audioInputButton.setTooltip("Appears red when audio input is being used. Click to enable audio input and close any open audio files.");
|
||||
audioInputButton.setClickingTogglesState(false);
|
||||
audioInputButton.setToggleState(!audioPlayer.isInitialised(), juce::NotificationType::dontSendNotification);
|
||||
audioPlayer.onParserChanged = [this] {
|
||||
juce::MessageManager::callAsync([this] {
|
||||
audioInputButton.setToggleState(!audioPlayer.isInitialised(), juce::NotificationType::dontSendNotification);
|
||||
});
|
||||
};
|
||||
audioInputButton.onClick = [this] {
|
||||
audioProcessor.stopAudioFile();
|
||||
};
|
||||
}
|
||||
|
||||
addAndMakeVisible(audioPlayer);
|
||||
|
||||
audioPlayer.addMouseListener(static_cast<juce::Component*>(this), true);
|
||||
|
||||
openGLContext.setRenderer(this);
|
||||
openGLContext.attachTo(*this);
|
||||
|
||||
|
@ -134,6 +150,13 @@ VisualiserComponent::~VisualiserComponent() {
|
|||
});
|
||||
}
|
||||
|
||||
void VisualiserComponent::setFullScreen(bool fullScreen) {
|
||||
this->fullScreen = fullScreen;
|
||||
hideButtonRow = false;
|
||||
setMouseCursor(juce::MouseCursor::PointingHandCursor);
|
||||
resized();
|
||||
}
|
||||
|
||||
void VisualiserComponent::setFullScreenCallback(std::function<void(FullScreenMode)> callback) {
|
||||
fullScreenCallback = callback;
|
||||
}
|
||||
|
@ -146,13 +169,22 @@ void VisualiserComponent::enableFullScreen() {
|
|||
}
|
||||
|
||||
void VisualiserComponent::mouseDoubleClick(const juce::MouseEvent& event) {
|
||||
enableFullScreen();
|
||||
if (event.originalComponent == this) {
|
||||
enableFullScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType lock(samplesLock);
|
||||
|
||||
// copy the points before applying effects
|
||||
audioOutputBuffer.setSize(2, points.size(), false, true, true);
|
||||
for (int i = 0; i < points.size(); ++i) {
|
||||
audioOutputBuffer.setSample(0, i, points[i].x);
|
||||
audioOutputBuffer.setSample(1, i, points[i].y);
|
||||
}
|
||||
|
||||
xSamples.clear();
|
||||
ySamples.clear();
|
||||
zSamples.clear();
|
||||
|
@ -186,11 +218,21 @@ void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
|
|||
sampleCount++;
|
||||
}
|
||||
} else {
|
||||
for (auto& point : points) {
|
||||
OsciPoint smoothPoint = settings.parameters.smoothEffect->apply(0, point);
|
||||
xSamples.push_back(smoothPoint.x);
|
||||
ySamples.push_back(smoothPoint.y);
|
||||
zSamples.push_back(smoothPoint.z);
|
||||
for (OsciPoint point : points) {
|
||||
for (auto& effect : settings.parameters.audioEffects) {
|
||||
point = effect->apply(0, point);
|
||||
}
|
||||
#if SOSCI_FEATURES
|
||||
if (settings.isFlippedHorizontal()) {
|
||||
point.x = -point.x;
|
||||
}
|
||||
if (settings.isFlippedVertical()) {
|
||||
point.y = -point.y;
|
||||
}
|
||||
#endif
|
||||
xSamples.push_back(point.x);
|
||||
ySamples.push_back(point.y);
|
||||
zSamples.push_back(point.z);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,7 +272,7 @@ void VisualiserComponent::runTask(const std::vector<OsciPoint>& points) {
|
|||
|
||||
// this just triggers a repaint
|
||||
triggerAsyncUpdate();
|
||||
// wait for rendering to complete
|
||||
// wait for rendering on the OpenGLRenderer thread to complete
|
||||
renderingSemaphore.acquire();
|
||||
}
|
||||
|
||||
|
@ -264,9 +306,49 @@ void VisualiserComponent::setPaused(bool paused) {
|
|||
repaint();
|
||||
}
|
||||
|
||||
void VisualiserComponent::mouseDrag(const juce::MouseEvent& event) {
|
||||
timerId = -1;
|
||||
}
|
||||
|
||||
void VisualiserComponent::mouseMove(const juce::MouseEvent& event) {
|
||||
if (event.getScreenX() == lastMouseX && event.getScreenY() == lastMouseY) {
|
||||
return;
|
||||
}
|
||||
hideButtonRow = false;
|
||||
setMouseCursor(juce::MouseCursor::PointingHandCursor);
|
||||
|
||||
if (fullScreen) {
|
||||
if (!getScreenBounds().removeFromBottom(25).contains(event.getScreenX(), event.getScreenY()) && !event.mods.isLeftButtonDown()) {
|
||||
lastMouseX = event.getScreenX();
|
||||
lastMouseY = event.getScreenY();
|
||||
|
||||
int newTimerId = juce::Random::getSystemRandom().nextInt();
|
||||
timerId = newTimerId;
|
||||
auto pos = event.getScreenPosition();
|
||||
auto parent = this->parent;
|
||||
|
||||
juce::WeakReference<VisualiserComponent> weakRef = this;
|
||||
juce::Timer::callAfterDelay(1000, [this, weakRef, newTimerId, pos, parent]() {
|
||||
if (weakRef) {
|
||||
if (parent == nullptr || parent->child == this) {
|
||||
if (timerId == newTimerId && fullScreen) {
|
||||
hideButtonRow = true;
|
||||
setMouseCursor(juce::MouseCursor::NoCursor);
|
||||
resized();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualiserComponent::mouseDown(const juce::MouseEvent& event) {
|
||||
if (event.mods.isLeftButtonDown() && child == nullptr && !record.getToggleState()) {
|
||||
setPaused(active);
|
||||
if (event.originalComponent == this) {
|
||||
if (event.mods.isLeftButtonDown() && child == nullptr && !record.getToggleState()) {
|
||||
setPaused(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,6 +358,9 @@ bool VisualiserComponent::keyPressed(const juce::KeyPress& key) {
|
|||
fullScreenCallback(FullScreenMode::MAIN_COMPONENT);
|
||||
}
|
||||
return true;
|
||||
} else if (key.isKeyCode(juce::KeyPress::spaceKey)) {
|
||||
setPaused(active);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -412,7 +497,11 @@ void VisualiserComponent::setRecording(bool recording) {
|
|||
|
||||
void VisualiserComponent::resized() {
|
||||
auto area = getLocalBounds();
|
||||
buttonRow = area.removeFromBottom(25);
|
||||
if (fullScreen && hideButtonRow) {
|
||||
buttonRow = area.removeFromBottom(0);
|
||||
} else {
|
||||
buttonRow = area.removeFromBottom(25);
|
||||
}
|
||||
auto buttons = buttonRow;
|
||||
if (parent == nullptr) {
|
||||
fullScreenButton.setBounds(buttons.removeFromRight(30));
|
||||
|
@ -426,6 +515,9 @@ void VisualiserComponent::resized() {
|
|||
} else {
|
||||
settingsButton.setVisible(false);
|
||||
}
|
||||
if (visualiserOnly && juce::JUCEApplication::isStandaloneApp()) {
|
||||
audioInputButton.setBounds(buttons.removeFromRight(30));
|
||||
}
|
||||
#if SOSCI_FEATURES
|
||||
sharedTextureButton.setBounds(buttons.removeFromRight(30));
|
||||
#endif
|
||||
|
@ -501,7 +593,7 @@ void VisualiserComponent::childUpdated() {
|
|||
|
||||
#if SOSCI_FEATURES
|
||||
void VisualiserComponent::initialiseSharedTexture() {
|
||||
sharedTextureSender = sharedTextureManager.addSender("osci-render - " + juce::String(juce::Time::getCurrentTime().toMilliseconds()), renderTexture.width, renderTexture.height);
|
||||
sharedTextureSender = sharedTextureManager.addSender(recordingSettings.getCustomSharedTextureServerName(), renderTexture.width, renderTexture.height);
|
||||
sharedTextureSender->initGL();
|
||||
sharedTextureSender->setSharedTextureId(renderTexture.id);
|
||||
sharedTextureSender->setDrawFunction([this] {
|
||||
|
@ -649,11 +741,7 @@ void VisualiserComponent::renderOpenGL() {
|
|||
}
|
||||
#endif
|
||||
if (recordingAudio) {
|
||||
if (settings.isSweepEnabled()) {
|
||||
audioRecorder.audioThreadCallback(ySamples, ySamples);
|
||||
} else {
|
||||
audioRecorder.audioThreadCallback(xSamples, ySamples);
|
||||
}
|
||||
audioRecorder.audioThreadCallback(audioOutputBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -740,7 +828,8 @@ void VisualiserComponent::setupTextures() {
|
|||
blur4Texture = makeTexture(128, 128);
|
||||
renderTexture = makeTexture(1024, 1024);
|
||||
|
||||
screenTexture = createScreenTexture();
|
||||
screenOpenGLTexture.loadImage(emptyScreenImage);
|
||||
screenTexture = { screenOpenGLTexture.getTextureID(), screenTextureImage.getWidth(), screenTextureImage.getHeight() };
|
||||
|
||||
#if SOSCI_FEATURES
|
||||
glowTexture = makeTexture(512, 512);
|
||||
|
@ -901,7 +990,6 @@ void VisualiserComponent::drawLine(const std::vector<float>& xPoints, const std:
|
|||
|
||||
setAdditiveBlending();
|
||||
|
||||
// TODO: need to add the last point from the previous frame to the start of the frame so they connect?
|
||||
int nPoints = xPoints.size();
|
||||
|
||||
// Without this, there's an access violation that seems to occur only on some systems
|
||||
|
@ -1029,7 +1117,11 @@ void VisualiserComponent::drawCRT() {
|
|||
setShader(outputShader.get());
|
||||
outputShader->setUniform("uExposure", 0.25f);
|
||||
outputShader->setUniform("uLineSaturation", (float) settings.getLineSaturation());
|
||||
#if SOSCI_FEATURES
|
||||
outputShader->setUniform("uScreenSaturation", (float) settings.getScreenSaturation());
|
||||
#else
|
||||
outputShader->setUniform("uScreenSaturation", 1.0f);
|
||||
#endif
|
||||
outputShader->setUniform("uNoise", (float) settings.getNoise());
|
||||
outputShader->setUniform("uRandom", juce::Random::getSystemRandom().nextFloat());
|
||||
outputShader->setUniform("uGlow", (float) settings.getGlow());
|
||||
|
@ -1102,12 +1194,16 @@ Texture VisualiserComponent::createScreenTexture() {
|
|||
} else {
|
||||
screenOpenGLTexture.loadImage(emptyScreenImage);
|
||||
}
|
||||
checkGLErrors(__FILE__, __LINE__);
|
||||
Texture texture = { screenOpenGLTexture.getTextureID(), screenTextureImage.getWidth(), screenTextureImage.getHeight() };
|
||||
|
||||
if (screenOverlay == ScreenOverlay::Graticule || screenOverlay == ScreenOverlay::SmudgedGraticule) {
|
||||
activateTargetTexture(texture);
|
||||
checkGLErrors(__FILE__, __LINE__);
|
||||
setNormalBlending();
|
||||
checkGLErrors(__FILE__, __LINE__);
|
||||
setShader(simpleShader.get());
|
||||
checkGLErrors(__FILE__, __LINE__);
|
||||
glColorMask(true, false, false, true);
|
||||
|
||||
std::vector<float> data;
|
||||
|
@ -1153,9 +1249,9 @@ Texture VisualiserComponent::createScreenTexture() {
|
|||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * data.size(), data.data(), GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(glGetAttribLocation(simpleShader->getProgramID(), "vertexPosition"), 2, GL_FLOAT, GL_FALSE, 0, nullptr);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
simpleShader->setUniform("colour", 0.01f, 0.1f, 0.01f, 1.0f);
|
||||
simpleShader->setUniform("colour", 0.01f, 0.05f, 0.01f, 1.0f);
|
||||
glLineWidth(2.0f);
|
||||
glDrawArrays(GL_LINES, 0, data.size());
|
||||
glDrawArrays(GL_LINES, 0, data.size() / 2);
|
||||
glBindTexture(GL_TEXTURE_2D, targetTexture.value().id);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
}
|
||||
|
@ -1163,7 +1259,7 @@ Texture VisualiserComponent::createScreenTexture() {
|
|||
return texture;
|
||||
}
|
||||
|
||||
void VisualiserComponent::checkGLErrors(const juce::String& location) {
|
||||
void VisualiserComponent::checkGLErrors(juce::String file, int line) {
|
||||
using namespace juce::gl;
|
||||
|
||||
GLenum error;
|
||||
|
@ -1179,7 +1275,7 @@ void VisualiserComponent::checkGLErrors(const juce::String& location) {
|
|||
case GL_INVALID_FRAMEBUFFER_OPERATION: errorMessage = "GL_INVALID_FRAMEBUFFER_OPERATION"; break;
|
||||
default: errorMessage = "Unknown OpenGL error"; break;
|
||||
}
|
||||
DBG("OpenGL error at " + location + ": " + errorMessage);
|
||||
DBG("OpenGL error at " + file + ":" + juce::String(line) + " - " + errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1217,7 +1313,7 @@ void VisualiserComponent::renderScope(const std::vector<float>& xPoints, const s
|
|||
renderScale = (float)openGLContext.getRenderingScale();
|
||||
|
||||
drawLineTexture(xPoints, yPoints, zPoints);
|
||||
checkGLErrors("drawLineTexture");
|
||||
checkGLErrors(__FILE__, __LINE__);
|
||||
drawCRT();
|
||||
checkGLErrors("drawCRT");
|
||||
checkGLErrors(__FILE__, __LINE__);
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ public:
|
|||
std::function<void()> closeSettings;
|
||||
|
||||
void enableFullScreen();
|
||||
void setFullScreen(bool fullScreen);
|
||||
void setFullScreenCallback(std::function<void(FullScreenMode)> callback);
|
||||
void mouseDoubleClick(const juce::MouseEvent& event) override;
|
||||
void resized() override;
|
||||
|
@ -60,6 +61,8 @@ public:
|
|||
void runTask(const std::vector<OsciPoint>& points) override;
|
||||
void stopTask() override;
|
||||
void setPaused(bool paused);
|
||||
void mouseDrag(const juce::MouseEvent& event) override;
|
||||
void mouseMove(const juce::MouseEvent& event) override;
|
||||
void mouseDown(const juce::MouseEvent& event) override;
|
||||
bool keyPressed(const juce::KeyPress& key) override;
|
||||
void handleAsyncUpdate() override;
|
||||
|
@ -87,6 +90,7 @@ private:
|
|||
SvgButton fullScreenButton{ "fullScreen", BinaryData::fullscreen_svg, juce::Colours::white, juce::Colours::white };
|
||||
SvgButton popOutButton{ "popOut", BinaryData::open_in_new_svg, juce::Colours::white, juce::Colours::white };
|
||||
SvgButton settingsButton{ "settings", BinaryData::cog_svg, juce::Colours::white, juce::Colours::white };
|
||||
SvgButton audioInputButton{ "audioInput", BinaryData::microphone_svg, juce::Colours::white, juce::Colours::red };
|
||||
|
||||
#if SOSCI_FEATURES
|
||||
SvgButton sharedTextureButton{ "sharedTexture", BinaryData::spout_svg, juce::Colours::white, juce::Colours::red };
|
||||
|
@ -94,6 +98,11 @@ private:
|
|||
SharedTextureSender* sharedTextureSender = nullptr;
|
||||
#endif
|
||||
|
||||
int lastMouseX = 0;
|
||||
int lastMouseY = 0;
|
||||
int timerId = 0;
|
||||
bool hideButtonRow = false;
|
||||
bool fullScreen = false;
|
||||
std::function<void(FullScreenMode)> fullScreenCallback;
|
||||
|
||||
VisualiserSettings& settings;
|
||||
|
@ -170,6 +179,7 @@ private:
|
|||
|
||||
juce::CriticalSection samplesLock;
|
||||
long sampleCount = 0;
|
||||
juce::AudioBuffer<float> audioOutputBuffer;
|
||||
std::vector<float> xSamples{2};
|
||||
std::vector<float> ySamples{2};
|
||||
std::vector<float> zSamples{2};
|
||||
|
@ -228,7 +238,7 @@ private:
|
|||
juce::OpenGLShaderProgram* currentShader;
|
||||
|
||||
float fadeAmount;
|
||||
ScreenOverlay screenOverlay = settings.getScreenOverlay();
|
||||
ScreenOverlay screenOverlay = ScreenOverlay::MAX;
|
||||
|
||||
const double RESAMPLE_RATIO = 6.0;
|
||||
double sampleRate = -1;
|
||||
|
@ -256,7 +266,7 @@ private:
|
|||
void drawLine(const std::vector<float>& xPoints, const std::vector<float>& yPoints, const std::vector<float>& zPoints);
|
||||
void fade();
|
||||
void drawCRT();
|
||||
void checkGLErrors(const juce::String& location);
|
||||
void checkGLErrors(juce::String file, int line);
|
||||
void viewportChanged(juce::Rectangle<int> area);
|
||||
|
||||
void renderScope(const std::vector<float>& xPoints, const std::vector<float>& yPoints, const std::vector<float>& zPoints);
|
||||
|
@ -269,11 +279,14 @@ private:
|
|||
juce::File audioFile;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserComponent)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE(VisualiserComponent)
|
||||
};
|
||||
|
||||
class VisualiserWindow : public juce::DocumentWindow {
|
||||
public:
|
||||
VisualiserWindow(juce::String name, VisualiserComponent* parent) : parent(parent), wasPaused(!parent->active), juce::DocumentWindow(name, juce::Colours::black, juce::DocumentWindow::TitleBarButtons::allButtons) {}
|
||||
VisualiserWindow(juce::String name, VisualiserComponent* parent) : parent(parent), wasPaused(!parent->active), juce::DocumentWindow(name, juce::Colours::black, juce::DocumentWindow::TitleBarButtons::allButtons) {
|
||||
setAlwaysOnTop(true);
|
||||
}
|
||||
|
||||
void closeButtonPressed() override {
|
||||
parent->setPaused(wasPaused);
|
||||
|
|
|
@ -8,7 +8,6 @@ VisualiserSettings::VisualiserSettings(VisualiserParameters& p, int numChannels)
|
|||
addAndMakeVisible(persistence);
|
||||
addAndMakeVisible(hue);
|
||||
addAndMakeVisible(lineSaturation);
|
||||
addAndMakeVisible(screenSaturation);
|
||||
addAndMakeVisible(focus);
|
||||
addAndMakeVisible(noise);
|
||||
addAndMakeVisible(glow);
|
||||
|
@ -20,6 +19,16 @@ VisualiserSettings::VisualiserSettings(VisualiserParameters& p, int numChannels)
|
|||
addAndMakeVisible(sweepToggle);
|
||||
addAndMakeVisible(screenOverlayLabel);
|
||||
addAndMakeVisible(screenOverlay);
|
||||
#if SOSCI_FEATURES
|
||||
addAndMakeVisible(screenSaturation);
|
||||
addAndMakeVisible(stereo);
|
||||
addAndMakeVisible(xOffset);
|
||||
addAndMakeVisible(yOffset);
|
||||
addAndMakeVisible(xScale);
|
||||
addAndMakeVisible(yScale);
|
||||
addAndMakeVisible(flipVerticalToggle);
|
||||
addAndMakeVisible(flipHorizontalToggle);
|
||||
#endif
|
||||
|
||||
for (int i = 1; i <= parameters.screenOverlay->max; i++) {
|
||||
screenOverlay.addItem(parameters.screenOverlay->getText(parameters.screenOverlay->getNormalisedValue(i)), i);
|
||||
|
@ -33,7 +42,6 @@ VisualiserSettings::VisualiserSettings(VisualiserParameters& p, int numChannels)
|
|||
persistence.setSliderOnValueChange();
|
||||
hue.setSliderOnValueChange();
|
||||
lineSaturation.setSliderOnValueChange();
|
||||
screenSaturation.setSliderOnValueChange();
|
||||
focus.setSliderOnValueChange();
|
||||
noise.setSliderOnValueChange();
|
||||
glow.setSliderOnValueChange();
|
||||
|
@ -53,6 +61,15 @@ VisualiserSettings::VisualiserSettings(VisualiserParameters& p, int numChannels)
|
|||
resized();
|
||||
};
|
||||
|
||||
#if SOSCI_FEATURES
|
||||
screenSaturation.setSliderOnValueChange();
|
||||
stereo.setSliderOnValueChange();
|
||||
xOffset.setSliderOnValueChange();
|
||||
yOffset.setSliderOnValueChange();
|
||||
xScale.setSliderOnValueChange();
|
||||
yScale.setSliderOnValueChange();
|
||||
#endif
|
||||
|
||||
parameters.screenOverlay->addListener(this);
|
||||
}
|
||||
|
||||
|
@ -77,15 +94,26 @@ void VisualiserSettings::resized() {
|
|||
persistence.setBounds(area.removeFromTop(rowHeight));
|
||||
hue.setBounds(area.removeFromTop(rowHeight));
|
||||
lineSaturation.setBounds(area.removeFromTop(rowHeight));
|
||||
#if SOSCI_FEATURES
|
||||
screenSaturation.setBounds(area.removeFromTop(rowHeight));
|
||||
#endif
|
||||
focus.setBounds(area.removeFromTop(rowHeight));
|
||||
noise.setBounds(area.removeFromTop(rowHeight));
|
||||
glow.setBounds(area.removeFromTop(rowHeight));
|
||||
ambient.setBounds(area.removeFromTop(rowHeight));
|
||||
smooth.setBounds(area.removeFromTop(rowHeight));
|
||||
|
||||
#if SOSCI_FEATURES
|
||||
stereo.setBounds(area.removeFromTop(rowHeight));
|
||||
xScale.setBounds(area.removeFromTop(rowHeight));
|
||||
yScale.setBounds(area.removeFromTop(rowHeight));
|
||||
xOffset.setBounds(area.removeFromTop(rowHeight));
|
||||
yOffset.setBounds(area.removeFromTop(rowHeight));
|
||||
flipVerticalToggle.setBounds(area.removeFromTop(rowHeight));
|
||||
flipHorizontalToggle.setBounds(area.removeFromTop(rowHeight));
|
||||
#endif
|
||||
|
||||
upsamplingToggle.setBounds(area.removeFromTop(rowHeight));
|
||||
|
||||
sweepToggle.setBounds(area.removeFromTop(rowHeight));
|
||||
sweepMs.setBounds(area.removeFromTop(rowHeight));
|
||||
triggerValue.setBounds(area.removeFromTop(rowHeight));
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "../LookAndFeel.h"
|
||||
#include "../components/SwitchButton.h"
|
||||
#include "../audio/SmoothEffect.h"
|
||||
#include "../audio/StereoEffect.h"
|
||||
|
||||
enum class ScreenOverlay : int {
|
||||
Empty = 1,
|
||||
|
@ -93,6 +94,66 @@ public:
|
|||
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.");
|
||||
|
||||
#if SOSCI_FEATURES
|
||||
BooleanParameter* flipVertical = new BooleanParameter("Flip Vertical", "flipVertical", VERSION_HINT, false, "Flips the visualiser vertically.");
|
||||
BooleanParameter* flipHorizontal = new BooleanParameter("Flip Horizontal", "flipHorizontal", VERSION_HINT, false, "Flips the visualiser horizontally.");
|
||||
|
||||
std::shared_ptr<Effect> screenSaturationEffect = std::make_shared<Effect>(
|
||||
new EffectParameter(
|
||||
"Screen Saturation",
|
||||
"Controls how saturated the colours are on the oscilloscope screen.",
|
||||
"screenSaturation",
|
||||
VERSION_HINT, 1.0, 0.0, 5.0
|
||||
)
|
||||
);
|
||||
std::shared_ptr<StereoEffect> stereoEffectApplication = std::make_shared<StereoEffect>();
|
||||
std::shared_ptr<Effect> stereoEffect = std::make_shared<Effect>(
|
||||
stereoEffectApplication,
|
||||
new EffectParameter(
|
||||
"Stereo",
|
||||
"Turns mono audio that is uninteresting to visualise into stereo audio that is interesting to visualise.",
|
||||
"stereo",
|
||||
VERSION_HINT, 0.0, 0.0, 1.0
|
||||
)
|
||||
);
|
||||
std::shared_ptr<Effect> scaleEffect = std::make_shared<Effect>(
|
||||
[this](int index, OsciPoint input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
input.scale(values[0].load(), values[1].load(), 1.0);
|
||||
return input;
|
||||
}, std::vector<EffectParameter*>{
|
||||
new EffectParameter(
|
||||
"X Scale",
|
||||
"Controls the horizontal scale of the oscilloscope display.",
|
||||
"xScale",
|
||||
VERSION_HINT, 1.0, -3.0, 3.0
|
||||
),
|
||||
new EffectParameter(
|
||||
"Y Scale",
|
||||
"Controls the vertical scale of the oscilloscope display.",
|
||||
"yScale",
|
||||
VERSION_HINT, 1.0, -3.0, 3.0
|
||||
),
|
||||
});
|
||||
std::shared_ptr<Effect> offsetEffect = std::make_shared<Effect>(
|
||||
[this](int index, OsciPoint input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
input.translate(values[0].load(), values[1].load(), 0.0);
|
||||
return input;
|
||||
}, std::vector<EffectParameter*>{
|
||||
new EffectParameter(
|
||||
"X Offset",
|
||||
"Controls the horizontal position offset of the oscilloscope display.",
|
||||
"xOffset",
|
||||
VERSION_HINT, 0.0, -1.0, 1.0
|
||||
),
|
||||
new EffectParameter(
|
||||
"Y Offset",
|
||||
"Controls the vertical position offset of the oscilloscope display.",
|
||||
"yOffset",
|
||||
VERSION_HINT, 0.0, -1.0, 1.0
|
||||
),
|
||||
});
|
||||
#endif
|
||||
|
||||
std::shared_ptr<Effect> persistenceEffect = std::make_shared<Effect>(
|
||||
new EffectParameter(
|
||||
"Persistence",
|
||||
|
@ -125,14 +186,6 @@ public:
|
|||
VERSION_HINT, 1.0, 0.0, 5.0
|
||||
)
|
||||
);
|
||||
std::shared_ptr<Effect> screenSaturationEffect = std::make_shared<Effect>(
|
||||
new EffectParameter(
|
||||
"Screen Saturation",
|
||||
"Controls how saturated the colours are on the oscilloscope screen.",
|
||||
"screenSaturation",
|
||||
VERSION_HINT, 1.0, 0.0, 5.0
|
||||
)
|
||||
);
|
||||
std::shared_ptr<Effect> focusEffect = std::make_shared<Effect>(
|
||||
new EffectParameter(
|
||||
"Focus",
|
||||
|
@ -191,9 +244,39 @@ public:
|
|||
)
|
||||
);
|
||||
|
||||
std::vector<std::shared_ptr<Effect>> effects = {persistenceEffect, hueEffect, intensityEffect, lineSaturationEffect, screenSaturationEffect, focusEffect, noiseEffect, glowEffect, ambientEffect, sweepMsEffect, triggerValueEffect};
|
||||
std::vector<BooleanParameter*> booleans = {upsamplingEnabled, visualiserFullScreen, sweepEnabled};
|
||||
std::vector<IntParameter*> integers = {screenOverlay};
|
||||
std::vector<std::shared_ptr<Effect>> effects = {
|
||||
persistenceEffect,
|
||||
hueEffect,
|
||||
intensityEffect,
|
||||
lineSaturationEffect,
|
||||
focusEffect,
|
||||
noiseEffect,
|
||||
glowEffect,
|
||||
ambientEffect,
|
||||
sweepMsEffect,
|
||||
triggerValueEffect,
|
||||
};
|
||||
std::vector<std::shared_ptr<Effect>> audioEffects = {
|
||||
smoothEffect,
|
||||
#if SOSCI_FEATURES
|
||||
screenSaturationEffect,
|
||||
stereoEffect,
|
||||
scaleEffect,
|
||||
offsetEffect,
|
||||
#endif
|
||||
};
|
||||
std::vector<BooleanParameter*> booleans = {
|
||||
upsamplingEnabled,
|
||||
visualiserFullScreen,
|
||||
sweepEnabled,
|
||||
#if SOSCI_FEATURES
|
||||
flipVertical,
|
||||
flipHorizontal,
|
||||
#endif
|
||||
};
|
||||
std::vector<IntParameter*> integers = {
|
||||
screenOverlay,
|
||||
};
|
||||
};
|
||||
|
||||
class VisualiserSettings : public juce::Component, public juce::AudioProcessorParameter::Listener {
|
||||
|
@ -222,9 +305,19 @@ public:
|
|||
return parameters.lineSaturationEffect->getActualValue();
|
||||
}
|
||||
|
||||
#if SOSCI_FEATURES
|
||||
double getScreenSaturation() {
|
||||
return parameters.screenSaturationEffect->getActualValue();
|
||||
}
|
||||
|
||||
bool isFlippedVertical() {
|
||||
return parameters.flipVertical->getBoolValue();
|
||||
}
|
||||
|
||||
bool isFlippedHorizontal() {
|
||||
return parameters.flipHorizontal->getBoolValue();
|
||||
}
|
||||
#endif
|
||||
|
||||
double getFocus() {
|
||||
return parameters.focusEffect->getActualValue() / 100;
|
||||
|
@ -270,7 +363,6 @@ private:
|
|||
EffectComponent persistence{*parameters.persistenceEffect};
|
||||
EffectComponent hue{*parameters.hueEffect};
|
||||
EffectComponent lineSaturation{*parameters.lineSaturationEffect};
|
||||
EffectComponent screenSaturation{*parameters.screenSaturationEffect};
|
||||
EffectComponent focus{*parameters.focusEffect};
|
||||
EffectComponent noise{*parameters.noiseEffect};
|
||||
EffectComponent glow{*parameters.glowEffect};
|
||||
|
@ -285,6 +377,18 @@ private:
|
|||
jux::SwitchButton upsamplingToggle{parameters.upsamplingEnabled};
|
||||
jux::SwitchButton sweepToggle{parameters.sweepEnabled};
|
||||
|
||||
#if SOSCI_FEATURES
|
||||
EffectComponent screenSaturation{*parameters.screenSaturationEffect};
|
||||
EffectComponent stereo{*parameters.stereoEffect};
|
||||
EffectComponent xScale{*parameters.scaleEffect, 0};
|
||||
EffectComponent yScale{*parameters.scaleEffect, 1};
|
||||
EffectComponent xOffset{*parameters.offsetEffect, 0};
|
||||
EffectComponent yOffset{*parameters.offsetEffect, 1};
|
||||
|
||||
jux::SwitchButton flipVerticalToggle{parameters.flipVertical};
|
||||
jux::SwitchButton flipHorizontalToggle{parameters.flipHorizontal};
|
||||
#endif
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserSettings)
|
||||
};
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ elif [ "$OS" = "mac" ]; then
|
|||
PROJUCER_OS="osx"
|
||||
fi
|
||||
|
||||
# curl -s -S -L "https://api.juce.com/api/v1/download/juce/latest/$PROJUCER_OS" -o Projucer.zip
|
||||
curl -s -S -L "https://github.com/juce-framework/JUCE/releases/download/8.0.4/juce-8.0.4-$PROJUCER_OS.zip" -o Projucer.zip
|
||||
curl -s -S -L "https://api.juce.com/api/v1/download/juce/latest/$PROJUCER_OS" -o Projucer.zip
|
||||
# curl -s -S -L "https://github.com/juce-framework/JUCE/releases/download/8.0.4/juce-8.0.4-$PROJUCER_OS.zip" -o Projucer.zip
|
||||
unzip Projucer.zip
|
||||
|
||||
# Set Projucer path based on OS
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginCharacteristicsValue="pluginWantsMidiIn"
|
||||
pluginManufacturer="jameshball" aaxIdentifier="sh.ball.oscirender"
|
||||
cppLanguageStandard="20" projectLineFeed=" " headerPath="./include"
|
||||
version="2.4.2" companyName="James H Ball" companyWebsite="https://osci-render.com"
|
||||
version="2.4.3.3" companyName="James H Ball" companyWebsite="https://osci-render.com"
|
||||
companyEmail="james@ball.sh" defines="NOMINMAX=1 INTERNET_FLAG_NO_AUTO_REDIRECT=0 SOSCI_FEATURES=1"
|
||||
pluginAUMainType="'aumf'">
|
||||
<MAINGROUP id="j5Ge2T" name="osci-render">
|
||||
|
@ -124,6 +124,9 @@
|
|||
<FILE id="iUEfwT" name="SmoothEffect.cpp" compile="1" resource="0"
|
||||
file="Source/audio/SmoothEffect.cpp"/>
|
||||
<FILE id="Vwjht7" name="SmoothEffect.h" compile="0" resource="0" file="Source/audio/SmoothEffect.h"/>
|
||||
<FILE id="agQEM3" name="StereoEffect.cpp" compile="1" resource="0"
|
||||
file="Source/audio/StereoEffect.cpp"/>
|
||||
<FILE id="WACNMe" name="StereoEffect.h" compile="0" resource="0" file="Source/audio/StereoEffect.h"/>
|
||||
<FILE id="VBskjq" name="VectorCancellingEffect.cpp" compile="1" resource="0"
|
||||
file="Source/audio/VectorCancellingEffect.cpp"/>
|
||||
<FILE id="Be21D0" name="VectorCancellingEffect.h" compile="0" resource="0"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<JUCERPROJECT id="HH2E72" name="sosci" projectType="audioplug" useAppConfig="0"
|
||||
addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginManufacturer="jameshball"
|
||||
aaxIdentifier="sh.ball.sosci" cppLanguageStandard="20" projectLineFeed=" "
|
||||
headerPath="./include" version="1.0.2" companyName="James H Ball"
|
||||
headerPath="./include" version="1.0.3.3" companyName="James H Ball"
|
||||
companyWebsite="https://osci-render.com" companyEmail="james@ball.sh"
|
||||
defines="NOMINMAX=1 INTERNET_FLAG_NO_AUTO_REDIRECT=0 SOSCI_FEATURES=1"
|
||||
pluginManufacturerCode="Jhba" pluginCode="Sosc" pluginAUMainType="'aufx'">
|
||||
|
@ -108,6 +108,9 @@
|
|||
<FILE id="vj5mRC" name="SmoothEffect.cpp" compile="1" resource="0"
|
||||
file="Source/audio/SmoothEffect.cpp"/>
|
||||
<FILE id="GSnwBW" name="SmoothEffect.h" compile="0" resource="0" file="Source/audio/SmoothEffect.h"/>
|
||||
<FILE id="Iq5LKp" name="StereoEffect.cpp" compile="1" resource="0"
|
||||
file="Source/audio/StereoEffect.cpp"/>
|
||||
<FILE id="jq3EXV" name="StereoEffect.h" compile="0" resource="0" file="Source/audio/StereoEffect.h"/>
|
||||
</GROUP>
|
||||
<GROUP id="{CD81913A-7F0E-5898-DA77-5EBEB369DEB1}" name="components">
|
||||
<FILE id="URb7Ok" name="AboutComponent.cpp" compile="1" resource="0"
|
||||
|
|
Ładowanie…
Reference in New Issue