Compare commits

...

4 Commits

Author SHA1 Message Date
aa02cf9fa4 visualizer controls 2025-01-14 13:34:29 -05:00
eabec44490 add settings ui 2025-01-08 12:41:45 -05:00
3f9048a395 visualizer 2024-12-28 19:37:15 -05:00
8238288df6 update ui with track info 2024-12-20 23:35:25 -05:00
3 changed files with 106 additions and 12 deletions

View File

@ -79,6 +79,18 @@ input[type="radio"]:checked+div {
"L D D R";
}
#settings {
grid-area: L;
display: flex;
flex-direction: column;
gap: 0.5em;
padding: 1em;
}
#settings select {
width: 100%;
}
#cover_art {
grid-area: A;
padding-right: 1em;
@ -94,6 +106,11 @@ img {
grid-area: B;
}
#visualizer {
width: 90%;
height: 4em;
}
#player_controls {
grid-area: C;
}

View File

@ -1,11 +1,15 @@
var player;
var pos_int;
var audio_ctx;
var analyser;
var audio_data;
var canvas_ctx;
var draw_loop;
var vol_prev;
function init() {
player = document.querySelector('#player');
init_analyzer();
init_canvas();
player.addEventListener('ended', nextTrack);
player.addEventListener('loadedmetadata', display_duration);
@ -34,8 +38,6 @@ function init() {
document.querySelector('#vol-off-btn').style.display = 'none';
}
});
init_analyzer();
}
async function get_rand_track() {
@ -47,6 +49,7 @@ async function get_rand_track() {
async function nextTrack() {
track = await get_rand_track();
player.src = track.source;
update_now_playing(track);
playTrack();
}
@ -60,7 +63,10 @@ async function playTrack() {
}
player.play();
navigator.mediaSession.playbackState = "playing";
setMediaSession(track);
if (document.querySelector("#visualizer-select").value === 'bar_graph') {
bar_graph();
}
document.querySelector("#play-btn").style.display = "none";
document.querySelector("#pause-btn").style.display = "initial";
@ -92,15 +98,23 @@ function setMediaSession(track) {
});
}
function update_now_playing(track) {
track.artwork.pop(1); // remove lower resolution option
document.querySelector('#now_playing_artist').textContent = track.artist;
document.querySelector('#now_playing_album').textContent = track.album;
document.querySelector('#now_playing_title').textContent = track.title;
document.querySelector('#cover_art').firstChild.src = track.artwork[0].src;
setMediaSession(track);
}
function update_position() {
if (!player.duration){return;}
navigator.mediaSession.setPositionState({
duration: Math.floor(player.duration),
playbackRate: player.playbackRate,
position: Math.floor(player.currentTime)
});
analyser.getByteTimeDomainData(audio_data);
document.querySelector('#seek-slider').value = Math.floor(player.currentTime);
display_current_time();
}
@ -143,13 +157,60 @@ function volume_restore() {
document.querySelector('#vol-off-btn').style.display = 'none';
}
function select_visualizer(event) {
if (event.target.value == 'bar_graph') {
bar_graph();
} else if (event.target.value == 'disabled') {
cancelAnimationFrame(draw_loop);
let canvas = document.querySelector('#visualizer');
canvas_ctx.clearRect(0, 0, canvas.width, canvas.height);
}
}
function init_analyzer() {
let audioCtx = new AudioContext();
analyser = audioCtx.createAnalyser();
let source = audioCtx.createMediaElementSource(player);
audio_ctx = new AudioContext();
analyser = audio_ctx.createAnalyser();
let source = audio_ctx.createMediaElementSource(player);
source.connect(analyser);
source.connect(audioCtx.destination);
audio_data = new Uint8Array(analyser.frequencyBinCount);
source.connect(audio_ctx.destination);
}
function init_canvas() {
let canvas = document.querySelector('#visualizer');
canvas_ctx = canvas.getContext('2d');
canvas_ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function bar_graph() {
analyser.fftSize = 256;
analyser.minDecibels = -90;
analyser.maxDecibels = -10;
analyser.smoothingTimeConstant = 0.85;
audio_data = new Uint8Array(analyser.frequencyBinCount);
draw();
}
function draw() {
draw_loop = requestAnimationFrame(draw);
analyser.getByteTimeDomainData(audio_data);
let canvas = document.querySelector('#visualizer');
canvas_ctx.fillStyle = "rgb(0 0 0)";
canvas_ctx.fillRect(0, 0, canvas.width, canvas.height);
const barWidth = (canvas.width / analyser.frequencyBinCount) * 2.5;
let barHeight;
let x = 0;
for (let i = 0; i < analyser.frequencyBinCount; i++) {
barHeight = audio_data[i];
canvas_ctx.fillStyle = `rgb(${barHeight + 100} 50 50)`;
canvas_ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);
x += barWidth + 1;
}
}

View File

@ -27,10 +27,26 @@
<div id="track_list" class="list">
</div>
<div id="player_container">
<div id="settings">
<div>
<label for="playlist-select">Playlist:</label>
<select id="playlist-select">
<option>All Tracks</option>
</select>
</div>
<div>
<label for="visualizer-select">Visualizer:</label>
<select id="visualizer-select" onchange="select_visualizer(event)">
<option value="disabled" selected>Disabled</option>
<option value="bar_graph">Bar Graph</option>
</select>
</div>
</div>
<div id="cover_art"><img alt="Album cover art" src="./static/pyrite.jpg"></div>
<div id="title">
<h4><span id="now_playing_artist">Artist</span> - <span id="now_playing_album">Album</span></h4>
<h3 id="now_playing_title">Title</h3>
<canvas id="visualizer" height="300" width="1000"></canvas>
</div>
<div id="player_controls">
<audio id="player" style="display:none"></audio>