找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1915|回复: 0
收起左侧

PCM数据以WAVE格式存到SD卡,程序源码

[复制链接]
ID:254724 发表于 2020-5-28 10:34 | 显示全部楼层 |阅读模式
电话录音采用什么文件格式?
小灵呼电话录音系统LV包括呼叫中心系统的录音功能均采用WAV格式作为文件存储,其实WAV格式也分2、4倍压缩率及线性无压缩之分,参见电话录音压缩率说明,我们可以看到a率、u率、adpcm、8位pcm,16位pcm均可采用WAV格式存储,可见采用WAV格式存储并不一定很占空间,它还是可以采用压缩格式的。
电话录音采用WAV有什么好处?
WAV是Windows世界里最通用最流行的语音压缩存储格式,采用此格式存放录音文件,可以被大多数播放软件播放,而不局限于通过小灵呼软件界面播放。
WAV文件格式介绍
文件是Windows标准的文件格式,WAV文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。RIFF是英文Resource Interchange FileFormat的缩写,每个WAV文件的头四个字节便是“RIFF”。WAV文件由文件头和数据体两大部分组成。其中文件头又分为RIFF/WAV文件标识段和声音数据格式说明段两部分。WAV文件各部分内容及格式见附表。常见的声音文件主要有两种,分别对应于单声道(11.025KHz采样率、8Bit的采样值)和双声道(44.1KHz采样率、16Bit的采样值)。采样率是指:声音信号在“模→数”转换过程中单位时间内采样的次数。采样值是指每一次采样周期内声音模拟信号的积分值。对于单声道声音文件,采样数据为八位的短整数(short int 00H-FFH); 而对于双声道立体声声音文件,每次采样数据为一个16位的整数(int),高八位和低八位分别代表左右两个声道。WAV文件数据块包含以脉冲编码调制(PCM)格式表示的样本。WAV文件是由样本组织而成的。在单声道WAV文件中,声道0代表左声道,声道1代表右声道。在多声道WAV文件中,样本是交替出现的。
WAV文件格式说明表
文件头
偏移地址
字节数
数据类型
内 容
00
H
4
char
"RIFF"标志
04
H
4
long
int 文件长度
08
H
4
char
"WAV"标志
0C
H
4
char
"fmt"标志
10
H
4


过渡字节(不定)
14
H
2
int
格式类别(10HPCM形式的声音数据)
16
H
2
int
单声道为1,双声道为2通道数
18
H
2
int
采样率(每秒样本数),表示每个通道的播放速度
1C
H
4
long
波形音频数据传送速率,其值为通道数×每秒数据位数×每样 本的数据位数/8。播放软件利用此值可以估计缓冲区的大小
22
H
2


每样本的数据位数,表示每个声道中各个样本的数据位数。如果有多 个声道,对每个声道而言,样本大小都一样。 24H 4 char 数据标记符"data28H 4 long int 语音数据的长度
PCM数据的存放方式:
样本1  样本2
8位单声道 0声道 0声道
8位立体声 0声道(左) 1声道(右) 0声道(左) 1声道(右)
16位单声道 0声道低字节 0声道高字节 0声道低字节 0声道高字节
16位立体声 0声道(左)低字节 0声道(左)高字节 1声道(右)低字节 1声道(右)高字节
PCM数据的存放方式:
WAV文件的每个样本值包含在一个整数i中,i的长度为容纳指定样本长度所需 的最小字节数。首先存储低有效字节,表示样本幅度的位放在i的高有效位上, 剩下的位置为0,这样8位和16位的PCM波形样本的数据格式如下所示。
样本大小
数据格式
最大值
最小值
8PCM
unsigned int
225
0
16PCM
int
327
67
多媒体技术近年来发展很快,较好品质的声卡可以提供16位的立体声及44KHZ的播放录制能力,它不仅可以提供原音逼真的取样,其合成的音质也十分理想,有的声卡还加入了数字信号处理器,可编程控制的DSP具有强大的运算能力,它可以用来作声音信息的压缩和一些特殊效果的处理。具有此功能的声卡提供的WAV文件提供的语音信息可以满足语音特征识别的要求。
RIFF概念
在Windows环境下,大部分的多媒体文件都依循着一种结构来存放信息,这种结构称为"资源互换文件格式"(Resources lnterchange File Format),简称RIFF。例如声音的WAV文件、视频的AV1文件等等均是由此结构衍生出来的。RIFF可以看做是一种树状结构,其基本构成单位为chunk,犹如树状结构中的节点,每个chunk由"辨别码"、"数据大小"及"数据"所组成。
块的标志符(4BYTES)
数据大小 (4BYTES)
数据
                               图一、 块的结构示意图
