javascript - Guardar AudioBuffer SIN usar MediaRecorder
Concateno dos AudioBuffers en uno.
Me parece que esto debería ser posible sin usar el MediaRecorder, ya que no necesito grabar en tiempo real.
Esto es lo que hago ahora:
concatAudio(buffers: AudioBuffer[]): void {
const totalLength = buffers.reduce((acc, buffer) => acc + buffer.length, 0);
const audioContext = new AudioContext();
const audioBuffer = audioContext.createBuffer(1, totalLength, 48000);
const src = audioContext.createBufferSource();
const dst = audioContext.createMediaStreamDestination();
let offset = 0;
buffers.map((buffer) => {
audioBuffer.getChannelData(0).set(buffer.getChannelData(0), offset);
offset += buffer.length;
});
src.buffer = audioBuffer;
src.connect(dst);
src.start();
this.recordAudio(dst.stream, audioBuffer.duration);
}
En recordAudio() alimento el flujo en un MediaRecorder.
Grabar el nuevo stream ahora lleva tanto tiempo como la duración de los dos Audiobuffer reproducidos en secuencia...
¿Hay otra manera?
Gracias.
Respuesta #1
Bueno, hay una forma [experimental y rudimentaria] de grabar buffers en paralelo y unir fragmentos. Puedes dividir un buffer de audio en tantos trozos como necesites, limitado por la longitud mínima de trozo. Eso funciona en Chrome, no en FF.
No uses eso bajo ninguna circunstancia. (Del resumen).
// try splitting buffer to N parts, recording in parallel, generating blob
async function recordParallel() {
const audioContext = new AudioContext();
const bufs = [], all = []
for (let i = 0; i < 10; i++ ) {
// 2705 - min chunk length for opus encoder in Chrome, so we increase block size to 4096 plus silent header
let buf = new AudioBuffer({length: 4096, sampleRate: audioContext.sampleRate})
bufs.push(buf)
let data = buf.getChannelData(0)
for (let j = 0; j < 4096; j++) data[j] = Math.sin(j / ((i + 1) * 2))
// create recorders
const source = audioContext.createBufferSource();
source.buffer = buf;
const chunks = []
all.push(new Promise(r => {
const dest = audioContext.createMediaStreamDestination();
const recorder = new MediaRecorder(dest.stream);
source.connect(dest)
recorder.start(10)
// delay is needed to shift encodingblocks
source.start(0)
recorder.ondataavailable = (e) => {
const blob = e.data
if (blob.size) chunks.push(blob)
}
recorder.onstop = e => {
r(chunks)
}
source.onended = e => {
recorder.stop()
}
}))
}
const blobs = await Promise.all(all);
// combine multiple recorders back
console.log(blobs)
var blob = new Blob([...blobs[0], ...blobs.slice(1).map(b => b.slice(1)).flat()], { 'type' : 'audio/ogg; codecs=opus' });
let audio = document.createElement('audio')
audio.src = URL.createObjectURL(blob);
audio.play()
}
