Как я могу слышать воспроизводимый звук во время очистки звука из тега «аудио»?

В моем примере у меня есть mp3, который пользователь загрузил на веб-страницу. Я создаю визуальную форму волны на странице, и все элементы управления по умолчанию закодированы и работают (воспроизведение, пауза, остановка, очистка и т. д.).

Я пытаюсь добиться эффекта очистки, который вы слышите в After Effects или Animate. Когда я очищаю звук, я хочу слышать тоны звука именно в том месте, где находится ползунок.

Я даже не знаю, с чего начать? Я заглянул в AudioContext и думаю, что мой ответ где-то там, но я не уверен. Я думал о том, чтобы иметь «теневой аудиотег», для которого я установил тот же src, а затем вызвал play() и довольно скоро приостановил его, но это похоже на беспорядок; особенно с агрессивным скруббером. Любое направление приветствуется!


person onekidney    schedule 06.02.2020    source источник


Ответы (2)


Да, ваш ответ в AudioContext API (все асинхронные и медленные элементы MediaElements не предназначены для этого). В частности, вам нужен интерфейс AudioBufferSourceNode, который может воспроизводить аудио мгновенно.

Все, что вам нужно сделать, это получить аудиофайл в виде ArrayBuffer, попросить AudioContext декодировать аудиоданные PCM из этого буфера и создать из него AudioBuffer.

Это была тяжелая часть, но это делается только один раз.

Затем вам просто нужно создать очень легкий AudioBufferSourceNode для каждого события "scrub".
Для этого вы можете использовать версию его start(when, offset, duration).

const slider = document.getElementById('slider');
const ctx = new AudioContext();

fetch("https://upload.wikimedia.org/wikipedia/en/d/dc/Strawberry_Fields_Forever_(Beatles_song_-_sample).ogg")
  .then( resp => resp.arrayBuffer() )
  .then( buf => ctx.decodeAudioData(buf) )
  .then( prepareUI )
  .catch( console.error );
  
function prepareUI( audioBuf ) {
  let source;
  slider.oninput = e => {
    if( source ) { source.stop(0); }
    source = ctx.createBufferSource();
    source.buffer = audioBuf;
    source.connect(ctx.destination);
    const offset = slider.value * audioBuf.duration;
    const duration = 0.1;
    source.start(0, offset, duration);
  };
  slider.disabled = false;
}
input { width: 350px }
<input type="range" id="slider" min="0" max="1" step="0.005" disabled>

И, конечно же, вы также можете повторно использовать этот AudioBuffer для всего вашего проигрывателя.

person Kaiido    schedule 07.02.2020
comment
Ничего себе... Я бы никогда не понял этого. Огромное спасибо! Мне пришлось немного настроить его для моего сценария, но он полностью работает и выполняет то, что мне нужно. ✋ - person onekidney; 07.02.2020

Я думаю, ты на правильном пути. Ваш пользовательский интерфейс ползунка может взаимодействовать с элементом Audio, который не привязан к DOM/странице, и может напрямую устанавливать audio.currentTime, воспроизведите несколько миллисекунд, а затем остановитесь. Вы можете использовать отмену setTimeout() очередей для создания коротких прокруток воспроизведения, а затем воспроизводить их непрерывно, когда прокрутка останавливается. Я предполагаю, что все это зависит от загрузки полного файла, чтобы избежать задержки в сети во время очистки.

person AnthumChris    schedule 07.02.2020