辨别码由4个ASCII码所构成,数据大小则标示出紧跟其后数据的长度(单位为Byte),而数据大小本身也用掉4个Byte,所以事实上一个chunk的长度为数据大小加8。一般而言,chunk本身并不允许内部再包含chunk,但有两种例外,分别为以"RIFF"及"L1ST"为辨别码的chunk。而针对此两种chunk,RIFF又从原先的"数据"中切出4个Byte。 此4个Byte称为"格式辨别码",然而RIFF又规定文件中仅能有一个以"RIFF"为辨别码的chunk。
RIFF/LIST标志符
数据1大小
数据1
格式/列表类型
数据
                               图二、RIFF/LIST块结构
只要依循此一结构的文件,我们均称之为RIFF档。此种结构提供了一种系统化的分类。如果和MS一DOS文件系统作比较,"RIFF"chunk就好比是一台硬盘的根目录,其格式辨别码便是此硬盘的逻辑代码(C:或D:),而"L1ST"chunk即为其下的子目录,其他的chunk则为一般的文件。至于在RIFF文件的处理方面,微软提供了相关的函数。视窗下的各种多媒体文件格式就如同在磁盘机下规定仅能放怎样的目录,而在该目录下仅能放何种数据。
WAV文件格式
WAVE文件是非常简单的一种RIFF文件,它的格式类型为"WAVE"。RIFF块包含两个子块,这两个子块的ID分别是"fmt"和"data",其中"fmt"子块由结构PCMWAVEFORMAT所组成,其子块的大小就是sizeofof(PCMWAVEFORMAT),数据组成就是PCMWAVEFORMAT结构中的数据。
标志符(RIFF)
数据大小
格式类型("WAVE")
"fmt"
Sizeof(PCMWAVEFORMAT)
PCMWAVEFORMAT
"data"
声音数据大小
声音数据
                                                      图三、WAVE文件结构
PCMWAVEFORMAT结构定义如下:
Typedef struct
{
     WAVEFORMAT wf;        /波形格式;
     WORD wBitsPerSample;    //WAVE文件的采样大小;
} PCMWAVEFORMAT;

//WAVEFORMAT结构定义如下:
typedef struct
{
     WORD wFormatag;        //编码格式,包括WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM等
     WORD nChannls;        //声道数,单声道为1,双声道为2;
     DWORD nSamplesPerSec;    //采样频率;
     DWORD nAvgBytesperSec;    //每秒的数据量;
      WORD nBlockAlign;        //块对齐;
} WAVEFORMAT;
"data"子块包含WAVE文件的数字化波形声音数据,其存放格式依赖于"fmt"子块中wFormatTag成员指定的格式种类,在多声道WAVE文件中,样本是交替出现的。如16bit的单声道WAVE文件和双声道WAVE文件的数据采样格式分别如图四所示:
16位单声道:
采样一
采样二
……
低字节
高字节
低字节
高字节
……





16位双声道:
采样一
……
左声道
右声道
……
低字节
高字节
低字节
高字节
……





                             图四、WAVE文件数据采样格式
WAV文件格式实例分析:


0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
00000000H
00000010H
00000020H
00000030H
00000040H
52  49  46  46  0A 06 01 00 57 41 56 45 66 6D 74 20
12 00 00 00 01 00 02 00 44 AC 00 00 10 B1 02 00
04 00 10 00 00 00 66 61 63 74 04 00 00 00 76 41
00 00 64 61 74 61 D8 05 01 00 00 00 00 00 FF FF
00 00 FE FF FE FF  00 00 00 00 FE FF FE FF 00 00


偏移地址
字节数
数据类型
内容






文件头
00H
4
char
“RIFF”;   RIFF标志
04H
4
long int
0x00 01 06 0A(注意数据存储顺序);   文件长度
08H
4
char
“WAVE”;   WAVE标志
0CH
4
char
“fmt ”;   fmt标志,最后一位为空
10H
4
long int
0x12;   sizeof(PCMWAVEFORMAT)
14H
2
int
1(WAVE_FORMAT_PCM); 格式类别,1表示为PCM形式的声音数据
16H
2
int
2; 通道数,单声道为1,双声道为2
18H
2
int
44100; 采样频率(每秒样本数)
1CH
4
long int
0x10B10000;   每秒数据量;其值为通道数×每秒数据位数×每样本的数据位数/8。播放软件利用此值可以估计缓冲区的大小。
20H
2
int
数据块的调整数(按字节算的),其值为通道数×每样本的数据位值/8。播放软件需要一次处理多个该值大小的字节数据,以便将其值用于缓冲区的调整。
22H
2


