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 до END

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

Я пытался использовать API веб-аудио для достижения этой цели, но я все еще не могу получить немедленный переход. В настоящее время я планирую сегменты загруженной песни, используя 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%, но вы можете увидеть мою попытку в консоли. Я также закомментировал код, поскольку StackOverflow не поддерживает AJAX.

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


person Toby Mellor    schedule 29.07.2017    source источник
comment
рабочая скрипка? возможно? Просто играть с   -  person Roko C. Buljan    schedule 30.07.2017


Ответы (3)


Вы никогда не показываете, когда вы на самом деле вызываете start() для AudioBufferSourceNode, поэтому я не могу явно отлаживать. Но вкратце, вам нужно запланировать остановки и запуски ВПЕРЕД, когда они должны произойти, а не пытаться «поменять местами» активный звук, воспроизводимый в обратном вызове 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

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

person cwilso    schedule 30.07.2017
comment
Спасибо за ответ! Да, что-то вроде этого, я думаю, сработает, но сначала мне нужно проверить это. Для чего 20 мс? В случае, когда в очереди бесконечно много пропусков, как лучше всего это сделать? - person Toby Mellor; 30.07.2017
comment
20 мс (или больше) нужны, потому что вам нужно запланировать события на временной шкале WebAudio ДО того, как они действительно должны произойти. см. Повесть о двух часах: html5rocks.com/en/tutorials/audio/scheduling< /а>. - person cwilso; 15.08.2017

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

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

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

person Abdul Aleem Khan    schedule 30.07.2017
comment
Спасибо за предложение! Я уже пробовал это и не думаю, что это будет соответствовать требованиям точности и немедленного поиска по дорожке. stackoverflow.com/a/12325960/2957677 - person Toby Mellor; 30.07.2017

Воспроизведение: от 0:00 до 0,05, Пропуск: от 0:05 до 0:30, Воспроизведение: от 0:30 до 0:35, Пропуск: от 0:35 до 0:05, Воспроизведение: от 0,05 до END

Вы можете использовать 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>

person guest271314    schedule 30.07.2017
comment
Спасибо за ответ! Я не знал, что ты можешь так поступить. Однако в примере переход не является немедленным и плавным переходом. Я собираюсь плавно переходить по песне, поэтому важно, чтобы переход происходил в очень точной точке, но использование ontimeupdate не может обеспечить достаточную точность (stackoverflow.com/a/12325960/2957677) - person Toby Mellor; 30.07.2017
comment
Событие @TobyMellor timeupdate использовалось только для отображения точного .currentTime воспроизведения мультимедиа. Обработчик событий pause — это место, где следующий медиафрейм устанавливается в .src элемента <audio>. Вы можете использовать .linearRampToValueAtTime() для смешивания фрагментов мультимедиа вместе; не уверен в плавности, хотя вы можете настроить параметры так, как ожидаете, см. Web audio API, остановите звук изящно - person guest271314; 30.07.2017
comment
@TobyMellor См. также Потоковое аудио HTML5: точное измерение задержки? - person guest271314; 30.07.2017