Javascript Audio API Visualizer not showing in canvas - javascript

So i followed this tutorial: https://youtu.be/IBHpSkGZtNM
And a while back (about a year ago or so) it worked. Now however it doesn't visualize anything. The sound plays but thats it.
I changed the old webkit functions to the new ones as i was getting errors. However now i don't have any errors and still nothing shows up.
Here is my code:
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
div#mp3_player{ width: 500px; height: 60px; background: #000000; padding: 5px; margin: 50px auto;}
div#mp3_player > div > audio{ width: 500px; background: #000000; float: left;}
div#mp3_player > canvas{width: 500px; height: 30px; background: #002D3C; float: left;}
</style>
<script>
var audio = new Audio();
audio.src = "audio/DieForYou-Starset.mp3";
audio.controls = true;
audio.loop = true;
audio.autoplay = false;
var canvas, ctx, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height;
window.addEventListener("load", initMp3Player, false);
function initMp3Player(){
document.getElementById("audio_box").appendChild(audio);
context = new AudioContext();
analyser = context.createAnalyser();
canvas = document.getElementById("analyser_renderer");
ctx = canvas.getContext("2d");
source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);
frameLooper()
}
function frameLooper(){
window.requestAnimationFrame(frameLooper);
fbc_array = new Uint8Array(analyser.frequencyBinCount);
ctx.clearRect(0,0 ,canvas.width, canvas.height)
ctx.fillStyle= "#2DCC70";
bars = 100;
for(var i = 0; i < bars; i++){
bar_x = i * 3;
bar_width = 2;
bar_height = -(fbc_array[1] / 2);
ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
}
}
</script>
</head>
<body>
<div id="mp3_player">
<div id="audio_box"></div>
<canvas id="analyser_renderer"></canvas>
</div>
</body>
</html>
I put everything in the html file because it was easier to post like that. I know it's bad practise.
Note: the sound wont play in the snippet because the audio file is stored locally.
I am also running this trough a local server as just opening the html file would result in MediaElementAudioSource outputs zeroes due to CORS access restrictions for file:///C:/xampp/htdocs/audio/DieForYou-Starset.mp3

You need to populate your array with one of the AnalyserNode methods, here you are only creating an empty one.
For example, you can call analyser.getByteFrequencyData(fbc_array); to get the current frequency data.
var audio = new Audio();
audio.crossOrigin = 'anonymous';
audio.src = "https://dl.dropboxusercontent.com/s/8c9m92u1euqnkaz/GershwinWhiteman-RhapsodyInBluePart1.mp3";
audio.controls = true;
audio.loop = true;
audio.autoplay = false;
window.onload = initMp3Player;
var canvas, ctx, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height;
function initMp3Player() {
document.getElementById("audio_box").appendChild(audio);
context = new AudioContext();
analyser = context.createAnalyser();
canvas = document.getElementById("analyser_renderer");
ctx = canvas.getContext("2d");
source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);
// initialise the Uint8Array only once
fbc_array = new Uint8Array(analyser.frequencyBinCount);
frameLooper()
}
function frameLooper() {
window.requestAnimationFrame(frameLooper);
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "#2DCC70";
bars = 100;
// here fill the Uint8Array with audio data
analyser.getByteFrequencyData(fbc_array);
for (var i = 0; i < bars; i++) {
bar_x = i * 3;
bar_width = 2;
bar_height = -(fbc_array[i] / 2); // there was a typo
ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
}
}
div#mp3_player {
width: 500px;
height: 60px;
background: #000000;
padding: 5px;
margin: 50px auto;
}
div#mp3_player>div>audio {
width: 500px;
background: #000000;
float: left;
}
div#mp3_player>canvas {
width: 500px;
height: 30px;
background: #002D3C;
float: left;
}
<div id="mp3_player">
<div id="audio_box"></div>
<canvas id="analyser_renderer"></canvas>
</div>

Related

Equalizer bar for muted audio

