VS语音信号处理(4) C语言WAV格式语音存为PCM格式语音工程实例
前言
语音识别相关算法一般在MATLAB上进行仿真验证与实验,在工程上一般还是在VS中进行实现落地,本系列将介绍语音信号处理在C语言中的一系列应用,后期将以此为基础,再落地移植到嵌入式平台。
今天介绍WAV格式语音存为PCM格式语音的工程应用。两种格式语音的唯一区别在于WAV语音包含文件头信息,而PCM格式语音仅包含语音数据流。语音格式转换也算是一种比较常见的应用,可以让我们更好的理解语音文件的格式信息。
一. 工程实现
打开VS2015 点击新建一个项目(工程),输入项目名:Wav2pcm 点击“确定”,创建项目 左侧解决方案资源管理器视图中,源文件栏右键添加新建项, 点击C++文件,新建一个源文件Wav2pcm.cpp,点击“添加” 输入代码如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma warning (disable :4996)
#define uint32_t unsigned int
#define uint16_t unsigned short
typedef struct _riff_t {
char riff[4];
uint32_t len;
char wave[4];
} riff_t;
typedef struct _format_t {
char fmt[4];
uint32_t len;
uint16_t type;
uint16_t channel;
uint32_t rate;
uint32_t bps;
uint16_t blockalign;
uint16_t bitpspl;
} format_t;
typedef struct _data_t {
char data[4];
uint32_t len;
}data_t;
typedef struct _wav_head
{
riff_t riff;
format_t format;
data_t data;
}wav_head;
int main(int argc, char **argv)
{
char *src_file = "test.wav";
uint32_t pcm_data_size = 0;
int pcm_channel = 0;
int pcm_samplerate = 0;
wav_head header;
FILE *fp = NULL, *fpCpy = NULL;
int flen;
if ((fp = fopen(src_file, "rb")) == NULL)
{
printf("open wav file %s error\n", argv[1]);
return -1;
}
if ((flen = fread(&header.riff.riff, 1, 4, fp)) != 4)
goto error;
if (memcmp(&header.riff.riff, "RIFF", 4))
goto error;
if ((flen = fread(&header.riff.len, 1, 4, fp)) != 4)
goto error;
if ((flen = fread(&header.riff.wave, 1, 4, fp)) != 4)
goto error;
if (memcmp(&header.riff.wave, "WAVE", 4))
goto error;
if ((flen = fread(&header.format.fmt, 1, 4, fp)) != 4)
goto error;
if (memcmp(&header.format.fmt, "fmt ", 4))
goto error;
if ((flen = fread(&header.format.len, 1, 4, fp)) != 4)
{
goto error;
}
if (header.format.len != 0x10)
{
printf("header.format.len!=0x10 :value:%d\n", header.format.len);
}
if ((flen = fread(&header.format.type, 1, 2, fp)) != 2)
{
goto error;
}
if (header.format.type != 0x1)
{
printf("header.format.type != 0x1 :value:%d\n", header.format.type);
}
if ((flen = fread(&header.format.channel, 1, 2, fp)) != 2)
{
goto error;
}
pcm_channel = header.format.channel;
if ((flen = fread(&header.format.rate, 1, 4, fp)) != 4)
{
goto error;
}
pcm_samplerate = header.format.rate;
if ((flen = fread(&header.format.bps, 1, 4, fp)) != 4)
{
goto error;
}
if ((flen = fread(&header.format.blockalign, 1, 2, fp)) != 2)
{
goto error;
}
if ((flen = fread(&header.format.bitpspl, 1, 2, fp)) != 2)
{
goto error;
}
int p = 0;
do {
if ((flen = fread(&header.data.data, 1, 4, fp)) != 4)
{
goto error;
}
if (memcmp(&header.data.data, "data", 4) == 0)
{
printf("find data chunk.");
if ((flen = fread(&header.data.len, 1, 4, fp)) != 4)
{
goto error;
}
pcm_data_size = header.data.len;
break;
}
else
p += 4;
} while (!feof(fp));
printf("解析成功,二段PCM数据长度分别为:%d, %d\n", pcm_data_size, header.riff.len - 36);
int otherdataSize = header.riff.len - 36 - pcm_data_size;
if (otherdataSize>0)
{
printf("多余的数据部分大小:%d , %d", p, otherdataSize);
char *otherdata = (char *)malloc(otherdataSize + 1);
memset(otherdata, 0, otherdataSize + 1);
fseek(fp, -otherdataSize - 8, SEEK_CUR);
fread(otherdata, 1, otherdataSize, fp);
*(otherdata + otherdataSize) = '\0';
fseek(fp, 8, SEEK_CUR);
printf(" other data:%s", otherdata);
}
fseek(fp, otherdataSize + 44, SEEK_SET);
unsigned char *buffer = (unsigned char*)malloc((pcm_data_size) * sizeof(char));
memset(buffer, 0, pcm_data_size);
int count1 = fread(buffer, 1, pcm_data_size, fp);
char output[1024] = { 0 };
sprintf(output, "%d_%d_.pcm", pcm_samplerate, pcm_channel);
if ((fpCpy = fopen(output, "wb+")))
{
fwrite(buffer, 1, pcm_data_size, fpCpy);
}
else
goto error;
free(buffer);
fclose(fp);
fclose(fpCpy);
return 0;
error:
if (fp)
{
fclose(fp);
}
if (fpCpy)
{
fclose(fpCpy);
}
return -1;
}
二. 生成编译
点击目录栏“生成”中“生成解决方案” 生成成功!找一个WAV格式语音,命名为test.wav,放在Wav2pcm.cpp同目录下,点击调试运行,生成一个48000_2_.pcm文件,调用成功。
三. 小结
最近项目上需要对语音信号进行处理,这个是对语音格式进行转换的工程,可以正常跑通,相对也比较简单,值得注意的是,如果没有将test.wav导入的话,工程不会报警提示,所以有需要的同学在应用的时候需要注意一下,没有源语音是得不到pcm语音的。
|