Spaces:
Running
Running
function _class_call_check(instance, Constructor) { | |
if (!(instance instanceof Constructor)) { | |
throw new TypeError("Cannot call a class as a function"); | |
} | |
} | |
function _defineProperties(target, props) { | |
for(var i = 0; i < props.length; i++){ | |
var descriptor = props[i]; | |
descriptor.enumerable = descriptor.enumerable || false; | |
descriptor.configurable = true; | |
if ("value" in descriptor) descriptor.writable = true; | |
Object.defineProperty(target, descriptor.key, descriptor); | |
} | |
} | |
function _create_class(Constructor, protoProps, staticProps) { | |
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | |
if (staticProps) _defineProperties(Constructor, staticProps); | |
return Constructor; | |
} | |
function _instanceof(left, right) { | |
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { | |
return !!right[Symbol.hasInstance](left); | |
} else { | |
return left instanceof right; | |
} | |
} | |
import * as THREE from 'three'; | |
// A class to create and manage a waveform visualizer using Tone.Analyser | |
export var WaveformVisualizer = /*#__PURE__*/ function() { | |
"use strict"; | |
function WaveformVisualizer(scene, analyser, canvasWidth, canvasHeight) { | |
_class_call_check(this, WaveformVisualizer); | |
this.scene = scene; | |
this.analyser = analyser; | |
this.mesh = null; | |
this.bufferLength = this.analyser.size; | |
this.dataArray = new Float32Array(this.bufferLength); | |
this.smoothedDataArray = new Float32Array(this.bufferLength); // For smoothing | |
// Visual properties | |
this.smoothingFactor = 0.4; // How much to smooth the wave (0.0 - 1.0) | |
this.width = canvasWidth * 0.8; // Occupy 80% of the screen width | |
this.height = 450; // The vertical amplitude of the wave | |
this.yPosition = 0; // The vertical center of the wave | |
this.thickness = 30.0; // The thickness of the line mesh | |
this.currentColor = new THREE.Color('#7B4394'); | |
this.targetColor = new THREE.Color('#7B4394'); | |
this.uniforms = { | |
solidColor: { | |
value: this.currentColor | |
} | |
}; | |
this._createVisualizer(); | |
} | |
_create_class(WaveformVisualizer, [ | |
{ | |
key: "_createVisualizer", | |
value: function _createVisualizer() { | |
var material = new THREE.ShaderMaterial({ | |
uniforms: this.uniforms, | |
vertexShader: "\n varying vec2 vUv;\n void main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n ", | |
fragmentShader: "\n uniform vec3 solidColor;\n void main() {\n gl_FragColor = vec4(solidColor, 0.9);\n }\n ", | |
transparent: true, | |
side: THREE.DoubleSide | |
}); | |
var geometry = new THREE.BufferGeometry(); | |
var positions = new Float32Array(this.bufferLength * 2 * 3); | |
var uvs = new Float32Array(this.bufferLength * 2 * 2); | |
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); | |
geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2)); | |
var indices = []; | |
for(var i = 0; i < this.bufferLength - 1; i++){ | |
var p1 = i * 2; // top-left | |
var p2 = p1 + 1; // bottom-left | |
var p3 = (i + 1) * 2; // top-right | |
var p4 = p3 + 1; // bottom-right | |
indices.push(p1, p2, p3); | |
indices.push(p2, p4, p3); | |
} | |
geometry.setIndex(indices); | |
this.mesh = new THREE.Mesh(geometry, material); | |
this.scene.add(this.mesh); | |
this.updatePosition(window.innerWidth, window.innerHeight); | |
} | |
}, | |
{ | |
// Call this from the main animation loop | |
key: "update", | |
value: function update() { | |
if (!this.analyser || !this.mesh) return; | |
// Smoothly interpolate the current color towards the target color | |
this.currentColor.lerp(this.targetColor, 0.05); | |
var newArray = this.analyser.getValue(); | |
if (_instanceof(newArray, Float32Array)) { | |
this.dataArray.set(newArray); | |
} | |
var positions = this.mesh.geometry.attributes.position.array; | |
var uvs = this.mesh.geometry.attributes.uv.array; | |
var startX = -this.width / 2; | |
var xStep = this.width / (this.bufferLength - 1); | |
var halfThickness = this.thickness / 2; | |
for(var i = 0; i < this.bufferLength; i++){ | |
// Apply exponential smoothing | |
this.smoothedDataArray[i] = this.smoothingFactor * this.dataArray[i] + (1 - this.smoothingFactor) * this.smoothedDataArray[i]; | |
var x = startX + i * xStep; | |
var y = this.yPosition + this.smoothedDataArray[i] * this.height; | |
// Set top and bottom vertices for the ribbon | |
var vertexIndex = i * 2 * 3; | |
positions[vertexIndex] = x; | |
positions[vertexIndex + 1] = y + halfThickness; | |
positions[vertexIndex + 2] = 2; | |
positions[vertexIndex + 3] = x; | |
positions[vertexIndex + 4] = y - halfThickness; | |
positions[vertexIndex + 5] = 2; | |
// Set UVs | |
var uvIndex = i * 2 * 2; | |
uvs[uvIndex] = i / (this.bufferLength - 1); // U coordinate | |
uvs[uvIndex + 1] = 1.0; // V for top vertex | |
uvs[uvIndex + 2] = i / (this.bufferLength - 1); // U coordinate | |
uvs[uvIndex + 3] = 0.0; // V for bottom vertex | |
} | |
this.mesh.geometry.attributes.position.needsUpdate = true; | |
this.mesh.geometry.attributes.uv.needsUpdate = true; | |
this.mesh.geometry.computeBoundingSphere(); | |
} | |
}, | |
{ | |
key: "updateColor", | |
value: function updateColor(newColor) { | |
if (this.uniforms) { | |
this.targetColor.set(newColor); | |
} | |
} | |
}, | |
{ | |
// Call this on window resize | |
key: "updatePosition", | |
value: function updatePosition(canvasWidth, canvasHeight) { | |
this.width = canvasWidth * 0.8; | |
this.yPosition = -canvasHeight / 2 + 250; // Position it higher, above the drum beat indicators | |
} | |
}, | |
{ | |
// Clean up Three.js resources | |
key: "dispose", | |
value: function dispose() { | |
if (this.mesh) { | |
this.scene.remove(this.mesh); | |
if (this.mesh.geometry) this.mesh.geometry.dispose(); | |
if (this.mesh.material) this.mesh.material.dispose(); | |
} | |
} | |
} | |
]); | |
return WaveformVisualizer; | |
}(); |