I am trying to create an audio equalizer through the guidance of this tutorial.
This works fine until you mute the audio through the controls(as shown in the image). Muting it also stops the equalizer bars.
How would I go about implementing the equalizer bars so that the bars don't stop even when the audio is muted?
let canvas,
ctx,
source,
context,
analyser,
fbc_array,
bar_count,
bar_pos,
bar_width,
bar_height;
let audio = new Audio();
audio.src = "test.mp3";
audio.controls = true; // Show audio controls so we can pause and play at will
audio.loop = true;
audio.autoplay = true;
window.addEventListener(
"load", // only executes after loading page fully
function() {
document.getElementById("audio").appendChild(audio); // putting the audio element inside the another HTML element
audio.onplay = function() {
if (typeof(context) === "undefined") {
context = new AudioContext();
analyser = context.createAnalyser();
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d"); // returns a drawing context
source = context.createMediaElementSource(audio); // creates an audio object which can be manipulated
canvas.width = window.innerWidth * 0.80;
canvas.height = window.innerHeight * 0.60;
source.connect(analyser);
analyser.connect(context.destination); // connect the analyzer and the context(which is used to draw)
}
FrameLooper(); // will handle all the animation and output
};
},
false
);
function FrameLooper() {
window.requestAnimationFrame(FrameLooper);
fbc_array = new Uint8Array(analyser.frequencyBinCount); // array for containing frequencies
bar_count = window.innerWidth / 2;
analyser.getByteFrequencyData(fbc_array);
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear old visual data
ctx.fillStyle = "#ffffff"; // fill it with white
/* For repainting */
for (let i = 0; i < bar_count; i++) {
bar_pos = i;
bar_width = 1;
bar_height = -(fbc_array[i] / 2);
ctx.fillRect(bar_pos, canvas.height, bar_width, bar_height);
}
}
body {
background: black;
padding: 0px;
margin: 0px;
}
div#player {
width: 80vw;
height: 60vh;
background: black;
margin: 20vh auto 0px auto;
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="styles.css">
<title>Equalizer bar</title>
</head>
<body>
<div id="player">
<div id="audio"></div>
<canvas id="canvas"></canvas>
</div>
</body>
<script type="text/javascript" src="functions.js"></script>
</html>

Audio is playing without visualization

I am trying to play audio without any visualisation. I have used the code below :
<!doctype html>
<html>
<head>
<style>
div#mp3_player{ width:500px; height:120px; background:#000; padding:5px; margin:50px auto; border-radius: 10px; }
div#mp3_player > div > audio{ width:500px; background:#000; float:left; }
div#mp3_player > canvas{ width:500px; height:60px; padding: 0px; background:#ffffff; float:left; border-radius: 10px;}
</style>
<script>
window.AudioContext = window.AudioContext || window.webkitAudioContext;
// Create a new instance of an audio object and adjust some of its properties
var audio = new Audio();
audio.src = 'UncleFlexxx - Camry 3.5.mp3';
audio.controls = true;
audio.loop = true;
audio.autoplay = true;
// Establish all variables that your Analyser will use
var canvas, ctx, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height;
// Initialize the MP3 player after the page loads all of its HTML into the window
window.addEventListener("load", initMp3Player, false);
function initMp3Player(){
document.getElementById('audio_box').appendChild(audio);
context = new webkitAudioContext(); // AudioContext object instance
analyser = context.createAnalyser(); // AnalyserNode method
canvas = document.getElementById('analyser_render');
ctx = canvas.getContext('2d');
// Re-route audio playback into the processing graph of the AudioContext
source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);
frameLooper();
}
// frameLooper() animates any style of graphics you wish to the audio frequency
// Looping at the default frame rate that the browser provides(approx. 60 FPS)
function frameLooper(){
window.webkitRequestAnimationFrame(frameLooper);
fbc_array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(fbc_array);
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
ctx.fillStyle = 'white'; // Color of the bars
bars = 100;
for (var i = 0; i < bars; i++) {
bar_x = i * 3;
bar_width = 4;
bar_height = -(fbc_array[i] / 2.5);
// fillRect( x, y, width, height ) // Explanation of the parameters below
ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
}
}
</script>
</head>
<body>
<div id="mp3_player">
<div id="audio_box"></div>
<canvas id="analyser_render"></canvas>
</div>
</body>
</html>
I do not know what to do! I've tried injecting window.AudioContext = window.AudioContext || window.webkitAudioContext; but nothing has changed! I have no familiar programmers, so I came here, please help. Looks like google can't read this code or what I don't know.
Google considers context = new webkitAudioContext(); as an error !
Just add the audio tag into the dom
var audio = new Audio();
audio.src = 'UncleFlexxx - Camry 3.5.mp3';
audio.controls = true;
audio.loop = true;
audio.autoplay = true;
document.getElementById("mp3_player").appendChild(audio);

