将 libsamplerate 与 libsndfile 一起使用
Posted
技术标签:
【中文标题】将 libsamplerate 与 libsndfile 一起使用【英文标题】:Using libsamplerate with libsndfile 【发布时间】:2013-12-31 03:04:39 【问题描述】:这段代码是我试图更好地理解音频编码的一部分。这里打开了一个文件 使用 libsndfile,使用 libsamplerate 转换为新的采样率,并播放结果 用力宝。
播放比特、通道和速率的各种组合时,结果如下:
测试数量、位、通道、速率、结果
-
8、1、11025、好的
8、2、11025,音频抖动。否则音高和速度还可以。
16, 1, 11025, 好的
16、2、11025,音频抖动。否则音高和速度还可以。
8, 1, 44100, 好的
8、2、44100、好的
16, 1, 44100, 好的
16、2、44100,好的
为什么测试 2 和 4 失败了?
/*
* Objective: sample rate conversion
* compile with
* "gcc -o glurp glurp.c -lao -lsndfile -lsamplerate"
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <ao/ao.h>
#include <sndfile.h>
#include <samplerate.h>
#define DEFAULT_CONVERTER SRC_SINC_MEDIUM_QUALITY
#define NEW_RATE 44100
#define BUFFSIZE 4096
#define MAX(x,y) ((x)>(y)) ? (x) : (y)
#define MIN(x,y) ((x)<(y)) ? (x) : (y)
int playfile(FILE *, int);
void floattopcm16(short *, float *, int);
void pcm16tofloat(float *, short *, int);
int main(int argc, char *argv[])
FILE *fp;
int newrate;
if (argc < 2)
printf("usage: %s <filename> <rate>\n", argv[0]);
exit(1);
fp = fopen(argv[1], "rb");
if (fp == NULL)
printf("Cannot open %s.\n", argv[1]);
exit(1);
if (argv[2])
newrate = atoi(argv[2]);
else
newrate = NEW_RATE;
playfile(fp, newrate);
return 0;
int playfile(FILE *fp, int newrate)
int default_driver;
int frames_read;
int count;
int toread;
int readnow;
float *floatbuffer;
float *floatbuffer2;
short *shortbuffer;
long filestart;
int volcount;
ao_device *device;
ao_sample_format format;
SNDFILE *sndfile;
SF_INFO sf_info;
SRC_STATE *src_state;
SRC_DATA src_data;
int error;
double max = 0.0;
sf_count_t output_count = 0;
ao_initialize();
default_driver = ao_default_driver_id();
sf_info.format = 0;
filestart = ftell(fp);
sndfile = sf_open_fd(fileno(fp), SFM_READ, &sf_info, 0);
memset(&format, 0, sizeof(ao_sample_format));
format.byte_format = AO_FMT_NATIVE;
format.bits = 16;
format.channels = sf_info.channels;
format.rate = newrate;
printf("Start sample rate: %d\n", sf_info.samplerate);
printf("Ending sample rate: %d\n", newrate);
device = ao_open_live(default_driver, &format, NULL /* no options */);
if (device == NULL)
printf("Error opening sound device.\n");
return 1;
floatbuffer = malloc(BUFFSIZE * sf_info.channels * sizeof(float));
floatbuffer2 = malloc(BUFFSIZE * sf_info.channels * sizeof(float));
shortbuffer = malloc(BUFFSIZE * sf_info.channels * sizeof(short));
frames_read = 0;
toread = sf_info.frames * sf_info.channels;
/* Set up for conversion */
if ((src_state = src_new(DEFAULT_CONVERTER, sf_info.channels, &error)) == NULL)
printf("Error: src_new() failed: %s.\n", src_strerror(error));
exit(1);
src_data.end_of_input = 0;
src_data.input_frames = 0;
src_data.data_in = floatbuffer;
src_data.src_ratio = (1.0 * newrate) / sf_info.samplerate;
src_data.data_out = floatbuffer2;
src_data.output_frames = BUFFSIZE / sf_info.channels;
while (1)
/* if floatbuffer is empty, refill it */
if (src_data.input_frames == 0)
src_data.input_frames = sf_read_float(sndfile, floatbuffer, BUFFSIZE / sf_info.channels);
src_data.data_in = floatbuffer;
/* mark end of input */
if (src_data.input_frames < BUFFSIZE / sf_info.channels)
src_data.end_of_input = SF_TRUE;
if ((error = src_process(src_state, &src_data)))
printf("Error: %s\n", src_strerror(error));
exit(1);
/* terminate if done */
if (src_data.end_of_input && src_data.output_frames_gen == 0)
break;
/* write output */
output_count += src_data.output_frames_gen;
src_data.data_in += src_data.input_frames_used * sf_info.channels;
src_data.input_frames -= src_data.input_frames_used;
floattopcm16(shortbuffer, floatbuffer2, src_data.output_frames_gen);
ao_play(device, (char *)shortbuffer, src_data.output_frames_gen * sizeof(short));
src_state = src_delete(src_state);
free(shortbuffer);
free(floatbuffer);
free(floatbuffer2);
fseek(fp, filestart, SEEK_SET);
ao_close(device);
sf_close(sndfile);
ao_shutdown();
printf("Finished\n");
return 0;
/* Convert back to shorts */
void floattopcm16(short *outbuf, float *inbuf, int length)
int count;
const float mul = (32768.0f);
for (count = 0; count <= length; count++)
int32_t tmp = (int32_t)(mul * inbuf[count]);
tmp = MAX( tmp, -32768 ); // CLIP < 32768
tmp = MIN( tmp, 32767 ); // CLIP > 32767
outbuf[count] = tmp;
【问题讨论】:
【参考方案1】:在 Erik 的帮助下,我终于得到了这个测试代码。我的问题是对音频帧与音频样本的误解。一帧由每个通道一个样本组成。样本就是这样,一个数字表示瞬间的音频信号。我以为我知道其中的区别,但是在应用 Erik 在主循环中看到的示例代码时忘记了。该代码来自 libsamplerate 分发包的示例目录中的 sndfile-resample.c
。这种误解的结果是,在立体声样本中,最后几个样本(大约 23 到 60 个,取决于缓冲区大小)将为零。这导致了紧张的播放。如果我将缓冲区大小减少到 512,我会得到听起来像模拟合成器上的环形调制器的失真。请注意从 sf_read_float()
到 sf_readf_float()
的更改。 floattopcm16()
中的循环错误地测试了 count <= length
。我已将其更正为count < length
。
对于那些也有问题的人,这里是可以工作并通过 -Wall 的代码。
/*
* Objective: sample rate conversion
* compile with
* "gcc -o glurp glurp.c -lao -lsndfile -lsamplerate"
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <ao/ao.h>
#include <sndfile.h>
#include <samplerate.h>
#define DEFAULT_CONVERTER SRC_SINC_MEDIUM_QUALITY
#define NEW_RATE 44100
#define BUFFSIZE 4096
#define MAX(x,y) ((x)>(y)) ? (x) : (y)
#define MIN(x,y) ((x)<(y)) ? (x) : (y)
int playfile(FILE *);
void floattopcm16(short *, float *, int);
void pcm16tofloat(float *, short *, int);
int main(int argc, char *argv[])
FILE *fp;
if (argc != 2)
printf("usage: %s <input>\n", argv[0]);
exit(1);
fp = fopen(argv[1], "rb");
if (fp == NULL)
printf("Cannot open %s.\n", argv[1]);
exit(1);
playfile(fp);
fclose(fp);
return 0;
int playfile(FILE *fp)
int default_driver;
float *floatbuffer;
float *floatbuffer2;
short *shortbuffer;
long filestart;
int newrate = NEW_RATE;
ao_device *device;
ao_sample_format format;
SNDFILE *sndfile;
SF_INFO sf_info;
SRC_STATE *src_state;
SRC_DATA src_data;
int error;
sf_count_t output_count = 0;
ao_initialize();
default_driver = ao_default_driver_id();
sf_info.format = 0;
filestart = ftell(fp);
sndfile = sf_open_fd(fileno(fp), SFM_READ, &sf_info, 0);
memset(&format, 0, sizeof(ao_sample_format));
format.byte_format = AO_FMT_NATIVE;
format.bits = 16;
format.channels = sf_info.channels;
format.rate = newrate;
printf("Channels: %d\n", sf_info.channels);
printf("Start sample rate: %d\n", sf_info.samplerate);
printf("Ending sample rate: %d\n", newrate);
device = ao_open_live(default_driver, &format, NULL /* no options */);
if (device == NULL)
printf("Error opening sound device.\n");
return 1;
floatbuffer = malloc(BUFFSIZE * sf_info.channels * sizeof(float));
floatbuffer2 = malloc(BUFFSIZE * sf_info.channels * sizeof(float));
shortbuffer = malloc(BUFFSIZE * sf_info.channels * sizeof(short));
/* Set up for conversion */
if ((src_state = src_new(DEFAULT_CONVERTER, sf_info.channels, &error)) == NULL)
printf("Error: src_new() failed: %s.\n", src_strerror(error));
exit(1);
src_data.end_of_input = 0;
src_data.input_frames = 0;
src_data.data_in = floatbuffer;
src_data.src_ratio = (1.0 * newrate) / sf_info.samplerate;
src_data.data_out = floatbuffer2;
src_data.output_frames = BUFFSIZE / sf_info.channels;
while (1)
/* if floatbuffer is empty, refill it */
if (src_data.input_frames == 0)
src_data.input_frames = sf_readf_float(sndfile, floatbuffer, BUFFSIZE / sf_info.channels);
src_data.data_in = floatbuffer;
/* mark end of input */
if (src_data.input_frames < BUFFSIZE / sf_info.channels)
src_data.end_of_input = SF_TRUE;
if ((error = src_process(src_state, &src_data)))
printf("Error: %s\n", src_strerror(error));
exit(1);
/* terminate if done */
if (src_data.end_of_input && src_data.output_frames_gen == 0)
break;
/* write output */
floattopcm16(shortbuffer, floatbuffer2, src_data.output_frames_gen * sf_info.channels);
ao_play(device, (char *)shortbuffer, src_data.output_frames_gen * sizeof(short) * sf_info.channels);
output_count += src_data.output_frames_gen;
src_data.data_in += src_data.input_frames_used * sf_info.channels;
src_data.input_frames -= src_data.input_frames_used;
src_state = src_delete(src_state);
free(shortbuffer);
free(floatbuffer);
free(floatbuffer2);
fseek(fp, filestart, SEEK_SET);
ao_close(device);
sf_close(sndfile);
ao_shutdown();
printf("Finished\n");
return 0;
/* Convert back to shorts */
void floattopcm16(short *outbuf, float *inbuf, int length)
int count;
const float mul = (32768.0f);
for (count = 0; count < length; count++)
int32_t tmp = (int32_t)(mul * inbuf[count]);
tmp = MAX( tmp, -32768 ); // CLIP < 32768
tmp = MIN( tmp, 32767 ); // CLIP > 32767
outbuf[count] = tmp;
【讨论】:
【参考方案2】:Libsndfile 不会去隔行立体声音频,您必须手动完成。
【讨论】:
对不起,没有。根据xiph.org/ao/doc/ao_play.htmlao_play() 接受交错数据。以上是关于将 libsamplerate 与 libsndfile 一起使用的主要内容,如果未能解决你的问题,请参考以下文章
使用 libsndfile 和 libsamplerate 的音频采样率转换器。不确定是不是正确使用函数 src_simple