Как зациклить WAV в веб-аудио определенное количество раз? - PullRequest
1 голос
/ 07 октября 2019

Я знаю, что есть опция loop=true на AudioBufferSourceNode. Но я просто хочу повторить wav определенное количество раз. Например, как я могу повторить это дважды?

var url  = 'main.wav';

var context = new AudioContext();
var source = context.createBufferSource();

source.connect(context.destination);

var request = new XMLHttpRequest();
request.open('GET', url, true); 
request.responseType = 'arraybuffer';
request.onload = function() {
    context.decodeAudioData(request.response, function(response) {

        source.buffer = response;

        source.start(0);
        source.start(source.buffer.duration); // doesn't work "cannot call start more than once."
        //source.loop = true; // this is infinite amount of times, not good

    }, function () { console.error('The request failed.'); } );
}
request.send();

Я также попытался создать второй буфер:

var source2 = context.createBufferSource();

// inside callback
source2.buffer = response; // same response so I don't need to load this separately
source2.start(source.buffer.duration); // doesn't work

Но это тоже не сработало. Есть идеи?

Ответы [ 4 ]

1 голос
/ 08 октября 2019

Другой альтернативой является использование 3-аргументной версии start () . Что-то вроде:

let nReps = 2; /* Number of times to repeat the source */
let s = new AudioBufferSourceNode(context, 
             {buffer: buffer, loop: true});
s.start(startTime, 0, buffer.duration * nReps);
1 голос
/ 07 октября 2019

Вы можете установить loop AudioBufferSource в true и вызвать

source.stop( ctx.currentTime + audioBuffer.duration * repeatTime )

(async () => {  
  const url = 'https://dl.dropboxusercontent.com/s/1cdwpm3gca9mlo0/kick.mp3';
  const buf = await fetch( url ).then( (r) => r.arrayBuffer() );
  const ctx = new AudioContext();
  const audioBuffer = await ctx.decodeAudioData( buf );
  
  btn.onclick = (e) => {
    const source = ctx.createBufferSource();
    source.buffer = audioBuffer;
    source.loop = true;
    source.connect( ctx.destination );
    source.start( 0 );
    source.stop( ctx.currentTime + audioBuffer.duration * inp.valueAsNumber );
  };
  btn.disabled = false;
})().catch( console.error );
<label> repeat<input id="inp" type="number" min="1" max="20" value="5"></label>
<button id="btn" disabled>start</button>
1 голос
/ 08 октября 2019

Ваш подход с использованием второго буфера почти правильный. Вам просто нужно использовать абсолютное время начала, а не относительное. Чтобы получить абсолютное время и текущее время звукового контекста и продолжительность звука:

// Play first sound now
var source1 = context.createBufferSource();
source1.buffer = response;
source1.start(context.currentTime);  // This is the same as source1.start();

// Play second sound with a delay
var source2 = context.createBufferSource();
source2.buffer = response;
source2.start(context.currentTime + response.duration);

Этот подход обеспечивает воспроизведение без пауз (если ваши файлы без пауз).

Вы можете обернуть это в простойфункция:

function play(context, buffer, delay=0) {
    var source = context.createBufferSource();
    source.buffer = buffer;
    source.start(context.currentTime + delay);
    return source;
}

...

// Play the sound twice, one after another
play(context, response, 0);
play(context, response, response.duration);
1 голос
/ 07 октября 2019

Одним из решений было бы разрешить поведение loop вашего source и затем запланировать обратный вызов для stop() воспроизведения источников по истечении периода времени, который позволил бы воспроизвести звуковой файл несколько раз:

const url = 'main.wav';
const context = new AudioContext();
const source = context.createBufferSource();

// Tracks the number to playback cycles remaining
const playbackCount = 5;

source.connect(context.destination);

const request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = () => {

  context.decodeAudioData(request.response, (response) => {

    source.buffer = response;

    // Schedule sound to stop at future time, after which sound
    // should have played "playbackCount" number of times
    setTimeout(() => {
        source.stop();
    }, response.duration * playbackCount * 1000);

    // Enable loop behavior
    source.loop = true;

    source.start(0);
  }, () => {
    console.error('The request failed.');
  });
}
request.send();
...