Multi-rate HiFi Sample Rate Conversion#

Usage#

Both SSRC and ASRC functions are accessed via standard function calls, making them accessible from C or XC. Both SSRC and ASRC functions are passed an external state structure which provides re-entrancy. The functions may be called in-line with other signal processing or placed on a logical core within its own task to provide guaranteed performance. By placing the calls to SRC functions on separate logical cores, multiple instances can be processed concurrently.

The API is designed to be simple and intuitive with just two public functions per sample rate converter type.

Initialization#

All public ASRC and SSRC functions are declared within the src.h header:

#include "src.h"

There are a number of arrays of structures that must be declared from the application which contain the buffers between the FIR stages, state and adapted coefficients (ASRC only). There must be one element of each structure declared for each channel handled by the SRC instance. The structures are then all linked into a single control structure, allowing a single reference to be passed each time a call to the SRC is made.

For SSRC, the following state structures are required:

//State of SSRC module
ssrc_state_t     ssrc_state[SSRC_CHANNELS_PER_INSTANCE];
//Buffers between processing stages
int              ssrc_stack[SSRC_CHANNELS_PER_INSTANCE][SSRC_STACK_LENGTH_MULT * SSRC_N_IN_SAMPLES];
//SSRC Control structure
ssrc_ctrl_t      ssrc_ctrl[SSRC_CHANNELS_PER_INSTANCE];

For ASRC, the following state structures are required. Note that only one instance of the filter coefficients need be declared because these are shared amongst channels within the instance:

//ASRC state
asrc_state_t       asrc_state[ASRC_CHANNELS_PER_INSTANCE];
int                asrc_stack[ASRC_CHANNELS_PER_INSTANCE][ASRC_STACK_LENGTH_MULT * ASRC_N_IN_SAMPLES];
//Control structure
asrc_ctrl_t        asrc_ctrl[ASRC_CHANNELS_PER_INSTANCE];
//Adaptive filter coefficients
asrc_adfir_coefs_t asrc_adfir_coefs;

There is an initialization call which sets up the variables within the structures associated with the SRC instance and clears the inter-stage buffers. Initialization ensures the correct selection, ordering and configuration of the filtering stages, be they decimators, interpolators or pass-through blocks. This initialization call contains arguments defining selected input and output nominal sample rates as well as settings for the sample rate converter:

ssrc_init()

The initialization call is the same for ASRC:

asrc_init()

The input block size must be a power of 2 and is set by the n_in_samples argument. In the case where more than one channel is to be processed per SRC instance, the total number of input samples expected for each processing call is n_in_samples * n_channels_per_instance.

Please ensure the settings within src_config.h are correct for the application. The default settings allow for any input/output ratio between 44.1 kHz and 192 kHz.

Processing#

Following initialization, the processing API is called for each block of input samples which will then produce a block of output samples as shown in Fig. 1.

../../_images/src_proc.pdf

Fig. 1 SRC Operation#

The logic is designed so that the final filtering stage always receives a sample to process. The sample rate converters have been designed to handle a maximum decimation of factor four from the first two stages. This architecture requires a minimum input block size of 4 to operate.

The processing function call is passed the input and output buffers and a reference to the control structure:

ssrc_process()

In the case of ASRC a fractional frequency ratio argument is also supplied:

asrc_process()

The SRC processing call always returns a whole number of output samples produced by the sample rate conversion. Depending on the sample ratios selected, this number may be between zero and (n_in_samples * n_channels_per_instance * SRC_N_OUT_IN_RATIO_MAX). SRC_N_OUT_IN_RATIO_MAX is the maximum number of output samples for a single input sample. For example, if the input frequency is 44.1 kHz and the output rate is 192 kHz then a sample rate conversion of one sample input may produce up to 5 output samples.

The fractional number of samples produced to be carried to the next operation is stored inside the control structure, and additional whole samples are added during subsequent calls to the sample rate converter as necessary.

For example, a sample rate conversion from 44.1 kHz to 48 kHz with a input block size of 4 will produce a 4 sample result with a 5 sample result approximately every third call.

Each SRC processing call returns the integer number of samples produced during the sample rate conversion.

The SSRC is synchronous in nature and assumes that the ratio is equal to the nominal sample rate ratio. For example, to convert from 44.1 kHz to 48 kHz, it is assumed that the word clocks of the input and output stream are derived from the same master clock and have an exact ratio of 147:160.

