diff --git a/web/src/analysis.js b/web/src/analysis.js index 4b52e47..04e4e11 100644 --- a/web/src/analysis.js +++ b/web/src/analysis.js @@ -77,9 +77,9 @@ export class DemoFeed { for (let k = 0; k < n; k++) { const f = k * hz + 1; let v = -105 + 12 * Math.sin(k * 12.9898 + t * 3) * 0.3; - v += 62 * kick * Math.exp(-Math.pow(Math.log2(f / 55), 2) * 1.5); - v += 52 * snare * Math.exp(-Math.pow(Math.log2(f / 1800), 2) * 0.35); - v += 45 * hat * Math.exp(-Math.pow(Math.log2(f / 9000), 2) * 0.5); + v += 78 * kick * Math.exp(-Math.pow(Math.log2(f / 55), 2) * 1.5); + v += 62 * snare * Math.exp(-Math.pow(Math.log2(f / 1800), 2) * 0.35); + v += 55 * hat * Math.exp(-Math.pow(Math.log2(f / 9000), 2) * 0.5); for (const c of chord) v += 55 * Math.exp(-Math.pow((f - c) / (hz * 0.8 + c * 0.01), 2)); db[k] = v; } diff --git a/web/src/dsp.js b/web/src/dsp.js index 2b21430..20760a1 100644 --- a/web/src/dsp.js +++ b/web/src/dsp.js @@ -60,11 +60,13 @@ export class OnsetDetector { constructor(opts = {}) { this.compress = opts.compress ?? 200; // log(1 + compress * mag) this.window = opts.window ?? 43; // median history, frames - this.delta = opts.delta ?? 0.35; // absolute floor above the median, in flux units + this.delta = opts.delta ?? 0.08; // absolute floor above the median, in flux units this.ratio = opts.ratio ?? 1.6; // and the flux must exceed ratio * median this.refractory = opts.refractory ?? 0.06; // seconds this.minBin = opts.minBin ?? 2; this.maxBin = opts.maxBin ?? 0; // 0 = all bins + this.bands = opts.bands ?? 6; // log-spaced flux bands between minBin and maxBin + this.edges = null; this.prev = null; this.hist = []; // recent flux values this.f1 = 0; this.f2 = 0; this.t1 = 0; // flux[n-1], flux[n-2], time[n-1] @@ -74,17 +76,27 @@ export class OnsetDetector { push(mags, t) { const n = this.maxBin || mags.length; + if (!this.edges) { + this.edges = Array.from({ length: this.bands + 1 }, (_, i) => Math.round(this.minBin * Math.pow(n / this.minBin, i / this.bands))); + for (let i = 1; i < this.edges.length; i++) this.edges[i] = Math.max(this.edges[i], this.edges[i - 1] + 1); + } const cur = new Float32Array(n); for (let k = 0; k < n; k++) cur[k] = Math.log1p(this.compress * mags[k]); let flux = 0; if (this.prev) { const p = this.prev; - for (let k = this.minBin; k < n; k++) { - const ref = Math.max(p[k - 1] ?? 0, p[k], p[k + 1] ?? 0); - const d = cur[k] - ref; - if (d > 0) flux += d; + // flux per log-spaced band, each divided by sqrt(its width): a kick (a few bass bins) then counts + // as much as a hat (hundreds of treble bins), which one whole-spectrum sum would bury + for (let b = 0; b < this.edges.length - 1; b++) { + let bf = 0; + for (let k = this.edges[b]; k < this.edges[b + 1]; k++) { + const ref = Math.max(p[k - 1] ?? 0, p[k], p[k + 1] ?? 0); + const d = cur[k] - ref; + if (d > 0) bf += d; + } + flux += bf / Math.sqrt(this.edges[b + 1] - this.edges[b]); } - flux /= Math.sqrt(n); + flux /= this.edges.length - 1; } this.prev = cur; this.flux = flux; diff --git a/web/src/scenes/aurora.js b/web/src/scenes/aurora.js index 1ac2c55..b381cfb 100644 --- a/web/src/scenes/aurora.js +++ b/web/src/scenes/aurora.js @@ -34,7 +34,7 @@ void main(){ vec3 c = mix(base, crest, smoothstep(0.1, 0.9, h)); float lines = 0.55 + 0.45 * smoothstep(0.82, 1.0, abs(sin(seg * 256.0 * 3.14159))); float fade = (1.0 - ring) * (1.0 - ring) * 0.9 + 0.05; - float a = (0.02 + h * h * 0.75) * lines * fade * (1.0 + 1.5 * kick); + float a = (0.015 + h * h * 0.42) * lines * fade * (1.0 + 1.2 * kick); o = vec4(c * a, 1.0); }`; diff --git a/web/src/scenes/water.js b/web/src/scenes/water.js index 490fa49..53c5337 100644 --- a/web/src/scenes/water.js +++ b/web/src/scenes/water.js @@ -35,7 +35,7 @@ void main(){ vec3 cold = tint * 0.6; vec3 c = mix(cold, hot, smoothstep(0.2, 0.9, flame + bass)) * (0.25 + 0.75 * exp(-depth * 0.12)); c *= mix(0.35, 1.0, tint.b + 0.2) ; - o = vec4(c * (0.15 + 0.5 * level + 0.35 * bass) * 0.22, 1.0); + o = vec4(c * (0.15 + 0.5 * level + 0.35 * bass) * 0.45, 1.0); }`; const PVS = GLSL_HEAD + ` @@ -45,14 +45,14 @@ out float life; out float heat; float h1(float x){ return fract(sin(x * 127.1) * 43758.5453); } void main(){ float age = time - a.x; - if (a.x < 0.0 || age < 0.0 || age > 2.6) { gl_Position = vec4(2.0, 2.0, 2.0, 1.0); gl_PointSize = 0.0; return; } + if (a.x < 0.0 || age < 0.0 || age > 2.0) { gl_Position = vec4(2.0, 2.0, 2.0, 1.0); gl_PointSize = 0.0; return; } float s = a.y, str = a.z; float vx = (h1(s) - 0.5) * 1.4 * (0.5 + str) + (a.w - 0.5) * 0.7; float vy = (1.2 + 2.6 * h1(s + 3.0) * str + 0.8 * str); float x = (a.w * 2.0 - 1.0) * 0.95 + vx * age + 0.08 * sin(age * 7.0 + s * 40.0); float y = -0.1 + vy * age - 1.9 * age * age; if (y < -0.1 && age > 0.1) { gl_Position = vec4(2.0, 2.0, 2.0, 1.0); gl_PointSize = 0.0; return; } - life = 1.0 - age / 2.6; heat = life * (0.4 + str); + life = 1.0 - age / 2.0; heat = life * (0.15 + 0.6 * str); gl_Position = vec4(x / (res.x / res.y), y, 0.0, 1.0); gl_PointSize = (2.0 + 9.0 * h1(s + 7.0) * str) * (0.4 + life) * res.y / 900.0; }`; @@ -61,7 +61,7 @@ in float life; in float heat; uniform vec3 tint; out vec4 o; void main(){ vec2 q = gl_PointCoord * 2.0 - 1.0; float r = dot(q, q); if (r > 1.0) discard; vec3 fire = mix(vec3(0.9, 0.12, 0.02), vec3(1.0, 0.85, 0.4), heat); - fire = mix(fire, tint * 1.3, 0.25 * (1.0 - heat)); + fire = mix(fire, tint * 1.3, 0.15 * (1.0 - heat)); o = vec4(fire * (1.0 - r) * life * 0.9, 1.0); }`; @@ -96,7 +96,7 @@ export class BurningWater { if (A.onset.count !== this.seen) { this.seen = A.onset.count; const s = A.onset.strength; - this.spawn(Math.round(80 + 520 * s), t, 0.35 + 0.65 * s); + this.spawn(Math.round(40 + 260 * s), t, 0.35 + 0.65 * s); const r = this.rhead; this.rhead = (this.rhead + 1) % 8; this.ripples.set([(Math.random() - 0.5) * 6, 1.5 + Math.random() * 4, t, 0.5 + s], r * 4); }