16

I am working on a script where i am playing multiple mp3 and each files is in queue. There is slight delay in playing next .mp3 file as it takes time to buffer/load the file.

How can i buffer the next .mp3 file which is queue so that all file run smoothly without any delay.

 getData(1);

 function getData(id) {
   //Emty div
   $("#surah-wrapper").empty();

   $.ajaxSetup({
 cache: true,
 jsonpCallback: 'quranData'
   }); // define ajax setup 
   // Quran Text Type quran-uthmani | quran-simple | quran-simple-clean | quran-wordbyword
   $.getJSON("http://api.globalquran.com/surah/" + id + "/quran-uthmani?key=api_key&jsoncallback=?", {
 format: "jsonp"
   }, function(data) {
 if (id > 1) {
   $("<span class='qspan qspan-bsm'>").html("بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ").appendTo("#surah-wrapper");
 }
 $.each(data.quran, function(i, by) {
   $.each(by, function(verseNo, line) {
     //$("<p>").html('('+ line.surah+':'+line.ayah+') '+line.verse).appendTo("#surah-wrapper");
     $("<span class='qspan' id='" + verseNo + "'>").html(line.verse + '<span class="qspan-ayahno">(' + line.surah + ':' + line.ayah + ')</span>').appendTo("#surah-wrapper");
   });
 });
   });
 }

 //Play Script & highlight script
 var audioIndex = 0;
 var countSpan = 0;
 countSpan = $('#surah-wrapper').children().length;

 var surahNo = 1;

 var strCat = "http://download.quranicaudio.com/verses/Sudais/mp3/001001.mp3,http://download.quranicaudio.com/verses/Sudais/mp3/001002.mp3,http://download.quranicaudio.com/verses/Sudais/mp3/001003.mp3,http://download.quranicaudio.com/verses/Sudais/mp3/001004.mp3,http://download.quranicaudio.com/verses/Sudais/mp3/001005.mp3,http://download.quranicaudio.com/verses/Sudais/mp3/001006.mp3,http://download.quranicaudio.com/verses/Sudais/mp3/001007.mp3";

 setPlayer();

 $('.customSurah').change(function() {

   $('.play-btn').css('display', 'none');
   $aud.pause();

   surahNo = $('#surah option:selected').val();

   setTimeout(function() {

 countSpan = $('#surah-wrapper').children().length;

 var i = 0;
 strCat = '';

 for (i = 0; i <= countSpan; i++) {

   if (i == 0) {
     strCat = "http://download.quranicaudio.com/verses/Sudais/mp3/001001.mp3,";
     i += 1
   }

   if (i == countSpan) {

     if (surahNo == 1) {

     } else {


       if (i < 10) {
         strCat += "http://download.quranicaudio.com/verses/Sudais/mp3/00" + surahNo + "00" + i + ".mp3,";
       }
      
     }
   } else {
     if (i < 10) {
       strCat += "http://download.quranicaudio.com/verses/Sudais/mp3/00" + surahNo + "00" + i + ".mp3,";
     }


     
   }
 }
 if (surahNo == 1) {
   strCat = null;
   strCat = "http://download.quranicaudio.com/verses/Sudais/mp3/001001.mp3,http://download.quranicaudio.com/verses/Sudais/mp3/001002.mp3,http://download.quranicaudio.com/verses/Sudais/mp3/001003.mp3,http://download.quranicaudio.com/verses/Sudais/mp3/001004.mp3,http://download.quranicaudio.com/verses/Sudais/mp3/001005.mp3,http://download.quranicaudio.com/verses/Sudais/mp3/001006.mp3,http://download.quranicaudio.com/verses/Sudais/mp3/001007.mp3";

 }


 setPlayer();
 $('.play-btn').css('display', 'block');

   }, 3000);

 });

 function setPlayer() {

   //reset values
   audioIndex = 0;
   countSpan = 0;
   countSpan = $('#surah-wrapper').children().length;

   strCat = strCat.trim();
   var audioTracks = strCat;

   var audioAddress = audioTracks.split(',');

   var playing = false;

   $(function() {

 $aud = $("#myAudio")[0];
 $btn = $(".play-btn");

 function setAudio(index) {
   $("#surah-wrapper > .qspan").removeClass("qplaying");
   $aud.preload = 'auto';
   $aud.src = audioAddress[index];
 }

 setAudio(audioIndex);

 $btn.click(function() {
   if (playing) {
     playing = false;
     $aud.pause();
   } else
     $aud.play();
 });

 $aud.onended = function() {
   if (audioIndex < audioAddress.length - 1) {
     audioIndex++;
     setAudio(audioIndex);
     $aud.play();
   } else {
     audioIndex = 0;
     setAudio(audioIndex);
     playing = false;
     $btn.text("Play");
   }
 };

 $aud.onpause = function() {
   if (!playing) $btn.text("Play");
   $(".play-btn").css("background-image", "url(https://cdn0.iconfinder.com/data/icons/cosmo-player/40/button_play_1-64.png)");
 };

 $aud.onplay = function() {
   $btn.text("Pause");
   $(".play-btn").css("background-image", "url(https://cdn0.iconfinder.com/data/icons/cosmo-player/40/button_pause_1-64.png)");
   playing = true;
   $("#surah-wrapper > .qspan:nth-child(" + (audioIndex + 1) + ")").addClass("qplaying");
   var wHeight = $(window).height();
   var wHalfHeight = wHeight;
   var x = $(".qplaying").offset();
   var curentSpanPosition = x.top;
   wHalfHeight = wHalfHeight / 2;
   if (curentSpanPosition > wHalfHeight) {
     $('html, body').animate({
       scrollTop: curentSpanPosition - 50
     }, 1000);
   }

 };
   });

 }
