将PCM格式存储成WAV格式文件

2023-02-18,,,

PCM格式存储成WAV格式文件

WAV比PCM多44个字节(在文件头位置多)

摘自:https://blog.csdn.net/u012173922/article/details/78849076

2017年12月20日 08:11:00 酒后午夜行为艺术家 阅读数 2815

 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012173922/article/details/78849076

前言:无论是文字,图像还是声音,都必须以一种特定的格式组织和存储起来,这样才能让显示器或播放器知道以怎样的一种方式去解析这些数据。

把PCM格式的数据存储成WAV格式数据的思路:先写头部,再写数据块。

WAV格式可以分成两个部分:

1.文件头,存储一些重要的参数信息,比如采样率,声道数,量化精度等等。

2.数据块,原始的PCM数据。

想要了解WAV格式的可以点击这里  点击打开链接

下面是WAV文件结构图

我们需要简单来说明一下这张图的结构:

可以分成三个部分:

第一部分RIFF :   ChunkID 存储了“RIFF”字段,表示这是一个“RIFF”格式的文件。

ChunkSize 记录整个wav文件的字节数。

Format  存储了“WAVE”字段,表示这是一个wav文件。

第二部分fmt: 这部分的内容主要是记录一些关键参数,比如采样率,声道数,量化精度等等。

Subchunk1 ID      存储了“fmt”字段

Subchunk1 Size  存储“fmt”字段的长度

AudioFormat        存储 量化精度

Num Channels    存储声道数

SampleRate        存储采样率

ByteRate             存储比特率      SampleRate * NumChannels * BitsPerSample/8

BlockAlign           == NumChannels * BitsPerSample/8

BitsPerSample    8 bits = 8, 16 bits = 16, etc.

第三部分data : 主要描述数据块

Subchunk2 ID     存储“data”字段

Subchunk2Size   记录存储的二进制原始音频数据的长度

data   存储二进制原始音频数据

我在网上找了一段wav写入头部的代码,亲测成功


    byte[] header = new byte[44]; //RIFF WAVE Chunk // RIFF标记占据四个字节 header[0] = 'R'; header[1] = 'I'; header[2] = 'F'; header[3] = 'F'; //数据大小表示,由于原始数据为long型,通过四次计算得到长度 header[4] = (byte) (totalDataLen & 0xff); header[5] = (byte) ((totalDataLen >> 8) & 0xff); header[6] = (byte) ((totalDataLen >> 16) & 0xff); header[7] = (byte) ((totalDataLen >> 24) & 0xff); //WAVE标记占据四个字节 header[8] = 'W'; 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) (longSampleRate & 0xff); header[25] = (byte) ((longSampleRate >> 8) & 0xff); header[26] = (byte) ((longSampleRate >> 16) & 0xff); header[27] = (byte) ((longSampleRate >> 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) (1 * 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) (totalAudioLen & 0xff); header[41] = (byte) ((totalAudioLen >> 8) & 0xff); header[42] = (byte) ((totalAudioLen >> 16) & 0xff); header[43] = (byte) ((totalAudioLen >> 24) & 0xff); out.write(header, 0, 44);

解决了最困难的点,下面的工作就好实现了。

下面是完成的代码:


    private void writeWav() { fileTarget = new File(file, "audiotest.pcm"); fileWav = new File(file, "audiotest.wav"); if (!fileTarget.exists()) { Log.e("tag", "目标文件不存在"); return; } DataInputStream dataInputStream = null; DataOutputStream dataOutputStream = null; try { dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(fileTarget))); dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fileWav))); int len = dataInputStream.available(); long totalAudioLen = 0; long totalDataLen = totalAudioLen + 36; long longSampleRate = 44100; int channels = 1; long byteRate = 16 * longSampleRate * channels / 8; //写wav头部 writeWavHeader(dataOutputStream, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate); byte[] bytes = new byte[bufferSize]; int lenthg = -1; while ((lenthg = dataInputStream.read(bytes)) != -1) { dataOutputStream.write(bytes, 0, lenthg); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { dataInputStream.close(); dataOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }

    private byte[] writeWavHeader(DataOutputStream dataOutputStream, long totalAudioLen, long totalDataLen, long longSampleRate, int channels, long byteRate) throws IOException { byte[] header = new byte[44]; //RIFF WAVE Chunk // RIFF标记占据四个字节 header[0] = 'R'; header[1] = 'I'; header[2] = 'F'; header[3] = 'F'; //数据大小表示,由于原始数据为long型,通过四次计算得到长度 header[4] = (byte) (totalDataLen & 0xff); header[5] = (byte) ((totalDataLen >> 8) & 0xff); header[6] = (byte) ((totalDataLen >> 16) & 0xff); header[7] = (byte) ((totalDataLen >> 24) & 0xff); //WAVE标记占据四个字节 header[8] = 'W'; 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) (longSampleRate & 0xff); header[25] = (byte) ((longSampleRate >> 8) & 0xff); header[26] = (byte) ((longSampleRate >> 16) & 0xff); header[27] = (byte) ((longSampleRate >> 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) (1 * 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) (totalAudioLen & 0xff); header[41] = (byte) ((totalAudioLen >> 8) & 0xff); header[42] = (byte) ((totalAudioLen >> 16) & 0xff); header[43] = (byte) ((totalAudioLen >> 24) & 0xff); dataOutputStream.write(header, 0, 44); return header; }

站在巨人的肩膀上

如何存储和解析wav文件

请多多指点

接下来一篇文章我会将带来  如何解析WAV格式文件

将PCM格式存储成WAV格式文件的相关教程结束。

《将PCM格式存储成WAV格式文件.doc》

下载本文的Word格式文档,以方便收藏与打印。