#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();
}