.play-btn {
  background-image: url("https://cdn0.iconfinder.com/data/icons/cosmo-player/40/button_play_1-64.png");
  float: none;
  font-size: 0 !important;
  height: 50px;
  margin: 15px auto;
  padding: 5px 10px;
  text-align: center;
  width: 50px;
}
body{float:right; direction:rtl;}
span{padding:5px 10px; direction:rtl; text-align:right;
  margin:5px 1px;
font-size:20px}
.qplaying {
  background: #f00 none repeat scroll 0 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
         <audio id="myAudio" >
         <source src="" type="audio/mpeg">
        </audio>

       
         <div class="play-btn-wrapper">
            <select class="customSurah form-control ddCountry styled-select" id="surah" name="surah" onchange="getData($('#surah option:selected').val())"><option value="1">Al-Faatiha</option><option value="2">Al-Baqara</option><option value="3">Aal-i-Imraan</option><option value="4">An-Nisaa</option><option value="5">Al-Maaida</option><option value="6">Al-An'aam</option><option value="7">Al-A'raaf</option><option value="8">Al-Anfaal</option><option value="9">At-Tawba</option><option value="10">Yunus</option><option value="11">Hud</option><option value="12">Yusuf</option><option value="13">Ar-Ra'd</option><option value="14">Ibrahim</option><option value="15">Al-Hijr</option><option value="16">An-Nahl</option><option value="17">Al-Israa</option><option value="18">Al-Kahf</option><option value="19">Maryam</option><option value="20">Taa-Haa</option><option value="21">Al-Anbiyaa</option><option value="22">Al-Hajj</option><option value="23">Al-Muminoon</option><option value="24">An-Noor</option><option value="25">Al-Furqaan</option><option value="26">Ash-Shu'araa</option><option value="27">An-Naml</option><option value="28">Al-Qasas</option><option value="29">Al-Ankaboot</option><option value="30">Ar-Room</option><option value="31">Luqman</option><option value="32">As-Sajda</option><option value="33">Al-Ahzaab</option><option value="34">Saba</option><option value="35">Faatir</option><option value="36">Yaseen</option><option value="37">As-Saaffaat</option><option value="38">Saad</option><option value="39">Az-Zumar</option><option value="40">Al-Ghaafir</option><option value="41">Fussilat</option><option value="42">Ash-Shura</option><option value="43">Az-Zukhruf</option><option value="44">Ad-Dukhaan</option><option value="45">Al-Jaathiya</option><option value="46">Al-Ahqaf</option><option value="47">Muhammad</option><option value="48">Al-Fath</option><option value="49">Al-Hujuraat</option><option value="50">Qaaf</option><option value="51">Adh-Dhaariyat</option><option value="52">At-Tur</option><option value="53">An-Najm</option><option value="54">Al-Qamar</option><option value="55">Ar-Rahmaan</option><option value="56">Al-Waaqia</option><option value="57">Al-Hadid</option><option value="58">Al-Mujaadila</option><option value="59">Al-Hashr</option><option value="60">Al-Mumtahana</option><option value="61">As-Saff</option><option value="62">Al-Jumu'a</option><option value="63">Al-Munaafiqoon</option><option value="64">At-Taghaabun</option><option value="65">At-Talaaq</option><option value="66">At-Tahrim</option><option value="67">Al-Mulk</option><option value="68">Al-Qalam</option><option value="69">Al-Haaqqa</option><option value="70">Al-Ma'aarij</option><option value="71">Nooh</option><option value="72">Al-Jinn</option><option value="73">Al-Muzzammil</option><option value="74">Al-Muddaththir</option><option value="75">Al-Qiyaama</option><option value="76">Al-Insaan</option><option value="77">Al-Mursalaat</option><option value="78">An-Naba</option><option value="79">An-Naazi'aat</option><option value="80">Abasa</option><option value="81">At-Takwir</option><option value="82">Al-Infitaar</option><option value="83">Al-Mutaffifin</option><option value="84">Al-Inshiqaaq</option><option value="85">Al-Burooj</option><option value="86">At-Taariq</option><option value="87">Al-A'laa</option><option value="88">Al-Ghaashiya</option><option value="89">Al-Fajr</option><option value="90">Al-Balad</option><option value="91">Ash-Shams</option><option value="92">Al-Lail</option><option value="93">Ad-Dhuhaa</option><option value="94">Ash-Sharh</option><option value="95">At-Tin</option><option value="96">Al-Alaq</option><option value="97">Al-Qadr</option><option value="98">Al-Bayyina</option><option value="99">Az-Zalzala</option><option value="100">Al-Aadiyaat</option><option value="101">Al-Qaari'a</option><option value="102">At-Takaathur</option><option value="103">Al-Asr</option><option value="104">Al-Humaza</option><option value="105">Al-Fil</option><option value="106">Quraish</option><option value="107">Al-Maa'un</option><option value="108">Al-Kawthar</option><option value="109">Al-Kaafiroon</option><option value="110">An-Nasr</option><option value="111">Al-Masad</option><option value="112">Al-Ikhlaas</option><option value="113">Al-Falaq</option><option value="114">An-Naas</option></select>
             <div class="play-btn"></div>
        </div>

        <div id="surah-wrapper"></div>

THIS IS THE ACTUAL SCRIPT that i want to implement same: I would appreciate solution in context with script mentioned in the fiddle.

http://codepen.io/anon/pen/pRKreo

Learning
  • 19,469
  • 39
  • 180
  • 373
  • 1
    [Controlling multiple parameters with ConstantSourceNode](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Controlling_multiple_parameters_with_ConstantSourceNode) and [ConstantSourceNode](https://developer.mozilla.org/en-US/docs/Web/API/ConstantSourceNode) – Ronnie Royston Feb 16 '17 at 22:13
  • Expanded a possible solution script. You can copy paste demo from here. https://github.com/rhroyston/mdl-audio – Ronnie Royston Mar 21 '17 at 21:29

2 Answers2

7

Here's you go. The biggest challenge I had was handling the value of this within the handler. Set your <audio> elements to preload="none". My script loads the next song as soon as you play one before it and auto-plays next song once first one finishes.

If you're worried about Global Scope just put it in an IIFE. Enjoy!

var files = document.getElementsByTagName('audio');
var songs = [];
var index = 0;

var Song = function(element) {

this.index = index;
this.playing = function(event) {
 try {
  files[this.index].preload = "auto";
 }
 catch (e) {

 }
};
this.ended = function(event) {
 try {
  files[this.index].play();
 }
 catch (e) {

 }
};
element.addEventListener('playing', this.playing.bind(this), false);
element.addEventListener('ended', this.ended.bind(this), false); // Trick
};

for (var len = files.length, i = 0; i < len; ++i) {
index++;
songs.push(new Song(files[i]));
}
ul{
  list-style: none;
}
<!DOCTYPE html>
<html lang="en">
    <meta name="description" content="HTML5 Media Auto Player Skeleton" />
    <title>HTML5 Media Auto Player Skeleton</title>
    <style>

    </style>
</head>

<body>
        
    <main>
<ul>
 <li class="album">
  <h3 class="album-title">HTML5 Media Player w Auto Next</h3>
 </li>
 <li>
  <audio controls="controls" class="full-width" preload="metadata">
   <source src="//rack.international/samples/AttritionDantesKitchenwWHellmixSAMPLE.mp3" type="audio/mpeg">
   <source src="//rack.international/samples/AttritionDantesKitchenwWHellmixSAMPLE.ogg" type="audio/ogg">
   Your browser does not support the audio element.
  </audio>
 </li>
 <li>
  <audio controls="controls" class="full-width" preload="metadata">
   <source src="//rack.international/samples/AttritionDantesKitchenRascalKlonermxSAMPLE3.mp3" type="audio/mpeg">
   <source src="//rack.international/samples/AttritionDantesKitchenRascalKlonermxSAMPLE3.ogg" type="audio/ogg">
   Your browser does not support the audio element.
  </audio>  
 </li>
 <li>
  <audio controls="controls" class="full-width" preload="metadata">
   <source src="//rack.international/samples/crankRingtone.mp3" type="audio/mpeg">
   <source src="//rack.international/samples/crankRingtone.ogg" type="audio/ogg">
   Your browser does not support the audio element.
  </audio>  
 </li>
        
</body>

</html>
Ronnie Royston
  • 16,778
  • 6
  • 77
  • 91
  • Thanks & i will let you know. Have you setup fiddle for same. – Learning Feb 13 '17 at 08:28
  • It seems you got me wrong, I want single player which will play first file and cache the next file in queue so that there is no delay. In your case you are using three players which is not practical for me as i will have 4-200 mp3 files to be played automatically one after another. – Learning Feb 14 '17 at 05:57
  • 2
    I see. Well I did the hard part for you. The framework is there for you. Just create and append next (or next +3) song (just like I modify the `preload` attribute) once a song starts. Basic XHR metadata (song name, url, etc) in then `.createElement` `.appendChild` and you're done. Hope that helps. – Ronnie Royston Feb 14 '17 at 14:40
  • One issue I observed was my solution does not autoplay on my mobile (android). I believe I read somewhere this is default for mobile's though - they require user input? Maybe sinlge ` – Ronnie Royston Feb 16 '17 at 05:34
  • @Learning _"I want single player"_ What do you mean by "single player"? Original Question does not specify that a single `AudioNode` should be used for media playback. You can use `MediaSource` `.appendBuffer()` to use single `AudioNode` for playback, see [HTML5 audio streaming: precisely measure latency?](http://stackoverflow.com/questions/38768375/html5-audio-streaming-precisely-measure-latency/). Though you will still need to request all resources before or during current playback to avoid delay while media loads at browser. – guest271314 Feb 17 '17 at 15:53
  • I mean single audio control ` – Learning Feb 19 '17 at 03:22
  • @guest271314, Object was to play audio files seemslessly, I used 3 audio controlls to load the next 2 files so that they will be cache, Audio for 2&3 audio control was muted. while audio control 1 acted as a primary control but this trick didnt work. I think solution lies in Howler.js control which allows playing of multiple file seemlessly. Need to check that. Overall object was to play audio without any delay – Learning Feb 20 '17 at 03:10
  • @guest271314, i am not a JS guy i do this task as & when they come so fine it hard at time to do things in a more professional way. We mostly use JS for Validation. – Learning Feb 20 '17 at 03:13
5

You can use Promise.all(), Array.prototype.map(), Audio() constructor, canplaythrough event to load all audio first; then use Array.prototype.reduce(), Promise constructor to play audio in sequence at ended event.

var audioAddress = [
  "http://download.quranicaudio.com/verses/Sudais/mp3/003001.mp3",
  "http://download.quranicaudio.com/verses/Sudais/mp3/003002.mp3",
  "http://download.quranicaudio.com/verses/Sudais/mp3/003003.mp3",
  "http://download.quranicaudio.com/verses/Sudais/mp3/003004.mp3",
  "http://download.quranicaudio.com/verses/Sudais/mp3/003005.mp3",
  "http://download.quranicaudio.com/verses/Sudais/mp3/003006.mp3"
];

$("button").click(function() {
  Promise.all(audioAddress.map(function(url) {
    return new Promise(function(resolve) {
      var audio = new Audio(url);
      audio.oncanplay = function() {
        resolve(audio);
      }
    })
  }))
  .then(function(data) {
    data.reduce(function(promise, a, index) {
      return promise.then(function() {
        return new Promise(function(resolve) {        
          a.onended = resolve;
          a.play();
          $("p > span").removeClass("playing");
          $("p > span:nth-child(" + (index + 1) + ")")
          .addClass("playing");
        })
      })
    }, Promise.resolve())
  })
});
#myAudio {
  display: none;
}
span {
  margin: 0px 10px;
}
.playing {
  background: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">  
</script>
<button>Play Audio</button>
<p>
  <span>Verse 1</span>
  <span>Verse 2</span>
  <span>Verse 3</span>
  <span>Verse 4</span>
  <span>Verse 5</span>
  <span>Verse 6</span>
</p>

You can also create a mix of the sequence of audio tracks to play as a single track Is it possible to mix multiple audio files on top of each other preferably with javascript and use AudioContext.linearRampToValueAtTime Web audio api, stop sound gracefully.

Community
  • 1
  • 1
guest271314
  • 1
  • 15
  • 104
  • 177
  • This will work fine if we have small number of file, Downloading 100-200 files for most of the Quran chapters will be very slow. For example this chapter of Quran has 200 verses to in array i will have 200 files. that will slow down as it will take time to download all the files. I only want to preload next 2 or 3 files so that it will be fast rather than dowloading all teh filess. If i didnt get your concept wrong – Learning Feb 05 '17 at 08:25
  • 1
    You can load `N` number of files at a time, for example 6. When `index` reaches `N -1`, load an additional `N` files that will be scheduled to be played when media file `N` completes playback. Or, use `WebWorker` to request all files, then transfer file to main thread when current media playback completes. – guest271314 Feb 05 '17 at 08:28
  • Thanks you, I will try to implement this and see if i can do it right – Learning Feb 05 '17 at 08:31
  • 1
    @Learning You could place and `return` `Promise.all()` from within a function, `return` `data.reduce()`, chain `.then()` to call same function again, until all 100-200 files have been played. – guest271314 Feb 05 '17 at 08:36
  • I am finding it hard to implement your code. this is my final script could you please implement the same in this script http://codepen.io/anon/pen/pRKreo – Learning Feb 05 '17 at 10:09
  • @Learning Do you mean use the pattern at Answer to play 100 audio tracks in sequence? – guest271314 Feb 05 '17 at 15:51
  • Actually each chapter in Quran has x number of version it could be minimum of 4 - 200. & each verse is downloaded as a mp3 file. In my case when first version of a chapter play then there is about 1 second break till next mp3 file for the the next verse is download. I want to close this gap and want to play each verse smoothly without 1 second delay. I add two more audio controls in muted forms & assign them the next version just so that they can be download while first is play. but this didnt make much different. I just want to play the next audio smoothly without much delay – Learning Feb 06 '17 at 03:36
  • Link for the same http://codepen.io/anon/pen/ZLRXVR?editors=1010. ` ` – Learning Feb 06 '17 at 03:36
  • You solution runs smoothly but i am not able to implement that in my current fiddle. – Learning Feb 06 '17 at 03:37