Creating an audio visualizer using HTML5

I am trying to use an audio visualisation for the stream of my online radio using the example that I found on this page.
However, similar to the problem found in this post , my audio file (even when testing with a local file) just does not sound and of course the visualisation does nothing as well.
My HTML is the following:
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<audio src="http://50.22.218.101:38838/;steam.mp3" id="audio"
controls>HTML5 Audio element not supported</audio>
<canvas id="canvas" width="800" height="350"></canvas>
<script src="main.js"></script>
</body>
</html>
This is "main.js":
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext;
window.onload = function() {
var audio = document.getElementById('audio');
var ctx = new AudioContext();
var analyser = ctx.createAnalyser();
var audioSrc = ctx.createMediaElementSource(audio);
// we have to connect the MediaElementSource with the analyser
audioSrc.connect(analyser);
analyser.connect(ctx.destination);
// we could configure the analyser: e.g. analyser.fftSize (for further infos read the spec)
// analyser.fftSize = 64;
// frequencyBinCount tells you how many values you'll receive from the analyser
var frequencyData = new Uint8Array(analyser.frequencyBinCount);
// we're ready to receive some data!
var canvas = document.getElementById('canvas'),
cwidth = canvas.width,
cheight = canvas.height - 2,
meterWidth = 10, //width of the meters in the spectrum
gap = 2, //gap between meters
capHeight = 2,
capStyle = '#fff',
meterNum = 800 / (10 + 2), //count of the meters
capYPositionArray = []; ////store the vertical position of hte caps for the preivous frame
ctx = canvas.getContext('2d'),
gradient = ctx.createLinearGradient(0, 0, 0, 300);
gradient.addColorStop(1, '#0f0');
gradient.addColorStop(0.5, '#ff0');
gradient.addColorStop(0, '#f00');
// loop
function renderFrame() {
var array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
var step = Math.round(array.length / meterNum); //sample limited data from the total array
ctx.clearRect(0, 0, cwidth, cheight);
for (var i = 0; i < meterNum; i++) {
var value = array[i * step];
if (capYPositionArray.length < Math.round(meterNum)) {
capYPositionArray.push(value);
};
ctx.fillStyle = capStyle;
//draw the cap, with transition effect
if (value < capYPositionArray[i]) {
ctx.fillRect(i * 12, cheight - (--capYPositionArray[i]), meterWidth, capHeight);
} else {
ctx.fillRect(i * 12, cheight - value, meterWidth, capHeight);
capYPositionArray[i] = value;
};
ctx.fillStyle = gradient; //set the filllStyle to gradient for a better look
ctx.fillRect(i * 12 /*meterWidth+gap*/ , cheight - value + capHeight, meterWidth, cheight); //the meter
}
requestAnimationFrame(renderFrame);
}
renderFrame();
audio.play();
};
Any idea what am I doing wrong?
You can usually fix this by simply adding a crossorigin option to your html audio tag
so this:
<audio src="http://50.22.218.101:38838/;steam.mp3" id="audio" controls>
becomes this:
<audio src="http://50.22.218.101:38838/;steam.mp3" crossorigin="anonymous" id="audio" controls>
Visualize stream using HTML5
Here's full example, combining #miknik's answer, your display code (which is awesome by the way), and a public web radio station
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext;
let audio = document.getElementById('audio')
let ctx = ""
let audioSrc = ""
function start(){
if (!ctx){
ctx = new AudioContext();
analyser = ctx.createAnalyser();
audioSrc = ctx.createMediaElementSource(audio)
// we have to connect the MediaElementSource with the analyser
audioSrc.connect(analyser)
analyser.connect(ctx.destination)
}
// we could configure the analyser: e.g. analyser.fftSize (for further infos read the spec)
// analyser.fftSize = 64;
// frequencyBinCount tells you how many values you'll receive from the analyser
var frequencyData = new Uint8Array(analyser.frequencyBinCount);
// we're ready to receive some data!
var canvas = document.getElementById('canvas'),
cwidth = canvas.width,
cheight = canvas.height - 2,
meterWidth = 10, //width of the meters in the spectrum
gap = 2, //gap between meters
capHeight = 2,
capStyle = '#fff',
meterNum = 800 / (10 + 2), //count of the meters
capYPositionArray = []; ////store the vertical position of hte caps for the preivous frame
ctx = canvas.getContext('2d'),
gradient = ctx.createLinearGradient(0, 0, 0, 300);
gradient.addColorStop(1, '#0f0');
gradient.addColorStop(0.5, '#ff0');
gradient.addColorStop(0, '#f00');
// loop
function renderFrame() {
var array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
var step = Math.round(array.length / meterNum); //sample limited data from the total array
ctx.clearRect(0, 0, cwidth, cheight);
for (var i = 0; i < meterNum; i++) {
var value = array[i * step];
if (capYPositionArray.length < Math.round(meterNum)) {
capYPositionArray.push(value);
};
ctx.fillStyle = capStyle;
//draw the cap, with transition effect
if (value < capYPositionArray[i]) {
ctx.fillRect(i * 12, cheight - (--capYPositionArray[i]), meterWidth, capHeight);
} else {
ctx.fillRect(i * 12, cheight - value, meterWidth, capHeight);
capYPositionArray[i] = value;
};
ctx.fillStyle = gradient; //set the filllStyle to gradient for a better look
ctx.fillRect(i * 12 /*meterWidth+gap*/ , cheight - value + capHeight, meterWidth, cheight); //the meter
}
requestAnimationFrame(renderFrame);
}
renderFrame();
audio.play();
}
function stop(){
audio.pause()
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#container {
position: absolute;
top: 0;
left: 0;
background: #000;
width: 100%;
height: 100%;
}
#canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: black;
z-index: -1;
opacity: .8;
}
.button-container{
padding-top: 5px;
display: grid;
grid-template-columns: repeat(2,300px);
grid-gap: 1px;
width: 100%;
align-items: center;
justify-content: center;
}
button{
width: 300px;
text-align: center;
}
<html>
<link rel="stylesheet" type="text/css" href="style.css">
<body>
<audio src="https://rfcmedia.streamguys1.com/MusicPulse.mp3" crossorigin="anonymous" id="audio" >HTML5 Audio element not supported</audio>
<div class="button-container">
<button onclick="start()">Stream Radio</button>
<button onclick="stop()">Stop Radio</button>
</div>
<canvas id="canvas"></canvas>
<script src="main.js"></script>
</body>
</html>

