Changes
This commit is contained in:
@@ -1,68 +1,15 @@
|
||||
import { useEffect, useRef, useCallback } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useSettings } from '@/contexts/SettingsContext';
|
||||
import { useMusic } from '@/contexts/MusicContext';
|
||||
import { useAudioAnalyzer } from '@/contexts/AudioAnalyzerContext';
|
||||
|
||||
export function MiniOscilloscope() {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const animationRef = useRef<number>();
|
||||
const analyzerRef = useRef<AnalyserNode | null>(null);
|
||||
const audioContextRef = useRef<AudioContext | null>(null);
|
||||
const sourceNodeRef = useRef<MediaElementAudioSourceNode | null>(null);
|
||||
const connectedElementRef = useRef<HTMLAudioElement | null>(null);
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { playSound } = useSettings();
|
||||
const { audioElement, isPlaying } = useMusic();
|
||||
|
||||
// Connect to music player's audio element
|
||||
useEffect(() => {
|
||||
if (!audioElement) return;
|
||||
|
||||
// Skip if already connected to this element
|
||||
if (connectedElementRef.current === audioElement) return;
|
||||
|
||||
const connectToAudio = async () => {
|
||||
try {
|
||||
// Create or resume audio context
|
||||
if (!audioContextRef.current) {
|
||||
audioContextRef.current = new AudioContext();
|
||||
}
|
||||
|
||||
if (audioContextRef.current.state === 'suspended') {
|
||||
await audioContextRef.current.resume();
|
||||
}
|
||||
|
||||
// Create analyzer if needed
|
||||
if (!analyzerRef.current) {
|
||||
analyzerRef.current = audioContextRef.current.createAnalyser();
|
||||
analyzerRef.current.fftSize = 256;
|
||||
analyzerRef.current.smoothingTimeConstant = 0.8;
|
||||
}
|
||||
|
||||
// Disconnect old source if exists
|
||||
if (sourceNodeRef.current) {
|
||||
try {
|
||||
sourceNodeRef.current.disconnect();
|
||||
} catch (e) {
|
||||
// Ignore disconnect errors
|
||||
}
|
||||
}
|
||||
|
||||
// Create new source from the audio element
|
||||
sourceNodeRef.current = audioContextRef.current.createMediaElementSource(audioElement);
|
||||
sourceNodeRef.current.connect(analyzerRef.current);
|
||||
sourceNodeRef.current.connect(audioContextRef.current.destination);
|
||||
connectedElementRef.current = audioElement;
|
||||
|
||||
console.log('MiniOscilloscope connected to audio element');
|
||||
} catch (e) {
|
||||
console.log('Could not connect to audio element:', e);
|
||||
}
|
||||
};
|
||||
|
||||
connectToAudio();
|
||||
}, [audioElement]);
|
||||
const { analyzerNode } = useAudioAnalyzer();
|
||||
|
||||
// Draw waveform
|
||||
useEffect(() => {
|
||||
@@ -102,42 +49,48 @@ export function MiniOscilloscope() {
|
||||
ctx.lineTo(width, height / 2);
|
||||
ctx.stroke();
|
||||
|
||||
// Draw waveform
|
||||
if (analyzerRef.current && isPlaying) {
|
||||
const bufferLength = analyzerRef.current.frequencyBinCount;
|
||||
// Draw waveform from analyzer
|
||||
let hasAudio = false;
|
||||
|
||||
if (analyzerNode) {
|
||||
const bufferLength = analyzerNode.frequencyBinCount;
|
||||
const dataArray = new Uint8Array(bufferLength);
|
||||
analyzerRef.current.getByteTimeDomainData(dataArray);
|
||||
analyzerNode.getByteTimeDomainData(dataArray);
|
||||
|
||||
// Check if there's actual audio (not just silence)
|
||||
const hasAudio = dataArray.some(v => Math.abs(v - 128) > 2);
|
||||
hasAudio = dataArray.some(v => Math.abs(v - 128) > 2);
|
||||
|
||||
ctx.strokeStyle = 'hsl(120, 100%, 50%)';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.shadowColor = 'hsl(120, 100%, 50%)';
|
||||
ctx.shadowBlur = hasAudio ? 10 : 5;
|
||||
ctx.beginPath();
|
||||
if (hasAudio) {
|
||||
ctx.strokeStyle = 'hsl(120, 100%, 50%)';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.shadowColor = 'hsl(120, 100%, 50%)';
|
||||
ctx.shadowBlur = 10;
|
||||
ctx.beginPath();
|
||||
|
||||
const sliceWidth = width / bufferLength;
|
||||
let x = 0;
|
||||
const sliceWidth = width / bufferLength;
|
||||
let x = 0;
|
||||
|
||||
for (let i = 0; i < bufferLength; i++) {
|
||||
const v = dataArray[i] / 128.0;
|
||||
const y = (v * height) / 2;
|
||||
for (let i = 0; i < bufferLength; i++) {
|
||||
const v = dataArray[i] / 128.0;
|
||||
const y = (v * height) / 2;
|
||||
|
||||
if (i === 0) {
|
||||
ctx.moveTo(x, y);
|
||||
} else {
|
||||
ctx.lineTo(x, y);
|
||||
if (i === 0) {
|
||||
ctx.moveTo(x, y);
|
||||
} else {
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
|
||||
x += sliceWidth;
|
||||
}
|
||||
|
||||
x += sliceWidth;
|
||||
ctx.lineTo(width, height / 2);
|
||||
ctx.stroke();
|
||||
ctx.shadowBlur = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.lineTo(width, height / 2);
|
||||
ctx.stroke();
|
||||
ctx.shadowBlur = 0;
|
||||
} else {
|
||||
// No audio playing - draw idle animation
|
||||
// Draw idle animation when no audio
|
||||
if (!hasAudio) {
|
||||
const time = Date.now() / 1000;
|
||||
ctx.strokeStyle = 'hsl(120, 100%, 50%, 0.5)';
|
||||
ctx.lineWidth = 1;
|
||||
@@ -164,7 +117,7 @@ export function MiniOscilloscope() {
|
||||
cancelAnimationFrame(animationRef.current);
|
||||
}
|
||||
};
|
||||
}, [isPlaying]);
|
||||
}, [analyzerNode]);
|
||||
|
||||
// Handle resize
|
||||
useEffect(() => {
|
||||
@@ -187,16 +140,6 @@ export function MiniOscilloscope() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Cleanup on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (animationRef.current) {
|
||||
cancelAnimationFrame(animationRef.current);
|
||||
}
|
||||
// Don't close audio context as it would break the music player
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleClick = () => {
|
||||
playSound('click');
|
||||
navigate('/oscilloscope');
|
||||
@@ -210,7 +153,7 @@ export function MiniOscilloscope() {
|
||||
return (
|
||||
<div
|
||||
onClick={handleClick}
|
||||
className="fixed bottom-4 left-1/2 -translate-x-1/2 w-[300px] md:w-[400px] h-[50px] z-50 cursor-pointer group"
|
||||
className="fixed bottom-4 left-1/2 -translate-x-1/2 w-[350px] md:w-[500px] h-[70px] z-50 cursor-pointer group"
|
||||
title="Open Oscilloscope"
|
||||
>
|
||||
<div className="relative w-full h-full rounded-lg border border-primary/50 overflow-hidden bg-background/80 backdrop-blur-sm transition-all duration-300 group-hover:border-primary group-hover:shadow-[0_0_20px_hsl(var(--primary)/0.4)]">
|
||||
|
||||
Reference in New Issue
Block a user