语音编码解码(一)——speex

PCM是当前最高保真水平的音频编码,同时额外的开销就是体积非常庞大。目前其他所有的语音编码可以理解为在音质与体积之间进行平衡。因此PCM编码也成为各种语音格式转换的中间编码。

之前的工作用到了iLBC、SILK、Speex和Opus的流编码和流解码,下面总结下这几种编码的细节:

pcm->speex编码:

编码的原理是每次读入160字节的PCM数据,然后根据压缩级别的设置,分别产生约10-62字节的压缩后数据(单声道,8000Hz采样频率)。

#include  
#include  
/*帧的大小在这个例程中是一个固定的值,但它并不是必须这样*/ 
#define FRAME_SIZE 160
#pragma comment (lib,"libspeex.lib")
int main(int argc, char **argv) 
{ 
	char *inFile = NULL; 
	FILE *fin = NULL, *fout = NULL; 
	short in[FRAME_SIZE] = {0}; 
	float input[FRAME_SIZE] = {0}; 
	char cbits[200] = {0}; 
	int nbBytes = 0; 
	/*保存编码的状态*/ 
	void *state = NULL; 
	/*保存字节因此他们可以被speex常规读写*/ 
	SpeexBits bits; 
	int i = 0, tmp = 0; 
	//新建一个新的编码状态在窄宽(narrowband)模式下 
	state = speex_encoder_init(speex_lib_get_mode(SPEEX_MODEID_NB));
	//设置质量为8(15kbps) 
	tmp=9; 
	speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp);
	tmp=8000;
	speex_encoder_ctl(state, SPEEX_SET_SAMPLING_RATE, &tmp);

	fin = fopen("G:\\55.pcm", "rb");
	fout = fopen("G:\\8000.spx", "wb");
	//初始化结构使他们保存数据 
	speex_bits_init(&bits); 
	while (1) 
	{
		//读入一帧16bits的声音 
		fread(in, sizeof(short), FRAME_SIZE, fin); 
		if (feof(fin)) 
			break; 
		//把16bits的值转化为float,以便speex库可以在上面工作 
		for (i=0;i

speex->pcm编码:

Speex流数据转换为PCM编码则是需要根据流的压缩方式分别对待,有两个函数,一个是对应上文的每次读取10-62字节的数据块,设置好压缩级别,然后调用speex_decode函数解析出160字节的PCM编码。但是吧,这个函数没能解压缩我的数据流,通过逆向软件发现其调用的为speex_decode_int函数。单个数据块长度为275字节,而且是调用了10次speex_decode_int函数进行解析,275/10,我数学不好,不知道这函数怎么解析的。希望有懂的同学,能够在回复里用自然语言解答下speex_decode_int和speex_decode的区别。

下面贴出代码,代码很挫,只为验证解析的准确与否,会意即可。

#include 
#include 
#include 
#include 

#define FRAME_SIZE 160
#define MAX_WAV_SIZE 10000000
#pragma comment (lib,"libspeex.lib")

char *wav_header="\x52\x49\x46\x46\x2C\x2C\x01\x00\x57\x41\x56\x45\x66\x6D\x74\x20\x10\x00\x00\x00\x01\x00\x01\x00\x40\x1F\x00\x00\x80\x3E\x00\x00\x02\x00\x10\x00\x64\x61\x74\x61\x00\x2C\x01\x00";
char wav_data[MAX_WAV_SIZE] = {0}, speex_data[MAX_WAV_SIZE] = {0};
int speex_size = 0;

int speex2wav()
{
	char cbits[0x200] = {0};
	int i=0, tmp=0, pcm_size = 0, offset = 0;

	spx_int16_t packet_len = 0;	

	void *state = NULL;
	SpeexBits bits;

	if (speex_size < 0x113)
	{
		printf("data too small\n");
		return 0;
	}

	memcpy(wav_data, wav_header, 0x2c);

	/*Create a new decoder state in narrowband mode*/
	state = speex_decoder_init(speex_lib_get_mode(SPEEX_MODEID_NB));

	/*Set the perceptual enhancement on*/
	tmp=1;
	speex_decoder_ctl(state, SPEEX_SET_ENH, &tmp);
	tmp=0;
	speex_decoder_ctl(state, SPEEX_GET_FRAME_SIZE, &tmp);
	tmp=0;
	speex_decoder_ctl(state, SPEEX_GET_BITRATE, &tmp);

	speex_bits_init(&bits);

	while (offset < speex_size-2)
	{
		packet_len = *(short *)(speex_data + offset);
		offset += 4;
		speex_bits_read_from(&bits, speex_data + offset, packet_len);
		offset += packet_len;

		for (i=0;i<10;i++)
		{
			speex_decode_int(state, &bits, (spx_int16_t *)(wav_data+0x2c+pcm_size));
			pcm_size += FRAME_SIZE*(sizeof(short));
		}
	}
	*(int *)(wav_data+0x28) = pcm_size;
	*(int *)(wav_data+4) = pcm_size + 0x2c;
	
	/*Destroy the decoder state*/
	speex_decoder_destroy(state);
	/*Destroy the bit-stream truct*/
	speex_bits_destroy(&bits);
	return pcm_size + 0x2c;
}

int main(int argc, char **argv)
{
	int wav_size = 0;
	FILE *fin = NULL, *fout = NULL;

	//检查头部是否符合
	//if(!speex_packet_to_header(speex_data + offset, header_length))
	//{
	//	return 0;
	//}

	fin = fopen(argv[1], "rb");
	//fin = fopen("G:\\test.spx", "rb");
	fseek(fin, 0L, SEEK_END);
	speex_size = ftell(fin);
	fseek(fin, 0, SEEK_SET);
	fread(speex_data, 1, speex_size, fin);
	fclose(fin);

	wav_size = speex2wav();

	strcpy(argv[1] + strlen(argv[1])- 3, "wav\0");
	printf("%s", argv[1]);
	fout = fopen(argv[1], "wb");
	fwrite(wav_data, 1, wav_size, fout);
	fclose(fout);
}

对应的库可以去speex官网下载,也可以在百度云盘下载

你可能感兴趣的:(数据流)