每样本的数据位数,表示每个声道中各个样本的数据位数。如果有多个声道,对每个声道而言,样本大小都一样。
50H
4
char
“data”;   数据标记符
54H
4
long int
0x00 01 05 D8;   语音数据大小
在Windows环境下,大部分的多媒体文件都依循着一种结构来存放信息,这种结构称为"资源互换文件格式"(Resources lnterchange File Format),简称RIFF。例如声音的WAV文件、视频的AVI文件等等均是由此结构衍生出来的。RIFF可以看做是一种树状结构,其基本构成单位为chunk,犹如树状结构中的节点,每个chunk由"辨别码"、"数据大小"及"数据"所组成。
  辨别码由4个ASCII码所构成,数据大小则标示出紧跟其后数据的长度(单位为Byte),而数据大小本身也用掉4个Byte,所以事实上一个chunk的长度为数据大小加8。一般而言,chunk本身并不允许内部再包含chunk,但有两种例外,分别为以"RIFF"及"LIST"为辨别码的chunk。而针对此两种chunk,RIFF又从原先的"数据"中切出4个Byte。 此4个Byte称为"格式辨别码",然而RIFF又规定文件中仅能有一个以"RIFF"为辨别码的chunk。
  只要依循此一结构的文件,我们均称之为RIFF档。此种结构提供了一种系统化的分类。如果和MS一DOS文件系统作比较,"RIFF"chunk就好比是一台硬盘的根目录,其格式辨别码便是此硬盘的逻辑代码(C:或D:),而"LIST"chunk即为其下的子目录,其他的chunk则为一般的文件。至于在RIFF文件的处理方面,微软提供了相关的函数。视窗下的各种多媒体文件格式就如同在磁盘机下规定仅能放怎样的目录,而在该目录下仅能放何种数据。
  WAV为WAVEFORM(波形)的缩写。在声音文件的结构中,"RIFF"的格式辨别码为"WAVE"。整个文件由两个chunk所组成:辨别码"fmt "(注意,后面有一个空格)及"data"。
  在"fmt "的chunk下包含了一个PCMWAVEFORMAT数据结构,其定义如下:
  typedef struct pcmwaveformat - tag {
  WAVEFORMAT wf ;
  WORD wBitsPerSample;
  } PCMWAVEFORMAT;
  typedef struct waveformat - tag {
  WORD wFormatTag ;
  WORD nChannels;
  DWORD nSamplesPerSec;
  DWORD nAvgBytesperSec;
  WORD nBlockAlign;
  } WAVEFORMAT;
  其意义分别为:
  wFormatTag:记录着此声音的格式代号,例如WAVE_FORMAT_PCM,WAVE_F0RAM_ADPCM等等。
  nChannels:记录声音的频道数。
  nSamp1esPerSec:记录每秒取样数。
  nAvgBytesPerSec:记录每秒的数据量。
  nBlockA1ign:记录区块的对齐单位。
  wBitsPerSample:记录每个取样所需的位元数。
  "data"Chunk包含真正的声音数据。Window目前仅提供WAVE_FORMAT_PCM一种数据格式,所代表的意义是脉派编码调变(Pu1se Code Modulation)。针对此格式,Windows定义了在"data"的chunk中数据的存放情形,图2中列出了四种不同频道数及取样所需的位元数以及位元位置的安排。
  "RIFF" 频道0 频道0 频道0 频道0
  xxxx nChannels=1,wBitsPerSample=8
  "WAVE" 频0(左) 频道1(右) 频道0(左) 频道1 (右)
  "fmt "
  nChannels=2,wBitsPerSample=8
  sizeof(PCMWAVEFORMAT)
  struct of PCMWAVEFORMAT 频道0(低位) 频道0(高位) 频道0(低位)频道0(高位)
  "data" nChannels=1,wBitsPerSample=16
  xxxx 频道0(低位) 频道0(高位) 频道0(低位)频道0(高位)
  (低位) (高位) (低位) (高位)
  wave form data
  nChannels=2,wBitsPerSample=16
  图1 WAV文件结构 图2 PCM文件中位元安排方式
  第一排表示单声道8位元,第二排表示双声道8位元,第三排表示单声道16位元,第四排表示双声道16位元。8位元代表音量大小由8个位元所表示,16位元则代表音量大小由16个位元所表示。理论上8位元可以表示0~255,16位元可表示0~65536,不过windows却定16位元其值的范围从-32168~32167。此外尚有一点要注意的是,0并不一定代表无声,而是由中间的数值来决定,也就是在8位元时为128,16位元时为0才是无声。所以,若程序设计时需放入无声的数据,糯特别注意声音格式是16或是8位元,以放入适当的值。
WAV文件信息的具体应用
  WAV文件中包括了对原始声音的高速率采样,并且以WAVE_PCM_FORMAT脉派编码调变格式,我们可以在VISUAL C++程序中实现,在读出WAVEHDR文件头之后,下面就是原始声音的高速率采样信息,我们可以对它作多方面的信息处理。
