zoukankan      html  css  js  c++  java
  • 将PCM格式存储成WAV格式文件

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

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


    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格式文件




  • 相关阅读:
    用AutoHotkey实现 iThoughts 思维导图节点文本一键转到Excel
    AutoHotkey+BUG小狼毫+BUG win10输入法设置的艰难摸索(未完待续)
    什么时候用接口,什么时候用抽象类?
    Java接口
    单例设计模式和多例
    MySQL大小写
    SQL LIKE 操作符
    SQL DELETE 语句
    IDEA自动补全返回值类型的快捷键
    IDEA导入jar包
  • 原文地址:https://www.cnblogs.com/LiuYanYGZ/p/11240355.html
Copyright © 2011-2022 走看看