kopia lustrzana https://github.com/jameshball/osci-render
Add options to remove smudges and grid, and make sample rate agnostic
rodzic
96fb3efbb0
commit
e25d8e6d71
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 1.1 KiB |
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -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] {
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
|
|
@ -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"/>
|
||||
|
|
Ładowanie…
Reference in New Issue