波形显示。
  我们可以以时域-幅度的方式显示出原始声音的波形,这是最简单同时也是最直接的信息处理方式。在时域范围内,我们可以观察该信号波形是否连续,中间是否有跳变等。
频谱显示
  我们可以以频域-幅度的方式显示出原始声音的频谱,在对原始信号经过FFT变换之后,可以得到该信号的频谱,进而得到该信号的能量集中带,分布特征,谱对称系数等等。
用于语音信号识别
  讲话者的个体识别是语音信号处理的一个重要内容,但它的一个前提条件是必须提供语音信号的数字波形,通常的方法是将原始的语音信号进行放大、抗混叠滤波、A/D采样、数值编码,最终得到语音信号的数字波形,通常多采用硬件处理,费时费力,如果我们借助非常成熟的声卡技术,将WAV文件打开,就非常方便地得到语音信号的数字波形,为下一步进行语音信号识别提供良好的前端预处理。

单片机源程序如下:
  1. #include "wave.h"
  2. char wave_file[]="00_1942.wav";
  3. FIL waveFile;
  4. UINT wave_bw;
  5. u8 wave_top_table[44];
  6. //u32 wave_write_table[1024];

  7. FRESULT wavefileStatus;
  8. void wave_top_init(u8 select,int channels,int sample_rate,unsigned char *table,int num)
  9. {
  10.         WAVE_HEADER   pcmHEADER;  
  11.   WAVE_FMT   pcmFMT;  
  12.   WAVE_DATA   pcmDATA;  
  13.         u16 i,j=0;
  14.         u8 *p;
  15.         u8 CounterTimer=0;
  16.         CounterTimer=select;
  17.         wavefileStatus = f_open(&waveFile,(TCHAR const*)wave_file, FA_OPEN_ALWAYS | FA_WRITE |FA_READ );
  18.           if(FR_OK==wavefileStatus)
  19.           {
  20.                         //WAVE_HEADER
  21.       memcpy(pcmHEADER.fccID,"RIFF",strlen("RIFF"));   
  22.       if(CounterTimer==0)
  23.                         {                        
  24.       pcmHEADER.dwSize=44-8+num;        
  25.                         }
  26.                         else
  27.                         {
  28.                           pcmHEADER.dwSize=num+waveFile.obj.objsize-8;        
  29.                         }
  30.       memcpy(pcmHEADER.fccType,"WAVE",strlen("WAVE"));  
  31.             //WAVE_FMT
  32.             
  33.             memcpy(pcmFMT.fccID,"fmt ",strlen("fmt "));
  34.             pcmFMT.dwSize=16;                              
  35.             pcmFMT.wFormatTag=1;                          
  36.             pcmFMT.wChannels=channels;                     
  37.       pcmFMT.dwSamplesPerSec=sample_rate;           
  38.       pcmFMT.dwAvgBytesPerSec=channels*sample_rate*24/8;  
  39.             pcmFMT.wBlockAlign=1*24/8;            
  40.       pcmFMT.uiBitsPerSample=24;                     
  41.             
  42.       //WAVE_DATA;
  43.       memcpy(pcmDATA.fccID,"data",strlen("data"));  
  44.                         if(CounterTimer==0)
  45.                         {
  46.       pcmDATA.dwSize=num;
  47.                         }
  48.                         else
  49.                         {
  50.                            pcmDATA.dwSize=waveFile.obj.objsize-44+num;
  51.                         }
  52.                         //waveFile.obj.objsize=0;
  53.             j=0;
  54.             p=(u8*)(&pcmHEADER);
  55.             for(i=0;i<sizeof(WAVE_HEADER);i++)
  56.             {
  57.               wave_top_table[j++]=*p;
  58.                     p++;
  59.             }
  60.             p=(u8*)(&pcmFMT);
  61.             for(i=0;i<sizeof(WAVE_FMT);i++)
  62.             {
  63.               wave_top_table[j++]=*p;
  64.                     p++;
  65.             }
  66.             p=(u8*)(&pcmDATA);
  67.             for(i=0;i<sizeof(WAVE_DATA);i++)
  68.             {
  69.               wave_top_table[j++]=*p;
  70.                     p++;
  71.             }  
  72.                         
  73.             f_lseek (&waveFile,0);
  74.             f_write(&waveFile, (const void*)wave_top_table, 44, &wave_bw);
  75.                         f_sync (&waveFile);
  76.                         
  77.       f_lseek (&waveFile,waveFile.obj.objsize);
  78.                         f_write(&waveFile, (const void*)table, num, &wave_bw);
  79.                         f_sync (&waveFile);
  80.             f_close(&waveFile);
  81.         }
  82. }
复制代码

头文件51hei提供下载:
PCM转wave存到sd卡.rar (1.33 KB, 下载次数: 21)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表