-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWaveformVisualizer.js
More file actions
155 lines (155 loc) · 7.23 KB
/
WaveformVisualizer.js
File metadata and controls
155 lines (155 loc) · 7.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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;
}();