To be as succinct as possible, I've written a Java method that implements resampling of a signal using the sinc function interpolation formula. When I test this code with a simple sinusoid, I'm able to take a test signal, upsample it, downsample it back to the original frequency, and it comes out identical. Perfect! Then when I test it with a 10 second audio wav file, it totally borks. In my successful test, the input is a 20Hz sine + 40Hz cosine which I sample at 80Hz, then upsample to 180Hz, then downsample back to 80Hz. For the audio file, its a mono 44.1khz sample which I downsample to 16kHz. Here's the code, then I'll paste the formula I've implemented (maybe I've got the formula wrong).
/**
* Calculates an interpolated value when resampling - THIS IS THE MAGIC!
* This method is used to calculate the M sample of the output signal, given the input signal.
* We don't necessarily need the entire input signal, but enough to encompass the window
*
* @param input (Xi) the input signal
* @param inputFreq (Fi) the input signal freqency
* @param output_m (M) the current sample number in the output signal we are calculating)
* @param windowLength (L) we will evaluate the input signal in the window N-L to N+L
* @param newfreq (Fo) the output frequency
* @return the interpolated value
*/
public static double interpolate(double[] input, int inputFreq, int output_m, int newfreq, int windowLength) {
double sum = 0.0;
double t_i = 1.0 / inputFreq;
double t_o = 1.0 / newfreq;
int q = (int) Math.floor(output_m * t_o / t_i); // this is the corresponding sampleNr in the input signal
for (int n = -windowLength + q; n <= windowLength + q; n++) {
// First handle edge conditions
if (n < 0) // skip n < 0
continue;
if (n >= input.length) // EOF
break;
double mTo_minus_nTi_over_Ti = (output_m * t_o - n * t_i) / t_i;
if (mTo_minus_nTi_over_Ti == 0)
sum += input[n];
else {
double pi_times = Math.PI * mTo_minus_nTi_over_Ti;
double sineterm = Math.sin(pi_times) / pi_times;
double xi_n = input[n];
double sumterm = xi_n * sineterm;
sum += sumterm;
}
}
return sum;
}
Here is the formula I used (I created this latex image to reflect what I implemented - please don't assume it's right, because if it is, I can't figure out where I've gone wrong! :) )
My simple visual test results:
My visual audio test results - all noise and static:
Maybe my error above is obvious. I hope so! But I wondered, in the sinc function, is "n" supposed to be the sample number from the input, or is it supposed to be the fraction that leads to the nearest input sample number? So I made a couple of changes, leaving "q" and "n" as doubles, and only taking Math.floor of n when I need the actual sample number.
public static double interpolate(double[] input, int inputFreq, int output_m, int newfreq, int windowLength) {
double sum = 0.0;
double t_i = 1.0 / inputFreq;
double t_o = 1.0 / newfreq;
double q = (output_m * t_o / t_i); // this is the corresponding sampleNr in the input signal
for (double n = -windowLength + q; n <= windowLength + q; n++) {
// First handle edge conditions
if (n < 0) // skip n < 0
continue;
if (n >= input.length) // EOF
break;
double mTo_minus_nTi_over_Ti = (output_m * t_o - n * t_i) / t_i;
if (mTo_minus_nTi_over_Ti == 0)
sum += input[(int) Math.floor(n)];
else {
double pi_times = Math.PI * mTo_minus_nTi_over_Ti;
double sineterm = Math.sin(pi_times) / pi_times;
double xi_n = input[(int) Math.floor(n)];
double sumterm = xi_n * sineterm;
sum += sumterm;
}
}
return sum;
}
Suddenly, the audio resampling looks (and sounds) perfect!
But the sinusoid plot now looks like this, which is clearly not a perfect reconstruction. What's happening is that all of the interpolated samples are equal to the nearest direct sample, thus the horizontal artifacts.
Apologies for the long post, I tried to keep it concise and directed. Full disclosure - yes this is a school assignment, but this isn't for a DSP class, its actually a parallel computing class where we need to process very large audio files very fast! I've shared all of the above and the link to this post with the professor.
Thanks in advance for any help!