最近要做些声学信号检测方面的工作。需要对一些 wav 格式的文件进行分析处理。google 了一番,发现了 libsndfile。libsndfile 是一个 C 语言写成的 开放源代码的音频文件读写的库。可以读写 WAV 格式,FLAC 格式和其他许多常见格式(因为专利原因不支持MP3)。LGPL 协议。
libsndfile 的用法很简单。
SNDFILE* sf_open(const char *path, int mode, SF_INFO *sfinfo);
用来打开一个音频文件。 mode 有三个选项。
SFM_READ - read only mode
SFM_WRITE - write only mode
SFM_RDWR - read/write mode
音频文件的一些基本信息由 sfinfo 来返回。
typedef struct
{ sf_count_t frames ; /* Used to be called samples. */
int samplerate ;
int channels ;
int format ;
int sections ;
int seekable ;
} SF_INFO ;
需要注意的是,如果是SFM_READ 模式打开一个文件, sfinfo 的 format 字段应预先写为 0 。
打开音频文件后,测试 format 的各个字段就能知道音频文件的基本类型。
sf_count_t sf_seek(SNDFILE *sndfile, sf_count_t frames, int whence);
用来在音频文件的数据区中移动文件指针,有点像 lseek 函数。不过sf_seek 函数自动的屏蔽了非数据区的部分。所有的移动都是在数据区中进行了,而且是以数据帧为单位,相当的方便。
sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
sf_count_t sf_readf_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) ;
sf_count_t sf_readf_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) ;
sf_count_t sf_readf_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) ;
sf_count_t sf_readf_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ;
上面 8 个函数用来从音频文件中读取数据。 这里来说一说 items 和 frames 的区别。
对于单声道的音频文件。 item 和 frame 是一样的。 一个 item 对应一个数据点。
对于多声道音频文件,一个 frame 包含多个数据点,比如双声道音频文件,一个 frame 包括 2 个 item。
另外,音频文件中可能用 8 bits 来存储一个数据点,也可能是 16bits,可能用整数也可能用浮点数来存储音频数据。sf_read_XXX 系列函数会替你完成数据的转换工作。
下面是一个简单的例子,读取一个音频文件,显示其基本信息,并将数据存储到文本文件中。 这里假设音频文件为单声道。
#include <>
#include <>
#include <>
void save(short *b1, double *b2, int n);
int main(int argc, char * argv[])
{
SF_INFO sf_info;
SNDFILE *snd_file;
short *buf1;
double *buf2;
if(argc != 2)
{
exit(1);
}
sf_info.format = 0;
snd_file = sf_open(argv[1], SFM_READ, &sf_info) ;
printf ("Using %s.\n", sf_version_string ()) ;
printf("File Name : %s\n", argv[1]);
printf("Sample Rate : %d\n", sf_info.samplerate);
printf("Channels : %d\n", sf_info.channels);
printf("Sections : %d\n", sf_info.sections );
printf("Frames : %d\n", (int)sf_info.frames );
buf1 = (short *)malloc(sf_info.frames *sizeof(short));
buf2 = (double *)malloc(sf_info.frames *sizeof(double));
sf_readf_short(snd_file, buf1, sf_info.frames) ;
sf_seek (snd_file, 0, SEEK_SET) ;
sf_readf_double(snd_file, buf2, sf_info.frames) ;
save(buf1, buf2, sf_info.frames);
free(buf1);
free(buf2);
sf_close(snd_file);
return 0;
}
void save(short *b1, double *b2, int n)
{
int i;
FILE *fp1;
FILE *fp2;
fp1 = fopen("", "w");
fp2 = fopen("", "w");
for(i = 0; i< n; i++)
{
fprintf(fp1, "%d\n", (int)b1[i]);
fprintf(fp2, "%f\n", b2[i]);
}
fclose(fp1);
fclose(fp2);
}
编译命令如下:
-Wall -g -IC:\MinGW\msys\1.0\local\include C:\MinGW\msys\1.0\home\Administrator\wav_test\ -o bin\Debug\wav_test.exe C:\MinGW\msys\1.0\local\lib\
与之相对应的是写音频文件。
sf_count_t sf_write_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
sf_count_t sf_write_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
sf_count_t sf_write_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
sf_count_t sf_write_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
sf_count_t sf_writef_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) ;
sf_count_t sf_writef_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) ;
sf_count_t sf_writef_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) ;
sf_count_t sf_writef_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ;
下面再给一个写音频文件的例子:
#include <>
#include <>
#include <>
#include <>
#include <>
#define SAMPLE_RATE 44100
#define SAMPLE_COUNT (SAMPLE_RATE * 4) /* 4 seconds */
#define AMPLITUDE (1.0 * 0x7F000000)
#define LEFT_FREQ (344.0 / SAMPLE_RATE)
#define RIGHT_FREQ (2 * 344.0 / SAMPLE_RATE)
int main (void)
{ SNDFILE *file ;
SF_INFO sfinfo ;
int k ;
int *buffer ;
if (! (buffer = malloc (2 * SAMPLE_COUNT * sizeof (int))))
{ printf ("Malloc failed.\n") ;
exit (0) ;
} ;
memset (&sfinfo, 0, sizeof (sfinfo)) ;
= SAMPLE_RATE ;
= SAMPLE_COUNT ;
= 2 ;
= (SF_FORMAT_WAV | SF_FORMAT_PCM_16) ;
if (! (file = sf_open ("", SFM_WRITE, &sfinfo)))
{ printf ("Error : Not able to open output file.\n") ;
return 1 ;
} ;
for (k = 0 ; k < SAMPLE_COUNT ; k++)
{ buffer [2 * k] = AMPLITUDE * sin (LEFT_FREQ * 2 * k * M_PI) ;
buffer [2 * k + 1] = AMPLITUDE * cos (RIGHT_FREQ * 2 * k * M_PI) ;
} ;
if (sf_write_int (file, buffer, * SAMPLE_COUNT) != * SAMPLE_COUNT)
puts (sf_strerror (file)) ;
sf_close (file) ;
return 0 ;
}
除此之外,还有些其他的函数。用法都比较简单,这里就不多介绍了。