javascript - Web Audio API - Javascript 创建的 WAV 文件长度不正确并且无声

标签 javascript audio wav web-audio-api

问题
Javascript 创建的 WAV 文件长度不正确并且无声。

详情
我一直在使用 JavaScript Web Audio API 创建一个 Web 应用程序,该应用程序可以获取多个声音文件,抓取每个声音文件的随机 block ,然后将它们“混合”到一个采样器中(串行,即 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);
}

通过此函数获取 channel 数(单声道/立体声/等):

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;
}

这是我得到的十六进制转储的片段:

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  ................

如果有人能帮助我发现我的方法的错误,我将不胜感激。很抱歉这篇文章很长,但我想预先发布所有相关的详细信息和代码。

谢谢!

最佳答案

在写出波形数据时,您忽略了增加偏移量。

试试这个:

output.setInt16(offset + i, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                      ^^^^

如果不移动写入位置,您将继续覆盖文件中的相同偏移量,直到最后它被最后一个样本的值覆盖。

另外,我看到您已经发布了 _writePCMSamples 代码,但调用者被注释掉了,您显然只是试图内联做同样的事情。内联代码也有同样的错误。

关于javascript - Web Audio API - Javascript 创建的 WAV 文件长度不正确并且无声,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27611607/

相关文章:

java - 无法在我的网站中播放 .m4a 或 .3gpp 音频文件

音频合成最佳实践

c - 声音字符串声明挂起 Arduino 编译器

c++ - 以编程方式转换 WAV

javascript - 使用localstorage保存表单数据并动态读取数据到表中

javascript - 添加工具提示 - 没有 jquery 的 Javascript

c# - 显示默认设备输出的音量峰值

c# - 在 C# 中创建 .wav 文件

javascript - AngularJs RouteProvider http 状态 403

javascript - Ng-repeat localStorage 数据