1.介绍
在博客《alsa配置文件asound.conf》中介绍了alsa的相关插件。本篇中,将会介绍alsa plugin-resample插件相关方面的内容,提供一种resample的方法。libsamplerate是由Erik de Castro Lopo编写,相关的用法:
pcm.my_rate {
type rate
slave.pcm "hw"
converter "samplerate"
}
在alsa-plugin/rate/rate_samplerate.c中是调用resamplerate的接口:
int SND_PCM_RATE_PLUGIN_ENTRY(samplerate) (unsigned int version, void **objp,
snd_pcm_rate_ops_t *ops)
{
return pcm_src_open(version, objp, ops, SRC_SINC_FASTEST);
}
int SND_PCM_RATE_PLUGIN_ENTRY(samplerate_best) (unsigned int version, void **objp,
snd_pcm_rate_ops_t *ops)
{
return pcm_src_open(version, objp, ops, SRC_SINC_BEST_QUALITY);
}
int SND_PCM_RATE_PLUGIN_ENTRY(samplerate_medium) (unsigned int version, void **objp,
snd_pcm_rate_ops_t *ops)
{
return pcm_src_open(version, objp, ops, SRC_SINC_MEDIUM_QUALITY);
}
int SND_PCM_RATE_PLUGIN_ENTRY(samplerate_order) (unsigned int version, void **objp,
snd_pcm_rate_ops_t *ops)
{
return pcm_src_open(version, objp, ops, SRC_ZERO_ORDER_HOLD);
}
int SND_PCM_RATE_PLUGIN_ENTRY(samplerate_linear) (unsigned int version, void **objp,
snd_pcm_rate_ops_t *ops)
{
return pcm_src_open(version, objp, ops, SRC_LINEAR);
}
可以看到converter类型有如下:
- samplerate_best --> Use SRC_SINC_BEST_QUALITY
- samplerate_medium-->Use SRC_SINC_MEDIUM_QUALITY
- samplerate -->Use SRC_SINC_FASTEST
- samplerate_order--> Use SRC_ZERO_ORDER_HOLD
- samplerate_linear-->Use SRC_LINEAR
在笔者的应用中,调用的resample插件是SRC(secret rabbit code),其官网:http://www.mega-nerd.com/SRC/
2.SRC介绍
SRC能够进行任意的和时变的转换,从256倍的下采样到相同因子的上采样。这种情况下的任意性意味着输入和输出采样率之比可以是无理数。转换率也可以随时间而变化,以加速和减速速效果。
3.评价频率转换器(Sample Rate Converter)性能的三个指标
- 信噪比SNR(Signal-to-Noise Ratio)
- 带宽:3db带宽
- 转换速度
3.1几种src
- sndfile-resample
- Resample
- ResampleAudio
- soX
- Shibatch
- sr-convert
3.2Interpolator(插补器)
1:AccelerateDecelerateInterpolator 加速减速插补器(先慢后快再慢)
2:AccelerateInterpolator 加速插补器(先慢后快)
3:AnticipateInterpolator 向前插补器(先往回跑一点,再加速向前跑)
4:AnticipateOvershootInterpolator 向前向后插补器(先往回跑一点,再向后跑一点,再回到终点)
5:BounceInterpolator 反弹插补器(在动画结束的时候回弹几下,如果是竖直向下运动的话,就是玻璃球下掉弹几下的效果)
6:CycleInterpolator 循环插补器(按指定的路径以指定时间(或者是偏移量)的1/4、变速地执行一遍,再按指定的轨迹的相反反向走1/2的时间,再按指定的路径方向走完剩余的1/4的时间,最后回到原点。假如:默认是让a从原点往东跑100米。它会先往东跑100米,然后往西跑200米,再往东跑100米回到原点。可在代码中指定循环的次数)
7:DecelerateInterpolator 减速插补器(先快后慢)
8:LinearInterpolator 直线插补器(匀速)
9:OvershootInterpolator 超出插补器(向前跑直到越界一点后,再往回跑)
10:FastOutLinearInInterpolator MaterialDesign基于贝塞尔曲线的插补器 效果:依次 慢慢快
11:FastOutSlowInInterpolator MaterialDesign基于贝塞尔曲线的插补器 效果:依次 慢快慢
12:LinearOutSlowInInterpolator MaterialDesign基于贝塞尔曲线的插补器 效果:依次 快慢慢
4.libsamplerate的编译和应用
4.1Package Information
Package Information
-
Download (HTTP): http://www.mega-nerd.com/SRC/libsamplerate-0.1.9.tar.gz
-
Download MD5 sum: 2b78ae9fe63b36b9fbb6267fad93f259
-
Download size: 4.1 MB
-
Estimated disk space required: 17 MB
-
Estimated build time: 0.1 SBU
Libsamplerate Depenencies
Optional
libsndfile-1.0.28 and fftw-3.3.8 (for tests) and sndfile-tools-1.0.3
4.2应用
4.2.1sndfile-resample.c
/*
** Copyright (c) 2002-2016, Erik de Castro Lopo <[email protected]>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#if (HAVE_SNDFILE)
#include <samplerate.h>
#include <sndfile.h>
#define DEFAULT_CONVERTER SRC_SINC_MEDIUM_QUALITY
#define BUFFER_LEN 4096 /*-(1<<16)-*/
static void usage_exit (const char *progname) ;
static sf_count_t sample_rate_convert (SNDFILE *infile, SNDFILE *outfile, int converter, double src_ratio, int channels, double * gain, int normalize) ;
static double apply_gain (float * data, long frames, int channels, double max, double gain) ;
int
main (int argc, char *argv [])
{ SNDFILE *infile, *outfile = NULL ;
SF_INFO sfinfo ;
int normalize = 1 ;
sf_count_t count ;
double src_ratio = -1.0, gain = 1.0 ;
int new_sample_rate = -1, k, converter, max_speed = SF_FALSE ;
if (argc == 2 && strcmp (argv [1], "--version") == 0)
{ char buffer [64], *cptr ;
if ((cptr = strrchr (argv [0], '/')) != NULL)
argv [0] = cptr + 1 ;
if ((cptr = strrchr (argv [0], '\\')) != NULL)
argv [0] = cptr + 1 ;
sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer)) ;
printf ("%s (%s,%s)\n", argv [0], src_get_version (), buffer) ;
exit (0) ;
} ;
if (argc != 5 && argc != 7 && argc != 8)
usage_exit (argv [0]) ;
/* Set default converter. */
converter = DEFAULT_CONVERTER ;
for (k = 1 ; k < argc - 2 ; k++)
{ if (strcmp (argv [k], "--max-speed") == 0)
max_speed = SF_TRUE ;
else if (strcmp (argv [k], "--no-normalize") == 0)
normalize = 0 ;
else if (strcmp (argv [k], "-to") == 0)
{ k ++ ;
new_sample_rate = atoi (argv [k]) ;
}
else if (strcmp (argv [k], "-by") == 0)
{ k ++ ;
src_ratio = atof (argv [k]) ;
}
else if (strcmp (argv [k], "-c") == 0)
{ k ++ ;
converter = atoi (argv [k]) ;
}
else
usage_exit (argv [0]) ;
} ;
if (new_sample_rate <= 0 && src_ratio <= 0.0)
usage_exit (argv [0]) ;
if (src_get_name (converter) == NULL)
{ printf ("Error : bad converter number.\n") ;
usage_exit (argv [0]) ;
} ;
if (strcmp (argv [argc - 2], argv [argc - 1]) == 0)
{ printf ("Error : input and output file names are the same.\n") ;
exit (1) ;
} ;
if ((infile = sf_open (argv [argc - 2], SFM_READ, &sfinfo)) == NULL)
{ printf ("Error : Not able to open input file '%s'\n", argv [argc - 2]) ;
exit (1) ;
} ;
printf ("Input File : %s\n", argv [argc - 2]) ;
printf ("Sample Rate : %d\n", sfinfo.samplerate) ;
printf ("Input Frames : %ld\n\n", (long) sfinfo.frames) ;
if (new_sample_rate > 0)
{ src_ratio = (1.0 * new_sample_rate) / sfinfo.samplerate ;
sfinfo.samplerate = new_sample_rate ;
}
else if (src_is_valid_ratio (src_ratio))
sfinfo.samplerate = (int) floor (sfinfo.samplerate * src_ratio) ;
else
{ printf ("Not able to determine new sample rate. Exiting.\n") ;
sf_close (infile) ;
exit (1) ;
} ;
if (fabs (src_ratio - 1.0) < 1e-20)
{ printf ("Target samplerate and input samplerate are the same. Exiting.\n") ;
sf_close (infile) ;
exit (0) ;
} ;
printf ("SRC Ratio : %f\n", src_ratio) ;
printf ("Converter : %s\n\n", src_get_name (converter)) ;
if (src_is_valid_ratio (src_ratio) == 0)
{ printf ("Error : Sample rate change out of valid range.\n") ;
sf_close (infile) ;
exit (1) ;
} ;
/* Delete the output file length to zero if already exists. */
remove (argv [argc - 1]) ;
printf ("Output file : %s\n", argv [argc - 1]) ;
printf ("Sample Rate : %d\n", sfinfo.samplerate) ;
do
{ sf_close (outfile) ;
if ((outfile = sf_open (argv [argc - 1], SFM_WRITE, &sfinfo)) == NULL)
{ printf ("Error : Not able to open output file '%s'\n", argv [argc - 1]) ;
sf_close (infile) ;
exit (1) ;
} ;
if (max_speed)
{ /* This is mainly for the comparison program tests/src-evaluate.c */
sf_command (outfile, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_FALSE) ;
}
else
{ /* Update the file header after every write. */
sf_command (outfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_TRUE) ;
} ;
sf_command (outfile, SFC_SET_CLIPPING, NULL, SF_TRUE) ;
count = sample_rate_convert (infile, outfile, converter, src_ratio, sfinfo.channels, &gain, normalize) ;
}
while (count < 0) ;
printf ("Output Frames : %ld\n\n", (long) count) ;
sf_close (infile) ;
sf_close (outfile) ;
return 0 ;
} /* main */
/*==============================================================================
*/
static sf_count_t
sample_rate_convert (SNDFILE *infile, SNDFILE *outfile, int converter, double src_ratio, int channels, double * gain, int normalize)
{ static float input [BUFFER_LEN] ;
static float output [BUFFER_LEN] ;
SRC_STATE *src_state ;
SRC_DATA src_data ;
int error ;
double max = 0.0 ;
sf_count_t output_count = 0 ;
sf_seek (infile, 0, SEEK_SET) ;
sf_seek (outfile, 0, SEEK_SET) ;
/* Initialize the sample rate converter. */
if ((src_state = src_new (converter, channels, &error)) == NULL)
{ printf ("\n\nError : src_new() failed : %s.\n\n", src_strerror (error)) ;
exit (1) ;
} ;
src_data.end_of_input = 0 ; /* Set this later. */
/* Start with zero to force load in while loop. */
src_data.input_frames = 0 ;
src_data.data_in = input ;
src_data.src_ratio = src_ratio ;
src_data.data_out = output ;
src_data.output_frames = BUFFER_LEN /channels ;
while (1)
{
/* If the input buffer is empty, refill it. */
if (src_data.input_frames == 0)
{ src_data.input_frames = sf_readf_float (infile, input, BUFFER_LEN / channels) ;
src_data.data_in = input ;
/* The last read will not be a full buffer, so snd_of_input. */
if (src_data.input_frames < BUFFER_LEN / channels)
src_data.end_of_input = SF_TRUE ;
} ;
if ((error = src_process (src_state, &src_data)))
{ printf ("\nError : %s\n", src_strerror (error)) ;
exit (1) ;
} ;
/* Terminate if done. */
if (src_data.end_of_input && src_data.output_frames_gen == 0)
break ;
max = apply_gain (src_data.data_out, src_data.output_frames_gen, channels, max, *gain) ;
/* Write output. */
sf_writef_float (outfile, output, src_data.output_frames_gen) ;
output_count += src_data.output_frames_gen ;
src_data.data_in += src_data.input_frames_used * channels ;
src_data.input_frames -= src_data.input_frames_used ;
} ;
src_delete (src_state) ;
if (normalize && max > 1.0)
{ *gain = 1.0 / max ;
printf ("\nOutput has clipped. Restarting conversion to prevent clipping.\n\n") ;
return -1 ;
} ;
return output_count ;
} /* sample_rate_convert */
static double
apply_gain (float * data, long frames, int channels, double max, double gain)
{
long k ;
for (k = 0 ; k < frames * channels ; k++)
{ data [k] *= gain ;
if (fabs (data [k]) > max)
max = fabs (data [k]) ;
} ;
return max ;
} /* apply_gain */
static void
usage_exit (const char *progname)
{ char lsf_ver [128] ;
const char *cptr ;
int k ;
if ((cptr = strrchr (progname, '/')) != NULL)
progname = cptr + 1 ;
if ((cptr = strrchr (progname, '\\')) != NULL)
progname = cptr + 1 ;
sf_command (NULL, SFC_GET_LIB_VERSION, lsf_ver, sizeof (lsf_ver)) ;
printf ("\n"
" A Sample Rate Converter using libsndfile for file I/O and Secret \n"
" Rabbit Code (aka libsamplerate) for performing the conversion.\n"
" It works on any file format supported by libsndfile with any \n"
" number of channels (limited only by host memory).\n"
"\n"
" %s\n"
" %s\n"
"\n"
" Usage : \n"
" %s -to <new sample rate> [-c <number>] <input file> <output file>\n"
" %s -by <amount> [-c <number>] <input file> <output file>\n"
"\n", src_get_version (), lsf_ver, progname, progname) ;
puts (
" The optional -c argument allows the converter type to be chosen from\n"
" the following list :"
"\n"
) ;
for (k = 0 ; (cptr = src_get_name (k)) != NULL ; k++)
printf (" %d : %s%s\n", k, cptr, k == DEFAULT_CONVERTER ? " (default)" : "") ;
puts ("\n"
" The --no-normalize option disables clipping check and normalization.") ;
puts ("") ;
exit (1) ;
} /* usage_exit */
/*==============================================================================
*/
#else /* (HAVE_SNFILE == 0) */
/* Alternative main function when libsndfile is not available. */
int
main (void)
{ puts (
"\n"
"****************************************************************\n"
" This example program was compiled without libsndfile \n"
" (http://www.mega-nerd.com/libsndfile/).\n"
" It is therefore completely broken and non-functional.\n"
"****************************************************************\n"
"\n"
) ;
return 0 ;
} /* main */
#endif
4.2.2 config.h
/* src/config.h. Generated from config.h.in by configure. */
/* src/config.h.in. Generated from configure.ac by autoheader. */
/* Set to 1 if the compile is GNU GCC. */
#define COMPILER_IS_GCC 1
/* Target processor clips on negative float to int conversion. */
#define CPU_CLIPS_NEGATIVE 0
/* Target processor clips on positive float to int conversion. */
#define CPU_CLIPS_POSITIVE 0
/* Target processor is big endian. */
#define CPU_IS_BIG_ENDIAN 0
/* Target processor is little endian. */
#define CPU_IS_LITTLE_ENDIAN 1
/* Major version of GCC or 3 otherwise. */
#define GCC_MAJOR_VERSION 5
/* Define to 1 if you have the `alarm' function. */
#define HAVE_ALARM 1
/* Define to 1 if you have the <alsa/asoundlib.h> header file. */
#define HAVE_ALSA_ASOUNDLIB_H 1
/* Define to 1 if you have the `calloc' function. */
#define HAVE_CALLOC 1
/* Define to 1 if you have the `ceil' function. */
#define HAVE_CEIL 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Set to 1 if you have libfftw3. */
#define HAVE_FFTW3 0
/* Define to 1 if you have the `floor' function. */
#define HAVE_FLOOR 1
/* Define to 1 if you have the `fmod' function. */
#define HAVE_FMOD 1
/* Define to 1 if you have the `free' function. */
#define HAVE_FREE 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `m' library (-lm). */
#define HAVE_LIBM 1
/* Define if you have C99's lrint function. */
#define HAVE_LRINT 1
/* Define if you have C99's lrintf function. */
#define HAVE_LRINTF 1
/* Define to 1 if you have the `malloc' function. */
#define HAVE_MALLOC 1
/* Define to 1 if you have the `memcpy' function. */
#define HAVE_MEMCPY 1
/* Define to 1 if you have the `memmove' function. */
#define HAVE_MEMMOVE 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define if you have signal SIGALRM. */
#define HAVE_SIGALRM 1
/* Define to 1 if you have the `signal' function. */
#define HAVE_SIGNAL 1
/* Set to 1 if you have libsndfile. */
#define HAVE_SNDFILE 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/times.h> header file. */
#define HAVE_SYS_TIMES_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#define LT_OBJDIR ".libs/"
/* Set to 1 if compiling for Win32 */
#define OS_IS_WIN32 0
/* Name of package */
#define PACKAGE "libsamplerate"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "[email protected]"
/* Define to the full name of this package. */
#define PACKAGE_NAME "libsamplerate"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "libsamplerate 0.1.9"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libsamplerate"
/* Define to the home page for this package. */
#define PACKAGE_URL "http://www.mega-nerd.com/libsamplerate/"
/* Define to the version of this package. */
#define PACKAGE_VERSION "0.1.9"
/* The size of `double', as computed by sizeof. */
#define SIZEOF_DOUBLE 8
/* The size of `float', as computed by sizeof. */
#define SIZEOF_FLOAT 4
/* The size of `int', as computed by sizeof. */
#define SIZEOF_INT 4
/* The size of `long', as computed by sizeof. */
#define SIZEOF_LONG 8
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "0.1.9"
4.2.3 Makefile
TOPDIR=~/work/muno/
CUR_DIR=$(dirname $0)
ifdef mips
CROSS_COMPILE=mipsel-linux-
CFLAGS = -DCONFIG_MIPS
endif
ifdef arm
CROSS_COMPILE=arm-linux-androideabi-
CFLAGS = -DCONFIG_ARM
endif
ifdef armhf
CROSS_COMPILE=arm-linux-gnueabihf-
CFLAGS = -DCONFIG_ARM
endif
ifdef aarch64
CROSS_COMPILE=aarch64-linux-gnu-
CFLAGS = -DCONFIG_ARM
endif
ifdef all
CROSS_COMPILE=
LDFLAGS += -L/usr/local/lib -lsndfile -lsamplerate
endif
CC = ${CROSS_COMPILE}gcc
CXX = ${CROSS_COMPILE}g++
LIBS =
CFLAGS += -O2
CPPFLAGS += $(CFLAGS) -std=c++11
LDFLAGS += -lasound -lpthread -lsndfile -lsamplerate
INC=
OBJECTS = $(SOURCES:.cpp=.o)
PRJOBJS = sndfile-resample.o
OBJECTS_INC = config.h
PRJNAME = sndfile-resample
all: $(PRJNAME)
$(PRJNAME):$(OBJECTS) $(PRJOBJS) $(OBJECTS_INC)
$(CXX) -o $@ $(LDFLAGS) $(OBJECTS) $(PRJOBJS) $(LIBS)
.c.o:
$(CC) $(INC) $(CFLAGS) -c -o $@ $<
.cpp.o:
$(CXX) $(INC) $(CPPFLAGS) -c -o $@ $<
clean:
rm -rf $(OBJECTS) $(PRJOBJS) $(PRJNAME) *.so *.o
编译后的结果:
ccion@ubuntu:~/ccion/audio_tools/resample$ make armhf=1
arm-linux-gnueabihf-gcc -DCONFIG_ARM -O2 -c -o sndfile-resample.o sndfile-resample.c
arm-linux-gnueabihf-g++ -o sndfile-resample -lasound -lpthread -lsndfile -lsamplerate sndfile-resample.o
ccion@ubuntu:~/ccion/audio_tools/resample$ ls
config.h Makefile sndfile-resample sndfile-resample.c sndfile-resample.o
./sndfile-resample -h
A Sample Rate Converter using libsndfile for file I/O and Secret
Rabbit Code (aka libsamplerate) for performing the conversion.
It works on any file format supported by libsndfile with any
number of channels (limited only by host memory).
libsamplerate-0.1.9 (c) 2002-2008 Erik de Castro Lopo
libsndfile-1.0.28
Usage :
sndfile-resample -to <new sample rate> [-c <number>] <input file> <output file>
sndfile-resample -by <amount> [-c <number>] <input file> <output file>
The optional -c argument allows the converter type to be chosen from
the following list :
0 : Best Sinc Interpolator
1 : Medium Sinc Interpolator (default)
2 : Fastest Sinc Interpolator
3 : ZOH Interpolator
4 : Linear Interpolator
The --no-normalize option disables clipping check and normalization.
主要用法:
Usage :
sndfile-resample -to <new sample rate> [-c <number>] <input file> <output file>
sndfile-resample -by <amount> [-c <number>] <input file> <output file>
例子:
./sndfile-resample -to 44100 48000-2ch_32.wav 44100.wav
Input File : 48000-2ch_32.wav
Sample Rate : 48000
Input Frames : 1228290
SRC Ratio : 0.918750
Converter : Medium Sinc Interpolator
Output file : 44100.wav
Sample Rate : 44100
Output has clipped. Restarting conversion to prevent clipping.
Output Frames : 1128492