skip to Main Content

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;
}

2

Answers


  1. 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…

    Login or Signup to reply.
  2. JavaScript math is at times inaccurate because it stores numbers in memory as binary floats, for details see Why JavaScript is Bad At Math. When converting sec to ms the calculations end up to be roughly a little under a minute over duration so when that invalid value was assigned to currentTime the Audio object wouldn’t play (as user2804429 explained). In the example below, the huge number that is Date.now() is more manageable as seconds. As of this writing the expression: (Date.now() / 1000) % duration🞷 is always less than duration.

    I liked user2804429’s solution to disable the UI until the mp3 was ready so I added my own version of that idea to the example below (<button>s are enabled when "canplay" event fires). Details are commented in the example.

    🞷 I increased the range of random times by dividing the EPOCH by 100 insead of 1000.
    See comments in randomPos() of the example.

    // Instintate Audio object
    const mp3 = new Audio("https://glsbx.s3.amazonaws.com/-/dd.mp3");
    
    // Reference each <time> and <button>
    const rdm = document.getElementById("rdm");
    const tog = document.getElementById("tog");
    const pos = document.getElementById("pos");
    const end = document.getElementById("end");
    const now = document.getElementById("now");
    const set = document.getElementById("set");
    
    // When mp3 is able to play...
    mp3.addEventListener("canplay", function(e) {
      // display mp3 duration...
      end.textContent = this.duration;
      // and enable <button>s
      rdm.disabled = false;
      tog.disabled = false;
    });
    
    // When the user clicks button#rdm call randomPos()
    rdm.addEventListener("click", randomPos);
    
    function randomPos(e) {
      /**
       * Convert ms to .01s
       * This increases the range from:
       *   120 to 224s (divide by 1000) 
       *   to:
       *   4 to 224s (divide by 100)
       */
      const time = Date.now() / 100;
      // Calculate the random time 
      const calc = time % mp3.duration;
      // Set currentTime to calc
      mp3.currentTime = calc;
      // Display EPOCH and calc
      now.textContent = time;
      set.textContent = calc;
    }
    
    // When user clicks button#tog call toggleMP3()
    tog.addEventListener("click", toggleMP3);
    
    // Play/pause mp3 
    function toggleMP3(e) {
      if (this.matches(".off")) {
        this.className = "on";
        mp3.play();
      } else {
        this.className = "off";
        mp3.pause();
      }
    }
    
    // When mp3 currentTime changes call trackPos()
    mp3.addEventListener("timeupdate", trackPos);
    
    // Displays currentTime of mp3
    function trackPos(e) {
      pos.textContent = this.currentTime;
    }
    :root {
      font: 8vmin/1.25 "Segoe UI"
    }
    
    menu {
      list-style: none;
      width: min-contemt;
      margin: 1rem auto;
    }
    
    li {
      margin-bottom: 1rem;
    }
    
    time {
      display: inline-block;
      min-width: 8ch;
      font-family: Consolas;
      color: blue
    }
    
    button {
      font: inherit;
      cursor: pointer
    }
    
    #now {
      min-width: 15ch
    }
    <menu>
      <li>
        <!-- Displays currentTime and duration -->
        Seek: <time id="pos">0.000000</time> / <time id="end"></time>
      </li>
      <li>
        <!-- Displays EPOCH and the calculated time -->
        Now: <time id="now"></time> Set: <time id="set"></time>
      </li>
      <li>
        <!-- Click to set currentTime to calculated time -->
        <button id="rdm" disabled>Random 🔀</button>
        <!-- Click to play/pause MP3 -->
        <button id="tog" class="off" disabled>Toggle ⏯️</button>
      </li>
    </menu>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search