title: Extracting fragment of audio from a url and play it with pure Web Audio API
body: <p>On the following url:</p>
  
  <p><a href="https://www.tophtml.com/snl/15.mp3" rel="nofollow noreferrer">https://www.tophtml.com/snl/15.mp3</a></p>
  
  <p>there is one audio I want to play using pure <code>Web Audio API</code> on the <code>following range</code>:</p>
  
  <pre><code>range from: second: 306.6
    range to: second: 311.8
       total: 5.2 seconds
  </code></pre>
  
  <p>I downloaded that file to my desktop (I'm using <code>Windows 10</code>), then opened it with <code>VLC</code> and got the following file info:</p>
  
  <p><a href="https://i.stack.imgur.com/30XCb.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/30XCb.png" alt="enter image description here"></a></p>
  
  <pre><code>number of channels: 2
         sample rate: 44100 Hz
     bits per sample: 32 (float32)
  </code></pre>
  
  <p>Here you have info about concepts on this:</p>
  
  <p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Basic_concepts_behind_Web_Audio_API#Audio_buffers_frames_samples_and_channels" rel="nofollow noreferrer">https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Basic_concepts_behind_Web_Audio_API#Audio_buffers_frames_samples_and_channels</a></p>
  
  <p>from where I got the following excerpt:</p>
  
  <p><a href="https://i.stack.imgur.com/8d0gP.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/8d0gP.png" alt="enter image description here"></a></p>
  
  <p>I want to play the <code>range</code> commented above (also pasting it here):</p>
  
  <pre><code>range from: second: 306.6
    range to: second: 311.8
       total: 5.2 seconds
  </code></pre>
  
  <p>by downloading just that fragment from the server, which supports the request header: <code>Range</code>.</p>
  
  <p>Then I tried the following code:</p>
  
  <pre><code>...
  let num_channels    = 2;
  let sample_rate     = 44100;
  let range_from      = 0;                                    // Goal: 306.6 seconds
  let range_length    = (sample_rate / num_channels) * 5.2;   // Goal:   5.2 seconds
  let range_to        = range_from + (range_length - 1);      // "range_to" is inclusive (confirmed)
  request.setRequestHeader("Range", "bytes=" + range_from + "-" + range_to);
  ...
  </code></pre>
  
  <p><strong>My questions are:</strong></p>
  
  <ol>
  <li><p>I need to find the right value for variable: <code>range_from</code> so it starts playing from second: <code>306.6</code>.</p></li>
  <li><p>I want to know if the value specified above for: <code>range_length</code> is correct or not since probably there are bytes used for headers, etc., I mean: <code>headers</code> + <code>data</code>.</p></li>
  </ol>
  
  <p>Here you have the code I have so far:</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>window.AudioContext = window.AudioContext || window.webkitAudioContext; // necessary for iPhone (maybe others). Could change a near future.
  
  const URL = 'https://www.tophtml.com/snl/15.mp3';
  const context = new AudioContext();
  
  window.addEventListener('load', function() {
  
   const button_option_1   = document.querySelector('.button_option_1');
   const button_option_1_play  = document.querySelector('.button_option_1_play');
   button_option_1_play.disabled = true;
  
   button_option_1.addEventListener('click', async function() {
    let time_start, duration;
    let buffer;
    log('...', false);
    button_option_1_play.disabled = true;
    button_option_1_play.onclick = () =&gt; playBuffer(buffer);
    //---
    time_start = new Date().getTime();
    let arrayBuffer = await fetch(URL);
    // download complete
    duration = sprintf('%.2fs', (new Date().getTime()-time_start)/1000);
    log(sprintf('P2. Delay: +%s for download. Wait...', duration));
    //---
    time_start = new Date().getTime();  
    let audioBuffer = await decodeAudioData(context, arrayBuffer);
    // decoding complete
    duration = sprintf('%.2fs', (new Date().getTime()-time_start)/1000);
    log(sprintf('P3. Delay: +%s for decoding.', duration));
    //---
    button_option_1_play.disabled = false;
    buffer = audioBuffer;
    button_option_1_play.click();
   });
  
  });
  function playBuffer(buffer, from, duration) {
   const source = context.createBufferSource(); // type of "source": "AudioBufferSourceNode"
   source.buffer = buffer;
   source.connect(context.destination);
   source.start(context.currentTime, from, duration);
  }
  function log(text, append = true) {
   let log = document.querySelector('.log');
   if (!append)
    log.innerHTML = '';
   let entry = document.createElement('div');
   entry.innerHTML = text;
   log.appendChild(entry);
  }
  function decodeAudioData(context, arrayBuffer) {
   return new Promise(async (resolve, reject) =&gt; {
    if (false) {}
    else if (context.decodeAudioData.length == 1) {
     // console.log('decodeAudioData / Way 1');
     let audioBuffer = await context.decodeAudioData(arrayBuffer);
     resolve(audioBuffer);
    }
    else if (context.decodeAudioData.length == 2) {
     // necessary for iPhone (Safari, Chrome) and Mac (Safari). Could change a near future.
     // console.log('decodeAudioData / Way 2');
     context.decodeAudioData(arrayBuffer, function onSuccess(audioBuffer) {
      resolve(audioBuffer);
     });
    }
   });
  }
  function fetch(url) {
   return new Promise((resolve, reject) =&gt; {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';
    let num_channels = 2;
    let sample_rate  = 44100;
    let range_from  = 0;         // Goal: 306.6 seconds
    let range_length = (sample_rate / num_channels) * 5.2; // Goal:   5.2 seconds
    let range_to  = range_from + (range_length - 1);  // "range_to" is inclusive (confirmed)
    request.setRequestHeader("Range", "bytes=" + range_from + "-" + range_to);
    request.onload = function() {
     let arrayBuffer = request.response;
     let byteArray = new Uint8Array(arrayBuffer);
     // console.log(Array.from(byteArray)); // just logging info
     resolve(arrayBuffer);
    }
    request.send();
   });
  }</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>.log {
   display: inline-block;
   font-family: "Courier New", Courier, monospace;
   font-size: 13px;
   margin-top: 10px;
   padding: 4px; 
   background-color: #d4e4ff;
  }
  .divider {
   border-top: 1px solid #ccc;
   margin: 10px 0;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/sprintf/1.1.1/sprintf.min.js"&gt;&lt;/script&gt;
  
  &lt;button class="button_option_1"&gt;Option 1&lt;/button&gt;
  &lt;button class="button_option_1_play"&gt;Play&lt;/button&gt;&lt;br /&gt;
  &lt;div class="log"&gt;[empty]&lt;/div&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>Here you have the corresponding <code>CodePen.io</code>:</p>
  
  <p><a href="https://codepen.io/anon/pen/RYXKmP" rel="nofollow noreferrer">https://codepen.io/anon/pen/RYXKmP</a></p>
  
  <p>Could you please, provide the right value for: <code>range_from</code> and use it on a forked code on <code>CodePen.io</code>?</p>
  
  <p>Related question: <a href="https://engineering.stackexchange.com/questions/23929">https://engineering.stackexchange.com/questions/23929</a></p>
  
  <p><strong>[EDIT 1]</strong></p>
  
  <p>Here is a simpler <code>CodePen.io</code>: <a href="https://codepen.io/anon/pen/YJKVde" rel="nofollow noreferrer">https://codepen.io/anon/pen/YJKVde</a>, which is focused on check the ability of the browser to move, given a random position, to the next valid frame.</p>
  
  <p>On a quick experiment I did, using combinations of <code>{ Windows 10, Android, iPhone } x { Native browser, Chrome, Firefox }</code>, the right above code only works on: <code>{ (Windows 10, Chrome), (Android, Chrome), (Android, Native browser) }</code>.</p>
  
  <p>It's a pity it doesn't work  on:</p>
  
  <pre><code>{ (iPhone, Safari), (iPhone, Chrome), (Windows 10, Firefox), (Android, Firefox) }
  </code></pre>
  
  <p>Is there a way we can submit a request to the browser developers to pay attention to this?</p>
  
  <p><code>Google Chrome</code> is doing really well on <code>Windows 10</code> and <code>Android</code>.</p>
  
  <p>It would be interesting that the rest of the browsers do the same.</p>
  
  <p>Thanks!</p>