ALSA resample插件-SRC

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

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

猜你喜欢

转载自blog.csdn.net/weixin_41965270/article/details/81272732