kopia lustrzana https://github.com/jameshball/osci-render
Get sosci fully working with RGB
rodzic
e6969845dc
commit
f49a29407c
|
|
@ -132,6 +132,7 @@ public:
|
|||
std::function<void()> haltRecording;
|
||||
|
||||
std::atomic<bool> forceDisableBrightnessInput = false;
|
||||
std::atomic<bool> forceDisableRgbInput = false;
|
||||
|
||||
// shouldn't be accessed by audio thread, but needs to persist when GUI is closed
|
||||
// so should only be accessed by message thread
|
||||
|
|
@ -163,6 +164,15 @@ public:
|
|||
protected:
|
||||
|
||||
bool brightnessEnabled = false;
|
||||
bool rgbEnabled = false;
|
||||
|
||||
// Expose flags to GUI thread safely
|
||||
public:
|
||||
bool isBrightnessEnabled() const { return brightnessEnabled; }
|
||||
bool isRgbEnabled() const { return rgbEnabled; }
|
||||
bool getForceDisableBrightnessInput() const { return forceDisableBrightnessInput.load(); }
|
||||
bool getForceDisableRgbInput() const { return forceDisableRgbInput.load(); }
|
||||
protected:
|
||||
|
||||
std::vector<osci::BooleanParameter*> booleanParameters;
|
||||
std::vector<osci::FloatParameter*> floatParameters;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "SosciPluginProcessor.h"
|
||||
#include "SosciPluginEditor.h"
|
||||
|
||||
SosciAudioProcessor::SosciAudioProcessor() : CommonAudioProcessor(BusesProperties().withInput("Input", juce::AudioChannelSet::namedChannelSet(4), true).withOutput("Output", juce::AudioChannelSet::stereo(), true)) {
|
||||
SosciAudioProcessor::SosciAudioProcessor() : CommonAudioProcessor(BusesProperties().withInput("Input", juce::AudioChannelSet::namedChannelSet(5), 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);
|
||||
|
|
@ -36,23 +36,41 @@ void SosciAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::M
|
|||
} else {
|
||||
float x = input.getNumChannels() > 0 ? inputArray[0][sample] : 0.0f;
|
||||
float y = input.getNumChannels() > 1 ? inputArray[1][sample] : 0.0f;
|
||||
float brightness = 1.0f;
|
||||
float zAsBrightnessOrR = 1.0f;
|
||||
if (input.getNumChannels() > 2 && !forceDisableBrightnessInput) {
|
||||
float brightnessChannel = inputArray[2][sample];
|
||||
// Only enable brightness if we actually receive a signal on the brightness channel
|
||||
if (!brightnessEnabled && brightnessChannel > EPSILON) {
|
||||
brightnessEnabled = true;
|
||||
float zChan = inputArray[2][sample];
|
||||
if (!brightnessEnabled && zChan > EPSILON) brightnessEnabled = true;
|
||||
if (brightnessEnabled) zAsBrightnessOrR = zChan;
|
||||
}
|
||||
// RGB detection: if channels 3 or 4 present and not forced off, treat as RGB mode.
|
||||
float gChan = 0.0f, bChan = 0.0f;
|
||||
bool haveG = input.getNumChannels() > 3;
|
||||
bool haveB = input.getNumChannels() > 4;
|
||||
if (!forceDisableRgbInput && (haveG || haveB)) {
|
||||
float gIn = haveG ? inputArray[3][sample] : 0.0f;
|
||||
float bIn = haveB ? inputArray[4][sample] : 0.0f;
|
||||
// Enable RGB only when we actually receive signal
|
||||
if (!rgbEnabled && (std::abs(gIn) > EPSILON || std::abs(bIn) > EPSILON)) {
|
||||
rgbEnabled = true;
|
||||
}
|
||||
if (brightnessEnabled) {
|
||||
brightness = brightnessChannel;
|
||||
if (rgbEnabled) {
|
||||
gChan = gIn;
|
||||
bChan = bIn;
|
||||
}
|
||||
}
|
||||
|
||||
point = { x, y, brightness };
|
||||
|
||||
// Build point:
|
||||
// - In RGB mode: use z as R, and channels 3/4 as G/B
|
||||
// - Otherwise: use z as brightness and leave RGB at defaults
|
||||
if (rgbEnabled && !forceDisableRgbInput) {
|
||||
point = osci::Point(x, y, 1.0f, zAsBrightnessOrR, gChan, bChan);
|
||||
} else {
|
||||
point = osci::Point(x, y, zAsBrightnessOrR);
|
||||
}
|
||||
}
|
||||
|
||||
// no negative brightness
|
||||
point.z = juce::jlimit(0.0, 1.0, point.z);
|
||||
// Clamp brightness
|
||||
point.z = juce::jlimit(0.0, 1.0, point.z);
|
||||
|
||||
for (auto& effect : permanentEffects) {
|
||||
point = effect->apply(sample, point);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public:
|
|||
readHead += buffer.size();
|
||||
}
|
||||
|
||||
return osci::Point(input.x, buffer[readHead].y, input.z);
|
||||
return osci::Point(input.x, buffer[readHead].y, input.z, input.r, input.g, input.b);
|
||||
}
|
||||
|
||||
std::shared_ptr<osci::Effect> build() const override {
|
||||
|
|
|
|||
|
|
@ -106,6 +106,18 @@ void SosciMainMenuBarModel::resetMenuItems() {
|
|||
|
||||
addMenuItem(3, "Force Disable Brightness Input", [&]() {
|
||||
processor.forceDisableBrightnessInput = !processor.forceDisableBrightnessInput;
|
||||
if (processor.forceDisableBrightnessInput) {
|
||||
// Disabling brightness should also disable RGB
|
||||
processor.forceDisableRgbInput = true;
|
||||
}
|
||||
menuItemsChanged();
|
||||
});
|
||||
addMenuItem(3, "Force Disable RGB Input", [&]() {
|
||||
processor.forceDisableRgbInput = !processor.forceDisableRgbInput;
|
||||
if (!processor.forceDisableRgbInput) {
|
||||
// Enabling RGB implies brightness is allowed too
|
||||
processor.forceDisableBrightnessInput = false;
|
||||
}
|
||||
menuItemsChanged();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ uniform vec2 uScale;
|
|||
uniform float uFishEye;
|
||||
uniform sampler2D uScreen; // still sampled for focus/gain texturing, but we'll reduce its influence on colour
|
||||
uniform float uLineHueShift; // 0..1 hue shift for the beam colour
|
||||
uniform float uUseVertexColor; // 1.0 to use per-vertex RGB, 0.0 to use hue-only
|
||||
varying float vSize;
|
||||
varying vec4 uvl;
|
||||
varying vec2 vTexCoord;
|
||||
|
|
@ -51,8 +52,14 @@ void main() {
|
|||
float len = uvl.z;
|
||||
vec2 xy = uvl.xy;
|
||||
float brightness;
|
||||
// Apply hue shift immediately to the incoming colour, before any further colour operations
|
||||
vec3 baseColor = hueShift(vColor, uLineHueShift);
|
||||
// Determine base color: either per-vertex RGB with hue shift, or hue-only using a fixed seed color
|
||||
vec3 baseColor;
|
||||
if (uUseVertexColor > 0.5) {
|
||||
baseColor = hueShift(vColor, uLineHueShift);
|
||||
} else {
|
||||
// Start from a seed color and rotate hue; using a non-primary seed to cover spectrum nicely
|
||||
baseColor = hueShift(vec3(1.0, 0.7, 0.2), uLineHueShift);
|
||||
}
|
||||
baseColor = clamp(baseColor, 0.0, 1.0);
|
||||
|
||||
float sigma = vSize/5.0;
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ VisualiserComponent::VisualiserComponent(
|
|||
|
||||
preRenderCallback = [this] {
|
||||
if (!record.getToggleState()) {
|
||||
updateRenderModeFromProcessor();
|
||||
setResolution(this->recordingSettings.getResolution());
|
||||
setFrameRate(this->recordingSettings.getFrameRate());
|
||||
}
|
||||
|
|
@ -518,6 +519,26 @@ void VisualiserComponent::childUpdated() {
|
|||
}
|
||||
}
|
||||
|
||||
void VisualiserComponent::updateRenderModeFromProcessor() {
|
||||
DBG(juce::String((int) getRenderMode()));
|
||||
// Called on message thread
|
||||
if (!visualiserOnly) {
|
||||
setRenderMode(RenderMode::XY);
|
||||
return;
|
||||
}
|
||||
// Determine based on whether brightness and RGB are enabled and not force-disabled
|
||||
bool brightnessAllowed = !audioProcessor.getForceDisableBrightnessInput();
|
||||
bool rgbAllowed = !audioProcessor.getForceDisableRgbInput();
|
||||
// Prefer RGB if we have 4th/5th channels effectively
|
||||
if (rgbAllowed && audioProcessor.isRgbEnabled()) {
|
||||
setRenderMode(RenderMode::XYRGB);
|
||||
} else if (brightnessAllowed && audioProcessor.isBrightnessEnabled()) {
|
||||
setRenderMode(RenderMode::XYZ);
|
||||
} else {
|
||||
setRenderMode(RenderMode::XY);
|
||||
}
|
||||
}
|
||||
|
||||
#if OSCI_PREMIUM
|
||||
void VisualiserComponent::initialiseSharedTexture() {
|
||||
Texture renderTexture = getRenderTexture();
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ public:
|
|||
bool keyPressed(const juce::KeyPress& key) override;
|
||||
void setRecording(bool recording);
|
||||
void childUpdated();
|
||||
void updateRenderModeFromProcessor();
|
||||
|
||||
VisualiserComponent* parent = nullptr;
|
||||
VisualiserComponent* child = nullptr;
|
||||
|
|
@ -93,6 +94,7 @@ private:
|
|||
int lastMouseX = 0;
|
||||
int lastMouseY = 0;
|
||||
int timerId = 0;
|
||||
int renderModeTimerId = 0;
|
||||
bool hideButtonRow = false;
|
||||
bool fullScreen = false;
|
||||
std::function<void(FullScreenMode)> fullScreenCallback;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ void VisualiserRenderer::runTask(const std::vector<osci::Point> &points) {
|
|||
|
||||
xSamples.clear();
|
||||
ySamples.clear();
|
||||
zSamples.clear();
|
||||
rSamples.clear();
|
||||
gSamples.clear();
|
||||
bSamples.clear();
|
||||
|
|
@ -71,6 +72,7 @@ void VisualiserRenderer::runTask(const std::vector<osci::Point> &points) {
|
|||
|
||||
static double testPhase = 0.0;
|
||||
|
||||
auto mode = renderMode.load();
|
||||
if (parameters.isSweepEnabled()) {
|
||||
double sweepIncrement = getSweepIncrement();
|
||||
long samplesPerSweep = sampleRate * parameters.getSweepSeconds();
|
||||
|
|
@ -96,11 +98,14 @@ void VisualiserRenderer::runTask(const std::vector<osci::Point> &points) {
|
|||
|
||||
xSamples.push_back(sweepPoint.x);
|
||||
ySamples.push_back(sweepPoint.y);
|
||||
|
||||
// Unconditional test pattern colour (ignore incoming point colour)
|
||||
rSamples.push_back(0.0);
|
||||
gSamples.push_back(1.0);
|
||||
bSamples.push_back(0.0);
|
||||
if (mode == RenderMode::XYZ) {
|
||||
zSamples.push_back(sweepPoint.z);
|
||||
} else if (mode == RenderMode::XYRGB) {
|
||||
// colour provided (if not, Z will mirror into r by upstream logic)
|
||||
rSamples.push_back((float) sweepPoint.r);
|
||||
gSamples.push_back((float) sweepPoint.g);
|
||||
bSamples.push_back((float) sweepPoint.b);
|
||||
}
|
||||
|
||||
sampleCount++;
|
||||
}
|
||||
|
|
@ -118,11 +123,13 @@ void VisualiserRenderer::runTask(const std::vector<osci::Point> &points) {
|
|||
|
||||
xSamples.push_back(point.x);
|
||||
ySamples.push_back(point.y);
|
||||
|
||||
// Unconditional test pattern colour (ignore incoming point colour)
|
||||
rSamples.push_back(1.0);
|
||||
gSamples.push_back(0.0);
|
||||
bSamples.push_back(0.0);
|
||||
if (mode == RenderMode::XYZ) {
|
||||
zSamples.push_back(point.z);
|
||||
} else if (mode == RenderMode::XYRGB) {
|
||||
rSamples.push_back((float) point.r);
|
||||
gSamples.push_back((float) point.g);
|
||||
bSamples.push_back((float) point.b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,9 +140,12 @@ void VisualiserRenderer::runTask(const std::vector<osci::Point> &points) {
|
|||
|
||||
smoothedXSamples.resize(newResampledSize);
|
||||
smoothedYSamples.resize(newResampledSize);
|
||||
smoothedRSamples.resize(newResampledSize);
|
||||
smoothedGSamples.resize(newResampledSize);
|
||||
smoothedBSamples.resize(newResampledSize);
|
||||
if (mode == RenderMode::XYZ) smoothedZSamples.resize(newResampledSize);
|
||||
if (mode == RenderMode::XYRGB) {
|
||||
smoothedRSamples.resize(newResampledSize);
|
||||
smoothedGSamples.resize(newResampledSize);
|
||||
smoothedBSamples.resize(newResampledSize);
|
||||
}
|
||||
|
||||
if (parameters.isSweepEnabled()) {
|
||||
// interpolate between sweep values to avoid any artifacts from quickly going from one sweep to the next
|
||||
|
|
@ -154,19 +164,16 @@ void VisualiserRenderer::runTask(const std::vector<osci::Point> &points) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
xResampler.process(xSamples.data(), smoothedXSamples.data(), xSamples.size());
|
||||
xResampler.process(xSamples.data(), smoothedXSamples.data(), (int) xSamples.size());
|
||||
}
|
||||
yResampler.process(ySamples.data(), smoothedYSamples.data(), (int) ySamples.size());
|
||||
if (mode == RenderMode::XYZ) {
|
||||
if (!zSamples.empty()) zResampler.process(zSamples.data(), smoothedZSamples.data(), (int) zSamples.size());
|
||||
} else if (mode == RenderMode::XYRGB) {
|
||||
if (!rSamples.empty()) rResampler.process(rSamples.data(), smoothedRSamples.data(), (int) rSamples.size());
|
||||
if (!gSamples.empty()) gResampler.process(gSamples.data(), smoothedGSamples.data(), (int) gSamples.size());
|
||||
if (!bSamples.empty()) bResampler.process(bSamples.data(), smoothedBSamples.data(), (int) bSamples.size());
|
||||
}
|
||||
yResampler.process(ySamples.data(), smoothedYSamples.data(), ySamples.size());
|
||||
// Legacy: replicate intensity from point.z across RGB until upstream provides distinct channels
|
||||
// When color data available, rSamples/gSamples/bSamples will hold independent values
|
||||
// Colour channels: independent resamplers to avoid cross-channel state pollution that caused
|
||||
// subtle colour discontinuities when a single shared zResampler was reused sequentially.
|
||||
// If residual banding persists, consider:
|
||||
// * adding slight pre-blur (lowpass) to each colour before upsampling
|
||||
// * or using a shared phase accumulator & polyphase kernel to guarantee alignment.
|
||||
rResampler.process(rSamples.data(), smoothedRSamples.data(), rSamples.size());
|
||||
gResampler.process(gSamples.data(), smoothedGSamples.data(), gSamples.size());
|
||||
bResampler.process(bSamples.data(), smoothedBSamples.data(), bSamples.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,6 +190,7 @@ int VisualiserRenderer::prepareTask(double sampleRate, int bufferSize) {
|
|||
this->sampleRate = sampleRate;
|
||||
xResampler.prepare(sampleRate, RESAMPLE_RATIO);
|
||||
yResampler.prepare(sampleRate, RESAMPLE_RATIO);
|
||||
zResampler.prepare(sampleRate, RESAMPLE_RATIO);
|
||||
rResampler.prepare(sampleRate, RESAMPLE_RATIO);
|
||||
gResampler.prepare(sampleRate, RESAMPLE_RATIO);
|
||||
bResampler.prepare(sampleRate, RESAMPLE_RATIO);
|
||||
|
|
@ -528,7 +536,12 @@ void VisualiserRenderer::drawLineTexture(const std::vector<float> &xPoints, cons
|
|||
|
||||
activateTargetTexture(lineTexture);
|
||||
fade();
|
||||
drawLine(xPoints, yPoints, rPoints, gPoints, bPoints);
|
||||
const std::vector<float>* brightness = nullptr;
|
||||
auto mode = renderMode.load();
|
||||
if (mode == RenderMode::XYZ) {
|
||||
brightness = parameters.getUpsamplingEnabled() ? &smoothedZSamples : &zSamples;
|
||||
}
|
||||
drawLine(xPoints, yPoints, brightness, rPoints, gPoints, bPoints, renderMode.load());
|
||||
glBindTexture(GL_TEXTURE_2D, targetTexture.value().id);
|
||||
}
|
||||
|
||||
|
|
@ -653,33 +666,45 @@ void VisualiserRenderer::setNormalBlending() {
|
|||
}
|
||||
|
||||
void VisualiserRenderer::drawLine(const std::vector<float> &xPoints, const std::vector<float> &yPoints,
|
||||
const std::vector<float> &rPoints, const std::vector<float> &gPoints, const std::vector<float> &bPoints) {
|
||||
const std::vector<float> *brightnessPoints,
|
||||
const std::vector<float> &rPoints, const std::vector<float> &gPoints, const std::vector<float> &bPoints,
|
||||
RenderMode mode) {
|
||||
using namespace juce::gl;
|
||||
|
||||
setAdditiveBlending();
|
||||
|
||||
int nPoints = xPoints.size();
|
||||
int nPoints = (int) xPoints.size();
|
||||
|
||||
// Without this, there's an access violation that seems to occur only on some systems
|
||||
std::vector<float> positionData(nPoints * 12);
|
||||
std::vector<float> colorData(nPoints * 12);
|
||||
std::vector<float> colorData;
|
||||
if (mode == RenderMode::XYRGB) colorData.resize(nPoints * 12);
|
||||
|
||||
for (int i = 0; i < nPoints; ++i) {
|
||||
int p = i * 12;
|
||||
float x = xPoints[i];
|
||||
float y = yPoints[i];
|
||||
float r = rPoints[i];
|
||||
float g = gPoints[i];
|
||||
float b = bPoints[i];
|
||||
// Use max channel as base beam intensity but scale to compensate for multi-channel energy
|
||||
float brightness = std::max(std::max(r, g), b);
|
||||
float brightness = 1.0f;
|
||||
if (mode == RenderMode::XYZ) {
|
||||
if (brightnessPoints != nullptr && i < (int) brightnessPoints->size()) brightness = (*brightnessPoints)[i];
|
||||
} else if (mode == RenderMode::XYRGB) {
|
||||
float r = rPoints[i];
|
||||
float g = gPoints[i];
|
||||
float b = bPoints[i];
|
||||
brightness = std::max(r, std::max(g, b));
|
||||
}
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
positionData[p + 3 * k] = x;
|
||||
positionData[p + 3 * k + 1] = y;
|
||||
positionData[p + 3 * k + 2] = brightness;
|
||||
colorData[p + 3 * k] = r;
|
||||
colorData[p + 3 * k + 1] = g;
|
||||
colorData[p + 3 * k + 2] = b;
|
||||
if (mode == RenderMode::XYRGB) {
|
||||
float r = rPoints[i];
|
||||
float g = gPoints[i];
|
||||
float b = bPoints[i];
|
||||
colorData[p + 3 * k] = r;
|
||||
colorData[p + 3 * k + 1] = g;
|
||||
colorData[p + 3 * k + 2] = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -687,9 +712,11 @@ void VisualiserRenderer::drawLine(const std::vector<float> &xPoints, const std::
|
|||
glBufferData(GL_ARRAY_BUFFER, positionData.size() * sizeof(float), positionData.data(), GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, colorData.size() * sizeof(float), colorData.data(), GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
if (mode == RenderMode::XYRGB) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, colorData.size() * sizeof(float), colorData.data(), GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
lineShader->use();
|
||||
GLint aStartLoc = glGetAttribLocation(lineShader->getProgramID(), "aStart");
|
||||
|
|
@ -700,16 +727,20 @@ void VisualiserRenderer::drawLine(const std::vector<float> &xPoints, const std::
|
|||
|
||||
glEnableVertexAttribArray(aStartLoc);
|
||||
glEnableVertexAttribArray(aEndLoc);
|
||||
glEnableVertexAttribArray(aStartColorLoc);
|
||||
glEnableVertexAttribArray(aEndColorLoc);
|
||||
if (mode == RenderMode::XYRGB) {
|
||||
if (aStartColorLoc >= 0) glEnableVertexAttribArray(aStartColorLoc);
|
||||
if (aEndColorLoc >= 0) glEnableVertexAttribArray(aEndColorLoc);
|
||||
}
|
||||
glEnableVertexAttribArray(aIdxLoc);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
|
||||
glVertexAttribPointer(aStartLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glVertexAttribPointer(aEndLoc, 3, GL_FLOAT, GL_FALSE, 0, (void *)(12 * sizeof(float)));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
|
||||
glVertexAttribPointer(aStartColorLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glVertexAttribPointer(aEndColorLoc, 3, GL_FLOAT, GL_FALSE, 0, (void *)(12 * sizeof(float)));
|
||||
if (mode == RenderMode::XYRGB) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
|
||||
if (aStartColorLoc >= 0) glVertexAttribPointer(aStartColorLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
if (aEndColorLoc >= 0) glVertexAttribPointer(aEndColorLoc, 3, GL_FLOAT, GL_FALSE, 0, (void *)(12 * sizeof(float)));
|
||||
}
|
||||
glBindBuffer(GL_ARRAY_BUFFER, quadIndexBuffer);
|
||||
glVertexAttribPointer(aIdxLoc, 1, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
|
|
@ -720,6 +751,7 @@ void VisualiserRenderer::drawLine(const std::vector<float> &xPoints, const std::
|
|||
lineShader->setUniform("uGain", 450.0f / 512.0f);
|
||||
lineShader->setUniform("uInvert", 1.0f);
|
||||
lineShader->setUniform("uLineHueShift", (GLfloat)(parameters.getHue() / 360.0));
|
||||
lineShader->setUniform("uUseVertexColor", mode == RenderMode::XYRGB ? 1.0f : 0.0f);
|
||||
|
||||
float intensity = parameters.getIntensity() * (41000.0f / sampleRate);
|
||||
if (parameters.getUpsamplingEnabled()) {
|
||||
|
|
@ -746,8 +778,10 @@ void VisualiserRenderer::drawLine(const std::vector<float> &xPoints, const std::
|
|||
|
||||
glDisableVertexAttribArray(aStartLoc);
|
||||
glDisableVertexAttribArray(aEndLoc);
|
||||
glDisableVertexAttribArray(aStartColorLoc);
|
||||
glDisableVertexAttribArray(aEndColorLoc);
|
||||
if (mode == RenderMode::XYRGB) {
|
||||
if (aStartColorLoc >= 0) glDisableVertexAttribArray(aStartColorLoc);
|
||||
if (aEndColorLoc >= 0) glDisableVertexAttribArray(aEndColorLoc);
|
||||
}
|
||||
glDisableVertexAttribArray(aIdxLoc);
|
||||
}
|
||||
|
||||
|
|
@ -1036,7 +1070,14 @@ void VisualiserRenderer::renderScope(const std::vector<float> &xPoints, const st
|
|||
|
||||
renderScale = (float)openGLContext.getRenderingScale();
|
||||
|
||||
drawLineTexture(xPoints, yPoints, rPoints, gPoints, bPoints);
|
||||
// Provide dummy colour buffers for non-RGB modes to avoid allocations
|
||||
static std::vector<float> empty;
|
||||
auto mode = renderMode.load();
|
||||
if (mode != RenderMode::XYRGB) {
|
||||
drawLineTexture(xPoints, yPoints, empty, empty, empty);
|
||||
} else {
|
||||
drawLineTexture(xPoints, yPoints, rPoints, gPoints, bPoints);
|
||||
}
|
||||
checkGLErrors(__FILE__, __LINE__);
|
||||
drawCRT();
|
||||
checkGLErrors(__FILE__, __LINE__);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,11 @@ struct Texture {
|
|||
class VisualiserWindow;
|
||||
class VisualiserRenderer : public juce::Component, public osci::AudioBackgroundThread, public juce::OpenGLRenderer, public juce::AsyncUpdater {
|
||||
public:
|
||||
enum class RenderMode : int {
|
||||
XY = 1,
|
||||
XYZ = 2,
|
||||
XYRGB = 3,
|
||||
};
|
||||
VisualiserRenderer(
|
||||
VisualiserParameters ¶meters,
|
||||
osci::AudioBackgroundThreadManager &threadManager,
|
||||
|
|
@ -34,6 +39,9 @@ public:
|
|||
void openGLContextClosing() override;
|
||||
void setResolution(int width);
|
||||
void setFrameRate(double frameRate);
|
||||
// Render mode can be changed from the message thread at any time
|
||||
void setRenderMode(RenderMode mode) { renderMode.store(mode); }
|
||||
RenderMode getRenderMode() const { return renderMode.load(); }
|
||||
|
||||
int getRenderWidth() const { return renderTexture.width; }
|
||||
int getRenderHeight() const { return renderTexture.height; }
|
||||
|
|
@ -82,11 +90,14 @@ private: juce::Rectangle<int> viewportArea;
|
|||
// XYRGB sample buffers (currently RGB derived from legacy Z brightness until full pipeline provides color)
|
||||
std::vector<float> xSamples{2};
|
||||
std::vector<float> ySamples{2};
|
||||
// brightness (Z) channel used only in XYZ mode
|
||||
std::vector<float> zSamples{2};
|
||||
std::vector<float> rSamples{2};
|
||||
std::vector<float> gSamples{2};
|
||||
std::vector<float> bSamples{2};
|
||||
std::vector<float> smoothedXSamples;
|
||||
std::vector<float> smoothedYSamples;
|
||||
std::vector<float> smoothedZSamples;
|
||||
std::vector<float> smoothedRSamples;
|
||||
std::vector<float> smoothedGSamples;
|
||||
std::vector<float> smoothedBSamples;
|
||||
|
|
@ -155,10 +166,12 @@ private: juce::Rectangle<int> viewportArea;
|
|||
double oldSampleRate = -1;
|
||||
chowdsp::ResamplingTypes::LanczosResampler<2048, 8> xResampler;
|
||||
chowdsp::ResamplingTypes::LanczosResampler<2048, 8> yResampler;
|
||||
chowdsp::ResamplingTypes::LanczosResampler<2048, 8> zResampler;
|
||||
// Dedicated colour channel resamplers to maintain independent filter state per channel
|
||||
chowdsp::ResamplingTypes::LanczosResampler<2048, 8> rResampler;
|
||||
chowdsp::ResamplingTypes::LanczosResampler<2048, 8> gResampler;
|
||||
chowdsp::ResamplingTypes::LanczosResampler<2048, 8> bResampler;
|
||||
std::atomic<RenderMode> renderMode { RenderMode::XYRGB };
|
||||
|
||||
void setOffsetAndScale(juce::OpenGLShaderProgram* shader);
|
||||
Texture makeTexture(int width, int height, GLuint textureID = 0);
|
||||
|
|
@ -173,7 +186,9 @@ private: juce::Rectangle<int> viewportArea;
|
|||
void setAdditiveBlending();
|
||||
void setNormalBlending();
|
||||
void drawLine(const std::vector<float>& xPoints, const std::vector<float>& yPoints,
|
||||
const std::vector<float>& rPoints, const std::vector<float>& gPoints, const std::vector<float>& bPoints);
|
||||
const std::vector<float>* brightnessPoints, // optional, used in XY/XYZ
|
||||
const std::vector<float>& rPoints, const std::vector<float>& gPoints, const std::vector<float>& bPoints,
|
||||
RenderMode mode);
|
||||
void fade();
|
||||
void drawCRT();
|
||||
void checkGLErrors(juce::String file, int line);
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue