fix(of): clamp postfx + scope4 + wave roll
Context: FX mode rendered pure white (bloom + grain + feedback saturating), sample rate slider was unwired, and there was no combined scope-style view. Approach: - Tone-map (Reinhard soft-knee) + clamp final color in postfx.frag. - Lower default bloom 0.4 -> 0.15 to match the new tone-map. - Wire sampleRateHz_ slider to HantekDevice::setSampleRate (initial + on change) so the rate actually applies. - WaveformVis: ring buffer (16x window) + timebase ms/div + scrollSpeed (0=freeze, >0=roll) + setSampleRate hook. - New Scope4 mode (key 'q'): waveform / spectrogram / polar / lissajous in 2x2 grid. - Also track previously-ignored shaders (postfx, plasma, tunnel) so other machines pick them up after pull.
This commit is contained in:
+7
-2
@@ -10,8 +10,13 @@ launcher/**/xcuserdata/
|
||||
launcher/**/*.xcuserstate
|
||||
launcher/**/Package.resolved
|
||||
|
||||
# openFrameworks
|
||||
oscope-of/bin/
|
||||
# openFrameworks — on garde les shaders + settings.json pour qu'ils
|
||||
# arrivent sur les autres machines, mais on ignore les binaires.
|
||||
oscope-of/bin/*
|
||||
!oscope-of/bin/data/
|
||||
oscope-of/bin/data/*
|
||||
!oscope-of/bin/data/shaders/
|
||||
!oscope-of/bin/data/settings.json
|
||||
oscope-of/obj/
|
||||
oscope-of/.vscode/
|
||||
oscope-of/Project.xcconfig
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#version 120
|
||||
|
||||
uniform vec2 uRes;
|
||||
uniform float uTime;
|
||||
uniform float uBpm;
|
||||
uniform float uBass;
|
||||
uniform float uLead;
|
||||
uniform float uKick;
|
||||
uniform float uPad;
|
||||
|
||||
vec3 palette(float t) {
|
||||
// Inigo Quilez-style cosine palette, hue tilted toward magenta/cyan
|
||||
vec3 a = vec3(0.5, 0.5, 0.5);
|
||||
vec3 b = vec3(0.5, 0.5, 0.5);
|
||||
vec3 c = vec3(1.0, 1.0, 1.0);
|
||||
vec3 d = vec3(0.0, 0.33, 0.67);
|
||||
return a + b * cos(6.28318 * (c * t + d));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 p = (gl_FragCoord.xy / uRes) * 2.0 - 1.0;
|
||||
p.x *= uRes.x / uRes.y;
|
||||
|
||||
float t = uTime * (0.4 + uBpm * 0.005);
|
||||
float turb = 1.0 + uLead * 1.8 + uKick * 1.0;
|
||||
|
||||
float v = 0.0;
|
||||
v += sin(p.x * 4.0 * turb + t * 1.6);
|
||||
v += sin(p.y * 5.0 * turb - t * 1.3);
|
||||
v += sin((p.x + p.y) * 3.0 * turb + t * 2.1);
|
||||
float r = length(p);
|
||||
v += sin(r * 8.0 - t * 2.0 + uBass * 6.0);
|
||||
v *= 0.25;
|
||||
|
||||
// Bass swells push the hue
|
||||
float hue = v + t * 0.15 + uBass * 0.3;
|
||||
vec3 col = palette(hue);
|
||||
|
||||
// Pad amplitude → glow toward white
|
||||
col = mix(col, vec3(1.0), uPad * 0.4 * smoothstep(0.0, 1.5, abs(v)));
|
||||
|
||||
// Kick → darker contrast pulse
|
||||
col *= mix(1.0, 1.6, uKick);
|
||||
|
||||
// Soft radial vignette
|
||||
col *= smoothstep(1.5, 0.2, r);
|
||||
|
||||
gl_FragColor = vec4(col, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#version 120
|
||||
uniform mat4 modelViewProjectionMatrix;
|
||||
void main() {
|
||||
gl_Position = modelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
#version 120
|
||||
#extension GL_ARB_texture_rectangle : enable
|
||||
|
||||
// Composite post-processing pass. Each effect is gated by an intensity
|
||||
// uniform — set to 0 to skip its branch. Order matters: spatial /
|
||||
// kaleido first (work in UV), then color / chromatic / glitch.
|
||||
|
||||
uniform sampler2DRect uScene;
|
||||
uniform sampler2DRect uPrev;
|
||||
uniform vec2 uRes;
|
||||
uniform float uTime;
|
||||
|
||||
uniform float uChroma;
|
||||
uniform float uBloom;
|
||||
uniform float uRgbShift;
|
||||
uniform float uSat;
|
||||
uniform float uScan;
|
||||
uniform float uVignette;
|
||||
uniform float uGrain;
|
||||
uniform float uPixelate;
|
||||
uniform float uKaleido;
|
||||
uniform float uFeedback;
|
||||
uniform float uFbZoom;
|
||||
uniform float uFbRot;
|
||||
uniform float uGlitch;
|
||||
uniform float uGlitchProb;
|
||||
|
||||
varying vec2 vTex;
|
||||
|
||||
const float TAU = 6.2831853;
|
||||
|
||||
float rand(vec2 p) {
|
||||
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
vec2 rot2(vec2 v, float a) {
|
||||
float c = cos(a), s = sin(a);
|
||||
return vec2(c*v.x - s*v.y, s*v.x + c*v.y);
|
||||
}
|
||||
|
||||
vec3 hueRotate(vec3 col, float a) {
|
||||
// Approximate hue rotation in YIQ space — fast and visually OK
|
||||
float c1 = cos(a), s1 = sin(a);
|
||||
mat3 m = mat3(
|
||||
0.299 + 0.701*c1 + 0.168*s1, 0.587 - 0.587*c1 + 0.330*s1, 0.114 - 0.114*c1 - 0.497*s1,
|
||||
0.299 - 0.299*c1 - 0.328*s1, 0.587 + 0.413*c1 + 0.035*s1, 0.114 - 0.114*c1 + 0.292*s1,
|
||||
0.299 - 0.300*c1 + 1.250*s1, 0.587 - 0.588*c1 - 1.050*s1, 0.114 + 0.886*c1 - 0.203*s1
|
||||
);
|
||||
return clamp(m * col, 0.0, 1.5);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 px = gl_FragCoord.xy;
|
||||
vec2 uv = px;
|
||||
vec2 c = (px / uRes) - 0.5;
|
||||
|
||||
// 1. Pixelate (snap to block centers)
|
||||
if (uPixelate > 0.001) {
|
||||
float ps = mix(1.0, 64.0, uPixelate);
|
||||
uv = floor(uv / ps) * ps + ps * 0.5;
|
||||
c = (uv / uRes) - 0.5;
|
||||
}
|
||||
|
||||
// 2. Kaleidoscope radial mirror
|
||||
if (uKaleido > 0.001) {
|
||||
float n = floor(mix(2.0, 12.0, uKaleido));
|
||||
float ang = atan(c.y, c.x);
|
||||
float seg = TAU / n;
|
||||
ang = abs(mod(ang + seg*0.5, seg) - seg*0.5);
|
||||
float r = length(c);
|
||||
c = vec2(cos(ang), sin(ang)) * r;
|
||||
uv = (c + 0.5) * uRes;
|
||||
}
|
||||
|
||||
// 3. Glitch — block displacement on horizontal slices
|
||||
vec2 glUv = uv;
|
||||
if (uGlitch > 0.001) {
|
||||
float blockY = floor(uv.y / 14.0);
|
||||
float t = floor(uTime * 24.0);
|
||||
float r = rand(vec2(blockY, t));
|
||||
float prob = mix(0.0, uGlitchProb * 2.5, uGlitch);
|
||||
if (r < prob) {
|
||||
float shift = (rand(vec2(blockY, t + 1.0)) - 0.5) * 240.0 * uGlitch;
|
||||
glUv.x += shift;
|
||||
}
|
||||
}
|
||||
glUv = clamp(glUv, vec2(0.0), uRes - 1.0);
|
||||
|
||||
// 4. Chromatic aberration : RGB channels sampled at radial offsets
|
||||
vec2 dir = c * uChroma * 14.0;
|
||||
vec3 col;
|
||||
col.r = texture2DRect(uScene, clamp(glUv + dir, vec2(0.0), uRes - 1.0)).r;
|
||||
col.g = texture2DRect(uScene, glUv).g;
|
||||
col.b = texture2DRect(uScene, clamp(glUv - dir, vec2(0.0), uRes - 1.0)).b;
|
||||
|
||||
// 5. Glitch channel swap
|
||||
if (uGlitch > 0.55) {
|
||||
float swap = step(0.5, rand(vec2(floor(uTime * 12.0), 1.0)));
|
||||
col = mix(col, col.brg, swap);
|
||||
}
|
||||
|
||||
// 6. Bloom — cheap 5x5 bright-pass average added back
|
||||
if (uBloom > 0.001) {
|
||||
vec3 bl = vec3(0.0);
|
||||
float w = 4.0 + uBloom * 12.0;
|
||||
for (int i = -2; i <= 2; i++) {
|
||||
for (int j = -2; j <= 2; j++) {
|
||||
vec3 s = texture2DRect(uScene,
|
||||
clamp(uv + vec2(i, j) * w, vec2(0.0), uRes - 1.0)).rgb;
|
||||
float lum = max(max(s.r, s.g), s.b);
|
||||
bl += s * smoothstep(0.45, 1.0, lum);
|
||||
}
|
||||
}
|
||||
bl /= 25.0;
|
||||
col += bl * uBloom * 1.6;
|
||||
}
|
||||
|
||||
// 7. Hue rotation
|
||||
if (uRgbShift > 0.001) {
|
||||
col = hueRotate(col, uRgbShift * TAU);
|
||||
}
|
||||
|
||||
// 8. Saturation
|
||||
{
|
||||
float lum = dot(col, vec3(0.299, 0.587, 0.114));
|
||||
col = mix(vec3(lum), col, uSat);
|
||||
}
|
||||
|
||||
// 9. Feedback (zoom + rotate previous frame)
|
||||
if (uFeedback > 0.001) {
|
||||
vec2 fc = uv - uRes * 0.5;
|
||||
fc /= uFbZoom;
|
||||
fc = rot2(fc, uFbRot);
|
||||
fc += uRes * 0.5;
|
||||
fc = clamp(fc, vec2(0.0), uRes - 1.0);
|
||||
vec3 prev = texture2DRect(uPrev, fc).rgb * 0.96;
|
||||
col = max(col, prev * uFeedback * 1.5);
|
||||
}
|
||||
|
||||
// 10. Scanlines
|
||||
if (uScan > 0.001) {
|
||||
float sl = sin(px.y * 3.14159) * 0.5 + 0.5;
|
||||
col *= mix(1.0, 0.45 + sl * 0.55, uScan);
|
||||
}
|
||||
|
||||
// 11. Film grain
|
||||
if (uGrain > 0.001) {
|
||||
float g = (rand(px + uTime * vec2(13.0, 7.0)) - 0.5) * uGrain * 0.6;
|
||||
col += vec3(g);
|
||||
}
|
||||
|
||||
// 12. Vignette
|
||||
if (uVignette > 0.001) {
|
||||
float v = smoothstep(0.95, 0.35, length(c) * 1.4);
|
||||
col *= mix(1.0, v, uVignette);
|
||||
}
|
||||
|
||||
// Final tone-map / clamp — without this, bloom + grain + feedback
|
||||
// saturate to pure white very quickly on bright visualizers.
|
||||
col = col / (col + vec3(0.6)); // Reinhard-ish soft knee
|
||||
col = clamp(col, 0.0, 1.0);
|
||||
gl_FragColor = vec4(col, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#version 120
|
||||
// openFrameworks injects modelViewProjectionMatrix and the standard
|
||||
// gl_Vertex / gl_MultiTexCoord0 inputs in compatibility mode.
|
||||
uniform mat4 modelViewProjectionMatrix;
|
||||
varying vec2 vTex;
|
||||
void main() {
|
||||
vTex = gl_MultiTexCoord0.xy;
|
||||
gl_Position = modelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#version 120
|
||||
|
||||
// Cylindrical pseudo-3D tunnel. Closed-form mapping from screen UV to
|
||||
// (angle, depth) — no real raymarching needed for an infinite straight
|
||||
// tunnel. Twist is added per-depth slice based on lead amplitude.
|
||||
|
||||
uniform vec2 uRes;
|
||||
uniform float uTime;
|
||||
uniform float uTravel;
|
||||
uniform float uBpm;
|
||||
uniform float uKick;
|
||||
uniform float uBass;
|
||||
uniform float uLead;
|
||||
uniform float uPad;
|
||||
|
||||
vec3 palette(float t) {
|
||||
return 0.5 + 0.5 * cos(6.28318 * (vec3(1.0) * t + vec3(0.0, 0.33, 0.67)));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 p = (gl_FragCoord.xy / uRes) * 2.0 - 1.0;
|
||||
p.x *= uRes.x / uRes.y;
|
||||
|
||||
// Polar coords -> tunnel space
|
||||
float r = length(p);
|
||||
float a = atan(p.y, p.x);
|
||||
if (r < 0.001) r = 0.001;
|
||||
|
||||
// depth z = 1/r so r→0 is far. Travel scrolls slices.
|
||||
float z = 1.0 / r + uTravel;
|
||||
// Twist: deeper = more rotation, modulated by lead
|
||||
a += z * (0.15 + uLead * 0.6);
|
||||
|
||||
// Slice index for repeating bands
|
||||
float slice = floor(z * 2.0);
|
||||
float frac = fract(z * 2.0);
|
||||
|
||||
// Brick / ring pattern — checker on (slice, angle)
|
||||
float ang = a / 6.28318 * 16.0;
|
||||
float wall = mod(floor(ang) + slice, 2.0);
|
||||
|
||||
// Base color cycles along depth + slight bass push
|
||||
vec3 col = palette(slice * 0.07 + uTime * 0.05 + uBass * 0.2);
|
||||
|
||||
// Bright "lamps" every 4 slices
|
||||
float lampMask = step(0.92, fract(slice / 4.0));
|
||||
float lamp = (1.0 - smoothstep(0.0, 0.4, abs(frac - 0.5))) * lampMask;
|
||||
col += vec3(1.0, 0.9, 0.7) * lamp * (0.6 + uKick * 1.5);
|
||||
|
||||
// Wall vs floor stripes alternate brightness
|
||||
col *= mix(0.45, 1.0, wall);
|
||||
|
||||
// Add edges between bricks (subtle dark line)
|
||||
float edge = smoothstep(0.0, 0.05, abs(frac - 0.5));
|
||||
col *= mix(0.7, 1.0, edge);
|
||||
|
||||
// Distance fog : pixels close to center (far away) fade to background
|
||||
float fog = smoothstep(0.0, 0.85, r);
|
||||
col = mix(vec3(0.0, 0.0, 0.05 + uPad * 0.15), col, fog);
|
||||
|
||||
// Outer ring vignette
|
||||
col *= smoothstep(2.0, 0.4, length(p));
|
||||
|
||||
gl_FragColor = vec4(col, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#version 120
|
||||
uniform mat4 modelViewProjectionMatrix;
|
||||
void main() { gl_Position = modelViewProjectionMatrix * gl_Vertex; }
|
||||
@@ -14,7 +14,7 @@ public:
|
||||
struct Params {
|
||||
// Color
|
||||
float chroma = 0.0f; // chromatic aberration radius
|
||||
float bloom = 0.4f; // bright-pass blur strength
|
||||
float bloom = 0.15f; // bright-pass blur strength (was 0.4 — saturated)
|
||||
float rgbShift = 0.0f; // hue rotation 0..1 = 0..2π
|
||||
float saturation = 1.0f; // 0..2 (1 = neutral)
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ const char* modeName(ofApp::Mode m) {
|
||||
case ofApp::Mode::Kaleido: return "Kaleido";
|
||||
case ofApp::Mode::Tunnel: return "Tunnel 3D";
|
||||
case ofApp::Mode::Mesh: return "Mesh 3D";
|
||||
case ofApp::Mode::Scope4: return "Scope 4-up";
|
||||
case ofApp::Mode::Hybrid: return "Hybrid";
|
||||
}
|
||||
return "?";
|
||||
@@ -100,6 +101,10 @@ void ofApp::setup() {
|
||||
gui_.add(gainCh2_.setup("CH2 V/div", 1.0f, 0.25f, 5.0f));
|
||||
gui_.add(trailFade_.setup("Trail fade", 0.07f, 0.0f, 1.0f));
|
||||
gui_.add(sampleRateHz_.setup("Sample rate", 8000000, 1000000, 48000000));
|
||||
gui_.add(timeMsPerDiv_.setup("Time ms/div", 5.0f, 0.05f, 200.0f));
|
||||
gui_.add(scrollSpeed_.setup("Scroll", 0.0f, 0.0f, 1.0f));
|
||||
lastSampleRateApplied_ = sampleRateHz_;
|
||||
scope_.setSampleRate(static_cast<uint32_t>(lastSampleRateApplied_));
|
||||
|
||||
// PostFx panel — placed to the right of the main GUI
|
||||
fxGui_.setup("post-fx", "fx-settings.xml", 230, 10);
|
||||
@@ -145,6 +150,8 @@ void ofApp::loadSettings() {
|
||||
else if (m == "kaleido") mode_ = Mode::Kaleido;
|
||||
else if (m == "tunnel") mode_ = Mode::Tunnel;
|
||||
else if (m == "mesh") mode_ = Mode::Mesh;
|
||||
else if (m == "scope4" ||
|
||||
m == "quad") mode_ = Mode::Scope4;
|
||||
else mode_ = Mode::Hybrid;
|
||||
}
|
||||
|
||||
@@ -183,6 +190,15 @@ void ofApp::applyOscFx() {
|
||||
|
||||
void ofApp::update() {
|
||||
osc_.update();
|
||||
// Re-apply sample rate to the scope when the slider changes.
|
||||
if (sampleRateHz_ != lastSampleRateApplied_) {
|
||||
lastSampleRateApplied_ = sampleRateHz_;
|
||||
scope_.setSampleRate(static_cast<uint32_t>(lastSampleRateApplied_));
|
||||
}
|
||||
// Push live timebase + scroll into WaveformVis.
|
||||
waveform_->setTimeMsPerDiv(timeMsPerDiv_);
|
||||
waveform_->setScrollSpeed(scrollSpeed_);
|
||||
waveform_->setSampleRate(static_cast<float>(lastSampleRateApplied_));
|
||||
scope_.ring().readLatest(ch1_, ch2_, static_cast<std::size_t>(bufferSize_));
|
||||
|
||||
oscope::VisFrame frame{ch1_, ch2_, osc_};
|
||||
@@ -241,6 +257,15 @@ void ofApp::drawMode(Mode m, int x, int y, int w, int h) {
|
||||
case Mode::Kaleido: kaleido_->draw(x, y, w, h); break;
|
||||
case Mode::Tunnel: tunnel_->draw(x, y, w, h); break;
|
||||
case Mode::Mesh: mesh_->draw(x, y, w, h); break;
|
||||
case Mode::Scope4: {
|
||||
const int hw = w / 2;
|
||||
const int hh = h / 2;
|
||||
waveform_->draw (x, y, hw, hh);
|
||||
spectro_->draw (x + hw, y, hw, hh);
|
||||
polar_->draw (x, y + hh, hw, hh);
|
||||
lissajous_->draw(x + hw, y + hh, hw, hh);
|
||||
break;
|
||||
}
|
||||
case Mode::Hybrid: drawHybrid(w, h); break;
|
||||
}
|
||||
}
|
||||
@@ -364,6 +389,7 @@ void ofApp::keyPressed(int key) {
|
||||
case '9': mode_ = Mode::Kaleido; break;
|
||||
case '0': mode_ = Mode::Tunnel; break;
|
||||
case '-': mode_ = Mode::Mesh; break;
|
||||
case 'q': mode_ = Mode::Scope4; break;
|
||||
case 'f':
|
||||
fullscreen_ = !fullscreen_;
|
||||
ofSetFullscreen(fullscreen_);
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
Lissajous, Spectrogram, Reactive, Waveform,
|
||||
Polar, Plasma, Particles, Kaleido,
|
||||
Tunnel, Mesh,
|
||||
Scope4, // waveform + spectrogram + polar pulsing + lissajous
|
||||
Hybrid
|
||||
};
|
||||
|
||||
@@ -76,6 +77,9 @@ private:
|
||||
ofxFloatSlider gainCh1_, gainCh2_;
|
||||
ofxFloatSlider trailFade_;
|
||||
ofxIntSlider sampleRateHz_;
|
||||
ofxFloatSlider timeMsPerDiv_;
|
||||
ofxFloatSlider scrollSpeed_;
|
||||
int lastSampleRateApplied_ = 0;
|
||||
ofxLabel scopeStatusLabel_;
|
||||
ofxLabel oscStatusLabel_;
|
||||
ofxLabel modeLabel_;
|
||||
|
||||
@@ -9,6 +9,11 @@ void WaveformVis::setup(int w, int h) {
|
||||
h_ = h;
|
||||
trace1_.assign(syntheticSize_, 0.0f);
|
||||
trace2_.assign(syntheticSize_, 0.0f);
|
||||
// Ring d'historique : ~16x la fenêtre pour pouvoir scroller en arrière
|
||||
// sans trou même avec un timebase lent.
|
||||
ring1_.assign(syntheticSize_ * 16, 0.0f);
|
||||
ring2_.assign(syntheticSize_ * 16, 0.0f);
|
||||
ringHead_ = 0;
|
||||
}
|
||||
|
||||
void WaveformVis::update(const VisFrame& frame) {
|
||||
@@ -22,16 +27,38 @@ void WaveformVis::update(const VisFrame& frame) {
|
||||
}
|
||||
beatPhase_ *= 0.92f;
|
||||
|
||||
// Si Hantek a fourni des samples, on les copie tels quels
|
||||
// Si Hantek a fourni des samples, on alimente le ring d'historique
|
||||
if (frame.ch1.size() > 16) {
|
||||
std::size_t n = std::min(frame.ch1.size(), trace1_.size());
|
||||
const std::size_t cap = ring1_.size();
|
||||
const std::size_t n = std::min(frame.ch1.size(), cap);
|
||||
for (std::size_t i = 0; i < n; ++i) {
|
||||
trace1_[i] = frame.ch1[i];
|
||||
trace2_[i] = (i < frame.ch2.size()) ? frame.ch2[i] : 0.0f;
|
||||
ring1_[ringHead_] = frame.ch1[i];
|
||||
ring2_[ringHead_] = (i < frame.ch2.size()) ? frame.ch2[i] : 0.0f;
|
||||
ringHead_ = (ringHead_ + 1) % cap;
|
||||
}
|
||||
if (n < trace1_.size()) {
|
||||
std::fill(trace1_.begin() + n, trace1_.end(), 0.0f);
|
||||
std::fill(trace2_.begin() + n, trace2_.end(), 0.0f);
|
||||
|
||||
// Mode FREEZE — scrollSpeed_ ~ 0 : on n'actualise pas la fenêtre
|
||||
// affichée, le trace gèle tel qu'il était (utile pour analyser).
|
||||
if (scrollSpeed_ < 0.01f) return;
|
||||
|
||||
// Largeur de la fenêtre = timebase × divisions, convertie en samples.
|
||||
// (sampleRate * (ms/div × divX) / 1000)
|
||||
const float winSec = (timeMsPerDiv_ * 0.001f) * divX_;
|
||||
std::size_t winSamples = static_cast<std::size_t>(
|
||||
std::max(64.0f, std::min(static_cast<float>(cap),
|
||||
winSec * sampleRateHz_)));
|
||||
winSamples = std::min(winSamples, trace1_.size());
|
||||
|
||||
// Position de lecture : tail = head - winSamples (mode freeze) ou
|
||||
// tail décalé pour un effet de scroll continu.
|
||||
const std::size_t off = (cap + ringHead_ - winSamples) % cap;
|
||||
for (std::size_t i = 0; i < trace1_.size(); ++i) {
|
||||
const float t = static_cast<float>(i) /
|
||||
static_cast<float>(trace1_.size() - 1);
|
||||
const std::size_t k = static_cast<std::size_t>(t * (winSamples - 1));
|
||||
const std::size_t idx = (off + k) % cap;
|
||||
trace1_[i] = ring1_[idx];
|
||||
trace2_[i] = ring2_[idx];
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -119,11 +146,14 @@ void WaveformVis::draw(int x, int y, int w, int h) {
|
||||
ofSetColor(160, 220, 180);
|
||||
const float vdiv1 = 0.5f;
|
||||
const float vdiv2 = 0.5f;
|
||||
const float tdiv = 5.0f; // ms/div approximatif
|
||||
ofDrawBitmapString("CH1 " + ofToString(vdiv1, 2) + " V/div", x + 10, y + 16);
|
||||
ofDrawBitmapString("CH2 " + ofToString(vdiv2, 2) + " V/div", x + 10, y + 32);
|
||||
ofDrawBitmapString("Time " + ofToString(tdiv, 1) + " ms/div", x + 10, y + 48);
|
||||
ofDrawBitmapString("Trig auto", x + 10, y + 64);
|
||||
ofDrawBitmapString("Time " + ofToString(timeMsPerDiv_, 2) + " ms/div",
|
||||
x + 10, y + 48);
|
||||
ofDrawBitmapString(scrollSpeed_ < 0.01f ? "FROZEN" : "ROLL",
|
||||
x + 10, y + 64);
|
||||
ofDrawBitmapString("Sr " + ofToString(sampleRateHz_ * 1e-6f, 1) + " MS/s",
|
||||
x + 10, y + 80);
|
||||
|
||||
ofPopMatrix();
|
||||
ofPopStyle();
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include "Visualizer.h"
|
||||
#include "ofMain.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace oscope {
|
||||
|
||||
class WaveformVis : public Visualizer {
|
||||
@@ -15,16 +18,33 @@ public:
|
||||
void update(const VisFrame& frame) override;
|
||||
void draw(int x, int y, int w, int h) override;
|
||||
|
||||
// Live tweaks (driven by GUI sliders / OSC).
|
||||
void setTimeMsPerDiv(float v) { timeMsPerDiv_ = std::max(0.05f, v); }
|
||||
void setScrollSpeed(float v) { scrollSpeed_ = std::max(0.0f, std::min(1.0f, v)); }
|
||||
void setSampleRate(float hz) { if (hz > 1.0f) sampleRateHz_ = hz; }
|
||||
|
||||
private:
|
||||
int w_ = 0, h_ = 0;
|
||||
float phase_ = 0.0f;
|
||||
float beatPhase_ = 0.0f;
|
||||
int prevBeat_ = 0;
|
||||
// trace1_/trace2_ : fenêtre actuelle affichée (taille fixe)
|
||||
std::vector<float> trace1_;
|
||||
std::vector<float> trace2_;
|
||||
// Ring d'historique pour le scrolling (plus long, on lit la queue)
|
||||
std::vector<float> ring1_;
|
||||
std::vector<float> ring2_;
|
||||
std::size_t ringHead_ = 0;
|
||||
int syntheticSize_ = 2048;
|
||||
float divX_ = 8.0f; // grid divisions
|
||||
float divY_ = 8.0f;
|
||||
|
||||
// Time base — combien de millisecondes représente une division horizontale.
|
||||
float timeMsPerDiv_ = 5.0f;
|
||||
// 0 = freeze (capture par trame), 1 = scroll continu (oscilloscope rolling).
|
||||
float scrollSpeed_ = 0.0f;
|
||||
// Hz — utilisé pour convertir ms/div en samples/div.
|
||||
float sampleRateHz_ = 8'000'000.0f;
|
||||
};
|
||||
|
||||
} // namespace oscope
|
||||
|
||||
Reference in New Issue
Block a user