Javascript - поиск аудио в определенном месте, когда в точном месте в звуковой дорожке
Сценарий:
- Звук начинает воспроизводиться с 0:00. Ровно в 0:05 трек переходит вперед к 0:30.
- Дорожка сразу начинает воспроизводиться в 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>