javascript - BiQuadFilter playing filtered audio but MediaRecorder records un-filtered raw audio stream - WebAudio API
问题描述
I am using WebAudio API to record audio from microphone using web browser (javascript). ref: https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/createMediaStreamSource
I am able to record and play the recorded audio using the code given below. I also want to apply some filters (low-pass/high-pass) before recording to get filtered audio recorded. I have tried BiQuadFilters which filters audio taken from source for ex: var source = audioCtx.createMediaStreamSource(stream);
.
I can also play the filtered audio in real time (while recording) by connecting the filter with the destination of the audio context biquadFilter.connect(audioCtx.destination);
. Following is the code I am using -
const record = document.querySelector('.record');
const stop = document.querySelector('.stop');
const soundClips = document.querySelector('.sound-clips');
const canvas = document.querySelector('.visualizer');
const mainSection = document.querySelector('.main-controls');
stop.disabled = true;
let audioCtx;
const canvasCtx = canvas.getContext("2d");
if (navigator.mediaDevices.getUserMedia) {
console.log('getUserMedia supported.');
const constraints = {
audio: true
};
let chunks = [];
let onSuccess = function (stream) {
if (!audioCtx) {
audioCtx = new AudioContext();
}
var source = audioCtx.createMediaStreamSource(stream);
// Create a biquadfilter
var biquadFilter = audioCtx.createBiquadFilter();
biquadFilter.type = "lowpass";
biquadFilter.frequency.value = 600;
biquadFilter.gain.value = 10;
source.connect(biquadFilter);
const mediaRecorder = new MediaRecorder(stream);
record.onclick = function () {
visualize(stream);
biquadFilter.connect(audioCtx.destination);
mediaRecorder.start();
console.log(mediaRecorder.state);
console.log("recorder started");
record.style.background = "red";
stop.disabled = false;
record.disabled = true;
}
stop.onclick = function() {
mediaRecorder.stop();
console.log(mediaRecorder.state);
console.log("recorder stopped");
record.style.background = "";
record.style.color = "";
// mediaRecorder.requestData();
stop.disabled = true;
record.disabled = false;
}
mediaRecorder.onstop = function(e) {
console.log("data available after MediaRecorder.stop() called.");
const clipName = prompt('Enter a name for your sound clip?', 'My unnamed clip');
const clipContainer = document.createElement('article');
const clipLabel = document.createElement('p');
const audio = document.createElement('audio');
const deleteButton = document.createElement('button');
clipContainer.classList.add('clip');
audio.setAttribute('controls', '');
deleteButton.textContent = 'Delete';
deleteButton.className = 'delete';
if (clipName === null) {
clipLabel.textContent = 'My unnamed clip';
} else {
clipLabel.textContent = clipName;
}
clipContainer.appendChild(audio);
clipContainer.appendChild(clipLabel);
clipContainer.appendChild(deleteButton);
soundClips.appendChild(clipContainer);
audio.controls = true;
const blob = new Blob(chunks, {
'type': 'audio/ogg; codecs=opus'
});
chunks = [];
const audioURL = window.URL.createObjectURL(blob);
audio.src = audioURL;
console.log("recorder stopped");
deleteButton.onclick = function(e) {
let evtTgt = e.target;
evtTgt.parentNode.parentNode.removeChild(evtTgt.parentNode);
}
clipLabel.onclick = function() {
const existingName = clipLabel.textContent;
const newClipName = prompt('Enter a new name for your sound clip?');
if (newClipName === null) {
clipLabel.textContent = existingName;
} else {
clipLabel.textContent = newClipName;
}
}
}
mediaRecorder.ondataavailable = function(e) {
chunks.push(e.data);
}
}
let onError = function(err) {
console.log('The following error occured: ' + err);
}
navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
} else {
console.log('getUserMedia not supported on your browser!');
}
function visualize(stream) {
if (!audioCtx) {
audioCtx = new AudioContext();
}
const source = audioCtx.createMediaStreamSource(stream);
const analyser = audioCtx.createAnalyser();
analyser.fftSize = 2048;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
source.connect(analyser);
//analyser.connect(audioCtx.destination);
draw()
function draw() {
const WIDTH = canvas.width
const HEIGHT = canvas.height;
requestAnimationFrame(draw);
analyser.getByteTimeDomainData(dataArray);
canvasCtx.fillStyle = 'rgb(200, 200, 200)';
canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
canvasCtx.lineWidth = 2;
canvasCtx.strokeStyle = 'rgb(0, 0, 0)';
canvasCtx.beginPath();
let sliceWidth = WIDTH * 1.0 / bufferLength;
let x = 0;
for (let i = 0; i < bufferLength; i++) {
let v = dataArray[i] / 128.0;
let y = v * HEIGHT / 2;
if (i === 0) {
canvasCtx.moveTo(x, y);
} else {
canvasCtx.lineTo(x, y);
}
x += sliceWidth;
}
canvasCtx.lineTo(canvas.width, canvas.height / 2);
canvasCtx.stroke();
}
}
My problem is, I am using MediaRecorder const mediaRecorder = new MediaRecorder(stream);
for recording audio which takes a stream in its constructor and I am not able to connect it with the filtered data(not sure about context or source) 1. to get the filtered audio recorded. 2. to get the visualization after filters.
Thanks in advance for any help. Any other reference or suggestions for better implementation is also welcome.
解决方案
推荐阅读
- r - 在现有图上添加线条的框架:ggplot 中的“点”和“图”
- r - 表格中的分组条形图
- javascript - 从只有部分文件名的 url 打开一个 pdf 文档
- c# - 实例化时,游戏对象显示为克隆。有什么方法可以识别每个克隆吗?
- sql-server-2012 - 如何对两列求和并与同一张表的另一列进行比较
- apache-camel - 使用骆驼将xml转换为excel
- c# - XDocument.Save() 东西块文件
- c - C语言中for和while循环的区别?
- c# - Dot Net Web Utility Html 编码和解码等号
- c# - 通过 UDP CSharp 发送字符串