If the word clocks are derived from separate oscillators, or are not synchronous (for example are derived from each other using a fractional PLL), the ASRC must be used.

Buffer Formats#

The format of the sample buffers sent and received from each SRC instance is time domain interleaved. How this looks in practice depends on the number of channels and SRC instances. Three examples are shown below, each showing n_in_samples = 4. The ordering of sample indicies is 0 representing the oldest sample and n - 1, where n is the buffer size, representing the newest sample.

In the case where two channels are handled by a single SRC instance Fig. 2 shows that the samples are interleaved into a single buffer of size 8.

../../_images/stereo_single_instance.pdf

Fig. 2 Buffer Format for Single Stereo SRC instance#

Where a single audio channel is mapped to a single instance, the buffers are simply an array of samples starting with the oldest sample and ending with the newest sample as shown in Fig. 3.

../../_images/stereo_dual_instance.pdf

Fig. 3 Buffer Format for Dual Mono SRC instances#

In the case where four channels are processed by two instances, channels 0 & 1 are processed by SRC instance 0 and channels 2 & 3 are processed by SRC instance 1 as shown in Fig. 4. For each instance, four pairs of samples are passed into the SRC processing function and n pairs of samples are returned, where n depends on the input and output sample rate ratio.

../../_images/quad_dual_instance.pdf

Fig. 4 Buffer Format for Dual Stereo SRC instances (4 channels total)#

In addition to the above arguments the asrc_process() call also requires an unsigned Q4.28 fixed point ratio value specifying the actual input to output ratio for the next calculated block of samples. This allows the input and output rates to be fully asynchronous by allowing rate changes on each call to the ASRC. The converter dynamically computes coefficients using a spline interpolation within the last filter stage. It is up to the callee to maintain the input and output sample rate ratio difference.

Further details about these function arguments are contained here: SSRC API.

Performance and resource utilization#

Audio Performance#

The performance of the SSRC library is as follows:

  • THD+N (1 kHz, 0 dBFs): better than -130 dB, depending on the accuracy of the ratio estimation

  • SNR: 140 dB (or better). Note that when dither is not used, SNR is infinite as output from a zero input signal is zero.

To see frequency plots illustrating the noise floor with respect to a sample rate converted tone please refer to the Performance Characterisation for SSRC ASRC DS3 and OS3 section of this document.

SSRC Resource utilization#

The SSRC algorithm runs a series of cascaded FIR filters to perform the rate conversion. This includes interpolation, decimation and bandwidth limiting filters with a final polyphase FIR filter. The last stage supports the rational rate change of 147:160 or 160:147 allowing conversion between 44.1 kHz family of sample rates to the 48 kHz family of sample rates.

Tip

Table 1 shows the worst case MHz consumption at a given sample rate using the minimum block size of 4 input samples with dithering disabled. The MHz requirement can be reduced by around 8-12%, depending on sample rate, by increasing the input block size to 16. It is not usefully reduced by increasing block size beyond 16.

Table 1 SSRC Processor Usage per Channel (MHz)#

Output rate

44.1 kHz

48 kHz

88.2 kHz

96 kHz

176.4 kHz

192 kHz

Input rate

44.1 kHz

1 MHz

23 MHz

16 MHz

26 MHz

26 MHz

46 MHz

48 kHz

26 MHz

1 MHz

28 MHz

17 MHz

48 MHz

29 MHz

88.2 kHz

18 MHz

43 MHz

1 MHz

46 MHz

32 MHz

53 MHz

96 kHz

48 MHz

20 MHz

52 MHz

2 MHz

56 MHz

35 MHz

176.4 kHz

33 MHz

61 MHz

37 MHz

67 MHz

3 MHz

76 MHz

192 kHz

66 MHz

36 MHz

70 MHz

40 MHz

80 MHz

4 MHz

ASRC Performance#

The performance of the ASRC library is as follows:

  • THD+N: (1 kHz, 0 dBFs): better than -130 dB

  • SNR: 135 dB (or better). Note that when dither is not used, SNR is infinite as output from a zero input signal is zero.

To see frequency plots illustrating the noise floor with respect to a sample rate converted tone please refer to the Performance Characterisation for SSRC ASRC DS3 and OS3 section of this document.

