Есть ли способ использовать Web Audio API для сэмплирования звука быстрее, чем в режиме реального времени?

#javascript #html #webkit #web-audio-api

#javascript #HTML #webkit #web-audio-api

Вопрос:

Я играюсь с Web Audio API и пытаюсь найти способ импортировать mp3 (поэтому это доступно только в Chrome) и сгенерировать его форму волны на холсте. Я могу сделать это в режиме реального времени, но моя цель — сделать это быстрее, чем в режиме реального времени.

Все примеры, которые я смог найти, связаны с чтением частотных данных из объекта analyser в функции, прикрепленной к событию onaudioprocess:

 processor = context.createJavascriptNode(2048,1,1);
processor.onaudioprocess = processAudio;
...
function processAudio{
    var freqByteData = new Uint8Array(analyser.frequencyBinCount);
    analyser.getByteFrequencyData(freqByteData);
    //calculate magnitude amp; render to canvas
}
  

Однако, похоже, что это analyser.frequencyBinCount заполняется только при воспроизведении звука (что-то о заполняемом буфере).

Чего я хочу, так это иметь возможность вручную / программно просматривать файл как можно быстрее, чтобы сгенерировать изображение canvas.

Что у меня пока есть, так это:

 $("#files").on('change',function(e){
    var FileList = e.target.files,
        Reader = new FileReader();

    var File = FileList[0];

    Reader.onload = (function(theFile){
        return function(e){
            context.decodeAudioData(e.target.result,function(buffer){
                source.buffer = buffer;
                source.connect(analyser);
                analyser.connect(jsNode);

                var freqData = new Uint8Array(buffer.getChannelData(0));

                console.dir(analyser);
                console.dir(jsNode);

                jsNode.connect(context.destination);
                //source.noteOn(0);
            });
        };
    })(File);

    Reader.readAsArrayBuffer(File);
});
  

Но getChannelData() всегда возвращает пустой типизированный массив.

Приветствуется любая информация, даже если оказывается, что это невозможно сделать. Я думаю, что я единственный, кто в Интернете не хочет делать что-то в режиме реального времени.

Спасибо.

Комментарии:

1. да — быстрее, чем в режиме реального времени. Например, если продолжительность трека составляет 5 минут, я не хочу ждать 5 минут для генерации формы сигнала. Я хочу обработать его как можно быстрее (надеюсь, за несколько секунд)

2. @Pickle, вы Uint8Array неправильно заполняете. Смотрите рабочее решение здесь .

Ответ №1:

Существует действительно удивительный «автономный» режим Web Audio API, который позволяет вам предварительно обработать весь файл через аудио контекст, а затем что-то сделать с результатом:

 var context = new webkitOfflineAudioContext();

var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.noteOn(0);

context.oncomplete = function(e) {
  var audioBuffer = e.renderedBuffer;
};

context.startRendering();
  

Таким образом, настройка выглядит точно так же, как режим обработки в режиме реального времени, за исключением того, что вы настроили oncomplete обратный вызов и вызов startRendering() . В ответ вы получаете e.redneredBuffer это AudioBuffer .

Комментарии:

1. Сейчас действительно поздно, и я работаю над другими материалами, но этот ответ звучит действительно многообещающе (в основном потому, что это единственный, который я получил). Я обязательно дам вам знать, как это происходит.

2. Хорошо, я играл с этим большую часть часа и заблудился. Как бы ваш код сочетался с моим? Когда вы set source.buffer = buffer , откуда buffer берется? Единственное место, где я могу видеть создаваемый аудиобуфер, — это аргумент функции успеха context.decodeAudioData(). Вот jsfiddle того, что у меня есть. Это полный беспорядок, но это то, где я нахожусь — в значительной степени блуждаю в темноте. jsfiddle.net/NW7E3 Убедитесь, что консоль активна

3. Правильно. buffer это AudioBuffer то, что вы получаете от decodeAudioData

4. Добавьте локальный AudioContext в свой oncomplete обратный вызов: var context = new webkitAudioContext(); . По сути, я думаю, вам нужно создать контекст для воспроизведения и контекст для обработки.

5. @Pickle вы когда-нибудь чего-нибудь добились с этим?

Ответ №2:

Я заставил это работать, используя OfflineAudioContext, используя следующий код. Приведенный здесь полный пример показывает, как использовать его для вычисления значений БПФ для линейного чириканья. Как только у вас появится концепция соединения узлов вместе, вы сможете делать с этим практически все, что угодно, в автономном режиме.

 function fsin(freq, phase, t) {
  return Math.sin(2 * Math.PI * freq * t   phase)
}

function linearChirp(startFreq, endFreq, duration, sampleRate) {
  if (duration === undefined) {
    duration = 1; // seconds
  }
  if (sampleRate === undefined) {
    sampleRate = 44100; // per second
  }
  var numSamples = Math.floor(duration * sampleRate);
  var chirp = new Array(numSamples);
  var df = (endFreq - startFreq) / numSamples;
  for (var i = 0; i < numSamples; i  ) {
    chirp[i] = fsin(startFreq   df * i, 0, i / sampleRate);
  }
  return chirp;
}

function AnalyzeWithFFT() {
  var numChannels = 1; // mono
  var duration = 1; // seconds
  var sampleRate = 44100; // Any value in [22050, 96000] is allowed
  var chirp = linearChirp(10000, 20000, duration, sampleRate);
  var numSamples = chirp.length;

  // Now we create the offline context to render this with.
  var ctx = new OfflineAudioContext(numChannels, numSamples, sampleRate);
  
  // Our example wires up an analyzer node in between source and destination.
  // You may or may not want to do that, but if you can follow how things are
  // connected, it will at least give you an idea of what is possible.
  //
  // This is what computes the spectrum (FFT) information for us.
  var analyser = ctx.createAnalyser();

  // There are abundant examples of how to get audio from a URL or the
  // microphone. This one shows you how to create it programmatically (we'll
  // use the chirp array above).
  var source = ctx.createBufferSource();
  var chirpBuffer = ctx.createBuffer(numChannels, numSamples, sampleRate);
  var data = chirpBuffer.getChannelData(0); // first and only channel
  for (var i = 0; i < numSamples; i  ) {
    data[i] = 128   Math.floor(chirp[i] * 127); // quantize to [0,256)
  }
  source.buffer = chirpBuffer;

  // Now we wire things up: source (data) -> analyser -> offline destination.
  source.connect(analyser);
  analyser.connect(ctx.destination);

  // When the audio buffer has been processed, this will be called.
  ctx.oncomplete = function(event) {
    console.log("audio processed");
    // To get the spectrum data (e.g., if you want to plot it), you use this.
    var frequencyBins = new Uint8Array(analyser.frequencyBinCount);
    analyser.getByteFrequencyData(frequencyBins);
    console.log(frequencyBins);
    // You can also get the result of any filtering or any other stage here:
    console.log(event.renderedBuffer);
  };

  // Everything is now wired up - start the source so that it produces a
  // signal, and tell the context to start rendering.
  //
  // oncomplete above will be called when it is done.
  source.start();
  ctx.startRendering();
}