Javier González
Javier González

Reputation: 31

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?

Upvotes: 3

Views: 3633

Answers (2)

Barak Binyamin
Barak Binyamin

Reputation: 404

Visualize stream using HTML5

Here's a 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://streaming.witr.rit.edu/live-aac-96" 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>

Upvotes: 1

miknik
miknik

Reputation: 5951

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>

Upvotes: 2

Related Questions