ASRC Resource utilization#

The ASRC algorithm also runs a series of cascaded FIR filters to perform the rate conversion. The final filter is different because it uses adaptive coefficients to handle the varying rate change between the input and the output. The adaptive coefficients must be computed for each output sample period, but can be shared amongst all channels within the ASRC instance. Consequently, the MHz usage of the ASRC is expressed as two tables; Table 2 quantifies the MHz required for the first channel with adaptive coefficients calculation and Table 3 specifies the MHz required for filtering of each additional channel processed by the ASRC instance.

Tip

The below tables show the worst case MHz consumption per sample, using the minimum block size of 4 input samples. The MHz requirement can be reduced by around 8-12% by increasing the input block size to 16.

Tip

Typically some performance headroom is needed for buffering (especially if the system is sample orientated rather than block orientated) and inter-task communication.

Table 2 ASRC Processor Usage ( MHz) for the First Channel in the ASRC Instance#

Output rate

44.1 kHz

48 kHz

88.2 kHz

96 kHz

176.4 kHz

192 kHz

Input rate

44.1 kHz

29 MHz

30 MHz

40 MHz

42 MHz

62 MHz

66 MHz

48 kHz

33 MHz

32 MHz

42 MHz

43 MHz

63 MHz

66 MHz

88.2 kHz

47 MHz

50 MHz

58 MHz

61 MHz

80 MHz

85 MHz

96 kHz

55 MHz

51 MHz

67 MHz

64 MHz

84 MHz

87 MHz

176.4 kHz

60 MHz

66 MHz

76 MHz

81 MHz

105 MHz

106 MHz

192 kHz

69 MHz

66 MHz

82 MHz

82 MHz

109 MHz

115 MHz

Caution

Configurations requiring more than 100 MHz may not be able run in real time on a single logical core. The performance limit for a single core on a 500 MHz xCORE-200 device is 100 MHz (500/5) however an XCORE-AI device running at 600 MHz can provide 120 MHz logical cores.

Table 3 ASRC Processor Usage (MHz) for Subsequent Channels in the ASRC Instance#

Output rate

44.1 kHz

48 kHz

88.2 kHz

96 kHz

176.4 kHz

192 kHz

Input rate

44.1 kHz

28 MHz

28 MHz

32 MHz

30 MHz

40 MHz

40 MHz

48 kHz

39 MHz

31 MHz

33 MHz

36 MHz

40 MHz

45 MHz

88.2 kHz

51 MHz

49 MHz

57 MHz

55 MHz

65 MHz

60 MHz

96 kHz

51 MHz

56 MHz

57 MHz

62 MHz

66 MHz

71 MHz

176.4 kHz

60 MHz

66 MHz

76 MHz

79 MHz

92 MHz

91 MHz

192 kHz

69 MHz

66 MHz

76 MHz

82 MHz

90 MHz

100 MHz

SRC Implementation#

The SSRC and ASRC implementations are closely related to each other and share the majority of the system building blocks. The key difference between them is that SSRC uses fixed polyphase 160:147 and 147:160 final rate change filters whereas the ASRC uses an adaptive polyphase filter. The ASRC adaptive polyphase coefficients are computed for every sample using second order spline based interpolation.

SSRC Structure#

The SSRC algorithm is based on three cascaded FIR filter stages (F1, F2 and F3). These stages are configured differently depending on rate change and only part of them is used in certain cases. Fig. 5 shows an overall view of the SSRC algorithm:

../../_images/ssrc_structure.pdf

Fig. 5 SSRC Algorithm Structure#

The SSRC algorithm is implemented as a two stage structure:

  • The bandwidth control stage which includes filters F1 and F2 is responsible for limiting the bandwidth of the input signal and for providing integer rate Sample Rate Conversion. It is also used for signal conditioning in the case of rational non-integer Sample Rate Conversion.

  • The polyphase filter stage which converts between the 44.1 kHz and the 48 kHz families of sample rates.

ASRC Structure#

Similar to the SSRC, the ASRC algorithm is based on three cascaded FIR filters (F1, F2 and F3). These are configured differently depending on rate change and F2 is not used in certain rate changes. Fig. 6 shows an overall view of the ASRC algorithm:

../../_images/asrc_structure.pdf

Fig. 6 ASRC Algorithm Structure#

