refactor AI WaveForm.jsx
Signed-off-by: ale <ale@manalejandro.com>
This commit is contained in:
parent
8bbd0c3b27
commit
1cb4b8a023
@ -29,7 +29,6 @@
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<script src="js/materialize.min.js" defer></script>
|
||||
<title>Stream Radio</title>
|
||||
</head>
|
||||
<body>
|
||||
|
6
public/js/materialize.min.js
vendored
6
public/js/materialize.min.js
vendored
File diff suppressed because one or more lines are too long
60
src/App.js
60
src/App.js
@ -1,7 +1,9 @@
|
||||
import "./App.css";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import WaveForm from "./WaveForm";
|
||||
import M from "materialize-css";
|
||||
import { Button, Icon, Range } from "react-materialize";
|
||||
import text from "./list.txt";
|
||||
|
||||
const App = () => {
|
||||
const [audioUrl, setAudioUrl] = useState(null),
|
||||
@ -17,6 +19,7 @@ const App = () => {
|
||||
[maxListeners, setMaxListeners] = useState(0),
|
||||
[paused, setPaused] = useState(true),
|
||||
audioElmRef = useRef(null),
|
||||
loadedAnalyzer = useRef(false),
|
||||
once = useRef(false),
|
||||
audioAnalyzer = () => {
|
||||
const audioCtx = new (window.AudioContext || window.webkitAudioContext)(),
|
||||
@ -32,23 +35,20 @@ const App = () => {
|
||||
}
|
||||
setAnalyzerData({ analyzer, bufferLength, dataArray })
|
||||
},
|
||||
loadData = () => {
|
||||
fetch('/stream.json').then(response => response.json()).then(json => {
|
||||
loadData = async () => {
|
||||
try {
|
||||
const response = await fetch('/stream.json'),
|
||||
json = await response.json()
|
||||
setJson(json)
|
||||
}).catch(err => {
|
||||
} catch (err) {
|
||||
console.error('Error fetching data: ' + err.message)
|
||||
})
|
||||
}
|
||||
},
|
||||
loadImages = () => {
|
||||
fetch('/wallpapers/list.txt').then(response => response.text()).then(text => {
|
||||
setImages(text.split('\n').filter(line => line.length > 0))
|
||||
}).catch(err => {
|
||||
console.error('Error fetching data: ' + err.message)
|
||||
})
|
||||
},
|
||||
loadListeners = () => {
|
||||
fetch('/status.xsl').then(response => response.text()).then(data => {
|
||||
const parser = new DOMParser(),
|
||||
loadListeners = async () => {
|
||||
try {
|
||||
const response = await fetch('/status.xsl'),
|
||||
data = await response.text(),
|
||||
parser = new DOMParser(),
|
||||
xmlDoc = parser.parseFromString(data, 'text/html'),
|
||||
listeners = xmlDoc.getElementsByTagName('td')
|
||||
for (let i = 0; i < listeners.length; i++) {
|
||||
@ -58,14 +58,23 @@ const App = () => {
|
||||
setMaxListeners(listeners[i].textContent)
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
} catch (err) {
|
||||
console.error('Error fetching data: ' + err.message)
|
||||
})
|
||||
}
|
||||
},
|
||||
loadImages = useCallback(async () => {
|
||||
const response = await fetch(text),
|
||||
data = await response.text()
|
||||
setImages(data.split('\n').filter(line => line.length > 0))
|
||||
}),
|
||||
load = useCallback(async () => {
|
||||
await loadData()
|
||||
await loadListeners()
|
||||
}),
|
||||
play = useCallback(async () => {
|
||||
if (!once.current) {
|
||||
if (!loadedAnalyzer.current) {
|
||||
audioAnalyzer()
|
||||
once.current = true
|
||||
loadedAnalyzer.current = true
|
||||
}
|
||||
setPaused(false)
|
||||
setMuted(false)
|
||||
@ -91,26 +100,27 @@ const App = () => {
|
||||
setBounce(data => data + json.media.track[0][key])
|
||||
}
|
||||
})
|
||||
} else if (!json?.media && bounce.search('Now Playing: null') === -1) {
|
||||
} else if (!json?.media && bounce.search('Now Playing: Title not available') === -1) {
|
||||
setLink('')
|
||||
setTitle('Stream Radio')
|
||||
setBounce('Now Playing: null')
|
||||
setBounce('Now Playing: Title not available')
|
||||
}
|
||||
}, [json])
|
||||
useEffect(() => {
|
||||
document.body.style.backgroundImage = `url('/wallpapers/${images[Math.floor(Math.random() * images.length)]}')`
|
||||
}, [images])
|
||||
useEffect(() => {
|
||||
loadData()
|
||||
if (once.current) return
|
||||
once.current = true
|
||||
M.AutoInit()
|
||||
load()
|
||||
loadImages()
|
||||
loadListeners()
|
||||
setAudioUrl('/stream.mp3')
|
||||
document.querySelector('input[type="range"]').addEventListener('change', (e) => {
|
||||
setCurrentVolume(e.target.value / 100)
|
||||
})
|
||||
const inter = setInterval(() => {
|
||||
loadData()
|
||||
loadListeners()
|
||||
load()
|
||||
}, (Math.floor(Math.random() * 20) + 10) * 1000),
|
||||
interback = setInterval(() => {
|
||||
loadImages()
|
||||
@ -159,7 +169,7 @@ const App = () => {
|
||||
<Icon tiny>people</Icon> {currentListeners} / {maxListeners}
|
||||
</span>
|
||||
</div >
|
||||
<audio src={audioUrl} ref={audioElmRef} volume={Math.log(currentVolume)} preload={"none"} muted={muted} controls={false} />
|
||||
<audio src={audioUrl} ref={audioElmRef} volume={Math.log10(currentVolume * 10)} preload={"none"} muted={muted} controls={false} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
115
src/WaveForm.jsx
115
src/WaveForm.jsx
@ -1,55 +1,68 @@
|
||||
import { useRef, useEffect } from "react";
|
||||
import useSize from "./useSize";
|
||||
|
||||
const animateBars = (analyser, canvas, canvasCtx, dataArray, bufferLength) => {
|
||||
analyser.getByteFrequencyData(dataArray)
|
||||
canvasCtx.fillStyle = "#000"
|
||||
const HEIGHT = canvas.height / 2
|
||||
var barWidth = Math.ceil(canvas.width / bufferLength) * 2.5
|
||||
let barHeight
|
||||
let x = 0
|
||||
for (var i = 0; i < bufferLength; i++) {
|
||||
barHeight = (dataArray[i] / 255) * HEIGHT
|
||||
const blueShade = Math.floor((dataArray[i] / 255) * 4) // generate a shade of blue based on the audio input
|
||||
const blueHex = ['rgba(255,255,255,0.5)', 'rgba(255,255,255,0.4)', 'rgba(255,255,255,0.3)', 'rgba(255,255,255,0.2)'][blueShade]; // use react logo blue shades
|
||||
canvasCtx.fillStyle = blueHex
|
||||
canvasCtx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight)
|
||||
x += barWidth + 1
|
||||
}
|
||||
},
|
||||
WaveForm = ({ analyzerData }) => {
|
||||
const canvasRef = useRef(null),
|
||||
{ dataArray, analyzer, bufferLength } = analyzerData,
|
||||
[width, height] = useSize(),
|
||||
draw = (dataArray, analyzer, bufferLength) => {
|
||||
const canvas = canvasRef.current
|
||||
if (!canvas || !analyzer) return
|
||||
const canvasCtx = canvas.getContext("2d"),
|
||||
animate = () => {
|
||||
requestAnimationFrame(animate)
|
||||
// eslint-disable-next-line no-self-assign
|
||||
canvas.width = canvas.width
|
||||
canvasCtx.translate(0, canvas.offsetHeight / 2)
|
||||
animateBars(analyzer, canvas, canvasCtx, dataArray, bufferLength)
|
||||
}
|
||||
animate()
|
||||
}
|
||||
useEffect(() => {
|
||||
draw(dataArray, analyzer, bufferLength)
|
||||
}, [dataArray, analyzer, bufferLength])
|
||||
return (
|
||||
<canvas
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "0",
|
||||
left: "0",
|
||||
zIndex: "-10"
|
||||
}}
|
||||
ref={canvasRef}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
)
|
||||
}
|
||||
const BLUE_SHADES = [
|
||||
"rgba(255,255,255,0.5)",
|
||||
"rgba(255,255,255,0.4)",
|
||||
"rgba(255,255,255,0.3)",
|
||||
"rgba(255,255,255,0.2)",
|
||||
];
|
||||
|
||||
export default WaveForm
|
||||
const animateBars = (analyser, canvas, ctx, dataArray, bufferLength) => {
|
||||
analyser.getByteFrequencyData(dataArray);
|
||||
const HEIGHT = canvas.height / 2;
|
||||
const barWidth = Math.ceil(canvas.width / bufferLength) * 2.5;
|
||||
let x = 0;
|
||||
for (let i = 0; i < bufferLength; i++) {
|
||||
const barHeight = (dataArray[i] / 255) * HEIGHT;
|
||||
const blueShade = Math.floor((dataArray[i] / 255) * 4);
|
||||
ctx.fillStyle = BLUE_SHADES[blueShade];
|
||||
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
|
||||
x += barWidth + 1;
|
||||
}
|
||||
};
|
||||
|
||||
const WaveForm = ({ analyzerData }) => {
|
||||
const canvasRef = useRef(null);
|
||||
const { dataArray, analyzer, bufferLength } = analyzerData;
|
||||
const [width, height] = useSize();
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas || !analyzer) return;
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
let animationId;
|
||||
|
||||
const render = () => {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.save();
|
||||
ctx.translate(0, canvas.height / 2);
|
||||
animateBars(analyzer, canvas, ctx, dataArray, bufferLength);
|
||||
ctx.restore();
|
||||
animationId = requestAnimationFrame(render);
|
||||
};
|
||||
|
||||
render();
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(animationId);
|
||||
};
|
||||
}, [dataArray, analyzer, bufferLength, width, height]);
|
||||
|
||||
return (
|
||||
<canvas
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 0,
|
||||
}}
|
||||
ref={canvasRef}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default WaveForm;
|
67
src/list.txt
Normal file
67
src/list.txt
Normal file
@ -0,0 +1,67 @@
|
||||
pexels-baskincreativeco-1480807.jpg
|
||||
pexels-belle-co-99483-847393.jpg
|
||||
pexels-bess-hamiti-83687-36487.jpg
|
||||
pexels-carlos-oliva-1966452-3586966.jpg
|
||||
pexels-christian-heitz-285904-842711.jpg
|
||||
pexels-dreamypixel-547115.jpg
|
||||
pexels-eberhardgross-1062249.jpg
|
||||
pexels-eberhardgross-1287075.jpg
|
||||
pexels-eberhardgross-1287089.jpg
|
||||
pexels-eberhardgross-1301976.jpg
|
||||
pexels-eberhardgross-1367192(1).jpg
|
||||
pexels-eberhardgross-1367192.jpg
|
||||
pexels-eberhardgross-1612351.jpg
|
||||
pexels-eberhardgross-1612360.jpg
|
||||
pexels-eberhardgross-1612362.jpg
|
||||
pexels-eberhardgross-1612371.jpg
|
||||
pexels-eberhardgross-1624255.jpg
|
||||
pexels-eberhardgross-534164.jpg
|
||||
pexels-eberhardgross-629167.jpg
|
||||
pexels-eberhardgross-691668.jpg
|
||||
pexels-eberhardgross-707344.jpg
|
||||
pexels-eberhardgross-730981.jpg
|
||||
pexels-esan-2085998.jpg
|
||||
pexels-fotios-photos-109260.jpg
|
||||
pexels-francesco-ungaro-1525041.jpg
|
||||
pexels-gochrisgoxyz-1477166.jpg
|
||||
pexels-johnnoibn-1448136.jpg
|
||||
pexels-joshkjack-135018.jpg
|
||||
pexels-jplenio-1110656.jpg
|
||||
pexels-jplenio-1146708.jpg
|
||||
pexels-jplenio-1435075.jpg
|
||||
pexels-kasperphotography-1042423.jpg
|
||||
pexels-katja-79053-592077.jpg
|
||||
pexels-lastly-937782.jpg
|
||||
pexels-lilartsy-1213447.jpg
|
||||
pexels-maxfrancis-2246476.jpg
|
||||
pexels-mdsnmdsnmdsn-1831234.jpg
|
||||
pexels-mdx014-814499.jpg
|
||||
pexels-michal-pech-213601-1632044.jpg
|
||||
pexels-no-name-14543-66997.jpg
|
||||
pexels-pixabay-158063.jpg
|
||||
pexels-pixabay-33109(1).jpg
|
||||
pexels-pixabay-33109.jpg
|
||||
pexels-pixabay-33545.jpg
|
||||
pexels-pixabay-358532.jpg
|
||||
pexels-pixabay-41004.jpg
|
||||
pexels-pixabay-414144.jpg
|
||||
pexels-pixabay-416160.jpg
|
||||
pexels-pixabay-459203.jpg
|
||||
pexels-pixabay-462162.jpg
|
||||
pexels-pixabay-50594.jpg
|
||||
pexels-pixabay-50686.jpg
|
||||
pexels-pixabay-52500.jpg
|
||||
pexels-rpnickson-2559941.jpg
|
||||
pexels-rpnickson-2647990.jpg
|
||||
pexels-samandgos-709552.jpg
|
||||
pexels-samkolder-2387873.jpg
|
||||
pexels-sebastian-312105.jpg
|
||||
pexels-simon73-1183099.jpg
|
||||
pexels-souvenirpixels-1519088.jpg
|
||||
pexels-souvenirpixels-417074.jpg
|
||||
pexels-stefanstefancik-919606.jpg
|
||||
pexels-stywo-1054289.jpg
|
||||
pexels-stywo-1668246.jpg
|
||||
pexels-therato-1933239.jpg
|
||||
pexels-todd-trapani-488382-1198817.jpg
|
||||
pexels-umaraffan499-21787.jpg
|
Loading…
x
Reference in New Issue
Block a user