# Distortion in sound after multiplying frequency spectrum by constant

I make a simple sound equalizer that operates in frequency domain and lets user to adjust frequencies in sound by using 4 sliders. The first one responsible for 0 - 5kHz, the fourth one for 15-20kHz.

Steps are as follows:

1. I read wav file and store it in float array
2. I perform complex fft on that array (separately for left and right channel)
3. I multiply real and imaginary parts of bins representing 0-5kHz frequencies (both positive and negative) by 1.1 3.981 to increase these low frequencies by 10% 12dB in the final sound.
4. I perform ifft on array
5. I alternate real parts of left and right channels (returned by ifft) to create the final audio

The problem is that after this process the sound is distorted. It sounds like the speakers were not plugged in correctly. I found that if I divide values returned by ifft by arbitrary constant then the final sound is right, but is much quieter. I make the division in time domain, on the results from ifft.

The problem doesn't occur if I multiply frequencies by a number less than 1. So if frequencies are attenuated no further division in time domain is needed.

I suppose there is a mistake in the whole process. But if all steps are fine, how should I deal with distorted sound? Is dividing in time domain a proper solution? What number should I use to divide the results then so the sound is not distorted?

EDIT

This is the code I use to perform presented steps. I use Apache Commons math implementation of FFT and SimpleAudioConversion class taken from there http://stackoverflow.com/a/26824664/2891664

Unfortunately, code highlighting doesn't work.

// read file and store playable content in byte array
AudioInputStream in = AudioSystem.getAudioInputStream(file);
AudioFormat fmt = in.getFormat();
byte[] bytes = new byte[in.available()];

// convert bytes to float array
float[] samples = new float[bytes.length * 8 / fmt.getSampleSizeInBits()];
int validSamples = SimpleAudioConversion.decode(bytes, samples, result, fmt);

// find nearest power of 2 to zero-pad array in order to use fft
int power = 0;
while (Math.pow(2, power) < samples.length / 2)
power++;

// divide data into left and right channels
double[][] left = new double[(int) Math.pow(2, power)];
double[][] right = new double[(int) Math.pow(2, power)];

for (int i = 0; i < samples.length / 2; i++) {
left[i] = samples[2 * i];
right[i] = samples[2 * i + 1];
}

//fft
FastFourierTransformer.transformInPlace(left, DftNormalization.STANDARD, TransformType.FORWARD);
FastFourierTransformer.transformInPlace(right, DftNormalization.STANDARD, TransformType.FORWARD);

// here I amplify the 0-4kHz frequencies by 12dB
// 0-4kHz is 1/5 of whole spectrum, and since there are negative frequencies in the array
// I iterate over 1/10 and multiply frequencies on both sides of the array
for (int i = 1; i < left.length / 10; i++) {
double factor = 3.981d; // ratio = 10^(12dB/20)
//positive frequencies 0-4kHz
left[i] *= factor;
right[i] *= factor;
left[i] *= factor;
right[i] *= factor;

// negative frequencies 0-4kHz
left[left.length - i] *= factor;
right[left.length - i] *= factor;
left[left.length - i] *= factor;
right[left.length - i] *= factor;
}

//ifft
FastFourierTransformer.transformInPlace(left, DftNormalization.STANDARD, TransformType.INVERSE);
FastFourierTransformer.transformInPlace(right, DftNormalization.STANDARD, TransformType.INVERSE);

// put left and right channel into array
float[] samples2 = new float[(left.length) * 2];
for (int i = 0; i < samples2.length / 2; i++) {
samples2[2 * i] = (float) left[i];
samples2[2 * i + 1] = (float) right[i];
}

// convert back to byte array which can be played
byte[] bytes2 = new byte[bytes.length];
int validBytes = SimpleAudioConversion.encode(samples2, bytes2, validSamples, fmt);


You may listen to the sound here https://vocaroo.com/i/s095uOJZiewf

• I could not hear the audio. If the distortion is important, it looks like an implementation problem – Damien Dec 26 '18 at 17:52
• The sound might be clipped. Check to see if you have values larger than 1 or smaller than -1 after the IFFT. – MBaz Dec 26 '18 at 17:59
• It must not be your problem, but it does not seem a good idea to have such a tough transition between the frequency bands: a smooth weighting function should be better – Damien Dec 26 '18 at 18:18
• I found similar problem here stackoverflow.com/questions/23298616/… People suggest using Hann window or/and some overlap algorithm. I apply whole audio to fft so I believe there is no need for any overlap-add or other similar thing in my code. I will try with Hann window. – mrJoe Dec 26 '18 at 18:34
• I posted my code. Please tell me if you see something wrong. – mrJoe Dec 27 '18 at 11:57

1. Chance are your output is clipping. Most commercial wave files are highly compressed (in terms of dynamics) and any type of processing is likely to cause clipping, unless you remove a lot of gain or apply a suitable limiter.
2. Processing directly in the frequency domain is difficult and likely to result in time domain aliasing. That seems like a poor choice for an audio equalizer
3. Your band selection is unusual. Most audio equalizer use log-spaced frequency bands to better match human auditory perception. You'll find, that there is almost no energy at all in the 15k-20k band.
• My academic teacher suggested making a simple equalizer app working in frequency domain, not in real time. The course is about mobile applications, so this particular topic is rather something extraordinary and the whole point was to create something more interesting than typical CRUD app. But it seams that this task is more complicated than I supposed. In fact, I am not sure now if I should continue with that or just change my topic to simple create-read-update-delete mobile app. – mrJoe Dec 26 '18 at 18:56
• Well, I posted my code. I would appreciate if you look at it. – mrJoe Dec 27 '18 at 11:57

Almost certainly clipping since your factor is greater than 1 (much greater!). You could achieve a similar result by just reducing the amplitude of the upper frequencies instead of boosting the lower ones (which might already be "loud"). Or doing what you're already doing but reducing the amplitude of the entire incoming signal just to be safe.

The slope of the transition between the bands won't make much of a difference computationally but will sound more like an equalizer that operates in the time domain.

And yes, if the FastFourierTransformer.transformInPlace does not apply a window, you'll need to do that for it to sound halfway decent. Usually in realtime FFT processing, we also need to window the signal on the way out to deal with the overlapping of the resynthesized windows (unless it does it automatically or you're doing 0 overlap which won't sound as good).

New contributor
Jacob Sundstrom is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.