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

将PCM格式存储成WAV格式文件的更多相关文章

  1. c# Use NAudio Library to Convert MP3 audio into WAV audio(将Mp3格式转换成Wav格式)

    Have you been in need of converting mp3 audios to wav audios?  If so, the skill in this article prov ...

  2. iOS: lame框架将PCM录音转成MP3格式

    lame框架将PCM录音转成MP3格式 1.lame下载地址:https://github.com/rbrito/lame,它是一个不可执行的文件,需要借助build-lame.sh脚本将其编译成.a ...

  3. Swift iOS实现把PCM语音转成MP3格式

    最近折腾了swift的语音录制识别和转码,这块还是比较坑的,由于语音识别的准确度实测大概也就80%左右,所以还是需要上传录音文件啊.首先是用讯飞语音SDK实现语音录制和识别(语音听写),第一个坑是讯飞 ...

  4. 分别用Excel和python进行日期格式转换成时间戳格式

    最近在处理一份驾驶行为方面的数据,其中要用到时间戳,因此就在此与大家一同分享学习一下. 1.什么是时间戳? 时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01 ...

  5. 怎样将M4A音频格式转换成MP3格式

    因为MP3音频格式应用的广泛性,所以很多时候我们都需要将不同的音频格式转换成MP3格式的,那么如果我们需要将M4A音频格式转换成MP3格式,我们应该怎样进行实现呢?下面我们就一起来看一下吧. 操作步骤 ...

  6. 09: xmltodict 模块将xml格式转成json格式

    1.1 : xmltodict 模块将xml格式转成json格式 <?xml version="1.0"?> <!--#版本号--> <data> ...

  7. python脚本实现音频m4a格式转成MP3格式

    群里看到有人询问:谁会用python将微信音频文件后缀m4a格式转成mp3格式,毫不犹豫回了句:我会.然后就私下聊起来了 解决方法介绍如下: 工具:windows系统,python2.7,转换库ffm ...

  8. linux环境下deb格式 转换成rpm格式

    linux环境下deb格式 转换成rpm格式 使用alien工具转换deb格式到rpm格式 alien_8.87.tar.gz 下载alien_8.87.tar.gz [root@mysqlnode2 ...

  9. Excel中将时间格式转化成时间戳格式

    时间戳转成正常日期的公式:C1=(A1+8*3600)/86400+70*365+19其中A1表示当时的1249488000时间戳数值其中C1就是所需的日期格式,C1单元格属性改成日期格式就可以了.正 ...

随机推荐

  1. vue 中数据共享的方式

    1.父子组件的数据传递2.store模式 - 局部的数据共享3.vuex 中共享 state - 全局的数据共享

  2. 2019-2020-1 20199312《Linux内核原理与分析》第七周作业

    进程的描述 操作系统内核实现操作系统的三大管理功能 进程管理(内核中最核心的功能) 内存管理 文件系统 在操作系统中,我们通过进程控制块PCB描述进程. 为了管理进程,内核必须对每个进程进行清晰的描述 ...

  3. 01_搭建新浪云SAE

    Step1:注册新浪云计算平台用新浪微博登陆新浪云计算平台,网址:http://sae.sina.com.cn/ 登陆成功之后会跳转到安全设置页面,安全设置页面要填写的东西比较多,需要注意:安全设置里 ...

  4. PHP命令行常用参数说明和使用

    -i 打印phpinfo命令 root@DK:/mnt/hgfs/cpp/php# php -i | grep session -v 输出php版本信息 root@DK:/mnt/hgfs/cpp/p ...

  5. 2017.10.3 国庆清北 D3T2 公交车

    题目描述 LYK在玩一个游戏. 有k群小怪兽想乘坐公交车.第i群小怪兽想从xi出发乘坐公交车到yi.但公交车的容量只有M,而且这辆公交车只会从1号点行驶到n号点. LYK想让小怪兽们尽可能的到达自己想 ...

  6. [php]Windows环境下Composer的安装教程

    方法一: 下载Composer-Setup.exe后安装,它会自动搜索 php.exe 路径, 如果找不到,则手动添加路径.   Windows安装Composer 方法二: 如果出现如下错误,说明伟 ...

  7. 关于Ubuntu中snap安装软件太慢解决办法

    两种方法,一是下载好包手动安装,二设置snap的代理. 下载安装包方式 到 https://uappexplorer.com/snaps 搜索需要的 snap 包,然后下载 下载的时候选择对应的平台. ...

  8. 常用命令备忘 lsof

    lsof命令 可以列出被进程所打开的文件的信息.被打开的文件可以是 1.普通的文件, 2.目录 3.网络文件系统的文件, 4.字符设备文件 5.(函数)共享库 6.管道,命名管道 7.符号链接 8.底 ...

  9. sql语句之where与having的区别

    where条件查询: 使用 where 可以将表中符合条件的数据筛选出来后,得到查询结果. 语法: select 字段名,……,字段名 from 表名 where 条件表达式; 例: 注意:在条件表达 ...

  10. ElasticSearch : 基础简介

    1.安装 我用的docker安装,这个用起来比较方便,我是在腾讯云部署的docker,具体的过两天总结一下 安装: docker pull elasticsearch 运行: docker run - ...