Add options to remove smudges and grid, and make sample rate agnostic

pull/249/head
James H Ball 2024-08-13 19:19:05 +01:00 zatwierdzone przez James H Ball
rodzic 96fb3efbb0
commit e25d8e6d71
14 zmienionych plików z 137 dodań i 75 usunięć

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.1 KiB

Wyświetl plik

@ -93,22 +93,13 @@
sweepOn : false,
sweepMsDiv : 1,
sweepTriggerValue : 0,
signalGeneratorOn : false,
mainGain : 0.0,
exposureStops : 0.0,
audioVolume : 1.0,
hue : 125,
freezeImage: false,
disableFilter: false,
aValue : 1.0,
aExponent : 0.0,
bValue : 1.0,
bExponent :0.0,
invertXY : false,
grid : true,
noise : true,
persistence : 0,
xExpression : "sin(2*PI*a*t)*cos(2*PI*b*t)",
yExpression : "cos(2*PI*a*t)*cos(2*PI*b*t)",
}
Number.prototype.toFixedMinus = function(k)
@ -142,6 +133,8 @@
let isDebug = true;
let paused = false;
let openInAnotherWindow = false;
let externalSampleRate = 96000;
let externalBufferSize = 1920;
</script>
@ -185,7 +178,6 @@
});
const isOverlayFn = Juce.getNativeFunction("isOverlay");
isOverlayFn().then(overlay => {
if (overlay) {
popout.remove();

Wyświetl plik

@ -6,9 +6,8 @@ var AudioSystem =
init : function (bufferSize)
{
this.sampleRate = 96000;
this.bufferSize = bufferSize;
this.timePerSample = 1/this.sampleRate;
this.timePerSample = 1/externalSampleRate;
this.oldXSamples = new Float32Array(this.bufferSize);
this.oldYSamples = new Float32Array(this.bufferSize);
this.smoothedXSamples = new Float32Array(Filter.nSmoothedSamples);
@ -231,7 +230,7 @@ var Render =
this.blur2Texture = this.makeTexture(256, 256);
this.blur3Texture = this.makeTexture(32, 32);
this.blur4Texture = this.makeTexture(32, 32);
this.screenTexture = this.loadTexture('noise.jpg');
this.screenTexture = Render.loadTexture('noise.jpg');
},
onResize : function()
@ -694,6 +693,16 @@ function doScriptProcessor(event) {
controls.exposureStops = settings.intensity;
controls.persistence = settings.persistence;
controls.hue = settings.hue;
if (controls.grid !== settings.graticule) {
controls.grid = settings.graticule;
const image = controls.noise ? 'noise.jpg' : 'empty.jpg';
Render.screenTexture = Render.loadTexture(image);
}
if (controls.noise !== settings.smudges) {
controls.noise = settings.smudges;
const image = controls.noise ? 'noise.jpg' : 'empty.jpg';
Render.screenTexture = Render.loadTexture(image);
}
});
if (controls.sweepOn) {
@ -743,14 +752,25 @@ function doScriptProcessor(event) {
function drawCRTFrame(timeStamp) {
Render.drawCRT();
}
var xSamples = new Float32Array(externalBufferSize);
var ySamples = new Float32Array(externalBufferSize);
const bufferSizeFn = Juce.getNativeFunction("bufferSize");
Juce.getNativeFunction("bufferSize")().then(bufferSize => {
externalBufferSize = bufferSize;
Juce.getNativeFunction("sampleRate")().then(sampleRate => {
externalSampleRate = sampleRate;
xSamples = new Float32Array(externalBufferSize);
ySamples = new Float32Array(externalBufferSize);
Render.init();
Filter.init(externalBufferSize, 8, 6);
AudioSystem.init(externalBufferSize);
Render.setupArrays(Filter.nSmoothedSamples);
AudioSystem.startSound();
requestAnimationFrame(drawCRTFrame);
Controls.setupControls();
});
});
var xSamples = new Float32Array(1920);
var ySamples = new Float32Array(1920);
Render.init();
Filter.init(1920, 8, 6);
AudioSystem.init(1920);
Render.setupArrays(Filter.nSmoothedSamples);
AudioSystem.startSound();
requestAnimationFrame(drawCRTFrame);
Controls.setupControls();

Wyświetl plik

@ -354,3 +354,21 @@ juce::MouseCursor OscirenderLookAndFeel::getMouseCursorFor(juce::Component& comp
}
return juce::LookAndFeel_V4::getMouseCursorFor(component);
}
void OscirenderLookAndFeel::drawCallOutBoxBackground(juce::CallOutBox& box, juce::Graphics& g, const juce::Path& path, juce::Image& cachedImage) {
if (cachedImage.isNull()) {
cachedImage = juce::Image(juce::Image::ARGB, box.getWidth(), box.getHeight(), true);
juce::Graphics g2(cachedImage);
juce::DropShadow(juce::Colours::black.withAlpha(0.7f), 8, juce::Point<int>(0, 2)).drawForPath(g2, path);
}
g.setColour(juce::Colours::black);
g.drawImageAt(cachedImage, 0, 0);
g.setColour(Colours::dark);
g.fillPath(path);
g.setColour(juce::Colours::black);
g.strokePath(path, juce::PathStrokeType(1.0f));
}

Wyświetl plik

@ -96,4 +96,5 @@ public:
void drawCornerResizer(juce::Graphics&, int w, int h, bool isMouseOver, bool isMouseDragging) override;
void drawToggleButton(juce::Graphics&, juce::ToggleButton&, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override;
juce::MouseCursor getMouseCursorFor(juce::Component& component) override;
void drawCallOutBoxBackground(juce::CallOutBox& box, juce::Graphics& g, const juce::Path& path, juce::Image& cachedImage) override;
};

Wyświetl plik

@ -175,6 +175,8 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
booleanParameters.push_back(animateFrames);
booleanParameters.push_back(animationSyncBPM);
booleanParameters.push_back(invertImage);
booleanParameters.push_back(graticuleEnabled);
booleanParameters.push_back(smudgesEnabled);
for (auto parameter : booleanParameters) {
addParameter(parameter);

Wyświetl plik

@ -159,12 +159,14 @@ public:
);
// visualiser settings
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, true, "Adds a subtle layer of dirt/smudges to the oscilloscope display to make it look more realistic.");
std::shared_ptr<Effect> persistenceEffect = std::make_shared<Effect>(
new EffectParameter(
"Persistence",
"Controls how long the light glows for on the oscilloscope display.",
"persistence",
VERSION_HINT, 0.0, -1.0, 1.0
VERSION_HINT, 0.0, -1.0, 2.0
)
);
std::shared_ptr<Effect> hueEffect = std::make_shared<Effect>(
@ -180,7 +182,7 @@ public:
"Intensity",
"Controls how bright the light glows for on the oscilloscope display.",
"intensity",
VERSION_HINT, 0.0, -2.0, 2.0
VERSION_HINT, 1.0, -2.0, 2.0
)
);

Wyświetl plik

@ -37,13 +37,9 @@ EffectComponent::EffectComponent(OscirenderAudioProcessor& p, Effect& effect, in
rangeButton.setTooltip("Click to change the range of the slider.");
rangeButton.onClick = [this] {
juce::PopupMenu menu;
menu.addCustomItem(1, popupLabel, 200, 30, false);
menu.addCustomItem(2, min, 160, 40, false);
menu.addCustomItem(3, max, 160, 40, false);
menu.showMenuAsync(juce::PopupMenu::Options(), [this](int result) {});
auto range = std::make_unique<EffectRangeComponent>(this);
range->setSize(200, 110);
auto& myBox = juce::CallOutBox::launchAsynchronously(std::move(range), rangeButton.getScreenBounds(), nullptr);
};
effect.addListener(index, this);
@ -98,35 +94,6 @@ void EffectComponent::setupComponent() {
effect.parameters[index]->lfoRate->setUnnormalisedValueNotifyingHost(lfoSlider.getValue());
};
}
min.textBox.setValue(parameter->min, juce::dontSendNotification);
max.textBox.setValue(parameter->max, juce::dontSendNotification);
min.textBox.onValueChange = [this]() {
double minValue = min.textBox.getValue();
double maxValue = max.textBox.getValue();
if (minValue >= maxValue) {
minValue = maxValue - effect.parameters[index]->step;
min.textBox.setValue(minValue, juce::dontSendNotification);
}
effect.parameters[index]->min = minValue;
slider.setRange(effect.parameters[index]->min, effect.parameters[index]->max, effect.parameters[index]->step);
};
max.textBox.onValueChange = [this]() {
double minValue = min.textBox.getValue();
double maxValue = max.textBox.getValue();
if (maxValue <= minValue) {
maxValue = minValue + effect.parameters[index]->step;
max.textBox.setValue(maxValue, juce::dontSendNotification);
}
effect.parameters[index]->max = maxValue;
slider.setRange(effect.parameters[index]->min, effect.parameters[index]->max, effect.parameters[index]->step);
};
popupLabel.setText(parameter->name + " Range", juce::dontSendNotification);
popupLabel.setJustificationType(juce::Justification::centred);
popupLabel.setFont(juce::Font(14.0f, juce::Font::bold));
if (sidechainEnabled) {
sidechainButton->onClick = [this] {

Wyświetl plik

@ -25,6 +25,58 @@ public:
Effect& effect;
int index = 0;
juce::ComboBox lfo;
class EffectRangeComponent : public juce::Component {
public:
EffectRangeComponent(EffectComponent* parent) {
addAndMakeVisible(popupLabel);
addAndMakeVisible(min);
addAndMakeVisible(max);
EffectParameter* parameter = parent->effect.parameters[parent->index];
min.textBox.setValue(parameter->min, juce::dontSendNotification);
max.textBox.setValue(parameter->max, juce::dontSendNotification);
min.textBox.onValueChange = [this, parameter, parent]() {
double minValue = min.textBox.getValue();
double maxValue = max.textBox.getValue();
if (minValue >= maxValue) {
minValue = maxValue - parameter->step;
min.textBox.setValue(minValue, juce::dontSendNotification);
}
parameter->min = minValue;
parent->slider.setRange(parameter->min, parameter->max, parameter->step);
};
max.textBox.onValueChange = [this, parameter, parent]() {
double minValue = min.textBox.getValue();
double maxValue = max.textBox.getValue();
if (maxValue <= minValue) {
maxValue = minValue + parameter->step;
max.textBox.setValue(maxValue, juce::dontSendNotification);
}
parameter->max = maxValue;
parent->slider.setRange(parameter->min, parameter->max, parameter->step);
};
popupLabel.setText(parameter->name + " Range", juce::dontSendNotification);
popupLabel.setJustificationType(juce::Justification::centred);
popupLabel.setFont(juce::Font(14.0f, juce::Font::bold));
}
void resized() override {
auto bounds = getLocalBounds();
popupLabel.setBounds(bounds.removeFromTop(30));
min.setBounds(bounds.removeFromTop(40));
max.setBounds(bounds.removeFromTop(40));
}
private:
juce::Label popupLabel;
LabelledTextBox min{"Min"};
LabelledTextBox max{"Max"};
};
private:
const int TEXT_BOX_WIDTH = 70;
@ -41,10 +93,7 @@ private:
std::unique_ptr<SvgButton> sidechainButton;
juce::Label popupLabel;
juce::Label label;
LabelledTextBox min{"Min"};
LabelledTextBox max{"Max"};
SvgButton rangeButton = { "rangeButton", BinaryData::range_svg, juce::Colours::white };

Wyświetl plik

@ -9,7 +9,7 @@ VisualiserComponent::VisualiserComponent(OscirenderAudioProcessor& p, Visualiser
settingsWindow.setResizable(false, false);
settingsWindow.setUsingNativeTitleBar(true);
settings.setLookAndFeel(&getLookAndFeel());
settings.setSize(550, 130);
settings.setSize(550, 200);
settingsWindow.setContentNonOwned(&settings, true);
setMouseCursor(juce::MouseCursor::PointingHandCursor);
@ -250,6 +250,7 @@ void VisualiserComponent::paintXY(juce::Graphics& g, juce::Rectangle<float> area
void VisualiserComponent::resetBuffer() {
sampleRate = (int) audioProcessor.currentSampleRate;
tempBuffer = std::vector<float>(2 * sampleRate * BUFFER_LENGTH_SECS);
browser.refresh();
}
void VisualiserComponent::resized() {

Wyświetl plik

@ -115,7 +115,11 @@ private:
openSettings();
})
.withNativeFunction("isDebug", [this](auto& var, auto complete) {
complete((bool) JUCE_DEBUG);
#if JUCE_DEBUG
complete(true);
#else
complete(false);
#endif
})
.withNativeFunction("isOverlay", [this](auto& var, auto complete) {
complete(parent != nullptr);
@ -126,6 +130,12 @@ private:
.withNativeFunction("getSettings", [this](auto& var, auto complete) {
complete(settings.getSettings());
})
.withNativeFunction("bufferSize", [this](auto& var, auto complete) {
complete((int) tempBuffer.size() / 2);
})
.withNativeFunction("sampleRate", [this](auto& var, auto complete) {
complete(sampleRate);
})
);
std::vector<float> tempBuffer;

Wyświetl plik

@ -6,17 +6,12 @@ VisualiserSettings::VisualiserSettings(OscirenderAudioProcessor& p, VisualiserCo
addAndMakeVisible(intensity);
addAndMakeVisible(persistence);
addAndMakeVisible(hue);
addAndMakeVisible(graticuleToggle);
addAndMakeVisible(smudgeToggle);
intensity.setSliderOnValueChange();
persistence.setSliderOnValueChange();
hue.setSliderOnValueChange();
gridToggle.setToggleState(audioProcessor.midiEnabled->getBoolValue(), juce::dontSendNotification);
gridToggle.setTooltip("Enables the oscilloscope graticule.");
gridToggle.onClick = [this]() {
audioProcessor.midiEnabled->setBoolValueNotifyingHost(gridToggle.getToggleState());
};
}
VisualiserSettings::~VisualiserSettings() {}
@ -27,6 +22,8 @@ void VisualiserSettings::resized() {
intensity.setBounds(area.removeFromTop(rowHeight));
persistence.setBounds(area.removeFromTop(rowHeight));
hue.setBounds(area.removeFromTop(rowHeight));
graticuleToggle.setBounds(area.removeFromTop(rowHeight));
smudgeToggle.setBounds(area.removeFromTop(rowHeight));
}
juce::var VisualiserSettings::getSettings() {
@ -34,5 +31,7 @@ juce::var VisualiserSettings::getSettings() {
settings->setProperty("intensity", audioProcessor.intensityEffect->getActualValue());
settings->setProperty("persistence", audioProcessor.persistenceEffect->getActualValue());
settings->setProperty("hue", audioProcessor.hueEffect->getActualValue());
settings->setProperty("graticule", audioProcessor.graticuleEnabled->getBoolValue());
settings->setProperty("smudges", audioProcessor.smudgesEnabled->getBoolValue());
return juce::var(settings);
}

Wyświetl plik

@ -22,8 +22,8 @@ private:
EffectComponent persistence{audioProcessor, *audioProcessor.persistenceEffect};
EffectComponent hue{audioProcessor, *audioProcessor.hueEffect};
jux::SwitchButton gridToggle = { "gridToggle", false };
jux::SwitchButton noiseToggle = { "noiseToggle", false };
jux::SwitchButton graticuleToggle{audioProcessor.graticuleEnabled};
jux::SwitchButton smudgeToggle{audioProcessor.smudgesEnabled};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserSettings)
};

Wyświetl plik

@ -29,6 +29,7 @@
<FILE id="ZEUE5w" name="index.js" compile="0" resource="1" file="Resources/oscilloscope/juce/index.js"/>
<FILE id="hWk293" name="package.json" compile="0" resource="1" file="Resources/oscilloscope/juce/package.json"/>
</GROUP>
<FILE id="qpPhpN" name="empty.jpg" compile="0" resource="1" file="Resources/oscilloscope/empty.jpg"/>
<FILE id="dNtZYs" name="noise.jpg" compile="0" resource="1" file="Resources/oscilloscope/noise.jpg"/>
<FILE id="YPMnjq" name="oscilloscope.html" compile="0" resource="1"
file="Resources/oscilloscope/oscilloscope.html"/>