将PCM格式存储成WAV格式文件 WAV比PCM多44个字节(在文件头位置多)

 

 
 

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

 

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

 

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

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

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

 

想要了解WAV格式的可以点击这里 

 

下面是WAV文件结构图

将PCM格式存储成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写入头部的代码,亲测成功

 

  1.  
    byte[] header = new byte[44];
  2.  
    //RIFF WAVE Chunk
  3.  
    // RIFF标记占据四个字节
  4.  
    header[0] = 'R';
  5.  
    header[1] = 'I';
  6.  
    header[2] = 'F';
  7.  
    header[3] = 'F';
  8.  
    //数据大小表示,由于原始数据为long型,通过四次计算得到长度
  9.  
    header[4] = (byte) (totalDataLen & 0xff);
  10.  
    header[5] = (byte) ((totalDataLen >> 8) & 0xff);
  11.  
    header[6] = (byte) ((totalDataLen >> 16) & 0xff);
  12.  
    header[7] = (byte) ((totalDataLen >> 24) & 0xff);
  13.  
    //WAVE标记占据四个字节
  14.  
    header[8] = 'W';
  15.  
    header[9] = 'A';
  16.  
    header[10] = 'V';
  17.  
    header[11] = 'E';
  18.  
    //FMT Chunk
  19.  
    header[12] = 'f';
  20.  
    // 'fmt '标记符占据四个字节
  21.  
    header[13] = 'm';
  22.  
    header[14] = 't';
  23.  
    header[15] = ' ';//过渡字节
  24.  
    //数据大小
  25.  
    header[16] = 16; // 4 bytes: size of 'fmt ' chunk
  26.  
    header[17] = 0;
  27.  
    header[18] = 0;
  28.  
    header[19] = 0;
  29.  
    //编码方式 10H为PCM编码格式
  30.  
    header[20] = 1; // format = 1
  31.  
    header[21] = 0;
  32.  
    //通道数
  33.  
    header[22] = (byte) channels;
  34.  
    header[23] = 0;
  35.  
    //采样率,每个通道的播放速度
  36.  
    header[24] = (byte) (longSampleRate & 0xff);
  37.  
    header[25] = (byte) ((longSampleRate >> 8) & 0xff);
  38.  
    header[26] = (byte) ((longSampleRate >> 16) & 0xff);
  39.  
    header[27] = (byte) ((longSampleRate >> 24) & 0xff);
  40.  
    //音频数据传送速率,采样率*通道数*采样深度/8
  41.  
    header[28] = (byte) (byteRate & 0xff);
  42.  
    header[29] = (byte) ((byteRate >> 8) & 0xff);
  43.  
    header[30] = (byte) ((byteRate >> 16) & 0xff);
  44.  
    header[31] = (byte) ((byteRate >> 24) & 0xff);
  45.  
    // 确定系统一次要处理多少个这样字节的数据,确定缓冲区,通道数*采样位数
  46.  
    header[32] = (byte) (1 * 16 / 8);
  47.  
    header[33] = 0;
  48.  
    //每个样本的数据位数
  49.  
    header[34] = 16;
  50.  
    header[35] = 0;
  51.  
    //Data chunk
  52.  
    header[36] = 'd';//data标记符
  53.  
    header[37] = 'a';
  54.  
    header[38] = 't';
  55.  
    header[39] = 'a';
  56.  
    //数据长度
  57.  
    header[40] = (byte) (totalAudioLen & 0xff);
  58.  
    header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
  59.  
    header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
  60.  
    header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
  61.  
    out.write(header, 0, 44);

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