The ASRC algorithm is implemented as a two stage structure:

  • The bandwidth control stage includes filters F1 and F2 which are responsible for limiting the bandwidth of the input signal and for providing integer rate sample rate conversion to condition the input signal for the adaptive polyphase stage (F3).

  • The polyphase filter stage consists of the adaptive polyphase filter F3, which effectively provides the asynchronous connection between the input and output clock domains.

SRC Filter list#

A complete list of the filters supported by the SRC library, both SSRC and ASRC, is shown in Table 4. The filters are implemented in C within the FilterDefs.c function and the coefficients can be found in the /FilterData folder. The particular combination of filters cascaded together for a given sample rate change is specified in ssrc.c and asrc.c.

Table 4 SRC Filter Specifications#

Filter

Fs (norm)

Passband

Stopband

Ripple

Attenuation

Taps

Notes

BL

2

0.454

0.546

0.01 dB

155 dB

144

Down-sampler by two, steep

BL9644

2

0.417

0.501

0.01 dB

155 dB

160

Low-pass filter, steep for 96 to 44.1

BL8848

2

0.494

0.594

0.01 dB

155 dB

144

Low-pass, steep for 88.2 to 48

BLF

2

0.41

0.546

0.01 dB

155 dB

96

Low-pass at half band

BL19288

2

0.365

0.501

0.01 dB

155 dB

96

Low pass, steep for 192 to 88.2

BL17696

2

0.455

0.594

0.01 dB

155 dB

96

Low-pass, steep for 176.4 to 96

UP

2

0.454

0.546

0.01 dB

155 dB

144

Over sample by 2, steep

UP4844

2

0.417

0.501

0.01 dB

155 dB

160

Over sample by 2, steep for 48 to 44.1

UPF

2

0.41

0.546

0.01 dB

155 dB

96

Over sample by 2, steep for 176.4 to 192

UP192176

2

0.365

0.501

0.01 dB

155 dB

96

Over sample by 2, steep for 192 to 176.4

DS

4

0.57

1.39

0.01 dB

160 dB

32

Down sample by 2, relaxed

OS

2

0.57

1.39

0.01 dB

160 dB

32

Over sample by 2, relaxed

HS294

284

0.55

1.39

0.01 dB

155 dB

2352

Polyphase 147/160 rate change

HS320

320

0.55

1.40

0.01 dB

151 dB

2560

Polyphase 160/147 rate change

ADFIR

256

0.45

1.45

0.012 dB

170 dB

1920

Adaptive polyphase prototype filter

SRC File Structure and Overview#

