javascript - Web Audio API:如何使用 FFT 从时域转换并使用 iFFT 将数据转换回来
问题描述
我一直在尝试将音频数据转换为频域数据,编辑该数据,并从该数据重建音频。
我按照以下说明进行操作:
OfflineAudioContext
为了获得一个缓冲区来执行分析,AnalyserNode
执行分析,以及PeriodicWave
重建波。
渲染的音频OfflineAudioContext
应该与 的音频匹配PeriodicWave
,但显然不匹配。说明说它应该,所以我显然错过了一些东西。
(另外,我不知道将什么用于PeriodicWave
.的 FFT 分析中没有余弦值AnalyserNode
,而且似乎没有其他方法。)
到目前为止,我得到的最简单和最接近的是以下脚本(https://jsfiddle.net/k81w04qv/1/):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width">
<title>Audio Test</title>
<link rel="stylesheet" href="">
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<h1>Audio Test</h1>
<button id='0'>Play original sound</button>
<button id='1'>Play reconstructed sound</button>
<pre></pre>
</body>
<script id='script'>
var pre = document.querySelector('pre');
var myScript = document.getElementById('script');
pre.innerHTML = myScript.innerHTML;
var buttonOriginal = document.getElementById('0');
var buttonReconstr = document.getElementById('1');
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var channels = 2;
var sampleRate = audioCtx.sampleRate;
var frameCount = sampleRate * 2.0;
var offlineCtx = new OfflineAudioContext(channels, frameCount, sampleRate);
var myArrayBuffer = offlineCtx.createBuffer(channels, frameCount, sampleRate);
var offlineSource = offlineCtx.createBufferSource();
var analyser = offlineCtx.createAnalyser();
var pi = Math.PI;
var songPos = [0, 0];
for (var channel = 0; channel < channels; channel++) {
var nowBuffering = myArrayBuffer.getChannelData(channel);
for (var i = 0; i < frameCount; i++) {
songPos[channel]++;
nowBuffering[i] = synth(channel);
}
}
analyser.connect(offlineCtx.destination);
offlineSource.connect(analyser);
offlineSource.buffer = myArrayBuffer;
offlineSource.start();
offlineCtx.startRendering().then(function (renderedBuffer) {
console.log('Rendering completed successfully');
analyser.fftSize = 2048;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Float32Array(bufferLength);
analyser.getFloatFrequencyData(dataArray);
console.log(dataArray);
// Remove -infinity
for (var i = 0; i < dataArray.length; i++) {
if(dataArray[i]==-Infinity)
dataArray[i] = -255;
}
/// Reconstruct
// Create array of zeros
var imagArray = new Float32Array(bufferLength);
for (var i = 0; i < imagArray.length; i++) imagArray[i] = 0;
var wave = audioCtx.createPeriodicWave(dataArray, imagArray, {disableNormalization: true});
console.log(wave);
buttonReconstr.onclick = function() {
var wave = audioCtx.createPeriodicWave(dataArray, imagArray, {disableNormalization: true});
var osc = audioCtx.createOscillator();
osc.setPeriodicWave(wave);
osc.connect(audioCtx.destination);
osc.start();
osc.stop(2);
osc.onended = () => {
console.log('Reconstructed sound finished');
}
}
buttonOriginal.onclick = function() {
var song = audioCtx.createBufferSource();
song.buffer = renderedBuffer;
song.connect(audioCtx.destination);
song.start();
song.onended = () => {
console.log('Original sound finished');
}
}
})/*.catch(function (err) {
console.log('Rendering failed: ' + err);
// Note: The promise should reject when startRendering is called a second time on an OfflineAudioContext
});*/
function freqSin(freq, time) {
return Math.sin(freq * (2 * pi) * time);
}
function synth(channel) {
var time = songPos[channel] / sampleRate;
switch (channel) {
case 0:
var freq = 200 + 10 * freqSin(9, time);;
var amp = 0.7;
var output = amp * Math.sin(freq * (2 * pi) * time);
break;
case 1:
var freq = 900 + 10 * freqSin(10, time);
var amp = 0.7;
var output = amp * Math.sin(freq * (2 * pi) * time);
break;
}
//console.log(output)
return output;
}
</script>
</html>
这个脚本的一个有趣的附带问题是,在播放完原始声音之后,您无法播放重建的声音(尽管您可以随意播放原始声音)。为了播放重建的声音,你必须先播放它,然后它只会在刷新时播放。(如果您在播放原声时播放,也可以在播放完原声后播放。)
解决方案
为此,您需要时域信号的 FFT 的实部和虚部。唯一给你的AnalyserNode
就是量级;您缺少相位组件。
抱歉,这行不通。
推荐阅读
- javascript - 使用ajax实现时如何在数据表中添加页面长度
- python-3.x - 重新启动时使用 Python 3.9 和 Django 3.1.3 服务器崩溃
- swift - 如何使用 Alamofire 处理两种不同的 JSON 响应
- svg - 如何理解以下 SVG 代码?
- javascript - 如何让我的代码每个问题只显示一张图片
- python - 为 pytorch 安装预训练模型时出错
- .htaccess - 如何设置数据库驱动的永久链接
- c - 使用 fseek 打印任意记录
- firebase - Firebase 事件是如何计算的?
- django - 开发时如何在 Kubernetes 上保持 Django 2.2 迁移?