Audio / App.js
Winhaa's picture
Upload App.js
01c2954 verified
Raw
History Blame Contribute Delete
6.44 kB
import React, { useRef, useEffect, useState } from 'react';
import './App.css';
function App() {
const audioContextRef = useRef(null);
const analyserRef = useRef(null);
const gainNodeRef = useRef(null);
const bassFilterRef = useRef(null);
const trebleFilterRef = useRef(null);
const sourceRef = useRef(null);
const canvasRef = useRef(null);
const animationFrameIdRef = useRef(null);
const [isPlaying, setIsPlaying] = useState(false);
const [volume, setVolume] = useState(0.7);
const [bass, setBass] = useState(0);
const [treble, setTreble] = useState(0);
useEffect(() => {
// Initialize AudioContext and nodes
audioContextRef.current = new (window.AudioContext || window.webkitAudioContext)();
analyserRef.current = audioContextRef.current.createAnalyser();
gainNodeRef.current = audioContextRef.current.createGain();
bassFilterRef.current = audioContextRef.current.createBiquadFilter();
trebleFilterRef.current = audioContextRef.current.createBiquadFilter();
// Configure filters
bassFilterRef.current.type = 'lowshelf';
bassFilterRef.current.frequency.value = 250; // Bass frequency
trebleFilterRef.current.type = 'highshelf';
trebleFilterRef.current.frequency.value = 4000; // Treble frequency
// Connect nodes: Source -> Bass -> Treble -> Gain -> Analyser -> Destination
// Source will be connected when audio starts playing
bassFilterRef.current.connect(trebleFilterRef.current);
trebleFilterRef.current.connect(gainNodeRef.current);
gainNodeRef.current.connect(analyserRef.current);
analyserRef.current.connect(audioContextRef.current.destination);
// Set initial volume
gainNodeRef.current.gain.value = volume;
// Clean up on unmount
return () => {
if (animationFrameIdRef.current) {
cancelAnimationFrame(animationFrameIdRef.current);
}
if (audioContextRef.current) {
audioContextRef.current.close();
}
};
}, []);
useEffect(() => {
if (gainNodeRef.current) {
gainNodeRef.current.gain.value = volume;
}
}, [volume]);
useEffect(() => {
if (bassFilterRef.current) {
bassFilterRef.current.gain.value = bass; // Gain in dB
}
}, [bass]);
useEffect(() => {
if (trebleFilterRef.current) {
trebleFilterRef.current.gain.value = treble; // Gain in dB
}
}, [treble]);
const loadAndPlayAudio = async () => {
if (sourceRef.current) {
sourceRef.current.stop();
sourceRef.current.disconnect();
}
// For demonstration, we'll use a dummy audio source.
// In a real app, you'd fetch an actual audio file.
const response = await fetch('https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3');
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContextRef.current.decodeAudioData(arrayBuffer);
sourceRef.current = audioContextRef.current.createBufferSource();
sourceRef.current.buffer = audioBuffer;
sourceRef.current.loop = true;
sourceRef.current.connect(bassFilterRef.current);
sourceRef.current.start(0);
setIsPlaying(true);
drawVisualizer();
};
const togglePlayPause = () => {
if (audioContextRef.current.state === 'suspended') {
audioContextRef.current.resume();
} else if (audioContextRef.current.state === 'running' && isPlaying) {
audioContextRef.current.suspend();
} else if (audioContextRef.current.state === 'suspended' && !isPlaying) {
audioContextRef.current.resume();
}
if (!isPlaying && !sourceRef.current) {
loadAndPlayAudio();
} else {
setIsPlaying(!isPlaying);
}
};
const drawVisualizer = () => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
const WIDTH = canvas.width;
const HEIGHT = canvas.height;
analyserRef.current.fftSize = 256;
const bufferLength = analyserRef.current.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
const renderFrame = () => {
animationFrameIdRef.current = requestAnimationFrame(renderFrame);
analyserRef.current.getByteFrequencyData(dataArray);
ctx.clearRect(0, 0, WIDTH, HEIGHT);
ctx.fillStyle = 'rgb(0, 0, 0)';
ctx.fillRect(0, 0, WIDTH, HEIGHT);
const barWidth = (WIDTH / bufferLength) * 2.5;
let x = 0;
for (let i = 0; i < bufferLength; i++) {
const barHeight = dataArray[i] / 2;
ctx.fillStyle = 'rgb(' + (barHeight + 100) + ',50,50)';
ctx.fillRect(x, HEIGHT - barHeight / 2, barWidth, barHeight);
x += barWidth + 1;
}
};
renderFrame();
};
// Placeholder for Telegram Mini App SDK initialization
useEffect(() => {
if (window.Telegram && window.Telegram.WebApp) {
window.Telegram.WebApp.ready();
window.Telegram.WebApp.expand();
console.log('Telegram Mini App initialized');
// Example: Set a button in Telegram
// window.Telegram.WebApp.MainButton.setText('Play Audio').show();
// window.Telegram.WebApp.MainButton.onClick(() => togglePlayPause());
}
}, []);
return (
<div className="App">
<header className="App-header">
<h1>HCY Audio System Clone</h1>
<canvas ref={canvasRef} width="300" height="100"></canvas>
<div className="controls">
<button onClick={togglePlayPause}>
{isPlaying ? 'Pause' : 'Play'}
</button>
<div>
<label>Volume:</label>
<input
type="range"
min="0"
max="1"
step="0.01"
value={volume}
onChange={(e) => setVolume(parseFloat(e.target.value))}
/>
</div>
<div>
<label>Bass:</label>
<input
type="range"
min="-20"
max="20"
step="1"
value={bass}
onChange={(e) => setBass(parseFloat(e.target.value))}
/>
</div>
<div>
<label>Treble:</label>
<input
type="range"
min="-20"
max="20"
step="1"
value={treble}
onChange={(e) => setTreble(parseFloat(e.target.value))}
/>
</div>
</div>
</header>
</div>
);
}
export default App;