Javascript - поиск аудио в определенном месте, когда в точном месте в звуковой дорожке

Сценарий:

  1. Звук начинает воспроизводиться с 0:00. Ровно в 0:05 трек переходит вперед к 0:30.
  2. Дорожка сразу начинает воспроизводиться в 0:30, и ровно в 0:35, дорожка переходит назад к 0:05 и воспроизводит оставшуюся часть аудиофайла.

Обзор: Воспроизведение: от 0:00 до 0,05, Пропуск: от 0:05 до 0:30, Воспроизведение: от 0:30 до 0:35, Пропуск: от 0:35 до 0:05, Воспроизведение: от 0,05 до КОНЕЦ

Настоящая проблема возникает, когда есть необходимость немедленного и плавного пропуска. Например, setTimeout очень неточно и дрейфует, что означает, что его нельзя использовать в этом случае.

Я пытался использовать API Web Audio для достижения этой цели, но я все еще не могу получить немедленный переход. В настоящее время я планирую сегменты загруженной песни, используя AudioBufferSourceNode.start(when, offset, duration); изначально проходя в (0 [when], 0 [offset], 0:05 [duration]) для примера выше. После этого я все еще использую setTimeout вызвать ту же функцию, чтобы запустить чуть меньше 0:30 и запланировать что-то вроде (0 [when] + 0:05 [previous duration], 0:30 [offset], 0:05 [duration]),

Пример этого

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

Я не могу заставить этот упрощенный пример работать на 100%, но вы можете увидеть, что моя попытка в консоли. Я также закомментировал код, так как Stackru не поддерживает AJAX.

Я открыт для использования любого другого API или метода, который вы имеете в виду!

3 ответа

Решение

Вы никогда не показываете, когда вы фактически вызываете start() для AudioBufferSourceNode, поэтому я не могу явно отладить. Но вкратце, вам нужно запланировать остановки и запускать AHEAD, когда они должны произойти, а не пытаться "поменять" активный звук, воспроизводимый в обратном вызове setTimeout (или попытаться выровнять их после того, как звук перестал воспроизводиться).

Например, вы бы сделали что-то вроде:

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

Если вы хотите отменить эту игру, вам придется отключить узлы буферного источника.

Главной проблемой в этом будет точность во времени и плавный переход от 0:05 до 0:30 и обратно от 0:35 до 0:05.

Я думаю, что вы должны создать два разных аудио источника. Начните один с 0:00 и ищите другой в 0:30, но сделайте паузу, как только он начнет играть, используя события.

Затем отследите время, используя событие ontimeupdate, и проверьте его, если оно равно 5, затем приостановите текущее и запустите второе, которое теперь должно быть буферизовано, а когда оно достигнет 0:35, приостановите его и запустите первое и дайте ему воспроизводиться, пока аудио не закончится.,

Воспроизведение: с 0:00 до 0,05, Пропуск: с 0:05 до 0:30, Воспроизведение: с 0:30 до 0:35, Пропуск: с 0:35 до 0:05, Воспроизведение: с 0,05 по КОНЕ

Вы можете использовать медиа-фрагменты URI и pause Событие для точного воспроизведения медиафайлов в последовательности. При необходимости сдайте HTMLMediaElement в 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>

Другие вопросы по тегам