0
votes

The issue

I've created a javascript/jQuery/HTML5 audio player that works very well, save a few exceptions...

  1. Sometimes the audio loads, but doesn't start playing, even though audio.play() is included in the script
  2. Sometimes the player returns a duration of 'Infinity:0NaN'
  3. Sometimes the audio doesn't load all the way, but the player plays anyway (i.e. - a song is 90 sec. long, but only loads, displays the duration of, and plays something like 6 seconds)

I would greatly appreciate any insight that can be offered. Thanks in advance!

You can test the action here: http://goo.gl/Aw2OaY

A few details

I'm dynamically loading each mp3 via a div that stores the mp3's location in a 'data' attribute. When clicked, a script loads a single html5 audio player located at the bottom of my page with the new mp3.

Here is a representation of the loader:

<div class="song-link" data="/song-url.mp3"><p class="song-title"> My Song</p></div>

The player

<!-- audioPlayer.js -->
<audio id="music">
  <source id="mp3Source" src="" type="audio/mpeg" />
</audio>

<div id="audioPlayerWrap">
  <div id="currentSong"></div>
  <div id="audioplayer">
    <button id="pButton" class="play"></button>
    <div id="timeline">
      <div id="playhead"></div>
    </div>
    <div id="time"></div>
    <div id="exitPlayer">X</div>
  </div>
</div>

The script

jQuery(document).ready(function() {

  //
  // Store elements
  //

  var audioPlayerWrap = document.getElementById('audioPlayerWrap');
  var audio = jQuery('audio');
  var music = document.getElementById('music');
  var mp3Source = jQuery('#mp3Source');
  var pButton = document.getElementById('pButton');
  var timeline = jQuery('#timeline');
  var playhead = document.getElementById('playhead');
  var songLink = jQuery('.song-link');
  var currentSong = jQuery('#currentSong');
  var exitPlayer = jQuery('#exitPlayer');

  //
  // Event Listeners  
  //  

  // Play audio on songLink click
  songLink.click(function() {

    // Get song URL from data attribute
    var songURL = jQuery(this).attr('data');
    // Set song URL as audio player source
    mp3Source.attr('src', songURL);

    // Get song name
    var songName = jQuery(this).text();
    // Set current song text
    currentSong.text('Now playing: ' + '"' + songName + '"');

    // Load music
    music.load();

    //Load meta data and play
    music.addEventListener("loadedmetadata", function(event) {
       var duration = music.duration;
       // Play music
       playAudio();
    });

    // Bind song time to timeline
    jQuery('#music').bind('timeupdate', updateTime);

    // Bind song end to time
    jQuery('#music').bind('ended', songEnded);

    // Show audio player
    audioPlayerWrap.style.opacity = 1;

  });

  // Add play/pause function pButton click
  pButton.addEventListener('click', function() {
    playAudio();
  });

  // Make Timeline clickable
  timeline.click(function(e) {
    // Store click position X and timeline width in px
    var offsetLeft = jQuery(this).parent().offset().left;
    var positionX = (e.pageX - offsetLeft) - 40;  // 40 makes up for width of playhead
    var timelineWidth = timeline.width();

    // Update audio position
    var songDuration = Math.floor(music.duration);
    var clickPercentage = positionX / timelineWidth;
    var clickInSeconds = clickPercentage * songDuration;

    music.currentTime = clickInSeconds;
  });

  // Close audio player on click
  exitPlayer.click(function() {
    audioPlayerWrap.style.opacity = 0;
    // Remove audio player once it's invisible
    setTimeout(function() {
      audioPlayerWrap.style.display = 'none';
    }, 500);

    music.pause();
    // remove pause, add play
    pButton.className = "";
    pButton.className = "play";
  });

  //
  // Functions
  //

  //Play/Pause function
  function playAudio() {
    // start music
    if (music.paused) {
      music.play();
      // remove play, add pause
      pButton.className = "";
      pButton.className = "pause";
      audioPlayerWrap.style.display = 'block';  // Make sure player can be seen
    } else { // pause music
      music.pause();
      // remove pause, add play
      pButton.className = "";
      pButton.className = "play";
    }
  }

  // time function
  var updateTime = function() { 
    // Timeline width
    timeline.css({'width': '100%'});
    var playheadPosition = Math.floor((this.currentTime / this.duration) * 100);
    playhead.style.marginLeft = playheadPosition + '%';
    // Update timer
    time.innerHTML =  readableDuration(Math.floor(this.currentTime)) + ' / ' + readableDuration(Math.floor(this.duration));
  }

  // Convert time to readable format
  function readableDuration(seconds) {
    sec = Math.floor( seconds );    
    min = Math.floor( sec / 60 );
    min = min >= 10 ? min : '0' + min;    
    sec = Math.floor( sec % 60 );
    sec = sec >= 10 ? sec : '0' + sec;    
    return min + ':' + sec;
  }

  // song end function
  var songEnded = function() {
    if (music.currentTime >= music.duration) {
      music.pause();
      // remove pause, add play
      pButton.className = "";
      pButton.className = "play";
      music.currentTime = 0;
    }
  }

});
1

1 Answers

1
votes

Your (Scrambled-eggs) audio file is returning 206 and showing the following:

"Range:bytes=0- "

The audio is fine when loaded independently (200 response) but it doesn't look for the Range header. For your loadedmetadata event to fire, the range header needs to be complete.

You need to ensure that the content-range header is being set wherever the audio is being served from for the stream to work. This will cause both the issues. If you can provide some details on your server config I could give you a better answer.