How prevent blurry drawing on canvas when using putImageData?

I want to use putImageData to paint on canvas. But for some reason the painted pixels are blurry and I don't know why. Here is a minimal example:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<style>
html, body, canvas { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; }
</style>
</head>
<body oncontextmenu="return false;">
<canvas id='canvas'></canvas>
<script type='text/javascript'>
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
let uarr = new Uint8ClampedArray(4);
uarr[0] = 0;
uarr[1] = 0;
uarr[2] = 0;
uarr[3] = 255;
let imgData = new ImageData(uarr , 1, 1);
ctx.putImageData(imgData, 20, 20);
</script>
</body>
</html>
This should paint one black pixel at the 20,20 coordinates, but for some reason it look blurry.
How can I force to draw sharp edges?
You need to set the canvas width and height attributes, as your CSS is stretching a default size canvas to the size of the screen, like stretching a too small image. After that ctx.imageSmoothingEnabled should work fine.
var canvas = document.getElementById('canvas');
// This will stretch correctly
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
var uarr = new Uint8ClampedArray(4);
uarr[0] = 0;
uarr[1] = 0;
uarr[2] = 0;
uarr[3] = 255;
var imgData = new ImageData(uarr , 1, 1);
ctx.putImageData(imgData, 20, 20);
html, body, canvas { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; }
<canvas id='canvas'></canvas>
As #Kaido mentions in the comments below, imageSmoothingEnabled does not really work that way. Check his fiddle for a little bit more of an in-depth look at what it can be used for.

