Javascript - Seek audio to certain position when at exact position in audio track

ε祈祈猫儿з 提交于 2019-12-11 05:18:29

问题


Scenario:

  1. Audio starts playing from 0:00. At exactly 0:05, the track skips forwards to 0:30.
  2. The track immediately starts playing at 0:30, and at exactly 0:35, the track skips backwards to 0:05 and plays the rest of the audio file

Summary: Play: 0:00 to 0.05, Skip: 0:05 to 0:30, Play: 0:30 to 0:35, Skip: 0:35 to 0:05, Play: 0.05 to END

The real problem comes when there is the need for a immediate and seamless skip. For example, setTimeout is very inaccurate and drifts meaning it cannot be used in this case.

I have tried to use the Web Audio API to accomplish this, but I'm still unable to get an immediate transition. I'm currently scheduling segments of a loaded song using AudioBufferSourceNode.start(when, offset, duration);, initially passing in (0 [when], 0 [offset], 0:05 [duration]) for the example above. After this, I'm still using setTimeout to call the same function to run just short of 0:30 and scheduling something like (0 [when] + 0:05 [previous duration], 0:30 [offset], 0:05 [duration]).

Example of this

var AudioContext = window.AudioContext || window.webkitAudioContext,
    audioCtx     = new AudioContext();
    
var skipQueue = [0, 5, 30, 35, 5];
    
// getSong(); // load in the preview song (https://audiojungle.net/item/inspiring/9325839?s_rank=1)

playSound(0, 0);

function getSong() {
    console.log("Loading song...");

    request = new XMLHttpRequest();
    // request.open('GET', "https://preview.s3.envato.com/files/230851789/preview.mp3?response-content-disposition=attachment%3Bfilename%3D20382637_uplifting-cinematic-epic_by_jwaldenmusic_preview.mp3&Expires=1501966849&Signature=HUMfPw3b4ap13cyrc0ZrNNumb0s4AXr7eKHezyIR-rU845u65oQpxjmZDl8AUZU7cR1KuQGV4TLkQ~egPt5hCiw7SUBRApXw3nnrRdtf~M6PXbNqVYhrhfNq4Y~MgvZdd1NEetv2rCjhirLw4OIhkiC2xH2jvbN6mggnhNnw8ZemBzDH3stCVDTEPGuRgUJrwLwsgBHmy5D2Ef0It~oN8LGG~O~LFB5MGHHmRSjejhjnrfSngWNF9SPI3qn7hOE6WDvcEbNe2vBm5TvEx2OTSlYQc1472rrkGDcxzOHGu9jLEizL-sSiV61uVAp5wqKxd2xNBcsUn3EXXnjMIAmUIQ__&Key-Pair-Id=APKAIEEC7ZU2JC6FKENA", true); // free preview from envato
    request.responseType = "arraybuffer";

    request.onload = function() {
        var audioData = request.response;

        audioCtx.decodeAudioData(audioData, function(buffer) {
            audioBuffer = buffer;

            console.log("Ready to play!");
            
            playSong()
        }, function(e) {
            "Error with decoding audio data" + e.err
        });
    }

    request.send();
}


function playSound(previousPlay, nextPlay) {
    // source        = audioCtx.createBufferSource();
    // source.buffer = audioBuffer;
    // source.connect(audioCtx.destination);
    
    skipQueue.shift();

    var duration = Math.abs(skipQueue[0] - previousPlay);

    // source.start(nextPlay, previousPlay, duration);
    
    console.log("Running: source.start(" + nextPlay + ", " + previousPlay + ", " + duration + ")");
    console.log("Current Time: " + previousPlay);
    console.log("Next Play in: " + duration + " (Skipping from " + skipQueue[0] + " to " + skipQueue[1] + ")");

    if (skipQueue.length > 1) {
      setTimeout(function() {
          playSound(skipQueue[0], nextPlay + duration);
      }, 1000 * duration - 50); // take 50ms off for drift that'll be corrected in scheduling
    }
}
<strong>Expected:</strong><br />
Play: 0:00 to 0.05, Skip: 0:05 to 0:30, Play: 0:30 to 0:35, Skip: 0:35 to 0:05, Play: 0.05 to END

I can't get this simplified down example 100% working, but you can see what my attempt is in the console. I've also commented out code since StackOverflow doesn't support AJAX.

I'm open to using any other API or method that you have in mind!


回答1:


You don't ever show when you actually call start() on an AudioBufferSourceNode, so I can't explicitly debug. But in short, you need to schedule the stops and starts AHEAD of when they need to happen, not try to "swap" the active sound playing in a setTimeout callback (or try to get them to align once the sound has stopped playing).

For example, you'd do something like:

var bs1 = audioCtx.createBufferSourceNode();
var bs2 = audioCtx.createBufferSourceNode();
var bs3 = audioCtx.createBufferSourceNode();
var now = audioCtx.currentTime + 0.020; // add 20ms for scheduling slop

bs1.buffer = audioBuffer;
bs1.connect( audioCtx.destination );
bs2.buffer = audioBuffer;
bs2.connect( audioCtx.destination );
bs3.buffer = audioBuffer;
bs3.connect( audioCtx.destination );
bs1.start( now, 0, 5 );  // time, offset, duration
bs2.start( now+5, 30, 5 );
bs3.start( now+10, 5 );   // no duration; play to end

If you want to cancel this playing, you'll have to disconnect the buffersource nodes.




回答2:


The main issue in this will be the accuracy in time and shifting seamlessly from 0:05 to 0:30 and back from 0:35 to 0:05.

I think you should create two different audio sources. Start one from 0:00 and seek the other one at 0:30 but pause it as soon as it starts playing using events.

Then track time using the ontimeupdate event and check it if it is 5 then pause the current and start the second which should now be buffered and when it reaches to 0:35 pause it and start the first one and let it play till the audio finishes.




回答3:


Play: 0:00 to 0.05, Skip: 0:05 to 0:30, Play: 0:30 to 0:35, Skip: 0:35 to 0:05, Play: 0.05 to END

You can use Media Fragments URI and pause event to render exact media playback in sequence. If necessary pass the HTMLMediaElement to AudioContext.createMediaElementSource()

const audio = document.querySelector("audio");

const mediaURL = "https://ia600305.us.archive.org/30/items/return_201605/return.mp3";

const timeSlices = ["#t=0,5", "#t=30,35", "#t=5"];
let index = 0;
audio.onpause = e => {
  if (index < timeSlices.length) {
    audio.src = `${mediaURL}${timeSlices[index++]}`
  } else {
    console.log("Media fragments playlist completed");
  }
}
audio.ontimeupdate = e => console.log(Math.ceil(audio.currentTime));
audio.oncanplay = e => audio.play();
audio.src = `${mediaURL}${timeSlices[index++]}`;
<audio controls></audio>


来源:https://stackoverflow.com/questions/45395073/javascript-seek-audio-to-certain-position-when-at-exact-position-in-audio-trac

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!