arpeggiator / WaveformVisualizer.js
stereoDrift's picture
Upload 14 files
f8b493b verified
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;
}();