97 lines
3.6 KiB
JavaScript
97 lines
3.6 KiB
JavaScript
document.addEventListener('DOMContentLoaded', () => {
|
|
const startButton = document.getElementById('startButton');
|
|
const canvas = document.getElementById('visualizer');
|
|
const statusEl = document.getElementById('status');
|
|
const canvasCtx = canvas.getContext('2d');
|
|
|
|
let audioContext;
|
|
let analyser;
|
|
let dataArray;
|
|
let bufferLength;
|
|
let animationFrameId;
|
|
let isVisualizing = false;
|
|
|
|
const draw = () => {
|
|
animationFrameId = requestAnimationFrame(draw);
|
|
|
|
analyser.getByteTimeDomainData(dataArray);
|
|
|
|
canvasCtx.fillStyle = '#f8f9fa'; // Light gray background
|
|
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
const gradient = canvasCtx.createLinearGradient(0, 0, canvas.width, 0);
|
|
gradient.addColorStop(0, '#6A5ACD'); // SlateBlue
|
|
gradient.addColorStop(1, '#48D1CC'); // MediumTurquoise
|
|
canvasCtx.strokeStyle = gradient;
|
|
|
|
canvasCtx.lineWidth = 3;
|
|
canvasCtx.beginPath();
|
|
|
|
const sliceWidth = canvas.width * 1.0 / bufferLength;
|
|
let x = 0;
|
|
|
|
for (let i = 0; i < bufferLength; i++) {
|
|
const v = dataArray[i] / 128.0;
|
|
const y = v * canvas.height / 2;
|
|
|
|
if (i === 0) {
|
|
canvasCtx.moveTo(x, y);
|
|
} else {
|
|
canvasCtx.lineTo(x, y);
|
|
}
|
|
|
|
x += sliceWidth;
|
|
}
|
|
|
|
canvasCtx.lineTo(canvas.width, canvas.height / 2);
|
|
canvasCtx.stroke();
|
|
};
|
|
|
|
const startVisualization = async () => {
|
|
if (isVisualizing) return;
|
|
|
|
try {
|
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
|
|
|
|
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
analyser = audioContext.createAnalyser();
|
|
|
|
const source = audioContext.createMediaStreamSource(stream);
|
|
source.connect(analyser);
|
|
|
|
analyser.fftSize = 2048;
|
|
bufferLength = analyser.frequencyBinCount;
|
|
dataArray = new Uint8Array(bufferLength);
|
|
|
|
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
isVisualizing = true;
|
|
startButton.disabled = true;
|
|
startButton.innerHTML = `
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-soundwave" viewBox="0 0 16 16">
|
|
<path fill-rule="evenodd" d="M8.5 2a.5.5 0 0 1 .5.5v11a.5.5 0 0 1-1 0v-11a.5.5 0 0 1 .5-.5zm-2 2a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5zm4 0a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5zm-6 1.5A.5.5 0 0 1 5 6v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm8 0a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm-10 1A.5.5 0 0 1 3 7v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5zm12 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5z"/>
|
|
</svg>
|
|
Visualizing...`;
|
|
statusEl.textContent = 'Your voice is being visualized in real-time.';
|
|
|
|
draw();
|
|
|
|
} catch (err) {
|
|
console.error('Error accessing microphone:', err);
|
|
statusEl.textContent = 'Could not access microphone. Please check permissions and try again.';
|
|
statusEl.style.color = 'red';
|
|
}
|
|
};
|
|
|
|
startButton.addEventListener('click', startVisualization);
|
|
|
|
// Clean up on page unload
|
|
window.addEventListener('beforeunload', () => {
|
|
if (audioContext) {
|
|
audioContext.close();
|
|
}
|
|
if (animationFrameId) {
|
|
cancelAnimationFrame(animationFrameId);
|
|
}
|
|
});
|
|
}); |