I am trying to write a small Discrete Fourier Transformation in Java to find the magnitude spectrum in a clear 400 Hz Sinus Signal (1 second as pcm signed-short)
So first I calculate the DFT for the complex values:
public void berechneDFT(int abtastwerte) {
int i;
int N = abtastwerte;
ReX = new double[N/2+1];
ImX = new double[N/2+1];
TextFileOperator tfo = new TextFileOperator(file.substring(0, file.length()-4)+"_DFT.txt");
try {
tfo.openOutputStream();
tfo.writeString("ReX ImX\n");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// real-Anteil berechnen
for (i=0, ReX[i] = 0, ImX[i] = 0; i <= N/2; i++)
{
for(int n=0; n < N; n++)
{
ReX[i] += x[n] * Math.cos( (2.0 * Math.PI * n * i) / (double) N);
ImX[i] += - (x[n] * Math.sin( (2.0 * Math.PI * n * i) / (double) N));
}
tfo.writeString(ReX[i] +" "+ImX[i]+"\n");
}
x = null;
tfo.closeOutputStream(); // flush
System.out.println("Anteile berechnet.");
}
And then I try to calculate the magnitude Spectrum:
public void berechneBetragsSpektrum() {
int N = ReX.length;
TextFileOperator tfo = new TextFileOperator("betragsspektrum_400hz.txt");
try {
tfo.openOutputStream();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
double powerAtFreq;
int marker = 0;
double maxPowerAtFreq = 0;
for(int i=0; i < N; i++)
{
double A1 = ReX[i] * ReX[i];
double A2 = ImX[i] * ImX[i];
powerAtFreq = Math.sqrt(A1+A2);
if(powerAtFreq > maxPowerAtFreq)
{
maxPowerAtFreq = powerAtFreq;
marker = i;
}
tfo.writeString(powerAtFreq+"\n");
}
tfo.closeOutputStream();
System.out.println("Stärkste Frequenz: "+(marker)+" Hz");
}
But for some reason I only get the result of 400 Hz in the 'marker' if I choose to check for all 16000 samples. But shouldn't I see the peak in 400 Hz also if I only choose 800 samples, because with 800 I could see 800/2 = 400 Hz as maximum frequency?
I guess some little thing must be wrong with the code, because if I choose 800 samples I get 20 Hz, for 1600 samples I get 40 Hz which is always 1/40 * sample rate.
What the hell do I miss or did wrong? The results are strange..
Note that if I do the inverse DFT with the complex values I can reconstruct the audio signal again!
The answer for the question is that if you calculate the fourier transforms, magnitude spectrum etc. the indices show relative frequencies which need to be calculated to their correct value.
Related
I am using Processing 3 with the Beads library in order to analyse a number of samples but each time I run the analysis on the same data, I get very different results. Here's the sample and analysis setup:
import beads.*;
import org.jaudiolibs.beads.*;
AudioContext ac;
GranularSamplePlayer sample;
Gain gain;
ShortFrameSegmenter sfs;
FFT fft;
PowerSpectrum ps;
Frequency f;
SpectralPeaks sp;
float[][] meanHarmonics;
int numPeaks = 6;
void setup() {
size(1600, 900);
ac = new AudioContext();
ac.start();
println(dataPath("") + "1.wav");
sample = new GranularSamplePlayer(ac, SampleManager.sample(dataPath("") + "\\1.wav"));
gain = new Gain(ac, 1, 1);
// input chaining
gain.addInput(sample);
ac.out.addInput(gain);
// setup analysis
// break audio into more manageable chunks
sfs = new ShortFrameSegmenter(ac);
sfs.addInput(sample);
// fast fourier transform to analyse the harmonic spectrum
fft = new FFT();
sfs.addListener(fft);
// PowerSpectrum turns the raw FFT output into proper audio data.
ps = new PowerSpectrum();
fft.addListener(ps);
// Frequency tries to determine the strongest frequency in the wave
// which is the fundamental that determines the pitch of the sound
f = new Frequency(44100.0f);
ps.addListener(f);
// Listens for harmonics
sp = new SpectralPeaks(ac, numPeaks);
ps.addListener(sp);
meanHarmonics = new float[numPeaks][2];
// initialise meanHarmonics
for(int i = 0; i < numPeaks; i++) {
for(int j = 0; j < 2; j++) {
meanHarmonics[i][j] = 0;
}
}
ac.out.addDependent(sfs);
int startTime = millis();
int loops = 0;
float meanFrequency = 0.0;
while(millis() - startTime < 1500) {
loops++;
if(loops == 1) {
sample.start(0);
}
Float inputFrequency = f.getFeatures();
if(inputFrequency != null) {
meanFrequency += inputFrequency;
}
float[][] harmonics = sp.getFeatures();
if(harmonics != null) {
for(int feature = 0; feature < numPeaks; feature++) {
// harmonic must be in human audible range
// and its amplitude must be large enough to be audible
if(harmonics[feature][0] < 20000.0 && harmonics[feature][1] > 0.01) {
// average out the frequencies
meanHarmonics[feature][0] += harmonics[feature][0];
// average out the amplitudes
meanHarmonics[feature][1] += harmonics[feature][1];
}
}
}
}
float maxAmp = 0.0;
float freq = 0.0;
sample.pause(true);
meanFrequency /= loops;
println(meanFrequency);
for(int feature = 0; feature < numPeaks; feature++) {
meanHarmonics[feature][0] /= loops;
meanHarmonics[feature][1] /= loops;
if(meanHarmonics[feature][1] > maxAmp) {
freq = meanHarmonics[feature][0];
maxAmp = meanHarmonics[feature][1];
}
println(meanHarmonics[feature][0] + " " + meanHarmonics[feature][1]);
}
println(freq + " " + meanFrequency);
println();
}
I run FFT for a set amount of time during which I sum the frequency returned by the Frequency object and the SpectralPeaks features.
At the end I divide accumulated frequencies and amplitudes to obtain the means. I also try to find the fundamental frequency in the SpectralPeaks array by finding the frequency with the largest amplitude.
But every time I run my program I get a different result, both from SpectralPeaks and Frequency(and their values also differ from each other).
Here are some example values:
1st run:
Spectral Peaks features:
914.84863 0.040409338
844.96295 0.033234257
816.0808 0.027509697
664.9141 0.022158746
633.3232 0.019597264
501.93716 0.01606628
Spectral Peaks fundamental: 914.84863
Frequency: 1028.1572
2nd run, same sample:
Spectral Peaks features:
1023.4123 0.03913592
1109.2562 0.031178929
967.0786 0.026673868
721.2698 0.021666735
629.9294 0.018046249
480.82416 0.014858524
Spectral Peaks fundamental: 1023.4123
Frequency: 1069.3387
Also, the value returned by Frequency is often NaN, I don't understand why that is.
The reason why your code returns different values is because it is sampling and analyzing the audio at different moments. Once you start playing the audio, you have no control when Float inputFrequency = f.getFeatures(); gets executed.
A better approach is not to use millis() and replace the while loop with a for loop, and use ac.runForMillisecondsNonRealTime(). This way you get know exactly that you performing the analysis for a 1500 milliseconds.
//while(millis() - startTime < 1500) {
for(int i = 0; i < numPeaks; i++) {
ac.runForNMillisecondsNonRealTime(1500/numPeaks);
Float inputFrequency = f.getFeatures();
if(inputFrequency != null) {
meanFrequency += inputFrequency;
}
float[][] harmonics = sp.getFeatures();
if(harmonics != null) {
for(int feature = 0; feature < numPeaks; feature++) {
// harmonic must be in human audible range
// and its amplitude must be large enough to be audible
if(harmonics[feature][0] < 20000.0 && harmonics[feature][1] > 0.01) {
// average out the frequencies
meanHarmonics[feature][0] += harmonics[feature][0];
// average out the amplitudes
meanHarmonics[feature][1] += harmonics[feature][1];
}
}
}
}
If I(in Java) have a double[] array containing audio samples, ranging from -1 to 1, which I can play, and have generated to sound like a guitar string being played, is there any way through which I can simulate the effect of distortion from an amplifier on these samples?
I apologize for the vagueness of the term "distortion", but I'm referring to any effect similar to setting a guitar amplifier to "distortion". What I already have sounds like an acoustic guitar, or an electric guitar with no distortion(set to "clean"), so how can I alter the array to sound more like what you would expect from an electric guitar in a rock or metal setting?
The current set of samples is calculated using the following method:
double[] samples = new double[duration]; //duration = seconds * sampleRate
int period = (float)sampleRate / (float)frequency;
double[] buf = new double[period]; //a ring buffer used for the sound generation
int count = 0, c1 = 1, c2 = 2;
for(int i=0; i<duration; i++){
if(count <= period)count = 0;
if(c1 <= period)c1 = 0;
if(c2 <= period)c2 = 0;
if(i < period){
buf[count] = rand.nextDouble() * 2 - 1; //rand being a Random
}
else{
buff[count] = (buff[c1] + buff[c2]) / 2;
}
samples[i] = buff[count];
count++;
c1++;
c2++;
}
There are three main types of distortion:
Hard distortion: simple clipping of the signal
for (int i = 0; i < duration; i++)
{
double sample = samples[i] * gain_pre;
if (sample > clip)
sample = clip;
else
if (sample < -clip)
sample = -clip;
samples[i] = sample * gain_post;
}
Normal distortion: exponential smooth scaling of the signal
double max = 1.0 / (1.0 - exp(-gain_pre));
for (int i = 0; i < duration; i++)
{
double sample = samples[i] * gain_pre;
double z = (sample < 0.0) ? (-1.0 + exp(sample)) :
(1.0 - exp(-sample));
samples[i] = z * max * gain_post;
}
Soft distortion: same as above, but using arc-tan (presumably more aggressive)
double max = 1.0 / atan(gain_pre);
for (int i = 0; i < duration; i++)
{
samples[i] = atan(samples[i] * gain_pre) * max * gain_post;
}
Variables:
gain_pre and gain_post: Pre-gain and Post-gain parameters
clip: maximum value for hard-distortion signal
samples: the sample sequence you calculate
References / more info:
http://cp-gfx.sourceforge.net/ (download the source code and look in /src/effects/)
https://en.wikipedia.org/wiki/Distortion_(music)#Theory_and_circuits
I need to get the amplitude of a signal at a certain frequency.
I use FFTAnalysis function. But I get all spectrum. How can I modify this for get the amplitude of a signal at a certain frequency?
For example I have:
data = array of 1024 points;
If I use FFTAnalysis I get FFTdata array of 1024 points.
But I need only FFTdata[454] for instance ();
public static float[] FFTAnalysis(short[] AVal, int Nvl, int Nft) {
double TwoPi = 6.283185307179586;
int i, j, n, m, Mmax, Istp;
double Tmpr, Tmpi, Wtmp, Theta;
double Wpr, Wpi, Wr, Wi;
double[] Tmvl;
float[] FTvl;
n = Nvl * 2;
Tmvl = new double[n];
FTvl = new float[Nvl];
for (i = 0; i < Nvl; i++) {
j = i * 2; Tmvl[j] = 0; Tmvl[j+1] = AVal[i];
}
i = 1; j = 1;
while (i < n) {
if (j > i) {
Tmpr = Tmvl[i]; Tmvl[i] = Tmvl[j]; Tmvl[j] = Tmpr;
Tmpr = Tmvl[i+1]; Tmvl[i+1] = Tmvl[j+1]; Tmvl[j+1] = Tmpr;
}
i = i + 2; m = Nvl;
while ((m >= 2) && (j > m)) {
j = j - m; m = m >> 1;
}
j = j + m;
}
Mmax = 2;
while (n > Mmax) {
Theta = -TwoPi / Mmax; Wpi = Math.sin(Theta);
Wtmp = Math.sin(Theta / 2); Wpr = Wtmp * Wtmp * 2;
Istp = Mmax * 2; Wr = 1; Wi = 0; m = 1;
while (m < Mmax) {
i = m; m = m + 2; Tmpr = Wr; Tmpi = Wi;
Wr = Wr - Tmpr * Wpr - Tmpi * Wpi;
Wi = Wi + Tmpr * Wpi - Tmpi * Wpr;
while (i < n) {
j = i + Mmax;
Tmpr = Wr * Tmvl[j] - Wi * Tmvl[j-1];
Tmpi = Wi * Tmvl[j] + Wr * Tmvl[j-1];
Tmvl[j] = Tmvl[i] - Tmpr; Tmvl[j-1] = Tmvl[i-1] - Tmpi;
Tmvl[i] = Tmvl[i] + Tmpr; Tmvl[i-1] = Tmvl[i-1] + Tmpi;
i = i + Istp;
}
}
Mmax = Istp;
}
for (i = 0; i < Nft; i++) {
j = i * 2; FTvl[Nft - i - 1] = (float) Math.sqrt((Tmvl[j]*Tmvl[j]) + (Tmvl[j+1]*Tmvl[j+1]));
}
return FTvl;
}
The Goertzel algorithm (or filter) is similar to computing the magnitude for just 1 bin of an FFT.
The Goertzel algorithm is identical to 1 bin of an FFT, except for numerical artifacts, if the period of the frequency is an exact submultiple of your Goertzel filter's length. Otherwise there are some added scalloping effects from a rectangular window of non-periodic-in-aperture size, and how that window relates to the phase of the input.
Multiplying by a complex sinusoid and taking the magnitude of the complex sum is also computationally similar to a Goertzel, except the Goertzel does not require separately calling (or looking up) a trig library function every point, as it usually includes a trig recursion at part of its algorithm.
You'd multiply a (complex) sine wave on the input data, and integrate the result.
Multiplying with a complex sine is equal to a frequency shift, you want to shift the target frequency down to 0 Hz. The integration is a low pass filtering step, with the bandwidth being the inverse of the sampling length.
You then end up with a complex number, which is the same number you would have found in the FFT bin for this frequency (because in essence this is what the FFT does).
The fast fourier transform (FFT) is a clever way of doing many discrete fourier transforms very quickly. As such, the FFT is designed for when one needs a lot of frequencies from the input. If you want just one frequency, the DFT is the way to go (as otherwise you're wasting resources).
The DFT is defined as:
So, in pseudocode:
samples = [#,#,#,#...]
FREQ = 440; // frequency to detect
PI = 3.14159;
E = 2.718;
DFT = 0i; // this is a complex number
for(int sampleNum=0; sampleNum<N; sampleNum++){
DFT += samples[sampleNum] * E^( (-2*PI*1i*N) / N ); //Note that "i" here means imaginary
}
The resulting variable DFT will be a complex number representing the real and imaginary values of the chosen frequency.
I am trying to write a simple band pass filter following the instructions in this book. My code creates a blackman window, and combines two low pass filter kernels to create a band pass filter kernel using spectral inversion, as described in the second example here (table 16-2).
I am testing my code by comparing it with the results I get in matlab. When I test the methods that create a blackman window and a low pass filter kernel separately, I get results that are close to what I see in matlab (up to some digits after the decimal point - I attribute the error to java double variables rounding issues), but my band pass filter kernel is incorrect.
Tests I ran:
Created a blackman window and compared it with what I get in matlab - all good.
Created a low pass filter using this window using my code and fir1(N, Fc1/(Fs/2), win, flag); in matlab (see full code below). I think the results are correct, although I get bigger error the bigger Fc1 is (why?)
Created a pand pass filter using my code and fir1(N, [Fc1 Fc2]/(Fs/2), 'bandpass', win, flag); in matlab - results are completely off.
Filtered my data using my code and the kernel generated by matlab - all good.
So - why is my band pass filter kernel off? What did I do wrong?
I think I either have a bug or fir1 uses a different algorithm, but I can't check because the article referenced in its documentation is not publicly available.
This is my matlab code:
Fs = 200; % Sampling Frequency
N = 10; % Order
Fc1 = 1.5; % First Cutoff Frequency
Fc2 = 7.5; % Second Cutoff Frequency
flag = 'scale'; % Sampling Flag
% Create the window vector for the design algorithm.
win = blackman(N+1);
% Calculate the coefficients using the FIR1 function.
b = fir1(N, [Fc1 Fc2]/(Fs/2), 'bandpass', win, flag);
Hd = dfilt.dffir(b);
res = filter(Hd, data);
This is my java code (I believe the bug is in bandPassKernel):
/**
* See - http://www.mathworks.com/help/signal/ref/blackman.html
* #param length
* #return
*/
private static double[] blackmanWindow(int length) {
double[] window = new double[length];
double factor = Math.PI / (length - 1);
for (int i = 0; i < window.length; ++i) {
window[i] = 0.42d - (0.5d * Math.cos(2 * factor * i)) + (0.08d * Math.cos(4 * factor * i));
}
return window;
}
private static double[] lowPassKernel(int length, double cutoffFreq, double[] window) {
double[] ker = new double[length + 1];
double factor = Math.PI * cutoffFreq * 2;
double sum = 0;
for (int i = 0; i < ker.length; i++) {
double d = i - length/2;
if (d == 0) ker[i] = factor;
else ker[i] = Math.sin(factor * d) / d;
ker[i] *= window[i];
sum += ker[i];
}
// Normalize the kernel
for (int i = 0; i < ker.length; ++i) {
ker[i] /= sum;
}
return ker;
}
private static double[] bandPassKernel(int length, double lowFreq, double highFreq) {
double[] ker = new double[length + 1];
double[] window = blackmanWindow(length + 1);
// Create a band reject filter kernel using a high pass and a low pass filter kernel
double[] lowPass = lowPassKernel(length, lowFreq, window);
// Create a high pass kernel for the high frequency
// by inverting a low pass kernel
double[] highPass = lowPassKernel(length, highFreq, window);
for (int i = 0; i < highPass.length; ++i) highPass[i] = -highPass[i];
highPass[length / 2] += 1;
// Combine the filters and invert to create a bandpass filter kernel
for (int i = 0; i < ker.length; ++i) ker[i] = -(lowPass[i] + highPass[i]);
ker[length / 2] += 1;
return ker;
}
private static double[] filter(double[] signal, double[] kernel) {
double[] res = new double[signal.length];
for (int r = 0; r < res.length; ++r) {
int M = Math.min(kernel.length, r + 1);
for (int k = 0; k < M; ++k) {
res[r] += kernel[k] * signal[r - k];
}
}
return res;
}
And this is how I use my code:
double[] kernel = bandPassKernel(10, 1.5d / (200/2), 7.5d / (200/2));
double[] res = filter(data, kernel);
I ended up implementing Matlab's fir1 function in Java. My results are quite accurate.
I would like to take an array of bytes of roughly size 70-80k and transform them from the time domain to the frequency domain (probably using a DFT). I have been following wiki and gotten this code so far.
for (int k = 0; k < windows.length; k++) {
double imag = 0.0;
double real = 0.0;
for (int n = 0; n < data.length; n++) {
double val = (data[n])
* Math.exp(-2.0 * Math.PI * n * k / data.length)
/ 128;
imag += Math.cos(val);
real += Math.sin(val);
}
windows[k] = Math.sqrt(imag * imag + real
* real);
}
and as far as I know, that finds the magnitude of each frequency window/bin. I then go through the windows and find the one with the highest magnitude. I add a flag to that frequency to be used when reconstructing the signal. I check to see if the reconstructed signal matches my original data set. If it doesn't find the next highest frequency window and flag that to be used when reconstructing the signal.
Here is the code I have for reconstructing the signal which I'm mostly certain is very wrong (it is supposed to perform an IDFT):
for (int n = 0; n < data.length; n++) {
double imag = 0.0;
double real = 0.0;
sinValue[n] = 0;
for (int k = 0; k < freqUsed.length; k++) {
if (freqUsed[k]) {
double val = (windows[k] * Math.exp(2.0 * Math.PI * n
* k / data.length));
imag += Math.cos(val);
real += Math.sin(val);
}
}
sinValue[n] = imag* imag + real * real;
sinValue[n] /= data.length;
newData[n] = (byte) (127 * sinValue[n]);
}
freqUsed is a boolean array used to mark whether or not a frequency window should be used when reconstructing the signal.
Anyway, here are the problems that arise:
Even if all of the frequency windows are used, the signal is not reconstructed. This may be due to the fact that ...
Sometimes the value of Math.exp() is too high and thus returns infinity. This makes it difficult to get accurate calculations.
While I have been following wiki as a guide, it is hard to tell whether or not my data is meaningful. This makes it hard to test and identify problems.
Off hand from the problem:
I am fairly new to this and do not fully understand everything. Thus, any help or insight is appreciated. Thanks for even taking the time to read all of that and thanks ahead of time for any help you can provide. Any help really would be good, even if you think I'm doing this the most worst awful way possible, I'd like to know. Thanks again.
-
EDIT:
So I updated my code to look like:
for (int k = 0; k < windows.length; k++) {
double imag = 0.0;
double real = 0.0;
for (int n = 0; n < data.length; n++) {
double val = (-2.0 * Math.PI * n * k / data.length);
imag += data[n]*-Math.sin(val);
real += data[n]*Math.cos(val);
}
windows[k] = Math.sqrt(imag * imag + real
* real);
}
for the original transform and :
for (int n = 0; n < data.length; n++) {
double imag = 0.0;
double real = 0.0;
sinValue[n] = 0;
for (int k = 0; k < freqUsed.length; k++) {
if (freqUsed[k]) {
double val = (2.0 * Math.PI * n
* k / data.length);
imag += windows[k]*-Math.sin(val);
real += windows[k]*Math.cos(val);
}
}
sinValue[n] = Math.sqrt(imag* imag + real * real);
sinValue[n] /= data.length;
newData[n] = (byte) (Math.floor(sinValue[n]));
}
for the inverse transform. Though I am still concerned that it isn't quite working correctly. I generated an array holding a single sine wave and it can't even decompose/reconstruct that. Any insight as to what I'm missing?
Yes, your code (for both DFT and IDFT) is broken. You are confusing the issue of how to use the exponential. The DFT can be written as:
N-1
X[k] = SUM { x[n] . exp(-j * 2 * pi * n * k / N) }
n=0
where j is sqrt(-1). That can be expressed as:
N-1
X[k] = SUM { (x_real[n] * cos(2*pi*n*k/N) + x_imag[n] * sin(2*pi*n*k/N))
n=0 +j.(x_imag[n] * cos(2*pi*n*k/N) - x_real[n] * sin(2*pi*n*k/N)) }
which in turn can be split into:
N-1
X_real[k] = SUM { x_real[n] * cos(2*pi*n*k/N) + x_imag[n] * sin(2*pi*n*k/N) }
n=0
N-1
X_imag[k] = SUM { x_imag[n] * cos(2*pi*n*k/N) - x_real[n] * sin(2*pi*n*k/N) }
n=0
If your input data is real-only, this simplifies to:
N-1
X_real[k] = SUM { x[n] * cos(2*pi*n*k/N) }
n=0
N-1
X_imag[k] = SUM { x[n] * -sin(2*pi*n*k/N) }
n=0
So in summary, you don't need both the exp and the cos/sin.
As well as the points that #Oli correctly makes, you also have a fundamental misunderstanding about conversion between time and frequency domains. Your real input signal becomes a complex signal in the frequency domain. You should not be taking the magnitude of this and converting back to the time domain (this will actually give you the time domain autocorrelation if done correctly, but this is not what you want). If you want to be able to reconstruct the time domain signal then you must keep the complex frequency domain signal as it is (i.e. separate real/imaginary components) and do a complex-to-real IDFT to get back to the time domain.
E.g. your forward transform should look something like this:
for (int k = 0; k < windows.length; k++) {
double imag = 0.0;
double real = 0.0;
for (int n = 0; n < data.length; n++) {
double val = (-2.0 * Math.PI * n * k / data.length);
imag += data[n]*-Math.sin(val);
real += data[n]*Math.cos(val);
}
windows[k].real = real;
windows[k].imag = image;
}
where windows is defined as an array of complex values.