feat(of): scope4 tunnel center + spectro ring
Layout retravaillé pour le mode Scope4 :
- Tunnel fullscreen en fond, focal central. Ses uniformes sont
pilotés par les fréquences :
- uTileZ (densité Z) : bass loud → grosses tuiles
- uTileX (angulaire) : lead loud → tuiles fines
- uDirection : signe -1..+1 lerp lente vers (lead-bass), inverse
sens de défilement et twist
- Spectrogramme circulaire colorisé centré : anneau de barres
FFT, hue bleu (basses) → rouge (aigus), magnitude = longueur
radiale. Mapping log pour étaler les basses.
- 3 satellites (Waveform, Polar, Lissajous) en orbite autour du
centre, position tournante (vitesse BPM-driven). Chaque
satellite a son label live.
- Label "TUNNEL" central avec BPM + balance LF/HF.
This commit is contained in:
@@ -14,6 +14,13 @@ uniform float uKick;
|
||||
uniform float uBass;
|
||||
uniform float uLead;
|
||||
uniform float uPad;
|
||||
// Pilotage par fréquence :
|
||||
// uTileZ — bass loud → grosses tuiles axe Z (valeur basse, défaut 2)
|
||||
// uTileX — lead loud → tuiles fines axe angulaire (valeur haute, défaut 16)
|
||||
// uDirection — signe du défilement / twist (-1..+1)
|
||||
uniform float uTileZ;
|
||||
uniform float uTileX;
|
||||
uniform float uDirection;
|
||||
|
||||
vec3 palette(float t) {
|
||||
return 0.5 + 0.5 * cos(6.28318 * (vec3(1.0) * t + vec3(0.0, 0.33, 0.67)));
|
||||
@@ -30,15 +37,15 @@ void main() {
|
||||
|
||||
// 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);
|
||||
// Twist: deeper = more rotation, modulated by lead, signé par direction
|
||||
a += z * (0.15 + uLead * 0.6) * uDirection;
|
||||
|
||||
// Slice index for repeating bands
|
||||
float slice = floor(z * 2.0);
|
||||
float frac = fract(z * 2.0);
|
||||
// Slice index — densité Z pilotée par bass (uTileZ).
|
||||
float slice = floor(z * uTileZ);
|
||||
float frac = fract(z * uTileZ);
|
||||
|
||||
// Brick / ring pattern — checker on (slice, angle)
|
||||
float ang = a / 6.28318 * 16.0;
|
||||
// Brick / ring pattern — densité angulaire pilotée par lead (uTileX).
|
||||
float ang = a / 6.28318 * uTileX;
|
||||
float wall = mod(floor(ang) + slice, 2.0);
|
||||
|
||||
// Base color cycles along depth + slight bass push
|
||||
|
||||
+39
-29
@@ -314,41 +314,51 @@ void ofApp::drawPanelLabel(int x, int y, const char* title,
|
||||
}
|
||||
|
||||
void ofApp::drawScope4(int W, int H) {
|
||||
// Lissajous fullscreen en fond — additif pour ne pas masquer.
|
||||
ofEnableBlendMode(OF_BLENDMODE_ADD);
|
||||
ofPushStyle();
|
||||
ofSetColor(255, 255, 255, 110);
|
||||
lissajous_->draw(0, 0, W, H);
|
||||
ofPopStyle();
|
||||
ofDisableBlendMode();
|
||||
// 1) Tunnel fullscreen en fond — focal central, fréquences pilotent
|
||||
// tile size / direction / vitesse (cf. TunnelVis).
|
||||
tunnel_->draw(0, 0, W, H);
|
||||
|
||||
// Bandeau haut : Waveform à gauche, Spectrogramme à droite (1/3 hauteur)
|
||||
const int topH = H / 3;
|
||||
const int hw = W / 2;
|
||||
waveform_->draw(0, 0, hw, topH);
|
||||
spectro_->draw (hw, 0, hw, topH);
|
||||
// 2) Spectrogramme circulaire colorisé par fréquence, centré.
|
||||
// Anneau de barres FFT, hue bleu→rouge, magnitude = longueur radiale.
|
||||
const float cx = W * 0.5f;
|
||||
const float cy = H * 0.5f;
|
||||
const float ringInner = std::min(W, H) * 0.18f;
|
||||
const float ringOuter = std::min(W, H) * 0.32f;
|
||||
spectro_->drawCircular(cx, cy, ringInner, ringOuter);
|
||||
|
||||
// Polar central, grand — couvre les 2/3 inférieurs centrés.
|
||||
const int polarW = static_cast<int>(W * 0.62f);
|
||||
const int polarH = static_cast<int>((H - topH) * 0.95f);
|
||||
const int polarX = (W - polarW) / 2;
|
||||
const int polarY = topH + ((H - topH) - polarH) / 2;
|
||||
polar_->draw(polarX, polarY, polarW, polarH);
|
||||
|
||||
// Labels live par panneau (coin haut-gauche de chaque zone).
|
||||
// 3) Trois satellites en orbite autour du tunnel — leur position
|
||||
// tourne lentement en suivant le BPM. Chacun garde son rendu
|
||||
// interne (pas de rotation du contenu, juste de la position).
|
||||
const float bpm = osc_.bpm();
|
||||
const float orbit = ofGetElapsedTimef() * (0.04f + bpm * 0.0003f);
|
||||
const float orbitR = std::min(W, H) * 0.40f;
|
||||
const int satW = static_cast<int>(W * 0.24f);
|
||||
const int satH = static_cast<int>(H * 0.20f);
|
||||
|
||||
struct Satellite { Visualizer* vis; const char* tag; std::string metric; };
|
||||
const float kick = osc_.amp("kick");
|
||||
const float lead = osc_.amp("lead");
|
||||
const float bass = osc_.amp("bass");
|
||||
drawPanelLabel(8, 6, "WAVE ",
|
||||
ofToString(static_cast<float>(timeMsPerDiv_), 2) + " ms/div " +
|
||||
ofToString(lastSampleRateApplied_ * 1e-6f, 1) + " MS/s");
|
||||
drawPanelLabel(hw + 8, 6, "SPECTRO",
|
||||
"lead " + ofToString(lead, 2) + " bass " + ofToString(bass, 2));
|
||||
drawPanelLabel(polarX + 8, polarY + 6, "POLAR ",
|
||||
ofToString(bpm, 1) + " bpm kick " + ofToString(kick, 2));
|
||||
drawPanelLabel(8, H - 28, "LISSA ",
|
||||
"trail blend ADD");
|
||||
Satellite sats[3] = {
|
||||
{waveform_.get(), "WAVE ",
|
||||
ofToString(static_cast<float>(timeMsPerDiv_), 2) + " ms"},
|
||||
{polar_.get(), "POLAR ",
|
||||
ofToString(bpm, 0) + " bpm k" + ofToString(kick, 1)},
|
||||
{lissajous_.get(), "LISSA ",
|
||||
"L" + ofToString(lead, 1) + " B" + ofToString(bass, 1)},
|
||||
};
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const float a = orbit + i * (TWO_PI / 3.0f);
|
||||
const int sx = static_cast<int>(cx + std::cos(a) * orbitR - satW * 0.5f);
|
||||
const int sy = static_cast<int>(cy + std::sin(a) * orbitR - satH * 0.5f);
|
||||
sats[i].vis->draw(sx, sy, satW, satH);
|
||||
drawPanelLabel(sx + 6, sy + 6, sats[i].tag, sats[i].metric);
|
||||
}
|
||||
|
||||
// 4) Label central : tunnel infos
|
||||
drawPanelLabel(static_cast<int>(cx) - 80, 6, "TUNNEL",
|
||||
ofToString(bpm, 1) + " bpm dir " +
|
||||
ofToString((osc_.amp("lead") - osc_.amp("bass")), 2));
|
||||
}
|
||||
|
||||
void ofApp::draw() {
|
||||
|
||||
@@ -68,4 +68,50 @@ void SpectrogramVis::draw(int x, int y, int w, int h) {
|
||||
ofPopStyle();
|
||||
}
|
||||
|
||||
void SpectrogramVis::drawCircular(int cx, int cy, float innerR, float outerR) {
|
||||
if (mag_.empty()) return;
|
||||
ofPushStyle();
|
||||
ofPushMatrix();
|
||||
ofTranslate(cx, cy);
|
||||
|
||||
// Anneau central — chaque bin FFT occupe un secteur angulaire,
|
||||
// longueur radiale = magnitude, hue = bin (basses bleu → aigus rouge).
|
||||
const std::size_t bins = mag_.size();
|
||||
// On limite au quart bas (~Nyquist/4) car la moitié haute est très
|
||||
// sparse et brouille le rendu.
|
||||
const std::size_t shown = bins / 4;
|
||||
const float step = TWO_PI / static_cast<float>(shown);
|
||||
ofSetLineWidth(2);
|
||||
for (std::size_t i = 0; i < shown; ++i) {
|
||||
const float a = i * step;
|
||||
// Mapping log : on remap i sur le spectre log pour étaler les basses.
|
||||
const float t = std::pow(static_cast<float>(i) / shown, 0.55f);
|
||||
const std::size_t bin = static_cast<std::size_t>(t * (bins - 1));
|
||||
const float magdb = 20.0f * std::log10(std::max(mag_[bin], 1e-6f)) + 60.0f;
|
||||
const float v = ofClamp(magdb / 60.0f, 0.0f, 1.0f);
|
||||
|
||||
// Hue colorisée par fréquence : 220 (bleu) → 0 (rouge) en hue HSB
|
||||
const float hue = 220.0f - 220.0f * t;
|
||||
ofColor col;
|
||||
col.setHsb(hue, 200.0f, 80.0f + 175.0f * v);
|
||||
col.a = static_cast<unsigned char>(120 + 135 * v);
|
||||
ofSetColor(col);
|
||||
|
||||
const float r0 = innerR;
|
||||
const float r1 = innerR + (outerR - innerR) * v;
|
||||
const float ca = std::cos(a);
|
||||
const float sa = std::sin(a);
|
||||
ofDrawLine(ca * r0, sa * r0, ca * r1, sa * r1);
|
||||
}
|
||||
|
||||
// Anneau interne discret pour cadrer
|
||||
ofNoFill();
|
||||
ofSetLineWidth(1);
|
||||
ofSetColor(60, 100, 140, 120);
|
||||
ofDrawCircle(0, 0, innerR);
|
||||
|
||||
ofPopMatrix();
|
||||
ofPopStyle();
|
||||
}
|
||||
|
||||
} // namespace oscope
|
||||
|
||||
@@ -15,6 +15,9 @@ public:
|
||||
void setup(int w, int h) override;
|
||||
void update(const VisFrame& frame) override;
|
||||
void draw(int x, int y, int w, int h) override;
|
||||
/// Render circulaire : anneau de barres FFT centré, couleur par bande
|
||||
/// (bleu = basses, vert = mediums, rouge = aigus). Utilisé en Scope4.
|
||||
void drawCircular(int cx, int cy, float innerR, float outerR);
|
||||
|
||||
private:
|
||||
ofFbo fbo_;
|
||||
|
||||
@@ -12,7 +12,17 @@ void TunnelVis::update(const VisFrame& frame) {
|
||||
bass_ = frame.osc.amp("bass");
|
||||
lead_ = frame.osc.amp("lead");
|
||||
pad_ = frame.osc.amp("pad");
|
||||
travel_ += static_cast<float>(ofGetLastFrameTime()) * (0.6f + bpm_ * 0.012f + kick_ * 1.5f);
|
||||
|
||||
// Direction : lerp lente vers +1 si la balance HF (lead) > LF (bass),
|
||||
// -1 sinon. Le signe contrôle le sens de défilement et le sens du twist.
|
||||
const float dirTarget = (lead_ - bass_) > 0.05f ? 1.0f
|
||||
: (bass_ - lead_) > 0.05f ? -1.0f
|
||||
: direction_;
|
||||
direction_ += (dirTarget - direction_) * 0.04f;
|
||||
|
||||
// Vitesse : base BPM + boost sur kick, signée par direction_.
|
||||
const float speed = (0.6f + bpm_ * 0.012f + kick_ * 1.5f) * direction_;
|
||||
travel_ += static_cast<float>(ofGetLastFrameTime()) * speed;
|
||||
}
|
||||
|
||||
void TunnelVis::draw(int x, int y, int w, int h) {
|
||||
@@ -34,6 +44,14 @@ void TunnelVis::draw(int x, int y, int w, int h) {
|
||||
shader_.setUniform1f("uBass", bass_);
|
||||
shader_.setUniform1f("uLead", lead_);
|
||||
shader_.setUniform1f("uPad", pad_);
|
||||
// Taille de tile pilotée par les fréquences :
|
||||
// - axe Z (profondeur) : bass loud → grosses tuiles (uTileZ bas)
|
||||
// - axe angulaire : lead loud → tuiles fines (uTileX haut)
|
||||
const float tileZ = ofLerp(3.0f, 0.5f, std::min(1.0f, bass_));
|
||||
const float tileX = ofLerp(8.0f, 32.0f, std::min(1.0f, lead_));
|
||||
shader_.setUniform1f("uTileZ", tileZ);
|
||||
shader_.setUniform1f("uTileX", tileX);
|
||||
shader_.setUniform1f("uDirection", direction_);
|
||||
ofDrawRectangle(x, y, w, h);
|
||||
shader_.end();
|
||||
}
|
||||
|
||||
@@ -17,8 +17,9 @@ public:
|
||||
void reloadShaders() override;
|
||||
|
||||
private:
|
||||
int w_ = 0, h_ = 0;
|
||||
float travel_ = 0.0f;
|
||||
int w_ = 0, h_ = 0;
|
||||
float travel_ = 0.0f;
|
||||
float direction_ = 1.0f; // smooth -1..+1 (lead-bass driven)
|
||||
float bpm_ = 120.0f, kick_ = 0.0f, bass_ = 0.0f, lead_ = 0.0f, pad_ = 0.0f;
|
||||
ofShader shader_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user