JS music visulalizer

I am trying to make a music visualizer in Javascript in the shape of circle, my problem is that even though I call clearRect, the canvas isnt clearing after a completion of a cycle.
$(document).ready(function() {
var canvas = document.getElementsByTagName("canvas")[0];
var context = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
context.translate(window.innerWidth / 2, window.innerHeight / 2);
var audioContext = new AudioContext();
var audio = document.getElementsByTagName('audio')[0];
var source = audioContext.createMediaElementSource(audio);
var analyser = audioContext.createAnalyser();
source.connect(analyser);
analyser.connect(audioContext.destination);
var BLenght = analyser.frequencyBinCount;
var data = new Uint8Array(BLenght);
function Visualizer() {
context.clearRect(0, 0, canvas.width, canvas.height);
analyser.getByteFrequencyData(data);
var fwidth = (canvas.width / BLenght);
var fheight = 0;
var maxCircle = 80;
var radius = 200;
for (var i = 0; i <= maxCircle; i++) {
fheight = (data[i] * (canvas.height * 0.003)) / 2;
context.fillStyle = "#000000";
context.rect(0, radius, fwidth, fheight);
context.rotate(2 * Math.PI / maxCircle);
context.fill();
}
call = requestAnimationFrame(Visualizer);
}
var button = document.getElementById("button");
var isPlaying = false;
button.addEventListener('click', function() {
if (isPlaying == false) {
audio.play();
Visualizer();
isPlaying = true;
button.style.boxShadow = "0px 0px 0px 0px #065e41";
var delay = 400;
setTimeout(function() {
button.style.boxShadow = "0px 5px 0px 0px #065e41";
}, delay);
} else {
audio.pause();
isPlaying = false;
cancelAnimationFrame(call);
button.style.boxShadow = "0px 0px 0px 0px #065e41";
var delay = 400;
setTimeout(function() {
button.style.boxShadow = "0px 5px 0px 0px #065e41";
}, delay);
}
});
});
* {
box-sizing: border-box;
}
canvas {
position: absolute;
top: 10%;
}
#button {
width: 150px;
height: 50px;
background: #0deaa1;
text-align: center;
border-radius: 5px;
box-shadow: 0px 5px 0px 0px #065e41;
cursor: pointer;
user-select: none;
transition: .5s;
}
#button h1 {
padding-top: 7px;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<audio src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/1715/the_xx_-_intro.mp3" preload="auto"></audio>
<canvas></canvas>
<div id="button">
<h1>PLAY</h1>
</div>
<p id="width">width</p>
<p id="buffer">buffer</p>
<p id="data">data</p>
It can be fixed by using fillRect, or by using context.beginPath. (currently you are adding every rect to the same path)
context.fillStyle = "#000000";
context.fillRect(0,radius,fwidth,fheight); // rect changed to fillrect
context.rotate(2*Math.PI/maxCircle);
To prevent the lines from spinning: reset the context transformation matrix:
context.setTransform(1, 0, 0, 1, 0, 0);
context.translate(window.innerWidth/2,window.innerHeight/2);
context.clearRect(-canvas.width/2,-canvas.height/2,canvas.width,canvas.height);
The Codepen example you provided was also updated: http://codepen.io/anon/pen/xbMxzY

Categories