I've been working on a frequency shifter using a primitive FFT algorithm supplied by Rosetta Code. I understand that to frequency shift a signal of samples, one applies an FFT to the original audio, multiplies the frequency of each resulting sine-wave by the frequency-shift factor (user defined), and then adds the sine-waves back together. When I run my algorithm, the output is of extremely low quality, as though there were not enough sine waves collected within the algorithm to have reproduced the signal close to correctly in the first place. The algorithm is implemented in a class in a header file and called (correctly) elsewhere.
#include <complex>
#include <valarray>
typedef std::complex<double> Complex;
typedef std::valarray<Complex> CArray;
class FrequencyShifter {
float sampleRate;
public:
FrequencyShifter() {
}
void setSampleRate(float inSampleRate) {
sampleRate = inSampleRate;
}
double abs(double in0) {
if (in0>=0) return in0;
else return -in0;
}
void fft(CArray& x)
{
const size_t N = x.size();
if (N <= 1) return;
// divide
CArray even = x[std::slice(0, N/2, 2)];
CArray odd = x[std::slice(1, N/2, 2)];
// conquer
fft(even);
fft(odd);
// combine
for (size_t k = 0; k < N/2; ++k)
{
Complex t = std::polar(1.0, -2 * PI * k / N) * odd[k];
x[k ] = even[k] + t;
x[k+N/2] = even[k] - t;
}
}
double convertToReal(double im, double re) {
return sqrt(abs(im*im - re*re));
}
void processBlock(float *inBlock, const int inFramesToProcess, float scale) {
//inFramesToProcess is the amount of samples in inBlock
Complex *copy = new Complex[inFramesToProcess];
for (int frame = 0; frame<inFramesToProcess; frame++) {
copy[frame] = Complex((double)inBlock[frame], 0.0);
}
CArray data(copy, inFramesToProcess);
fft(data);
const float freqoffsets = sampleRate/inFramesToProcess;
for (float x = 0; x<data.size()/2; x++) {
for (float frame = 0; frame<inFramesToProcess; frame++) {
inBlock[(int)frame] = (float)(convertToReal(data[(int)x].imag(), data[(int)x].real())*sin(freqoffsets*x*frame*scale));
}
}
}
};
I'm assuming that part of the problem is that I'm only including sampleRate/inFramesToProcess
frequencies for the sine waves to cover. Would sending larger audio files (thus larger *inBlock
s and inFramesToProcess
s) make the audio less grainy? How would I accomplish this without just changing the values or lengths of the arguments?
*inBlock
there is no level (audio level is 0 or some other error was encountered). Essentially, there is some mistake in the algorithm which I am unable to detect and fix. – Linus RastegarconvertToReal
the correct way round? Trivially, ifinFramesToProcess
is 1,data
will have a complex number with no imaginary part in it.fft
won't do anything to it, so when this gets converted back you'll try to take the sqrt of a negative number. Nontrivially,fft
won't do anything to the last element ofx
ifx.size()
is odd. – 1201ProgramAlarm