Main Content

Multistage rate conversion is an approach that splits rate conversion into several stages. For example, instead of decimation by a factor of 18, decimate by factor of 3, followed by another decimation by 3, and and then by a factor of 2. Using multiple stages reduces the computational complexity of filtered rate conversion. Furthermore, if one already has the converter units for the different prime factors, they can be used as building blocks for higher rates. This example will demonstrate multistage rate conversion designs.

Consider decimation system of rate *M*=8. One can implement such a system in two ways:

A single decimator of rate

*M*=8.A cascade of three half-rate decimators (

`M=2`

)

While the two alternatives effectively have the same decimation factor, they differ in their numerical complexites. Evaluate the cost of implementing a multistage decimator using the `cost`

function, and compare it to the cost of implementing a single-stage decimator.

bDecim2 = designMultirateFIR(1,2); firDecim2_1 = dsp.FIRDecimator(2,bDecim2); firDecim2_2 = dsp.FIRDecimator(2,bDecim2); firDecim2_3 = dsp.FIRDecimator(2,bDecim2); firDecim2cascade = dsp.FilterCascade(firDecim2_1,firDecim2_2,firDecim2_3); cost2cascade = cost(firDecim2cascade) bDecim8 = designMultirateFIR(1,8); firDecim8 = dsp.FIRDecimator(8,bDecim8); cost8 = cost(firDecim8)

cost2cascade = struct with fields: NumCoefficients: 75 NumStates: 138 MultiplicationsPerInputSample: 21.8750 AdditionsPerInputSample: 21 cost8 = struct with fields: NumCoefficients: 169 NumStates: 184 MultiplicationsPerInputSample: 21.1250 AdditionsPerInputSample: 21

Cascading three decimators of rate *M*=2 consumes less memory (states and coefficients) compared to a single-stage decimator of *M*=8, making the multistage converter more memory efficient. The arithmetic load (operations per sample) of the single-stage and multistage implementation are equivalent. Note that the number of samples drops by half after each decimation stage. In conclusion, it is often better to split the decimation into multiple stages (given that the rate change factor is not a prime number, of course).

There is usually more than one way to factor a (non-prime) conversion rate, and even more degrees of freedom multistage design. The DSP System Toolbox (TM) offers several tools to simplify the design process. We will examine two of them in what follows.

`designMultistageDecimator`

and `designMultistageInterpolator`

functionsThe `designMultistageInterpolator`

and `designMultistageDecimator`

functions automatically determine an optimal configuation, that includes determining the number of stages along with their arrangements, lowpass parameters, etc. The result is a filter cascade system object, which encapsualtes all the stages. To illustrate, let us design a decimator of rate *M*=12.

M = 12; fcDecMulti = designMultistageDecimator(M); info(fcDecMulti)

ans = 'Discrete-Time Filter Cascade ---------------------------- Number of stages: 3 Stage1: dsp.FIRDecimator ------- Discrete-Time FIR Multirate Filter (real) ----------------------------------------- Filter Structure : Direct-Form FIR Polyphase Decimator Decimation Factor : 2 Polyphase Length : 6 Filter Length : 11 Stable : Yes Linear Phase : Yes (Type 1) Arithmetic : double Stage2: dsp.FIRDecimator ------- Discrete-Time FIR Multirate Filter (real) ----------------------------------------- Filter Structure : Direct-Form FIR Polyphase Decimator Decimation Factor : 2 Polyphase Length : 8 Filter Length : 15 Stable : Yes Linear Phase : Yes (Type 1) Arithmetic : double Stage3: dsp.FIRDecimator ------- Discrete-Time FIR Multirate Filter (real) ----------------------------------------- Filter Structure : Direct-Form FIR Polyphase Decimator Decimation Factor : 3 Polyphase Length : 27 Filter Length : 79 Stable : Yes Linear Phase : Yes (Type 1) Arithmetic : double '

This particular design has 3 stages (), where the lowpass of the last stage is the longest.

Repeat the design with a single-stage.

```
fcDecSingle = designMultistageDecimator(M,'NumStages',1);
info(fcDecSingle)
```

ans = 'Discrete-Time Filter Cascade ---------------------------- Number of stages: 1 Stage1: dsp.FIRDecimator ------- Discrete-Time FIR Multirate Filter (real) ----------------------------------------- Filter Structure : Direct-Form FIR Polyphase Decimator Decimation Factor : 12 Polyphase Length : 26 Filter Length : 307 Stable : Yes Linear Phase : Yes (Type 1) Arithmetic : double '

Compare the cost of the two implementations. Obivously, the multistage approach is more efficient.

