基于C语言实现PCM音频流或音频文件重采样(48K到16K)
由于云厂商SDK所需的音频采样率为16K的,而SFU回调的流量是48K因此,我们仍然需要正确的PCM采样处理音频数据。
转换的原理比较简单,从48KHz降到16KHz,下降3倍,即同时在单位范围内,48KHz采样3点,16点KHz只采样了一个点,即从48KHz的 PCM流中每读取三个数据,就要根据这三个数据计算一个数据,对应16个数据KHz PCM流中的数据。
从三个数据中获取一个数据的方法有很多,如取平均值、取样点等:
(1). 取平均值:
取3点,加后求平均值。这种效果不好,转换后会有噪音。这是因为转换成16kHz根据奈奎斯特采样定律,当采样频率fs.max大于信号中最高频率fmax的2倍时(fs.max>2fmax),,根据这些抽样值,原始信号可以完全恢复,因此可以转换为16kHz采样率不能有8K上述频率。对于三点的平均值,有可能存在8khz上述频段,所以效果不好。
(2). 取特征点:
取三个采样点中固定位置的一个点,如固定三个点中的第一个点,然后合并成一个新的数据流。这种效果,测量相对清晰,虽然有可能失去一些声音细节,但整体仍能满足语音识别。
综上所述,我们采用了第一个特征点的方法。
具体实现如下
C语言实现
处理pcm流:
/** PCM frame. */ typedef struct { uint8_t *buf; int len; int sample_rate; int sample_depth; } pcm_frame_t; void pcm_resample_16k(const pcm_frame_t *frame_in, pcm_frame_t *frame_out) { int i; int magnification = frame_in->sample_rate / 16000; frame_out->buf = frame_in->buf; frame_out->len = frame_in->len / magnification; frame_out->sample_rate = 16000; frame_out->sample_depth = frame_in->sample_depth; for (i = 0; i < frame_out->len; i ) { frame_out->buf[i] = frame_in->buf[i*magnification]; /* 取第一个特征值 */ } /* 如果按照16bit来 */ // for (i = 0; i < len / 3; i ) { // if (0 == (i % 2)) { // buf[i] = buf[i*3 2]; // } else { // buf[i] = buf[i*3]; // } // } }
处理pcm文件:
void pcm_resample(void) { short read_buf = 0; int size = 0; int cnt = 0; FILE *fp = fopen("in.pcm", "rb "); FILE *fp_out = fopen("out.pcm", "wb "); while (!feof(fp)) { size = fread(&read_buf, 2, 1, fp); // 16bit,所以你需要一次读两个字节 if (size > 0) { cnt ; if (cnt == 3) { //每采集3个点 cnt = 0; // 重置计数 fwrite(&read_buf, 2, 1, fp_out); ///写入数据 } } } fclose(fp); fclose(fp_out); }