一开始,先煽情下。(呵呵……)明天就是教师节了,在这里祝愿所有的老师们身体健康,工作顺利。或许若干年后,我也可以过这个节日。
ok,回归正题,接着昨晚的继续说。
昨晚说到matlab读取语音,感觉一个很简单的读取语音是不是给我说复杂了。希望大家给予批评和指正。接下来,就真正的进入主题了,希望大家可以学到想学的。
c语言读取语音就没matlab那么简单了。读取wav文件,主要是读取里面存储的数据。一些具体的就不说了。 WAVE文件是由若干个Chunk组成的。按照在文件中的出现位置包括:RIFF WAVE Chunk, Format Chunk, Fact Chunk(可选), Data Chunk。具体见下图:
------------------------------------------------
| RIFF WAVE Chunk |
| ID = 'RIFF' |
| RiffType = 'WAVE' |
------------------------------------------------
| Format Chunk |
| ID = 'fmt ' |
------------------------------------------------
| Fact Chunk(optional) |
| ID = 'fact' |
------------------------------------------------
| Data Chunk |
| ID = 'data' |
------------------------------------------------
图1 Wav格式包含Chunk示例
其中除了Fact Chunk外,其他三个Chunk是必须的。每个Chunk有各自的ID,位于Chunk最开始位置,作为标示,而且均为4个字节。并且紧跟在ID后面的是Chunk大小(去除ID和Size所占的字节数后剩下的其他字节数目),4个字节表示,低字节表示数值低位,高字节表示数值高位。下面具体介绍各个Chunk内容。
PS:所有数值表示均为低字节表示低位,高字节表示高位。
其中每个部分也有具体的组成:
RIFF WAVE Chunk
==================================
| |所占字节数| 具体内容 |
==================================
| ID | 4 Bytes | 'RIFF' |
----------------------------------
| Size | 4 Bytes | |
----------------------------------
| Type | 4 Bytes | 'WAVE' |
----------------------------------
图2 RIFF WAVE Chunk
Format Chunk
====================================================================
| | 字节数 | 具体内容 |
====================================================================
| ID | 4 Bytes | 'fmt ' |
--------------------------------------------------------------------
| Size | 4 Bytes | 数值为16或18,18则最后又附加信息 |
-------------------------------------------------------------------- ----
| FormatTag | 2 Bytes | 编码方式,一般为0x0001 | |
-------------------------------------------------------------------- |
| Channels | 2 Bytes | 声道数目,1--单声道;2--双声道 | |
-------------------------------------------------------------------- |
| SamplesPerSec | 4 Bytes | 采样频率 | |
-------------------------------------------------------------------- |
| AvgBytesPerSec| 4 Bytes | 每秒所需字节数 | |===> WAVE_FORMAT
-------------------------------------------------------------------- |
| BlockAlign | 2 Bytes | 数据块对齐单位(每个采样需要的字节数) | |
-------------------------------------------------------------------- |
| BitsPerSample | 2 Bytes | 每个采样需要的bit数 | |
-------------------------------------------------------------------- |
| | 2 Bytes | 附加信息(可选,通过Size来判断有无) | |
-------------------------------------------------------------------- ----
图3 Format Chunk
Fact Chunk
==================================
| |所占字节数| 具体内容 |
==================================
| ID | 4 Bytes | 'fact' |
----------------------------------
| Size | 4 Bytes | 数值为4 |
----------------------------------
| data | 4 Bytes | |
----------------------------------
图4 Fact Chunk
Data Chunk
==================================
| |所占字节数| 具体内容 |
==================================
| ID | 4 Bytes | 'data' |
----------------------------------
| Size | 4 Bytes | |
----------------------------------
| data | | |
----------------------------------
图5 Data Chunk
不知道你看懂这些图没?也许你只要知道个大概就可以。最重要的到来了,下面的大家记住的哦!!!
文件头长度加起来是44个字节(用UltraEdit打开一个WAVE文件,数一下就知道了)。如果用以个结构体来定义WAVE文件头应该为:
struct WAVEFILEHEADER
{
char chRIFF[4];
DWORD dwRIFFLen;
char chWAVE[4];
char chFMT[4];
DWORD dwFMTLen;
PCMWAVEFORMAT pwf;
char chDATA[4];
DWORD dwDATALen;
};
最后贴出c语言实现的代码。代码只是实现了这个功能,不是很规范。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
// Microsoft wav pcm sound file format. Normal 44 bytes header
typedef struct _tagMsWavPcmHeader44{
unsigned char ChunkID[4]; // "RIFF"; The "RIFF" the mainchunk;
unsigned long ChunkSize; // FileSize - 8; The size following this data
unsigned char Format[4]; // "WAVE"; The "WAVE" format consists of two subchunks: "fmt " and "data"
unsigned char SubChunk1ID[4]; // "fmt "
unsigned long SubChunk1Size; // 16 for PCM. This is the size of the rest of the subchunk which follows this data.
unsigned short AudioFormat; // 1 for PCM. Linear quantization
unsigned short NumChannels; // 1->Mono, 2->stereo, etc..
unsigned long SampleRate; // 8000, 11025, 16000, 44100, 48000, etc..
unsigned long ByteRate; // = SampleRate * NumChannels * BitsPerSample/8
unsigned short BlockAlign; // = NumChannels * BitsPerSample / 8
unsigned short BitsPerSample; // 8->8bits, 16->16bits, etc..
unsigned char SubChunk2ID[4]; // "data"
unsigned long SubChun2Size; // = NumSamples * NumChannels * BitsPerSample / 8. The size of data
} wav_pcm_header44;
void wave_generator(FILE *fpwav, wav_pcm_header44 *phwav);
void main(int argc, char **argv)
{
FILE *fpWav=NULL;
FILE *fp;
unsigned long size;
short *wav_data;
unsigned int i;
wav_pcm_header44 hwav;
/*
* 1. Get wave header information demo
*/
fpWav = fopen("back.wav", "rb");
if (fpWav)
{
fread(&hwav, sizeof(wav_pcm_header44), 1, fpWav);
/* Check wave header */
if ( (0==memcmp(hwav.ChunkID, "RIFF", 4)) &&
(0==memcmp(hwav.Format, "WAVE", 4)) &&
(0==memcmp(hwav.SubChunk1ID, "fmt ", 4)) &&
(0==memcmp(hwav.SubChunk2ID, "data", 4)) &&
(1==hwav.AudioFormat))
{
printf("Wave audio data format:\n");
printf("Channel number: %d\n", hwav.NumChannels);
printf("SampleRate: %dHz\n", hwav.SampleRate);
printf("BitsPerSample: %dbits\n", hwav.BitsPerSample);
printf("Audio data size:%d\n", hwav.SubChun2Size*8/hwav.BitsPerSample);
}
fseek(fpWav,0,SEEK_END);
wav_data=(short*)malloc(sizeof(short)*hwav.SubChun2Size*8/hwav.BitsPerSample);
fseek(fpWav,44,SEEK_SET);
fread(wav_data,2,hwav.SubChun2Size*8/hwav.BitsPerSample,fpWav);
fp=fopen("shujupiano2.txt","w");
for (i=0;i<hwav.SubChun2Size*8/hwav.BitsPerSample;i++)
{
fprintf(fp,"%d\n",wav_data[i]);
}
fclose(fpWav);
fpWav = NULL;
}
else
{
printf("Open wave file %s failed!\n", argv[1]);
}
}
最后,需要指出的是,对于语音的处理,读取wav文件只是很小的一步。也是比较简单的一步。当我们遇到MP3,wma怎么去读取数据了?或许你需要去思考。当然解码是很重要的,但是MP3已经压缩了,还是返回完整的数据吗?我也不是很清楚。
此外,附上现在存在的音频格式:pcm,wav,mp2,mp3.aac,wb+,729等。
matlab读取音频除了wavread还有Dan Ellis写的audioread,详细见http://www.ee.columbia.edu/~dpwe/resources/matlab/audioread/。
再者,在http://www.ee.columbia.edu/~dpwe/resources/matlab/这个网址里,Dan Ellis教授开源了许多matlab处理语音的代码。大家可以去关注下,学习下。
同样,上次说的台湾的张智星教授也提供了matlab处理语音的一些代码。
至此,语音知识回顾和总结的第一篇---读取语音部分就基本说完了。感觉自己的思路不是很清楚,说的也不清楚,大家凑合看。不了解的随时找我,欢迎大家指正。