js实现pcm音频转wav与播放
前面我们已经实现了js录制pcm编码的功能了,但是很遗憾,浏览器并不能播放pcm音频,但可以支持与他比较相近的wav格式的文件,来琢磨下这两者的转化吧。
wav
Waveform Audio File Format,是微软与IBM公司所开发在个人计算机存储音频流的编码格式。
wav可以使用多种音频编码来压缩其音频流,不过我们常见的都是音频流被pcm编码处理的wav。但这不表示wav只能使用pcm编码,mp3编码同样也可以运用在wav中。简单来说,pcm是无损wav文件中音频数据的一种编码方式,但wav还可以用其它方式编码。
wav是一种无损的音频文件格式,由于此音频格式未经过压缩,所以在音质方面不会出现失真的情况,但文件的体积因而在众多音频格式中较为大。
一般情况下,wav数据实际上就是裸数据pcm外面包了一层文件头。在前面的文章中,我们已经拿到了pcm数据了,只要在其前部增加44个字节的wav头就行了。
wav头
先上这张图吧,
ChunkID
偏移量0,占用了4字节,大端字节序,表示资源交换文件标识符,一般固定是"RIFF"。
ChunkSize
偏移量4,占用4字节,小端字节序,下个地址开始到文件尾总字节数,即文件大小-8。
Format
偏移量8,占用4字节,大端字节序,表示wav文件标志,一般固定为"WAVE"。
Subchunk1 ID
偏移量12,占用4字节,大端字节序,表示波形格式标志,一般固定为"fmt ",注意最后有空格。
Subchunk1 Size
偏移量16,占用4字节,小端字节序,表示过滤字节,一般为 0x10 = 16。
AudioFormat
偏移量20,占用2字节,小端字节序,表示格式类别,1是PCM形式采样数据,故此处填1。
Num Channels
偏移量22,占用2字节,小端字节序,表示声道数。
SampleRate
偏移量24,占用4字节,小端字节序,表示采样率。
ByteRate
偏移量28,占用4字节,小端字节序,表示波特率,即声道数 × 采样频率 × 采样位数 / 8。
BlockAlign
偏移量32,占用2字节,小端字节序,声道数 × 采样位数 / 8。
Bits Per Sample
偏移量34,占用2字节,小端字节序,采样位数.
Subchunk2 Id
偏移量36,占用4字节,大端字节序,数据标识符,一般固定为"data"。
Subchunk2 Size
偏移量40,占用4字节,小端字节序,表示采样数据总数,即数据总大小-44。
data
偏移量44,小端字节序,pcm数据。
js拼装
给pcm的数据加上wav头就可以播放了,简单的处理如下:
// 资源交换文件标识符
writeString(data, offset, 'RIFF'); offset += 4;
// 下个地址开始到文件尾总字节数,即文件大小-8
data.setUint32(offset, 36 + bytes.byteLength, true); offset += 4;
// WAV文件标志
writeString(data, offset, 'WAVE'); offset += 4;
// 波形格式标志
writeString(data, offset, 'fmt '); offset += 4;
// 过滤字节,一般为 0x10 = 16
data.setUint32(offset, 16, true); offset += 4;
// 格式类别 (PCM形式采样数据)
data.setUint16(offset, 1, true); offset += 2;
// 声道数
data.setUint16(offset, channelCount, true); offset += 2;
// 采样率,每秒样本数,表示每个通道的播放速度
data.setUint32(offset, sampleRate, true); offset += 4;
// 波形数据传输率 (每秒平均字节数) 声道数 × 采样频率 × 采样位数 / 8
data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;
// 快数据调整数 采样一次占用字节数 声道数 × 采样位数 / 8
data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
// 采样位数
data.setUint16(offset, sampleBits, true); offset += 2;
// 数据标识符
writeString(data, offset, 'data'); offset += 4;
// 采样数据总数,即数据总大小-44
data.setUint32(offset, bytes.byteLength, true); offset += 4;
// 给wav头增加pcm体
for (let i = 0; i < bytes.byteLength; ++i) {
data.setUint8(offset, bytes.getUint8(i, true), true);
offset++;
}
给pcm嵌入上wav头,那么浏览器就可以播放了。
播放
createBufferSource方式
该方法在web Audio学习与音频播放中就有了,将二进制传入就会被转成audioBuffer,这儿不多说了。
audio方式
可以创建audio标签,外加window.createObjectURL方法返回资源路径给audio标签,再通过audio的播放就ok了。
总结
到这,录音功能也算差不多完成了,当然还有很多细节待完善的,以及一些小功能可以开发。
本篇对应的demo请查看:js转化pcm到wav格式与播放。完整的录音插件请查看:recorder。