costMulti = cost(fcDecMulti) costSingle = cost(fcDecSingle)

costMulti = struct with fields: NumCoefficients: 69 NumStates: 102 MultiplicationsPerInputSample: 10.1667 AdditionsPerInputSample: 9.3333 costSingle = struct with fields: NumCoefficients: 283 NumStates: 300 MultiplicationsPerInputSample: 23.5833 AdditionsPerInputSample: 23.5000

Now, let us compare the combined frequency response of the decimation filters. While the filters of the two implementations differ in the stopband, the passband and transition band are nearly identical.

hfv = fvtool(fcDecMulti, fcDecSingle); legend(hfv,'Multistage Combined Response', 'Single-Stage Response');

The same methodology applies for `designMultistageInterpolator`

. Create two interpolators (single-stage and multistage) and compare their outputs. Note that the outputs are nearly identical, except a slightly longer latency of the multistage interpolator.

n = (1:20)'; x = (abs(n-5)<=5).*(5-abs(n-5)); L = 12; fcIntrMulti = designMultistageInterpolator(L); fcIntrSingle = designMultistageInterpolator(L,'NumStages',1); xInterpSingle = fcIntrSingle(x); xInterpMulti = fcIntrMulti(x); release(fcIntrMulti); release(fcIntrSingle); subplot(3,1,1); stem(x); xlim([1,20]); title('Input Sequence'); subplot(3,1,2); stem(xInterpSingle); title('Single-Stage Interpolated') subplot(3,1,3); stem(xInterpMulti); title('Multistage Interpolated')

`dsp.SampleRateConverter`

System ObjectThe `dsp.SampleRateConverter`

system object provides a convenient interface for arbitrary rate conversion, combining interpolation and decimation as needed.

src = dsp.SampleRateConverter('InputSampleRate',18,'OutputSampleRate',16,'Bandwidth',13); info(src)

ans = 'Overall Interpolation Factor : 8 Overall Decimation Factor : 9 Number of Filters : 1 Multiplications per Input Sample: 24.333333 Number of Coefficients : 219 Filters: Filter 1: dsp.FIRRateConverter - Interpolation Factor: 8 - Decimation Factor : 9 '

The different stages can be extracted with the `getFilters`

function:

firs = getFilters(src)

firs = dsp.FilterCascade with properties: Stage1: [1x1 dsp.FIRRateConverter]

We can also specify absolute frequencies (rather than ratios). For example, the `dsp.SampleRateConverter`

object can convert audio data sample rate from 48 kHz to 44.1 kHz.

src = dsp.SampleRateConverter('InputSampleRate',48000,'OutputSampleRate',44100); [L,M] = getRateChangeFactors(src); firs = getFilters(src); reader = dsp.AudioFileReader('audio48kHz.wav','SamplesPerFrame',4*M); x = reader(); xr = src(x); % Obtain the rate conversion FIR b = firs.Stage1.Numerator; % Calculate the resampling delay i0 = floor(length(b)/2)/L; figure; hold on; stem((1:length(x))+i0,x); stem(linspace(1,length(x),length(xr)),xr,'r'); hold off; legend('Input Audio','Resampled Audio'); xlim([150,200]) release(reader);

Conversion ratios like (used in the previous section) requires a large upsampling and downsampling ratios, as even its reduced form is . The filters required for such a conversion are fairly long, introducing a significant latency in addition to the memory and computational load.

cost(src)

ans = struct with fields: NumCoefficients: 8587 NumStates: 58 MultiplicationsPerInputSample: 53.6688 AdditionsPerInputSample: 52.7500

We can mitigate the costly conversion by approximating the rate conversion factor. For example,

The deviation of 100Hz is small, only 0.23 % of the absolute frequencies. The `dsp.SampleRateConverter`

can automatically approximate the rate conversion factor by allowing the output frequency to be perturbed. The perturbation tolerance is specified through the `'OutputRateTolerance'`

property. The default tolerance is 0, meaning, no slack. In other words, slack means the deviation from the specified output rate value. Clearly, the approximated rate conversion has much smaller computational cost, and suffices for many applications, such as standard definition audio processing.

src_approx = dsp.SampleRateConverter('InputSampleRate',48000,... 'OutputSampleRate',44100,'Bandwidth',13,... 'OutputRateTolerance',0.01); [L_approx,M_approx] = getRateChangeFactors(src_approx) cost(src_approx)

L_approx = 11 M_approx = 12 ans = struct with fields: NumCoefficients: 61 NumStates: 5 MultiplicationsPerInputSample: 5.0833 AdditionsPerInputSample: 4.1667