Hide navigation sidebar
Hide table of contents sidebar
Back to top
Toggle table of contents sidebar

# HiFi quality multi-rate SRC

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 in a thread on it’s own to provide guaranteed performance. By placing the calls to SRC functions on separate threads, 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.

## Initialisation

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;

Note

`lib_src` expects `SSRC_N_CHANNELS`/`ASRC_N_CHANNELS` and `SSRC_N_IN_SAMPLES`/`ASRC_N_IN_SAMPLES` to be defined by the user. It will define `SSRC_STACK_LENGTH_MULT`/`ASRC_STACK_LENGTH_MULT` based on these values. `SSRC_N_CHANNELS`/`ASRC_N_CHANNELS` values are expected to match the `n_in_samples` parameter to the initialisation functions.

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

`ssrc_init()`

The initialisation call is similar for ASRC:

`asrc_init()`

The input block size must be a power of 2 and is function of the `n_in_samples` and `n_channels_per_instance` arguments - the total number of input samples expected for each processing call is `n_in_samples * n_channels_per_instance`.

## Processing

Following initialisation, 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.

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 takes the the input and output buffers and a reference to the control structure as parameters. In the case of ASRC a fractional frequency ratio parameter must also be supplied. The processing functions are as follows:

`ssrc_process()` `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)`. Where `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 sample clocks of the input and output stream are derived from the same master-clock and have an exact ratio of 147:160.

If the sample clocks are derived from separate master-clocks, different oscillators for example, or are not synchronous (for example are derived from each other using a fractional PLL), ASRC must be used rather than SSRC.

## 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.

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.

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.

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.60 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 utilisation

### 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 refer to the Performance characterisation section of this document.

### SSRC resource utilisation

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.

Tip

Table 1 is timed on xcore-200. When using xcore.ai the performance requirement is roughly halved due to VPU optimisations.

Table 1 SSRC Processor Usage per Channel (MHz) for xcore-200
|  | 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 refer to the Performance characterisation section of this document.

### ASRC latency / group delay

The ASRC algorithm runs a series of cascaded FIR filters to perform the rate conversion including a final adaptive filter to handle the varying rate change between the input and the output. The latency or group delay through the filter depends on the input rate and output rate and the input block size. Table 2 quantifies the amount of latency in milliseconds seen from the filtering. For input block sizes of greater than four please add 1000 * (INPUT_BLOCK_SIZE - 4) / INPUT_SAMPLE_RATE milliseconds to the numbers in the table.

Table 2 ASRC total filter delay by sample rate in milliseconds for input block size of four
|  | Output rate |  |  |  |  |
--- | --- | --- | --- | --- | --- | --- | ---
|  | 44.1 kHz | 48 kHz | 88.2 kHz | 96 kHz | 176.4 kHz | 192 kHz
Input rate | 44.1 kHz | 0.907 | 0.899 | 0.896 | 0.899 | 0.896 | 0.894
| 48 kHz | 0.910 | 0.833 | 0.830 | 0.823 | 0.825 | 0.823
| 88.2 kHz | 0.907 | 0.908 | 0.454 | 0.450 | 0.448 | 0.450
| 96 kHz | 0.931 | 0.833 | 0.455 | 0.417 | 0.415 | 0.411
| 176.4 kHz | 0.998 | 0.996 | 0.317 | 0.319 | 0.159 | 0.157
| 192 kHz | 0.998 | 0.917 | 0.295 | 0.292 | 0.148 | 0.146

#### ASRC resource utilisation

The ASRC algorithm 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 3 quantifies the MHz required for the first channel with adaptive coefficients calculation and Table 4 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.

Tip

Table 3 is timed on xcore-200. When using xcore.ai the performance requirement is roughly halved due to VPU optimisations.

Table 3 ASRC Processor Usage (MHz) for the First Channel in the ASRC Instance for xcore-200
|  | 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.

Tip

Table 4 is timed on xcore-200. When using xcore.ai the performance requirement is roughly halved due to VPU optimisations.

Table 4 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

## Implementation detail

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:

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:

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 5. 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 5 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

## 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.