下面是完成的代码:

 

  1.  
    private void writeWav() {
  2.  
    fileTarget = new File(file, "audiotest.pcm");
  3.  
    fileWav = new File(file, "audiotest.wav");
  4.  
     
  5.  
    if (!fileTarget.exists()) {
  6.  
    Log.e("tag", "目标文件不存在");
  7.  
    return;
  8.  
    }
  9.  
    DataInputStream dataInputStream = null;
  10.  
    DataOutputStream dataOutputStream = null;
  11.  
    try {
  12.  
    dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(fileTarget)));
  13.  
    dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fileWav)));
  14.  
    int len = dataInputStream.available();
  15.  
    long totalAudioLen = 0;
  16.  
    long totalDataLen = totalAudioLen + 36;
  17.  
    long longSampleRate = 44100;
  18.  
    int channels = 1;
  19.  
    long byteRate = 16 * longSampleRate * channels / 8;
  20.  
    //写wav头部
  21.  
    writeWavHeader(dataOutputStream, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate);
  22.  
    byte[] bytes = new byte[bufferSize];
  23.  
    int lenthg = -1;
  24.  
    while ((lenthg = dataInputStream.read(bytes)) != -1) {
  25.  
    dataOutputStream.write(bytes, 0, lenthg);
  26.  
    }
  27.  
    } catch (FileNotFoundException e) {
  28.  
    e.printStackTrace();
  29.  
    } catch (IOException e) {
  30.  
    e.printStackTrace();
  31.  
    } finally {
  32.  
    try {
  33.  
    dataInputStream.close();
  34.  
    dataOutputStream.close();
  35.  
    } catch (IOException e) {
  36.  
    e.printStackTrace();
  37.  
    }
  38.  
    }
  39.  
    }


 

  1.  
    private byte[] writeWavHeader(DataOutputStream dataOutputStream, long totalAudioLen, long totalDataLen, long longSampleRate,
  2.  
    int channels, long byteRate) throws IOException {
  3.  
    byte[] header = new byte[44];
  4.  
    //RIFF WAVE Chunk
  5.  
    // RIFF标记占据四个字节
  6.  
    header[0] = 'R';
  7.  
    header[1] = 'I';
  8.  
    header[2] = 'F';
  9.  
    header[3] = 'F';
  10.  
    //数据大小表示,由于原始数据为long型,通过四次计算得到长度
  11.  
    header[4] = (byte) (totalDataLen & 0xff);
  12.  
    header[5] = (byte) ((totalDataLen >> 8) & 0xff);
  13.  
    header[6] = (byte) ((totalDataLen >> 16) & 0xff);
  14.  
    header[7] = (byte) ((totalDataLen >> 24) & 0xff);
  15.  
    //WAVE标记占据四个字节
  16.  
    header[8] = 'W';
  17.  
    header[9] = 'A';
  18.  
    header[10] = 'V';
  19.  
    header[11] = 'E';
  20.  
    //FMT Chunk
  21.  
    header[12] = 'f';
  22.  
    // 'fmt '标记符占据四个字节
  23.  
    header[13] = 'm';
  24.  
    header[14] = 't';
  25.  
    header[15] = ' ';//过渡字节
  26.  
    //数据大小
  27.  
    header[16] = 16; // 4 bytes: size of 'fmt ' chunk
  28.  
    header[17] = 0;
  29.  
    header[18] = 0;
  30.  
    header[19] = 0;
  31.  
    //编码方式 10H为PCM编码格式
  32.  
    header[20] = 1; // format = 1
  33.  
    header[21] = 0;
  34.  
    //通道数
  35.  
    header[22] = (byte) channels;
  36.  
    header[23] = 0;
  37.  
    //采样率,每个通道的播放速度
  38.  
    header[24] = (byte) (longSampleRate & 0xff);
  39.  
    header[25] = (byte) ((longSampleRate >> 8) & 0xff);
  40.  
    header[26] = (byte) ((longSampleRate >> 16) & 0xff);
  41.  
    header[27] = (byte) ((longSampleRate >> 24) & 0xff);
  42.  
    //音频数据传送速率,采样率*通道数*采样深度/8
  43.  
    header[28] = (byte) (byteRate & 0xff);
  44.  
    header[29] = (byte) ((byteRate >> 8) & 0xff);
  45.  
    header[30] = (byte) ((byteRate >> 16) & 0xff);
  46.  
    header[31] = (byte) ((byteRate >> 24) & 0xff);
  47.  
    // 确定系统一次要处理多少个这样字节的数据,确定缓冲区,通道数*采样位数
  48.  
    header[32] = (byte) (1 * 16 / 8);
  49.  
    header[33] = 0;
  50.  
    //每个样本的数据位数
  51.  
    header[34] = 16;
  52.  
    header[35] = 0;
  53.  
    //Data chunk
  54.  
    header[36] = 'd';//data标记符
  55.  
    header[37] = 'a';
  56.  
    header[38] = 't';
  57.  
    header[39] = 'a';
  58.  
    //数据长度
  59.  
    header[40] = (byte) (totalAudioLen & 0xff);
  60.  
    header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
  61.  
    header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
  62.  
    header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
  63.  
    dataOutputStream.write(header, 0, 44);
  64.  
    return header;
  65.  
    }


站在巨人的肩膀上

如何存储和解析wav文件

 

请多多指点

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