Search code examples
canvasaudioaudiocontext

Canvas clear path with clearRect and AudioContext


I have this visualizer, everthing is working well. The only problem is that when you clear canvas with clean function, then click changeSrc, you can always see a visualizer from last song for a second. Can I get rid of this?

You might need to run it on js fiddle.

https://jsfiddle.net/t87wyzgd/6/

var renderVizualitationID,
  canvas, ctx, center_x, center_y, radius, bars,
  x_end, y_end, bar_height, bar_width,
  frequency_array,
  wrap, source;



wrap = $('.w')



canvas = document.getElementById("renderer");
ctx = canvas.getContext("2d");

bars = 200;
bar_width = 2;
multiplier = 0.7;



audio = new Audio();
audio.crossOrigin = "anonymous";

context = new(window.AudioContext || window.webkitAudioContext)();
analyser = context.createAnalyser();

audio.src = "https://api.soundcloud.com/tracks/42328219/stream?client_id=b1495e39071bd7081a74093816f77ddb"; // the source path
audio.loop = true;
source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);

frequency_array = new Uint8Array(analyser.frequencyBinCount);

audio.play();
animationLooper();







function animationLooper() {

  // set to the size of device

  canvas.width = wrap.width();
  canvas.height = wrap.height();


  // find the center of the window
  center_x = canvas.width / 2;
  center_y = canvas.height / 2;
  radius = canvas.height / 6;

  //draw a circle
  ctx.beginPath();
  ctx.arc(center_x, center_y, radius, 0, 2 * Math.PI);
  ctx.stroke();

  analyser.getByteFrequencyData(frequency_array);
  var i;
  for (i = 0; i < bars; i++) {

    //divide a circle into equal parts
    rads = Math.PI * 2 / bars;

    bar_height = frequency_array[i] * multiplier;

    // set coordinates
    x = center_x + Math.cos(rads * i) * (radius);
    y = center_y + Math.sin(rads * i) * (radius);
    x_end = center_x + Math.cos(rads * i) * (radius + bar_height);
    y_end = center_y + Math.sin(rads * i) * (radius + bar_height);

    //draw a bar
    var lineColor = "rgb(" + frequency_array[i] + ", " + frequency_array[i] + ", " + 205 + ")";

    ctx.strokeStyle = lineColor;
    ctx.lineWidth = bar_width;
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x_end, y_end);
    ctx.stroke();

  }

  renderVizualitationID = requestAnimationFrame(animationLooper);
}

$('#clean').on('click',function() {
  if (renderVizualitationID) cancelAnimationFrame(renderVizualitationID);

  audio.pause();
  audio.src = '';

  ctx.clearRect(0, 0, canvas.width, canvas.height);

});

$('#changeSrc').on('click',function() {

 audio.src = "https://api.soundcloud.com/tracks/693365626/stream?client_id=r4wruADPCq7iqJomagvYpdehvILa2bgE"; // the source path

  audio.play();
  animationLooper();

});
.w {
  position: relative;
  top: 0;
  left: 0;
  width: 400px;
  height: 400px;
  background: #ccc;
}

#renderer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #111;

  background: #1D4350;

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="w">
  <canvas id="renderer"></canvas>
</div>

<a id="clean" href="#" >clean</a>
<a id="changeSrc" href="#" >changeSrc</a>


Solution

  • This happens because your AudioAnalyser works by buffering the data, it simply unshifts new data as time passes. So when you pause your audio, it will still contain some data.

    To avoid this, you can simply create a new AudioAnalyser in the clean function

    var renderVizualitationID,
      canvas, ctx, center_x, center_y, radius, bars,
      x_end, y_end, bar_height, bar_width,
      frequency_array,
      wrap, source;
    
    
    
    wrap = $('.w')
    
    
    
    canvas = document.getElementById("renderer");
    ctx = canvas.getContext("2d");
    
    bars = 200;
    bar_width = 2;
    multiplier = 0.7;
    
    
    
    audio = new Audio();
    audio.crossOrigin = "anonymous";
    
    context = new(window.AudioContext || window.webkitAudioContext)();
    analyser = context.createAnalyser();
    
    audio.src = "https://api.soundcloud.com/tracks/42328219/stream?client_id=b1495e39071bd7081a74093816f77ddb"; // the source path
    audio.loop = true;
    source = context.createMediaElementSource(audio);
    source.connect(analyser);
    analyser.connect(context.destination);
    
    frequency_array = new Uint8Array(analyser.frequencyBinCount);
    
    audio.play();
    animationLooper();
    
    
    
    
    
    // do not change the size of your canvas when you are drawing on it
    // that will reset the whole canvas and require anew pixel buffer
    // so do it only when really required
    window.onresize = function() {
      // set to the size of device
    
      canvas.width = wrap.width();
      canvas.height = wrap.height();
    };
    window.onresize();
    
    function animationLooper() {
    
      ctx.clearRect( 0, 0, canvas.width, canvas.height )
    
      // find the center of the window
      center_x = canvas.width / 2;
      center_y = canvas.height / 2;
      radius = canvas.height / 6;
    
      //draw a circle
      ctx.beginPath();
      ctx.arc(center_x, center_y, radius, 0, 2 * Math.PI);
      ctx.stroke();
    
      analyser.getByteFrequencyData(frequency_array);
      var i;
      for (i = 0; i < bars; i++) {
    
        //divide a circle into equal parts
        rads = Math.PI * 2 / bars;
    
        bar_height = frequency_array[i] * multiplier;
    
        // set coordinates
        x = center_x + Math.cos(rads * i) * (radius);
        y = center_y + Math.sin(rads * i) * (radius);
        x_end = center_x + Math.cos(rads * i) * (radius + bar_height);
        y_end = center_y + Math.sin(rads * i) * (radius + bar_height);
    
        //draw a bar
        var lineColor = "rgb(" + frequency_array[i] + ", " + frequency_array[i] + ", " + 205 + ")";
    
        ctx.strokeStyle = lineColor;
        ctx.lineWidth = bar_width;
        ctx.beginPath();
        ctx.moveTo(x, y);
        ctx.lineTo(x_end, y_end);
        ctx.stroke();
    
      }
    
      renderVizualitationID = requestAnimationFrame(animationLooper);
    }
    
    $('#clean').on('click',function() {
      if (renderVizualitationID) cancelAnimationFrame(renderVizualitationID);
    
      audio.pause();
      audio.src = '';
      frequency_array.fill( 0 );
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      // kill the previous one
      analyser.disconnect();
      // create a new analyser
      analyser = context.createAnalyser();
      source.connect( analyser );
      analyser.connect(context.destination);
    });
    
    $('#changeSrc').on('click',function() {
    
     audio.src = "https://api.soundcloud.com/tracks/693365626/stream?client_id=r4wruADPCq7iqJomagvYpdehvILa2bgE"; // the source path
      audio.play();
      animationLooper();  
    });
    .w {
      position: relative;
      top: 0;
      left: 0;
      width: 400px;
      height: 400px;
      background: #ccc;
    }
    
    #renderer {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: #111;
    
      background: #1D4350;
    
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <div class="w">
      <canvas id="renderer"></canvas>
    </div>
    
    <a id="clean" href="#" >clean</a>
    <a id="changeSrc" href="#" >changeSrc</a>