-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaudiio.html
More file actions
134 lines (90 loc) · 2.97 KB
/
Copy pathaudiio.html
File metadata and controls
134 lines (90 loc) · 2.97 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
<!DOCTYPE html>
<html>
<head>
<style>
.visualiser-container {
position: absolute;
top: 200px;
left: 0;
right: 0;
height: 200px;
}
canvas {
position: absolute;
top: 0;
left: 0;
}
#playhead {
position: absolute;
top: 0;
width: 2px;
height: 200px;
background: white;
z-index: 2;
}
</style>
</head>
<body>
<audio src="audio_test.mp3" controls style="position: absolute; z-index: 3;"></audio>
<div class="visualiser-container">
<canvas id="spectrogram"></canvas>
<div id="playhead"></div>
</div>
<script>
(function() {
const audio = document.querySelector("audio");
const canvas = document.getElementById("spectrogram");
const ctx = canvas.getContext("2d");
const playhead = document.getElementById("playhead");
canvas.width = window.innerWidth;
canvas.height = 200;
async function generateSpectrogram() {
const context = new AudioContext();
// fetch + decode full audio buffer
const response = await fetch(audio.src);
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await context.decodeAudioData(arrayBuffer);
const analyser = context.createAnalyser();
analyser.fftSize = 2048;
const bufferSource = context.createBufferSource();
bufferSource.buffer = audioBuffer;
bufferSource.connect(analyser);
const freqData = new Uint8Array(analyser.frequencyBinCount);
const duration = audioBuffer.duration;
const step = 0.005; // seconds per slice (controls horizontal resolution)
const totalSlices = Math.floor(duration / step);
const sliceWidth = canvas.width / totalSlices;
// offline processing
for (let i = 0; i < totalSlices; i++) {
const time = i * step;
// create tiny buffer slice
const tempSource = context.createBufferSource();
tempSource.buffer = audioBuffer;
tempSource.connect(analyser);
tempSource.start(0, time, step);
await new Promise(r => setTimeout(r, 1)); // allow analyser to fill
analyser.getByteFrequencyData(freqData);
for (let j = 0; j < freqData.length; j++) {
const value = freqData[j];
const percent = value / 255;
const y = canvas.height - (j / freqData.length) * canvas.height;
const hue = 240 - percent * 240;
ctx.fillStyle = `hsl(${hue}, 100%, ${percent * 50}%)`;
ctx.fillRect(i * sliceWidth, y, sliceWidth, canvas.height / freqData.length);
}
}
}
function animatePlayhead() {
requestAnimationFrame(animatePlayhead);
const percent = audio.currentTime / audio.duration;
const x = percent * canvas.width;
playhead.style.left = x + "px";
}
audio.onplay = () => {
animatePlayhead();
};
generateSpectrogram();
})();
</script>
</body>
</html>