UNPKG

thetascape-sound-engine

Version:
358 lines (357 loc) 12.3 kB
import * as m from "tone"; import B, { useRef as x, useMemo as V, useEffect as T } from "react"; import W from "react-p5"; const h = Object.freeze({ INTRO: 0, DELTA: 1, THETA: 2, ALPHA: 3, BETA: 4, GAMMA: 5 }), R = Object.freeze( [2.5, 2.5, 5, 10, 20, 40] ); class E { constructor(t, i, e, r) { console.log("VisualizerBar: constructor", t, i, e, r), this.brainwaveID = t, this.barWidth = i, this.containerHeight = e, this.colors = r, this.carrierValue = 0, this.carrierAmp = 1, this.modulatorValue = 0.5, this.currentHeight = e, this.currentOffset = 0, this.gradient = null, this.lastGradientHeight = null, this._configureBrainwave(t); } // Just factor out the switch into a helper _configureBrainwave(t) { switch (t) { case 1: this.carrierAmp = 1.1, this.animSmoothing = 5, this.minWaveHeight = this.containerHeight * 0.8, this.maxWaveHeight = this.containerHeight * 1.1, this.modulationStrength = 0.12; break; case 2: this.carrierAmp = 1.1, this.animSmoothing = 4, this.minWaveHeight = this.containerHeight * 0.85, this.maxWaveHeight = this.containerHeight * 1.1, this.modulationStrength = 0.05; break; case 3: this.carrierAmp = 1.45, this.animSmoothing = 2, this.minWaveHeight = this.containerHeight * 1.25, this.maxWaveHeight = this.containerHeight * 1.55, this.modulationStrength = 0.075; break; case 4: this.carrierAmp = 1.2, this.animSmoothing = 3, this.minWaveHeight = this.containerHeight * 1.55, this.maxWaveHeight = this.containerHeight * 4, this.modulationStrength = 0.2; break; case 5: this.carrierAmp = 1.2, this.animSmoothing = 1, this.minWaveHeight = this.containerHeight * 1.1, this.maxWaveHeight = this.containerHeight * 2.75, this.modulationStrength = 0.115; break; //intro state default: this.carrierAmp = 1, this.animSmoothing = 1, this.minWaveHeight = this.containerHeight, this.maxWaveHeight = this.containerHeight, this.modulationStrength = 0; break; } } reset(t, i, e) { this.brainwaveID = t, this.containerHeight = i, this.colors = e, this.carrierValue = 0, this.modulatorValue = 0.5, this.currentHeight = i, this.currentOffset = 0, this.gradient = null, this.lastGradientHeight = null, this._configureBrainwave(t); } setValues(t, i) { this.carrierValue = t * this.carrierAmp, this.modulatorValue = i; } updateColors(t) { this.colors = t, this.gradient = null; } update(t) { const i = 1 / this.animSmoothing, e = this.maxWaveHeight - this.carrierValue * (this.maxWaveHeight - this.minWaveHeight); this.currentHeight += (e - this.currentHeight) * i; const r = this.modulatorValue - 0.5, a = this.containerHeight * this.modulationStrength, u = r * 2 * a; this.currentOffset += (u - this.currentOffset) * i; } draw(t, i, e) { if (this.currentHeight < 1) return; t.push(), t.translate(i, e + this.currentOffset); const r = Math.abs(this.currentHeight - this.lastGradientHeight); (!this.gradient || r > 5) && (this.gradient = t.drawingContext.createLinearGradient( 0, -this.currentHeight / 2, 0, this.currentHeight / 2 ), this.gradient.addColorStop(0, this.colors[0]), this.gradient.addColorStop(0.25, this.colors[1]), this.gradient.addColorStop(0.5, this.colors[2]), this.gradient.addColorStop(0.75, this.colors[3]), this.gradient.addColorStop(1, this.colors[4]), this.lastGradientHeight = this.currentHeight), t.drawingContext.fillStyle = this.gradient, t.noStroke(), t.rect(-this.barWidth / 2, -this.currentHeight / 2, this.barWidth, this.currentHeight), t.pop(); } } class k { constructor(t, i, e, r, a, u, M) { this.p5 = t, this.bars = []; const f = e * (r + a), g = (t.width - f) / 2 + a / 2; for (let s = 0; s < e; s++) { const d = g + s * (r + a), c = new E(i, r, u, M); this.bars.push({ bar: c, x: d }); } } resetAllBars(t, i, e) { for (const { bar: r } of this.bars) r.reset(t, i, e); } updateColors(t) { for (const { bar: i } of this.bars) i.updateColors?.(t); } updateAndDraw(t, i) { if (t.length !== i.length) return; const { p5: e } = this; for (let r = 0; r < this.bars.length; r++) { const { bar: a, x: u } = this.bars[r]; a.setValues(t[r] || 0, i[r] || 0.5), a.update(e), a.draw(e, u, e.height / 2); } } } const P = { getColors(l) { switch (l) { case h.DELTA: return { barColors: [ "#1d262d", "#0468b8", "#bbd0e0", "#43b3d3", "#202025" ], bgColors: [ "#1d262d", "#15445d", "#202025" ] }; case h.THETA: return { barColors: [ "#22242f", "#7d82a6", "#f8f8fa", "#5d6393", "#1f1f24" ], bgColors: [ "#22242f", "#373e73", "#1f1f24" ] }; case h.ALPHA: return { barColors: [ "#01a2cb", "#45bbd9", "#ffffff", "#038ecf", "#0398cf" ], bgColors: [ "#01a2cb", "#e1eff3", "#0398cf" ] }; case h.BETA: return { barColors: [ "#0072CE", "#127cd1", "#ffffff", "#00a2cb", "#022d97" ], bgColors: [ "#0072CE", "#a9cfee", "#022d97" ] }; case h.GAMMA: return { barColors: [ "#168cb9", "#168cb9", "#ffffff", "#5e6ac9", "#5e6ac9" ], bgColors: [ "#168cb9", "#ffffff", "#5e6ac9" ] }; default: return { barColors: [ "#8A8FB6", "#aab5bf", "#D7E4F0", "#aab5bf", "#8A8FB6" ], bgColors: [ "#8A8FB6", "#D7E4F0", "#8A8FB6" ] }; } } }, q = { processMeters: (l, t, i = [0, 16], e = !1) => { const r = l(), a = Object.values(r), [u, M] = i, f = a.slice(u, M), g = [], s = f.length - 1, d = -135, c = -80, H = (o) => Math.max(0, Math.min(1, o)), n = (o) => H((o - d) / (c - d)); if (e) { const o = Math.ceil(t / 2); for (let p = 0; p < o; p++) { const w = p / (o - 1 || 1) * s, A = Math.floor(w), S = Math.min(A + 1, s), C = w - A, z = f[A] * (1 - C) + f[S] * C, v = n(z); g[p] = v, g[t - 1 - p] = v; } return g; } else { for (let o = 0; o < t; o++) { const b = o / (t - 1 || 1) * s, w = Math.floor(b), A = Math.min(w + 1, s), S = b - w, C = f[w] * (1 - S) + f[A] * S, z = n(C); g.push(z); } return g; } } }; class O { constructor(t) { this.barTotal = t, this.phase = 0, this.freqModPhase = 0, this.ampModPhase = 0, this.widthModPhase = 0, this.freqModSpeed = 0, this.ampModSpeed = 0, this.widthModSpeed = 0, this.ampMultiplier = 1, this.widthMultiplier = 1, this.minHz = 1, this.maxHz = 1, this.speed = 0, this._ampModValue = 1, this._widthModValue = 1, this.active = !1, this._idleArray = new Array(this.barTotal).fill(0.5); } setBrainwave(t) { const e = { [h.DELTA]: { minHz: 0.075, maxHz: 0.175, amp: 0.8, width: 0.04, freq: 3e-3, ampS: 4e-3, widthS: 3e-3 }, [h.THETA]: { minHz: 0.175, maxHz: 0.3, amp: 1.2, width: 0.05, freq: 4e-3, ampS: 5e-3, widthS: 4e-3 }, [h.ALPHA]: { minHz: 0.2, maxHz: 0.4, amp: 1.5, width: 0.05, freq: 5e-3, ampS: 1e-3, widthS: 25e-4 }, [h.BETA]: { minHz: 2.2, maxHz: 3.3, amp: 1.5, width: 1.5, freq: 1e-3, ampS: 0, widthS: 1e-3 }, [h.GAMMA]: { minHz: 1.1, maxHz: 1.6, amp: 1.025, width: 0.75, freq: 1e-3, ampS: 1e-3, widthS: 0 } }[t]; if (!e) { console.warn("Unknown brainwave ID:", t); return; } this.minHz = e.minHz, this.maxHz = e.maxHz, this.ampMultiplier = e.amp, this.widthMultiplier = e.width, this.freqModSpeed = e.freq, this.ampModSpeed = e.ampS, this.widthModSpeed = e.widthS, this.active = !0; } _computeSpeed(t) { const i = 8 / t; return 2 * Math.PI / i; } update() { if (!this.active) return; this.freqModPhase += this.freqModSpeed, this.ampModPhase += this.ampModSpeed, this.widthModPhase += this.widthModSpeed, this.freqModPhase > Math.PI * 2 && (this.freqModPhase -= Math.PI * 2), this.ampModPhase > Math.PI * 2 && (this.ampModPhase -= Math.PI * 2), this.widthModPhase > Math.PI * 2 && (this.widthModPhase -= Math.PI * 2); const t = Math.sin(this.freqModPhase) * 0.5 + 0.5, i = Math.sin(this.ampModPhase) * 0.5 + 0.5, e = Math.sin(this.widthModPhase) * 0.5 + 0.5, r = this.minHz + t * (this.maxHz - this.minHz); this.speed = this._computeSpeed(r), this._ampModValue = i * this.ampMultiplier, this._widthModValue = 1 + e * this.widthMultiplier, this.phase += this.speed, this.phase > Math.PI * 2 && (this.phase -= Math.PI * 2); } getModulatorWave() { if (!this.active) return this._idleArray; const t = this._ampModValue, i = this._widthModValue, e = new Array(this.barTotal), r = Math.PI * 2; for (let a = 0; a < this.barTotal; a++) { const M = a / this.barTotal * r * i; e[a] = Math.sin(this.phase + M) * 0.5 * t + 0.5; } return e; } } const L = ({ getLevels: l, brainwaveID: t }) => { console.log("Visualizer: brainwaveID", t); const i = x(null), e = x(null), r = x(null), a = x(!1), u = V(() => P.getColors(t), [t]); T(() => { if (e.current && e.current.setBrainwave(t), a.current = !1, i.current) { const { p5: s } = i.current, d = s.height, c = P.getColors(t).barColors; i.current.resetAllBars(t, d, c); } }, [t]); const M = (s, d) => { s.createCanvas(s.windowWidth, s.windowHeight).parent(d), s.frameRate(8), s.noSmooth(); const c = 2, H = 11, n = Math.floor(s.width / (c + H)); r.current = n, i.current = new k( s, t, n, c, H, s.height, u.barColors ), e.current || (e.current = new O(n), e.current.setBrainwave(t)); }, f = (s) => { const d = P.getColors(t).bgColors, c = s.drawingContext.createLinearGradient(0, 0, 0, s.height); if (c.addColorStop(0, d[0]), c.addColorStop(0.5, d[1]), c.addColorStop(1, d[2]), s.drawingContext.fillStyle = c, s.noStroke(), s.rect(0, 0, s.width, s.height), a.current = !0, !i.current || !l || !e.current) return; const H = l(); let n = [0, 16], o = !1; switch (t) { case h.DELTA: n = [0, 4], o = !0; break; case h.THETA: n = [2, 8], o = !0; break; case h.ALPHA: n = [2, 10], o = !0; break; case h.BETA: n = [4, 12], o = !1; break; case h.GAMMA: n = [6, 16], o = !0; break; default: n = [0, 16], o = !0; break; } const p = q.processMeters(() => H, i.current.bars.length, n, o); e.current.update(); const b = e.current.getModulatorWave(); i.current.updateAndDraw(p, b); }, g = (s) => { s.resizeCanvas(s.windowWidth, s.windowHeight); }; return /* @__PURE__ */ B.createElement(W, { setup: M, draw: f, windowResized: g }); }, _ = async () => { console.log("APP: Init ToneJS and Audio Engine"); const l = new m.Context( { latencyHint: "playback" } ); m.setContext(l), console.log("AUDIO: Tone.Context: Latency Hint:", m.getContext().latencyHint, "| Sample Rate:", m.getContext().sampleRate), m.getContext().rawContext.suspend(), await m.start(), console.log("AUDIO: Tone started", m.getContext().state, m); const { default: t } = await import("./AudioEngine-BGZy8HUE.js"); return new t(m); }, F = { init: _ }; export { R as SongFrequencies, h as SongIDs, L as Visualizer, F as default };