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编码则是需要根据流的压缩方式分别对待,有两个函数,一个是对应上文的每次读取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官网下载,也可以在百度云盘下载