不懂 PCM 的请看上一篇文章 https://www.jianshu.com/p/b6e951510cf0
什么是 WAV ?
想详细了解 WAV http://soundfile.sapp.org/doc/WaveFormat/
WAV为微软公司开发的一种声音文件格式,它符合RIFF(Resource Interchange File Format)文件规范,RIFF文件以文件头开头,后跟一系列数据块(chunk)。WAV文件一般是用于CD存储原声带的,是一种无损的音频文件格式。
这个结构图整体分成了3部分
RIFF部分:
ChunkID 存储了“RIFF”字段,表示这是一个“RIFF”格式的文件。
ChunkSize 记录整个wav文件的字节数
Format 存储了“WAVE”字段,表示这是一个wav文件。
fmt部分:
Subchunk1 ID 存储了“fmt”字段
Subchunk1 Size 存储“fmt”字段的长度
AudioFormat 表示Data区块存储的音频数据的格式,PCM音频数据的值为1
Num Channels 存储声道数,1:单声道,2:双声道
SampleRate 音频采样采样率
ByteRate 存储比特率 SampleRate *NumChannels * BitsPerSample/8
BlockAlign 每个采样所需的字节数 = NumChannels * BitsPerSample / 8
BitsPerSample 每个采样存储的bit数,8:8bit,16:16bit,32:32bit
data:
Subchunk2 ID 存储data字段
Subchunk2 Size 语音数据的长度
转换
WAV = PCM + 文件头
根据上面点结构图,和公式我们就能转换了, 标准模板如下
byte[] header = new byte[44];
header[0] = 'R'; // RIFF
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
header[4] = (byte) (totalWavSize & 0xff);//数据大小
header[5] = (byte) ((totalWavSize >> 8) & 0xff);
header[6] = (byte) ((totalWavSize >> 16) & 0xff);
header[7] = (byte) ((totalWavSize >> 24) & 0xff);
header[8] = 'W';//WAVE
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
//FMT Chunk
header[12] = 'f'; // 'fmt '
header[13] = 'm';
header[14] = 't';
header[15] = ' ';//过渡字节
//数据大小
header[16] = 16; // 4 bytes: size of 'fmt ' chunk
header[17] = 0;
header[18] = 0;
header[19] = 0;
//编码方式 10H为PCM编码格式
header[20] = 1; // format = 1
header[21] = 0;
//通道数
header[22] = (byte) channels;
header[23] = 0;
//采样率,每个通道的播放速度
header[24] = (byte) (sampleRate & 0xff);
header[25] = (byte) ((sampleRate >> 8) & 0xff);
header[26] = (byte) ((sampleRate >> 16) & 0xff);
header[27] = (byte) ((sampleRate >> 24) & 0xff);
//音频数据传送速率,采样率*通道数*采样深度/8
header[28] = (byte) (byteRate & 0xff);
header[29] = (byte) ((byteRate >> 8) & 0xff);
header[30] = (byte) ((byteRate >> 16) & 0xff);
header[31] = (byte) ((byteRate >> 24) & 0xff);
// 确定系统一次要处理多少个这样字节的数据,确定缓冲区,通道数*采样位数
header[32] = (byte) (channels * 16 / 8);
header[33] = 0;
//每个样本的数据位数
header[34] = 16;
header[35] = 0;
//Data chunk
header[36] = 'd';//data
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
header[40] = (byte) (totalPcmSize & 0xff);
header[41] = (byte) ((totalPcmSize >> 8) & 0xff);
header[42] = (byte) ((totalPcmSize >> 16) & 0xff);
header[43] = (byte) ((totalPcmSize >> 24) & 0xff);
os.write(header, 0, 44);
转换文件头的模版,跟上面的结构图一样,是按顺序的,值就是上面概念中提到的,按顺序写就可以了,其中:
totalWavSize: 总wav的文件大小
byteRate: 音频数据传送速率,采样率通道数采样深度/8
上一篇文章的代码有定义
long byteRate = SAMPLE_RATE_INHZ * CHANNEL_CONFIG * ENCODING_FORMAT / 8;
totalPcmSize:PCM的大小
有了文件头和PCM数据之后就可以开始合成了,还是通过流来操作,关键代码:
long byteRate = SAMPLE_RATE_INHZ * CHANNEL_CONFIG * ENCODING_FORMAT / 8;
WavHeader.WavHeader(mOutStream, totalPcmSize, totalWavSize, Config.SAMPLE_RATE_INHZ,
Config.CHANNEL_CONFIG, byteRate);
int length = 0;
while ((length = mInputStream.read(mBuffer)) > 0) {
mOutStream.write(mBuffer, 0, length);
}
至此,PCM就转换成WAV文件了
Demo:https://github.com/wubobo952/LearnAudio