我如何使用Web Audio API解码RAW音频文件的数组并将其播放到客户端?

问题描述 投票:1回答:1

我目前正在一个项目中,该项目由一个图表组成,该图表显示了另一台设备拾取的音频电平。图表是通过flot API制作的,我具有缩放和选择功能,以便在图表上选择时间范围并放大到所选区域。我的下一步是允许用户收听与图表的该区域相对应的音频。我将音频文件存储在共享服务器上,并且所有文件都单独,逐分钟地存储在RAW数据文件中。我没有在网页中使用音频的经验,目前正在努力完成此任务。据我所知,<audio> HTML标签无法处理RAW数据文件进行播放。我一直在研究Web Audio API,但对它的工作方式和实现方式感到困惑。

我的第一个问题是如何从服务器解码RAW音频文件并将其显示在HTML页面上以供客户端收听?

我的第二项任务是获取与所选范围相对应的所有音频文件,并将它们组合为一个音频输出。例如,如果客户选择的时间范围是1:00 pm-1:50 pm,则我需要每分钟长度访问50个RAW数据音频文件。然后,我想将它们组合在一起以产生一个单一的播放声音。因此,我的第二个问题是,是否有人知道一种顺利完成此操作的方法。

感谢您提供任何帮助!

javascript audio web-audio-api raw-data
1个回答
0
投票

RAW文件是解码的PCM数据,但是Audio元素不能直接播放它们。您需要先在文件字节后附加一个RIFF / WAV标头]

async function play() {
  // specify your file and whether it's integer or floating point PCM
  const url = 'https://dev.anthum.com/audio-worklet/audio/decoded-left.raw'
  const isFloat = true

  const buffer = await (await fetch(url)).arrayBuffer()
  const [type, format] = isFloat ? [Float32Array, 3] : [Uint8Array, 1] 
  const wavHeader = new Uint8Array(buildWaveHeader({
    numFrames: buffer.byteLength / type.BYTES_PER_ELEMENT,
    sampleRate: 48000,
    numChannels: 1,
    bytesPerSample: type.BYTES_PER_ELEMENT,
    format
  }))

  // build WAV file using header and raw bytes
  const wavBytes = new Uint8Array(wavHeader.length + buffer.byteLength)
  wavBytes.set(wavHeader, 0)
  wavBytes.set(new Uint8Array(buffer), wavHeader.length)

  const audio = document.querySelector('audio')
  const blob = new Blob([wavBytes], { type: 'audio/wav' })
  audio.src = URL.createObjectURL(blob)
  audio.hidden = false
  audio.play()
}


// adapted from https://gist.github.com/also/900023
function buildWaveHeader(opts) {
  const numFrames =      opts.numFrames;
  const numChannels =    opts.numChannels || 2;
  const sampleRate =     opts.sampleRate || 44100;
  const bytesPerSample = opts.bytesPerSample || 2;
  const format =         opts.format

  const blockAlign = numChannels * bytesPerSample;
  const byteRate = sampleRate * blockAlign;
  const dataSize = numFrames * blockAlign;

  const buffer = new ArrayBuffer(44);
  const dv = new DataView(buffer);

  let p = 0;

  function writeString(s) {
    for (let i = 0; i < s.length; i++) {
      dv.setUint8(p + i, s.charCodeAt(i));
    }
    p += s.length;
}

  function writeUint32(d) {
    dv.setUint32(p, d, true);
    p += 4;
  }

  function writeUint16(d) {
    dv.setUint16(p, d, true);
    p += 2;
  }

  writeString('RIFF');              // ChunkID
  writeUint32(dataSize + 36);       // ChunkSize
  writeString('WAVE');              // Format
  writeString('fmt ');              // Subchunk1ID
  writeUint32(16);                  // Subchunk1Size
  writeUint16(format);              // AudioFormat
  writeUint16(numChannels);         // NumChannels
  writeUint32(sampleRate);          // SampleRate
  writeUint32(byteRate);            // ByteRate
  writeUint16(blockAlign);          // BlockAlign
  writeUint16(bytesPerSample * 8);  // BitsPerSample
  writeString('data');              // Subchunk2ID
  writeUint32(dataSize);            // Subchunk2Size

  return buffer;
}
body {
  text-align: center;
}
[hidden] {
  display: none;
}
audio {
  display: inline-block;
  margin-top: 1rem;
}
<div>
  <button onclick="play()" style="padding: 1em 2em">Play</button>
</div>
<audio hidden controls></audio>
© www.soinside.com 2019 - 2024. All rights reserved.