我目前正在一个项目中,该项目包含一个图表,该图表显示了另一台设备拾取的音频电平。图表是通过flot API制作的,我具有缩放和选择功能,以便在图表上选择时间范围并放大到所选区域。我的下一步是允许用户收听与图表的该区域相对应的音频。我将音频文件存储在共享服务器上,并且所有文件都单独,逐分钟地存储在RAW数据文件中。我没有在网页中使用音频的经验,目前正在努力完成此任务。据我所知,<audio>
HTML标签无法处理RAW数据文件进行播放。我一直在研究Web Audio API,但对它的工作方式和实现方式感到困惑。
我的第一个问题是如何从服务器解码RAW音频文件并将其显示在HTML页面上以供客户端收听?
我的第二个任务是获取与选定范围相对应的所有音频文件,并将它们组合为一个音频输出。例如,如果客户端选择的时间范围为1:00 pm-1:50 pm,则我需要每分钟长度访问50个RAW数据音频文件。然后,我想将它们组合在一起以产生一个单一的播放声音。因此,我的第二个问题是,是否有人知道一种顺利完成此操作的方法。
感谢您提供任何帮助!
答案 0 :(得分:4)
RAW文件已经被解码为PCM音频,但是Audio
元素不能直接播放PCM。您首先需要将RIFF / WAV标头附加到PCM字节。可以合并多个RAW文件,在标题中设置总样本/帧长度。 50分钟的解码音频将在浏览器中占用大量内存,因此请密切注意并相应地进行测量/优化。
initAudio()
async function initAudio() {
// specify your file and its audio properties
const url = 'https://dev.anthum.com/audio-worklet/audio/decoded-left.raw'
const sampleRate = 48000
const numChannels = 1 // mono or stereo
const isFloat = true // integer or floating point
const buffer = await (await fetch(url)).arrayBuffer()
// create WAV header
const [type, format] = isFloat ? [Float32Array, 3] : [Uint8Array, 1]
const wavHeader = new Uint8Array(buildWaveHeader({
numFrames: buffer.byteLength / type.BYTES_PER_ELEMENT,
bytesPerSample: type.BYTES_PER_ELEMENT,
sampleRate,
numChannels,
format
}))
// create WAV file with header and downloaded PCM audio
const wavBytes = new Uint8Array(wavHeader.length + buffer.byteLength)
wavBytes.set(wavHeader, 0)
wavBytes.set(new Uint8Array(buffer), wavHeader.length)
// show audio player
const audio = document.querySelector('audio')
const blob = new Blob([wavBytes], { type: 'audio/wav' })
audio.src = URL.createObjectURL(blob)
document.querySelector('#loading').hidden = true
audio.hidden = false
}
// 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;
padding-top: 1rem;
}
[hidden] {
display: none;
}
audio {
display: inline-block;
}
<div id="loading">Loading...</div>
<audio hidden controls></audio>
答案 1 :(得分:0)
使用Web Audio可能更容易一些,您基本上可以执行与上述相同的操作,但不要使用Audio
元素。如有必要,将原始音频数据转换为浮点数组,例如f
,然后执行以下操作:
// Only need to do this once when setting up the page
let c = new AudioContext();
// Do this for each clip:
let b = new AudioBuffer({length: f.length, sampleRate: c.sampleRate});
b.copyToChannel(f, 0);
let s = new AudioBufferSourceNode(c, {buffer: b});
s.connect(c.destination);
s.start();
这是如何使用Web Audio进行播放的粗略草图。可以对其进行优化以重用AudioBuffers
。而且,您必须注意以正确的时间值调用s.start()
。但我希望这足以使您入门。如果没有,请提出更多问题。