This is a simple sample rate converter (source code for RE). It can only upsample or downsample by 2^n ratio, that's faster and just what is needed most of the time.
Although I didn't test it thoroughly, it should work.
EDIT: updated the attachment.
A simple sample rate converter

 RE Developer
 Posts: 2185
 Joined: 22 Jan 2015
 Location: 55°09'24.5"N 37°27'41.4"E
You do not have the required permissions to view the files attached to this post.

 RE Developer
 Posts: 2185
 Joined: 22 Jan 2015
 Location: 55°09'24.5"N 37°27'41.4"E
Sorry, there was a bug. I updated it in the first post.

 RE Developer
 Posts: 2185
 Joined: 22 Jan 2015
 Location: 55°09'24.5"N 37°27'41.4"E
HOW IT WORKS
All the work is done by a Brickwall Filter, which filters out the frequency content that can't be represented in lower sample rate. Here is an example of such filter response (suppressing all frequencies above 24kHz):
Downsampling
Let's say we have a waveform at 96k sample rate that we would like to downsample to 48k:
We apply the brickwall filter:
Then take every second sample at 48k sample rate (we may choose even or odd ones, the resulting waveform is the same):
Upsampling
Let's upsample the result we got above (@48k) back to 96k. We interleave the stream with zero samples:
Then apply the brickwall filter:
The result is the upsampled waveform.
All the work is done by a Brickwall Filter, which filters out the frequency content that can't be represented in lower sample rate. Here is an example of such filter response (suppressing all frequencies above 24kHz):
Downsampling
Let's say we have a waveform at 96k sample rate that we would like to downsample to 48k:
We apply the brickwall filter:
Then take every second sample at 48k sample rate (we may choose even or odd ones, the resulting waveform is the same):
Upsampling
Let's upsample the result we got above (@48k) back to 96k. We interleave the stream with zero samples:
Then apply the brickwall filter:
The result is the upsampled waveform.
Last edited by orthodox on 31 Mar 2022, edited 3 times in total.

 RE Developer
 Posts: 394
 Joined: 21 Apr 2016
 Location: Las Vegas
Thank you for the explanation!

 RE Developer
 Posts: 2185
 Joined: 22 Jan 2015
 Location: 55°09'24.5"N 37°27'41.4"E
HOW IT WORKS 2 (on filters)
FIR filters
A FIR (Finite Impulse Response or stateless) filter is an array/vector of coefficients. An Nth order filter has N+1 coefficients. It is applied by calculating the next output sample as the dot product of the coefficients vector and the same number of last input samples:
Symmetric FIR filters (where the coefficients remain the same in reverse order) have some useful properties, one of them being linear phase response, which means the filter effectively has zero phase shift across the entire frequency range, it just delays the filtered signal in time by N/2 samples, which can be compensated.
Filters can have even or odd order. Evenorder filters have a central element (which matches the position in time of the output sample) and are often used for oversampling as interpolating filters (so that the existing input samples stay intact and only the "missing" samples are calculated).
This implementation uses an odd order (N=2^k1). That means the latency of one resampling is 2^(k1)0.5 samples, but since it is done twice (both ways), the total latency of up/downresampling is integer again (2^k1).
Brickwall LP filter
The brickwall filter used in this implementation is a fragment of sinc function (which determines the cutoff frequency) modified by DolphChebyshev window:
You can check out various other window functions here: (window functions). I prefer the DolphChebyshev one as it allows for direct control over the ripple level (explained below).
A resampler is only as good as its lowpass filter. The brickwall lowpass filter has two quality measures: the transition band width and the ripple level:
The ripple is the response variation in the pass and stop bands. In the passband it will lead to EQ coloring. The HF signal infiltrated through the stopband will be mirrored onto the passband due to aliasing. It is a signal distortion and it should be made as lower as possible (or as necessary).
The transition band should be made as narrow that it doesn't overlap the audible range (<20kHz). It gets wider when you lower the ripple and shrinks if you increase the filter window size, but a larger window will also increase the CPU load. The choice of window size in this implementation is limited to degrees of 2. Here is the transition start frequency (that we would like to keep over 20kHz) depending on the required ripple level (k stands for the window size of 2^k):
44.1k sample rate is worse for resamplers, as there is only 2 kHz of room for the transition band, as compared to 4 kHz at 48k.
One other consideration is where to place the center of the transition band (the cutoff frequency). It is often placed right at the Nyquist frequency (of the lowest sample rate):
That has some benefits: that only half the transition band resides below the Nyquist (which allows to reduce the window size and CPU load by half), and that the sinc function has many zeros at the sampling points so those may be skipped from the calculation. I prefer to place the transition band entirely below the Nyquist to get rid of any aliasing (despite that the aliasing could affect only the transition band):

I may have made mistakes in my choice of filter parameters, I learned all that stuff from Wikipedia and by experimenting. If you have any ideas or questions, please welcome.
FIR filters
A FIR (Finite Impulse Response or stateless) filter is an array/vector of coefficients. An Nth order filter has N+1 coefficients. It is applied by calculating the next output sample as the dot product of the coefficients vector and the same number of last input samples:
Symmetric FIR filters (where the coefficients remain the same in reverse order) have some useful properties, one of them being linear phase response, which means the filter effectively has zero phase shift across the entire frequency range, it just delays the filtered signal in time by N/2 samples, which can be compensated.
Filters can have even or odd order. Evenorder filters have a central element (which matches the position in time of the output sample) and are often used for oversampling as interpolating filters (so that the existing input samples stay intact and only the "missing" samples are calculated).
This implementation uses an odd order (N=2^k1). That means the latency of one resampling is 2^(k1)0.5 samples, but since it is done twice (both ways), the total latency of up/downresampling is integer again (2^k1).
Brickwall LP filter
The brickwall filter used in this implementation is a fragment of sinc function (which determines the cutoff frequency) modified by DolphChebyshev window:
You can check out various other window functions here: (window functions). I prefer the DolphChebyshev one as it allows for direct control over the ripple level (explained below).
A resampler is only as good as its lowpass filter. The brickwall lowpass filter has two quality measures: the transition band width and the ripple level:
The ripple is the response variation in the pass and stop bands. In the passband it will lead to EQ coloring. The HF signal infiltrated through the stopband will be mirrored onto the passband due to aliasing. It is a signal distortion and it should be made as lower as possible (or as necessary).
The transition band should be made as narrow that it doesn't overlap the audible range (<20kHz). It gets wider when you lower the ripple and shrinks if you increase the filter window size, but a larger window will also increase the CPU load. The choice of window size in this implementation is limited to degrees of 2. Here is the transition start frequency (that we would like to keep over 20kHz) depending on the required ripple level (k stands for the window size of 2^k):
44.1k sample rate is worse for resamplers, as there is only 2 kHz of room for the transition band, as compared to 4 kHz at 48k.
One other consideration is where to place the center of the transition band (the cutoff frequency). It is often placed right at the Nyquist frequency (of the lowest sample rate):
That has some benefits: that only half the transition band resides below the Nyquist (which allows to reduce the window size and CPU load by half), and that the sinc function has many zeros at the sampling points so those may be skipped from the calculation. I prefer to place the transition band entirely below the Nyquist to get rid of any aliasing (despite that the aliasing could affect only the transition band):

I may have made mistakes in my choice of filter parameters, I learned all that stuff from Wikipedia and by experimenting. If you have any ideas or questions, please welcome.

 Information

Who is online
Users browsing this forum: No registered users and 1 guest