Search code examples
javascriptaudio

Get audio.duration with JavaScript from URL


I am trying to set the position of an audio source to the remainder of the time since the UNIX epoch divided by the duration of the audio in milliseconds. audio0.duration and the other two return a value of NaN. How can I get the audio duration to be an integer that I can use?

JavaScript code is below.

function PlaySample (file) {  
  const audio0 = new Audio('https://brysonnoble.github.io/Programming%20Projects/SmartSync/Sample_Audio/a0.mp3');
  const audio1 = new Audio('https://brysonnoble.github.io/Programming%20Projects/SmartSync/Sample_Audio/a1.mp3');
  const audio2 = new Audio('https://brysonnoble.github.io/Programming%20Projects/SmartSync/Sample_Audio/a2.mp3');

  audio0.pause();
  audio1.pause();
  audio2.pause();
  
  switch (file) {
    case 0:
      audio0.currentTime = PlayheadPos(audio0.duration * 1000);
      audio0.play();
      break;
    case 1:
      audio1.currentTime = PlayheadPos(audio1.duration * 1000);
      audio1.play();
      break;
    case 2:
      audio2.currentTime = PlayheadPos(audio2.duration * 1000);
      audio2.play();
      break;
    default:
      break;
  }
}

function PlayheadPos (audioLen) {
  alert(Date.now() % audioLen);
  return Date.now() % audioLen;
}

Solution

  • That happens because the duration property of the audio/media is undefined. Try enabling the play function when the event durationchanges is fired in the audio/media element.

    I created this snippet for you that can help you solve your problem.

    const PreloadAudio =
    [
      new Audio('https://brysonnoble.github.io/Programming%20Projects/SmartSync/Sample_Audio/a0.mp3'),
      new Audio('https://brysonnoble.github.io/Programming%20Projects/SmartSync/Sample_Audio/a1.mp3'),
      new Audio('https://brysonnoble.github.io/Programming%20Projects/SmartSync/Sample_Audio/a2.mp3'),
    ];
    
    for ( const MyAudio of PreloadAudio )
    {
      MyAudio.pause();
      
      const MyAudioFile = ( new URL(MyAudio.src) ).pathname.split(/[\\/]/).at(-1);
      
      const MyPlayButton = document.createElement('button');
      {
        MyPlayButton.innerText = `Resume / Pause < ${MyAudioFile} >`;
        MyPlayButton.style.display = 'block';
        MyPlayButton.disabled = true;
      }
      
      // Add the play/pause button to the layout.
      MyMedias.appendChild(MyPlayButton);
      
      // Only enables the MyPlayButton when there's a duration change
      // (different from undefined) and define its click event.
      MyAudio.addEventListener
      (
        'durationchange', Event =>
        {
          MyPlayButton.disabled = false;
          
          MyPlayButton.addEventListener
          (
            'click', Event =>
            {
              if ( ! MyAudio.paused )
              {
                console.log('Pausing audio:', MyAudioFile);
                MyAudio.pause();
              }
              
              else
              {
                // It will play at least the last 10% of the audio.
                const NewTime = Math.min
                (
                  // Last 10% of the audio as first parameter of Math.min().
                  MyAudio.duration * 0.9,
                  // Your custom expression below as second parameter of Math.min().
                  Date.now() % ( MyAudio.duration * 1000 )
                );
                
                console.log('Playing audio:', MyAudioFile, 'Time position:', NewTime);
                
                MyAudio.currentTime = NewTime;
                MyAudio.play();
              }
            }
          );
        }
      );
    }
    <div id="MyMedias"></div>

    This snippet is fully working and plays at least the last 10% of the audio or the result of your custom expression.

    I did this because the result of your custom expression sometimes result in a time that is greater than the duration of the audio and it will not play at all.

    NOTE: The buttons will only be enabled when there's a duration for that audio.

    I hope this helps you. But you'll have to adjust it though...