All source files for the SSRC and ASRC are located within the multirate_hifi subdirectory.

  • src_mrhf_ssrc_wrapper.c / src_mrhf_ssrc_wrapper.h

    These wrapper files provide a simplified public API to the SSRC initialization and processing functions.

  • src_mrhf_asrc_wrapper.c / src_mrhf_asrc_wrapper.h

    These wrapper files provide a simplified public API to the ASRC initialization and processing functions.

  • src_mrhf_ssrc.c / src_mrhf_ssrc.h

    These files contain the core of the SSRC algorithm. They set up the correct filtering chains depending on rate change and apply in the processing calls. The table sFiltersIDs declared in SSRC.c contains definitions of the filter chains for all supported rate changes. The files also integrate the code for the optional dithering function.

  • src_mrhf_asrc.c / src_mrhf_asrc.h

    These files contain the core of the ASRC algorithm. They setup the correct filtering chains depending on rate change and apply them for the corresponding processing calls. Note that filters F1, F2 and dithering are implemented using a block based approach similar to SSRC. The adaptive polyphase filter (ADFIR) is implemented on a sample by sample basis. These files also contain functions to compute the adaptive polyphase filter coefficients.

  • src_mrhf_fir.c / src_mrhf_fir.h

    These files provide Finite Impulse Response (FIR) filtering setup, with calls to the assembler-optimized inner loops. They provide functions for handling down-sampling by 2, synchronous or over-sampling by 2 FIRs. They also provides functions for handling polyphase filters used for rational ratio rate change in the SSRC and adaptive FIR filters used in the asynchronous section of the ASRC.

  • src_mrhf_filter_defs.c / src_mrhf_filter_defs.h

    These files define the size and coefficient sources for all the filters used by the SRC algorithms.

  • /FilterData directory (various files)

    This directory contains the pre-computed coefficients for all of the fixed FIR filters. The numbers are stored as signed Q1.31 format and are directly included in the source of FilterDefs.c. Both the .dat files used by the C compiler and the .sfp ScopeFIR (http://iowegian.com/scopefir/) design source files, used to originally create the filters, are included.

  • src_mrhf_fir_inner_loop_asm.S / src_mrhf_fir_inner_loop_asm.h

    Inner loop for the standard FIR function optimized for double-word load and store, 32 bit * 32 bit -> 64 bit MACC and saturation instructions. Even and odd sample long word alignment versions are provided.

  • src_mrhf_fir_os_inner_loop_asm.S / scr_mrhf_fir_os_inner_loop_asm.h

    Inner loop for the oversampling FIR function optimized for double-word load and store, 32 bit * 32 bit -> 64 bit MACC and saturation instructions. Both (long word) even and odd sample input versions are provided.

  • src_mrhf_spline_coeff_gen_inner_loop_asm.S / src_mrhf_spline_coeff_gen_inner_loop_asm.h

    Inner loop for generating the spline interpolated coefficients. This assembler function is optimized for double-word load and store, 32 bit * 32 bit -> 64 bit MACC and saturation instructions.

  • src_mrhf_adfir_inner_loop_asm.S / src_mrhf_adfir_inner_loop_asm.h

    Inner loop for the adaptive FIR function using the previously computed spline interpolated coefficients. It is optimized for double-word load and store, 32 bit * 32 bit -> 64 bit MACC and saturation instructions. Both (long word) even and odd sample input versions are provided.

  • src_mrhf_int_arithmetic.c / src_mrhf_int_arithmetic.h

    These files contain simulation implementations of XMOS ISA specific assembler instructions. These are only used for dithering functions, and may be eliminated during future optimizations.

SSRC API#

void ssrc_init(const fs_code_t sr_in, const fs_code_t sr_out, ssrc_ctrl_t ssrc_ctrl[], const unsigned n_channels_per_instance, const unsigned n_in_samples, const dither_flag_t dither_on_off)#

initializes synchronous sample rate conversion instance.

Parameters:
  • sr_in – Nominal sample rate code of input stream

  • sr_out – Nominal sample rate code of output stream

  • ssrc_ctrl – Reference to array of SSRC control stuctures

  • n_channels_per_instance – Number of channels handled by this instance of SSRC

  • n_in_samples – Number of input samples per SSRC call

  • dither_on_off – Dither to 24b on/off

unsigned ssrc_process(int in_buff[], int out_buff[], ssrc_ctrl_t ssrc_ctrl[])#

Perform synchronous sample rate conversion processing on block of input samples using previously initialized settings.

Parameters:
  • in_buff – Reference to input sample buffer array

  • out_buff – Reference to output sample buffer array

  • ssrc_ctrl – Reference to array of SSRC control stuctures

Returns:

The number of output samples produced by the SRC operation

ASRC API#

uint64_t asrc_init(const fs_code_t sr_in, const fs_code_t sr_out, asrc_ctrl_t asrc_ctrl[], const unsigned n_channels_per_instance, const unsigned n_in_samples, const dither_flag_t dither_on_off)#

initializes asynchronous sample rate conversion instance.

Parameters:
  • sr_in – Nominal sample rate code of input stream

  • sr_out – Nominal sample rate code of output stream

  • asrc_ctrl – Reference to array of ASRC control structures

  • n_channels_per_instance – Number of channels handled by this instance of SSRC

  • n_in_samples – Number of input samples per SSRC call

  • dither_on_off – Dither to 24b on/off

Returns:

The nominal sample rate ratio of in to out in Q4.60 format

unsigned asrc_process(int in_buff[], int out_buff[], uint64_t fs_ratio, asrc_ctrl_t asrc_ctrl[])#

Perform asynchronous sample rate conversion processing on block of input samples using previously initialized settings.

Parameters:
  • in_buff – Reference to input sample buffer array

  • out_buff – Reference to output sample buffer array

  • fs_ratio – Fixed point ratio of in/out sample rates in Q4.60 format

  • asrc_ctrl – Reference to array of ASRC control structures

Returns:

The number of output samples produced by the SRC operation.