【发布时间】:2023-03-04 21:49:01
【问题描述】:
问题
Javascript 创建的 WAV 文件长度不正确且无声。
详情
我一直在使用 JavaScript Web Audio API 创建一个 Web 应用程序,它可以获取多个声音文件,随机抓取每个文件,然后将它们“混合”到一个采样器中(连续,即 file1 + file2 + ... + fileN),就像某种混搭。声音可以成功加载到我的自定义 SoundPlayer 对象中,并且可以播放。但是,当您实际将它们混合在一起时,生成的 WAV 文件长度错误并且完全无声。
该界面最多允许加载 10 种声音并以各自的音量播放。您单击一个按钮将它们组合在一起制作一个采样器 WAV,它会动态创建一个链接来下载它。我还有一种方法可以查看结果文件的十六进制转储,它在底部显示了一堆 00 甚至一些 NaN,所以很明显我的算法有缺陷,但我就是不知道是怎么回事。
当您单击“制作采样器!”时按钮,它运行以下函数(使用自定义 SoundPlayer 对象数组,其中包含音频缓冲区,作为其参数):
function createSampler(sndArr) {
var numberOfChannels = _getSoundChannelsMin(sndArr);
var sndLengthSum = (function() {
var lng = 0;
for (var i = 0; i < sndArr.length; i++) {
lng += sndArr[i].audioBuffer.length;
}
return lng;
})();
var samplerBuffer = getAudioContext().createBuffer(
numberOfChannels,
sndLengthSum,
sndArr[0].audioBuffer.sampleRate
);
for (var i = 0; i < numberOfChannels; i++) {
var channel = samplerBuffer.getChannelData(i);
channel.set(sndArr[0].audioBuffer.getChannelData(i), 0);
for (var j = 1; j < sndArr.length; j++) {
channel.set(sndArr[j].audioBuffer.getChannelData(i), sndArr[j-1].audioBuffer.length);
}
}
// encode our newly made audio blob into a wav file
var dataView = _encodeWavFile(samplerBuffer, samplerBuffer.sampleRate);
var audioBlob = new Blob([dataView], { type : 'audio/wav' });
// post new wav file to download link
_enableDownload(audioBlob);
}
使用此功能获取通道数(单声道/立体声/等):
function _getSoundChannelsMin(sndArr) {
var sndChannelsArr = [];
sndArr.forEach(function(snd) {
sndChannelsArr.push(snd.audioBuffer.numberOfChannels);
});
return Math.min.apply(Math, sndChannelsArr);
}
WAV 是用这个函数编码的:
function _encodeWavFile(samples, sampleRate) {
var buffer = new ArrayBuffer(44 + samples.length * 2);
var view = new DataView(buffer);
// RIFF identifier
_writeString(view, 0, 'RIFF');
// file length
view.setUint32(4, 36 + samples.length * 2, true);
// RIFF type
_writeString(view, 8, 'WAVE');
// format chunk identifier
_writeString(view, 12, 'fmt ');
// format chunk length
view.setUint32(16, 16, true);
// sample format (raw)
view.setUint16(20, 1, true);
// stereo (2 channels)
view.setUint16(22, 2, true);
// sample rate
view.setUint32(24, sampleRate, true);
// byte rate (sample rate * block align)
view.setUint32(28, sampleRate * 4, true);
// block align (channels * bytes/sample)
view.setUint16(32, 4, true);
// bits/sample
view.setUint16(34, 16, true);
// data chunk identifier
_writeString(view, 36, 'data');
// data chunk length
view.setUint32(40, samples.length * 2, true);
// write the PCM samples
_writePCMSamples(view, 44, samples);
return view;
}
WAV 文件中的字符串是这样处理的:
function _writeString(view, offset, string) {
for (var i = 0; i < string.length; i++){
view.setUint8(offset + i, string.charCodeAt(i));
}
}
PCM 样本是这样处理的:
function _writePCMSamples(output, offset, input) {
for (var i = 0; i < input.length; i++, offset+=2){
var s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
}
最后,WAV文件变成了这样的链接:
function _enableDownload(blob, givenFilename) {
var url = (window.URL || window.webkitURL).createObjectURL(blob);
var link = document.getElementById("linkDownloadSampler");
var d = new Date();
var defaultFilename = "sampler" + d.curDateTime() + ".wav";
link.style.display = "inline";
link.href = url;
link.download = givenFilename || defaultFilename;
}
这是我得到的十六进制转储的 sn-p:
52 49 46 46 FFFD FFFD 02 00 57 41 56 45 66 6D 74 20 RIFF....WAVEfmt
10 00 00 00 01 00 02 00 FFFD FFFD 00 00 00 FFFD 02 00 ................
04 00 10 00 64 61 74 61 FFFD FFFD 02 00 00 00 00 00 ....data........
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
如果有人可以帮助我了解我的方法的错误,我将不胜感激。很抱歉发了这么长的帖子,但我想提前发布所有相关的细节和代码。
谢谢!
【问题讨论】:
标签: javascript audio wav web-audio-api