Search code examples
javascriptweb-audio-api

Obtaining Current Time


I am trying to understand how can I obtain the current time of the audio while the audio is playing. So when the audio starts playing it will log 0 and at the end of play it will log the total duration. This is what I have tried below and it is not returning what I had in mind. I am probably doing it wrong. But I am not sure where it is going wrong.

For example, at the end of the audio, it returns currentTime:36.02133333333333 totalDuration36.006875 where currentTime>totalDuration which is completely wrong. I want to utilize these values, hence they need to be accurate.

const audioUrl = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/128337/ongoing-thing-crop.ogg';  

/*test url2 - https://s.cdpn.io/1202/Star_Wars_original_opening_crawl_1977.mp3*/

const div = document.querySelector('div');

const audioContext = new(window.AudioContext || window.webkitAudioContext)();
let bufferSource = audioContext.createBufferSource();
let analyser = audioContext.createAnalyser();
let gainNode = audioContext.createGain();

let startTime;

async function setupAudio(url) {
    const response = await fetch(url);
    const audioData = await response.arrayBuffer();
    const buffer = await audioContext.decodeAudioData(audioData);

    bufferSource.buffer = buffer;
    bufferSource.connect(analyser);
    analyser.connect(gainNode);
    gainNode.connect(audioContext.destination);

    function updateTime() {
        let currentTime = audioContext.currentTime - startTime;
        console.log(`currentTime:${currentTime}`,`totalDuration${buffer.duration}`);
        if (currentTime < buffer.duration) {
            requestAnimationFrame(updateTime);
        }
    }

    div.addEventListener('change', () => {
        startTime = audioContext.currentTime;
        bufferSource.start(0);
        requestAnimationFrame(updateTime);
    });
}

setupAudio(audioUrl);
<div id="selector">
  <label for="valueSelection">Choose a value <select id="valueSelection">
      <option>off</option>
      <option>on</option>
    </select>
  </label>
</div>
<audio></audio>


Solution

  • The issue happens because the code is printing console output outside of the conditional check in updateTime function:

       function updateTime() {
            let currentTime = audioContext.currentTime - startTime;
            console.log(`currentTime:${currentTime}`,`totalDuration${buffer.duration}`);
            if (currentTime < buffer.duration) {
                requestAnimationFrame(updateTime);
            }
        }
    

    What you are basically doing is printing console log output in following cases:

    • Print log every time when currentTime is less than buffer.duration , and

    • Print log once when currentTime is greater than or equal to buffer.duration

    Note: During last log output when currentTime > totalDuration, you actually aren't calling the function updateTime, since the conditional checking will be false:

          // false when currentTime >= buffer.duration
          // so you won't call updateTime, but you are printing console log once
           if (currentTime < buffer.duration) {
                requestAnimationFrame(updateTime);
            }
    

    To fix the issue, just move the console log statement inside your conditional checking logic like following:

       function updateTime() {
            let currentTime = audioContext.currentTime - startTime;
                    if (currentTime < buffer.duration) {
                    console.log(`currentTime:${currentTime}`,`totalDuration${buffer.duration}`);
                    requestAnimationFrame(updateTime);
            }
        }
    

    This will only print console log output when currentTime < buffer.duration is true.

    Check following modified code:

    const audioUrl = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/128337/ongoing-thing-crop.ogg';
    
    /*test url2 - https://s.cdpn.io/1202/Star_Wars_original_opening_crawl_1977.mp3*/
    
    const div = document.querySelector('div');
    
    const audioContext = new(window.AudioContext || window.webkitAudioContext)();
    let bufferSource = audioContext.createBufferSource();
    let analyser = audioContext.createAnalyser();
    let gainNode = audioContext.createGain();
    
    let startTime;
    
    async function setupAudio(url) {
      const response = await fetch(url);
      const audioData = await response.arrayBuffer();
      const buffer = await audioContext.decodeAudioData(audioData);
    
      bufferSource.buffer = buffer;
      bufferSource.connect(analyser);
      analyser.connect(gainNode);
      gainNode.connect(audioContext.destination);
    
      function updateTime() {
        let currentTime = audioContext.currentTime - startTime;
        if (currentTime < buffer.duration) {
          console.log(`currentTime:${currentTime}`, `totalDuration${buffer.duration}`);
          requestAnimationFrame(updateTime);
        }
      }
    
      div.addEventListener('change', () => {
        startTime = audioContext.currentTime;
        bufferSource.start(0);
        requestAnimationFrame(updateTime);
      });
    }
    
    setupAudio(audioUrl);
    <div id="selector">
      <label for="valueSelection">Choose a value <select id="valueSelection">
          <option>off</option>
          <option>on</option>
        </select>
      </label>
    </div>
    <audio></audio>