I have recorded an array[1024] of data from my mic on my Android phone, passed it through a 1D forward DFT of the real data (setting a further 1024 bits to 0). I saved the array to a text file, and repeated this 8 times.
I got back 16384 results. I opened the text file in Excel and made a graph to see what it looked like(x=index of array, y=size of number returned). There are some massive spikes (both positive and negative) in magnitude around 110, 232, and small spikes continuing in that fashion until around 1817 and 1941 where the spikes get big again, then drop again.
My problem is that wherever I look for help on the topic it mentions gettng the real and imaginary numbers, I only have a 1D array, that I got back from the method I used from Piotr Wendykier's class:
DoubleFFT_1D.realForwardFull(audioDataArray); // from the library JTransforms.
My question is: What do I need to do to this data to return a frequency?
The sound recorded was me playing an 'A' on the bottom string (5th fret) of my guitar (at roughly 440Hz) .
The complex data is interleaved, with real components at even indices and imaginary components at odd indices, i.e. the real components are at index 2*i, the imaginary components are at index 2*i+1.
To get the magnitude of the spectrum at index i, you want:
re = fft[2*i];
im = fft[2*i+1];
magnitude[i] = sqrt(re*re+im*im);
Then you can plot magnitude[i] for i = 0 to N / 2 to get the power spectrum. Depending on the nature of your audio input you should see one or more peaks in the spectrum.
To get the approximate frequency of any given peak you can convert the index of the peak as follows:
freq = i * Fs / N;
where:
freq = frequency in Hz
i = index of peak
Fs = sample rate in Hz (e.g. 44100 Hz, or whatever you are using)
N = size of FFT (e.g. 1024 in your case)
Note: if you have not previously applied a suitable window function to the time-domain input data then you will get a certain amount of spectral leakage and the power spectrum will look rather "smeared".
To expand on this further, here is pseudo-code for a complete example where we take audio data and identify the frequency of the largest peak:
N = 1024 // size of FFT and sample window
Fs = 44100 // sample rate = 44.1 kHz
data[N] // input PCM data buffer
fft[N * 2] // FFT complex buffer (interleaved real/imag)
magnitude[N / 2] // power spectrum
// capture audio in data[] buffer
// ...
// apply window function to data[]
// ...
// copy real input data to complex FFT buffer
for i = 0 to N - 1
fft[2*i] = data[i]
fft[2*i+1] = 0
// perform in-place complex-to-complex FFT on fft[] buffer
// ...
// calculate power spectrum (magnitude) values from fft[]
for i = 0 to N / 2 - 1
re = fft[2*i]
im = fft[2*i+1]
magnitude[i] = sqrt(re*re+im*im)
// find largest peak in power spectrum
max_magnitude = -INF
max_index = -1
for i = 0 to N / 2 - 1
if magnitude[i] > max_magnitude
max_magnitude = magnitude[i]
max_index = i
// convert index of largest peak to frequency
freq = max_index * Fs / N
Related
I'm trying to get the most representative frequency (or first harmonic) from an audio file using the Noise FFT library (https://github.com/paramsen/noise). I have an array with the values of size x and the output array's size is x+2. I'm not familiar with Fourier Transform, so maybe I'm missing something, but from my understanding I should have something that represents the frequencies and stores the magnitude (or in this case a complex number from with to calculate it) of each one.
The thing is: since each position in the array should be a frequency, how can I know the range of the output frequencies, what frequency is each position or something like that?
Edit: This is part of the code I'm using
float[] mono = new float[size];
// I fill the array with the appropiate values
Noise noise = Noise.real(size);
float[] dst = new float[size + 2];
float[] fft = noise.fft(mono, dst);
// The result array has the pairs of real+imaginary floats in a one dimensional array; even indices
// are real, odd indices are imaginary. DC bin is located at index 0, 1, nyquist at index n-2, n-1
double greatest = 0;
int greatestIdx = 0;
for(int i = 0; i < fft.length / 2; i++) {
float real = fft[i * 2];
float imaginary = fft[i * 2 + 1];
double magnitude = Math.sqrt(real*real+imaginary*imaginary);
if (magnitude > greatest) {
greatest = magnitude;
greatestIdx = i;
}
System.out.printf("index: %d, real: %.5f, imaginary: %.5f\n", i, real, imaginary);
}
I just noticed something I had overlooked. When reading the comment just before the for loop (which is from the sample code provided in GitHub) it says that nyquist is located at the last pair of values of the array. From what I searched, nyquist is 22050Hz, so... To know the frequency corresponding to greatestIdx I should map the range [0,size+2] to the range [0,22050] and calculate the new value? It seems like a pretty unprecise measure.
Taking the prior things into account, maybe I should use another library for more precision? If that is the case, what would be one that let me specify the output frequency range or that gives me approximately the human hearing range by default?
I believe that the answer to your question is here if I understand it correctly https://stackoverflow.com/a/4371627/9834835
To determine the frequency for each FFT bin you may use the formula
F = i * sample / nFFt
where:
i = the FFT index
sample = the sample rate
nFft = your FFT size
Off Topic: Let me start by saying Java is completely new to me. I've been programming for over 15 years and never have had a need for it beyond modifying others' codebases, so please forgive my ignorance and possibly improper terminology. I'm also not very familiar with RF, so if I'm way left field here, please let me know!
I'm building an SDR (Software Defined Radio) radio transmitter, and while I can successfully transmit on a frequency, when I send the stream (either from the device's microphone or bytes from a tone generator), what is coming through my handheld receiver sounds like static.
I believe this to be due to my receiver being set up to receive NFM (Narrowband Frequency Modulation) and WFM (Wideband Frequency Modulation) while the transmission coming from my SDR is sending raw, unmodulated data.
My question is: how do I modulate audio bytes (i.e. an InputStream) so that the resulting bytes are modulated in FM (Frequency Modulation) or AM (Amplitude Modulation), which I can then transmit through the SDR?
I can't seem to find a class or package that handles modulation (eventually I'm going to have to modulate WFM, FM, AM, SB, LSB, USB, DSB, etc.) despite there being quite a few open-source SDR codebases, but if you know where I can find this, that basically answers this question. Everything I've found so far has been for demodulation.
This is a class I've built around Xarph's Answer here on StackOverflow, it simply returns a byte array containing a simple, unmodulated audio signal, which can then be used to play sound through speakers (or transmit over an SDR, but due to the result not being properly modulated, it doesn't come through correctly on the receiver's end, which is what I'm having trouble figuring out)
public class ToneGenerator {
public static byte[] generateTone() {
return generateTone(60, 1000, 8000);
}
public static byte[] generateTone(double duration) {
return generateTone(duration, 1000, 8000);
}
public static byte[] generateTone(double duration, double freqOfTone) {
return generateTone(duration, freqOfTone, 8000);
}
public static byte[] generateTone(double duration, double freqOfTone, int sampleRate) {
double dnumSamples = duration * sampleRate;
dnumSamples = Math.ceil(dnumSamples);
int numSamples = (int) dnumSamples;
double sample[] = new double[numSamples];
byte generatedSnd[] = new byte[2 * numSamples];
for (int i = 0; i < numSamples; ++i) { // Fill the sample array
sample[i] = Math.sin(freqOfTone * 2 * Math.PI * i / (sampleRate));
}
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalized.
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalised.
int idx = 0;
int i = 0 ;
int ramp = numSamples / 20 ; // Amplitude ramp as a percent of sample count
for (i = 0; i< ramp; ++i) { // Ramp amplitude up (to avoid clicks)
double dVal = sample[i];
// Ramp up to maximum
final short val = (short) ((dVal * 32767 * i/ramp));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
for (i = i; i< numSamples - ramp; ++i) { // Max amplitude for most of the samples
double dVal = sample[i];
// scale to maximum amplitude
final short val = (short) ((dVal * 32767));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
for (i = i; i< numSamples; ++i) { // Ramp amplitude down
double dVal = sample[i];
// Ramp down to zero
final short val = (short) ((dVal * 32767 * (numSamples-i)/ramp ));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
return generatedSnd;
}
}
An answer to this doesn't necessarily need to be code, actually theory and an understanding of how FM or AM modulation works when it comes to processing a byte array and converting it to the proper format would probably be more valuable since I'll have to implement more modes in the future.
There is a lot that I don't know about radio. But I think I can say a couple things about the basics of modulation and the problem at hand given the modicum of physics that I have and the experience of coding an FM synthesizer.
First off, I think you might find it easier to work with the source signal's PCM data points if you convert them to normalized floats (ranging from -1f to 1f), rather than working with shorts.
The target frequency of the receiver, 510-1700 kHz (AM radio) is significantly faster than the sample rate of the source sound (presumably 44.1kHz). Assuming you have a way to output the resulting data, the math would involve taking a PCM value from your signal, scaling it appropriately (IDK how much) and multiplying the value against the PCM data points generated by your carrier signal that corresponds to the time interval.
For example, if the carrier signal were 882 kHz, you would multiply a sequence of 20 carrier signal values with the source signal value before moving on to the next source signal value. Again, my ignorance: the tech may have some sort of smoothing algorithm for the transition between the source signal data points. I really don't know about that or not, or at what stage it occurs.
For FM, we have carrier signals in the MHz range, so we are talking orders of magnitude more data being generated per each source signal value than with AM. I don't know the exact algorithm used but here is a simple conceptual way to implement frequency modulation of a sine that I used with my FM synthesizer.
Let's say you have a table with 1000 data points that represents a single sine wave that ranges between -1f to 1f. Let's say you have a cursor that repeatedly traverses the table. If the cursor advanced exactly 1 data point at 44100 fps and delivered the values at that rate, the resulting tone would be 44.1 Hz, yes? But you can also traverse the table via intervals larger than 1, for example 1.5. When the cursor lands in between two table values, one can use linear interpolation to determine the value to output. The cursor increment of 1.5 would result in the sine wave being pitched at 66.2 Hz.
What I think is happening with FM is that this cursor increment is continuously varied, and the amount it is varied depends on some sort of scaling from the source signal translated into a range of increments.
The specifics of the scaling are unknown to me. But suppose a signal is being transmitted with a carrier of 10MHz and ranges ~1% (roughly from 9.9 MHz to 10.1 MHz), the normalized source signal would have some sort of algorithm where a PCM value of -1 match an increment that traverses the carrier wave causing it to produce the slower frequency and +1 match an increment that traverses the carrier wave causing it to produce the higher frequency. So, if an increment of +1 delivers 10 MHz, maybe a source wave PCM signal of -1 elicits a cursor increment of +0.99, a PCM value of -0.5 elicits an increment of +0.995, a value of +0.5 elicits an increment of +1.005, a value of +1 elicits a cursor increment of 1.01.
This is pure speculation on my part as to the relationship between the source PCM values and how that are used to modulate the carrier frequency. But maybe it helps give a concrete image of the basic mechanism?
(I use something similar, employing a cursor to iterate over wav PCM data points at arbitrary increments, in AudioCue (a class for playing back audio data based on the Java Clip), for real time frequency shifting. Code line 1183 holds the cursor that iterates over the PCM data that was imported from the wav file, with the variable idx holding the cursor increment amount. Line 1317 is where we fetch the audio value after incrementing the cursor. Code lines 1372 has the method readFractionalFrame() which performs the linear interpolation. Real time volume changes are also implemented, and I use smoothing on the values that are provided from the public input hooks.)
Again, IDK if any sort of smoothing is used between source signal values or not. In my experience a lot of the tech involves filtering and other tricks of various sorts that improve fidelity or processing calculations.
How can I get the magnitudes and corresponding frequencies after performing FFT on a dataset. I need to plot the magnitude vs frequencies for a dataset. Also, why are we increasing the size of our FFT array as twice the size of actual dataset? Then the size of resulting output array is again different, Please help me understand this FFT code. Further, when is complexforward FFT and when realForward FFT is performed? Difference between the two? I need to perform FFT on a dataset and get the magnitude after FFT and corresponding frequencies for each magnitude.
int length = data.length;
FloatFFT_1D fftDo = new FloatFFT_1D(length);
float[] fft = new float[length * 2];
System.arraycopy(data, 0, fft, 0, length);
fftDo.complexForward(fft);
//for(double d: fft) {
//System.out.println(d);
//}
float outputfft[] = new float[(fft.length+1)/2];
if(fft.length%2==0){
for(int i = 0; i < length/2; i++){
outputfft[i]= (float) Math.sqrt((Math.pow(fft[2*i],2))+(Math.pow(fft[(2*(i))+1], 2)));
}
}else{
for(int i = 0; i < (length/2)+1; i++){
outputfft[i]= (float) Math.sqrt((Math.pow(fft[2*i],2))+(Math.pow(fft[(2*i)+1], 2)));
}
}
for (float f : outputfft) {
System.out.println(f);
}
The FFT of a real-valued data vector is by definition complex and symmetric. If you have a vector length of N samples you will get a FFT of N frequency data separated in frequency by Fs/N, where Fs is the sampling frequency. Your output vector is twice the size since the complex data is interleaved [re,im,re,im ...].
The output data is half the size since it is symmetric and you only need to view the first half corresponding to frequencies [0 .. Fs/2] the upper half is [-Fs/2 .. 0)
If you have an even-symmetric input data, X(-n)=X(n), or odd-symmetric, X(-n)=-X(n), you may use the realForward function
I've written this small signal generating method. My goal is to generate a beep with a slight time delay between the two channels (left and right) or a slight difference in gain between the channels.
Currently I create the delay by filling a buffer with zeros for one channel and values for the second and further down swapping the behavior between the channels (If you have any tips or ideas how to do this better it would be appreciated.)
The next stage is doing something similar with the gain. I have seen that Java gives built in gain control via FloatControl:
FloatControl gainControl =
(FloatControl) sdl.getControl(FloatControl.Type.MASTER_GAIN);
But I am not sure how to control the gain for each channel separately. Is there a built in way to do this?
Would I need two separate streams, one for each channel? If so how do I play them simultaneously?
I am rather new to sound programming, if there are better ways to do this please let me know. Any help is very much appreciated.
This is my code so far:
public static void generateTone(int delayR, int delayL, double gainRightDB, double gainLeftDB)
throws LineUnavailableException, IOException {
// in hz, number of samples in one second
int sampleRate = 100000; // let sample rate and frequency be the same
// how much to add to each side:
double gainLeft = 100;//Math.pow(10.0, gainLeftDB / 20.0);
double gainRight = 100;// Math.pow(10.0, gainRightDB / 20.0);;
// click duration = 40 us
double duration = 0.08;
double durationInSamples = Math.ceil(duration * sampleRate);
// single delay window duration = 225 us
double baseDelay = 0.000225;
double samplesPerDelay = Math.ceil(baseDelay * sampleRate);
AudioFormat af;
byte buf[] = new byte[sampleRate * 4]; // one second of audio in total
af = new AudioFormat(sampleRate, 16, 2, true, true); // 44100 Hz, 16 bit, 2 channels
SourceDataLine sdl = AudioSystem.getSourceDataLine(af);
sdl.open(af);
sdl.start();
// only one should be delayed at a time
int delayRight = delayR;
int delayLeft = delayL;
int freq = 1000;
/*
* NOTE:
* The buffer holds data in groups of 4. Every 4 bytes represent a single sample. The first 2 bytes
* are for the left side, the other two are for the right. We take 2 each time because of a 16 bit rate.
*
*
*/
for(int i = 0; i < sampleRate * 4; i++){
double time = ((double)i/((double)sampleRate));
// Left side:
if (i >= delayLeft * samplesPerDelay * 4 // when the left side plays
&& i % 4 < 2 // access first two bytes in sample
&& i <= (delayLeft * 4 * samplesPerDelay)
+ (4 * durationInSamples)) // make sure to stop after your delay window
buf[i] = (byte) ((1+gainLeft) * Math.sin(2*Math.PI*(freq)*time)); // sound in left ear
//Right side:
else if (i >= delayRight * samplesPerDelay * 4 // time for right side
&& i % 4 >= 2 // use second 2 bytes
&& i <= (delayRight * 4 * samplesPerDelay)
+ (4 * durationInSamples)) // stop after your delay window
buf[i] = (byte) ((1+gainRight) * Math.sin(2*Math.PI*(freq)*time)); // sound in right ear
}
for (byte b : buf)
System.out.print(b + " ");
System.out.println();
sdl.write(buf,0,buf.length);
sdl.drain();
sdl.stop();
sdl.close();
}
How far apart did you want to have your beeps? I wrote a program that made sine beeps sound up to a couple hundred frames (at 44100 fps) apart, and posted it with source code here which you are welcome to inspect/copy/rewrite.
At such low levels of separation, the sound remains fused, perceptually, but can start to move to one ear or another. I wrote this because I wanted to compare volume panning with delay-based panning. In order to be able to flexibly test multiple files, the code is a slightly more modular than what you have started with. I'm not going to claim what I wrote is any better, though.
One class takes a mono PCM (range is floats, -1 to 1) array and converts it to a stereo array with the desired frame delay between the channels. That same class can also split the mono file into a stereo file where the only difference is volume, and has a third option where you can use a combination of delay and volume differences when you turn the mono data to stereo.
Monofile: F1, F2, F3, ...
Stereofile F1L, F1R, F2L, F2R, F3L, F3R, ...
but if you add delay, say 2 frames to the right:
Stereofile F1L, 0, F2L, 0, F3L, F1R, F4L, F2R, ...
Where F is a normalized float (between -1 and 1) representing an audio wave.
Making the first mono array of a beep is just a matter of using a sine function pretty much as you do. You might 'round off the edges' by ramping the volume over the course of some frames to minimize the clicks that come from the discontinuities of suddenly starting or stopping.
Another class was written whose sole purpose is to output stereo float arrays via a SourceDataLine. Volume is handled by multiplying the audio output by a factor that ranges from 0 to 1. The normalized values are multiplied by 32767 to convert them to signed shorts, and the shorts broken into bytes for the format that I use (16-bit, 44100 fps, stereo, little-endian).
Having an array-playing audio class is kind of neat. The arrays for it are a lot like Clips, but you have direct access to the data. With this class, you can build and reuse many sound arrays. I think I have some code included that loads a wav file into this DIY clip, as well.
There is more discussion of this code on this thread at Java-Gaming.org.
I eventually used some of what I learned here to make a simplified real-time 3D sound system. The "best" way to set up something like this, though, depends on your goals. For my 3D, for example, I wrote a delay tool that allows separate reads from stereo left and right, and the audio mixer & playback is more involved than the simple array-to-SourceDataLine player.
Im trying to implement something like discribed here and here, Specifically i want to be able to perform the following operation as in the following image :
That is, given N discrete points with constant time interval, i want to create a function that converges to those points as in the image...
So far what i did was :
imported jtransform
used it
private double[] doDFT(double[] data, int start, int end) {
DoubleFFT_1D doubleFFT_1D = new DoubleFFT_1D(end-start);
double[] array = new double[(end-start)*2];
for (int i=0;i<end-start;i++) {
array[i] = data[i+start];
array[i+1] = data[i+start+1];
}
doubleFFT_1D.complexForward(array);
return array;
}
and Now im stuck, how do i use the output array to produce the function that converges to the points in the original data array?
Just to clearify what i want : for example in the image the data array that is inputted to doDFT is the blue line plot, and what i want is to produce a function f that its image is the red line plot.
You probably want to set the imaginary component of your complex input to zero, not to the next point.
The functions you want are sinusoids. Each sinusoid will have a frequency of an FFT result bin index * Fs/N. The magnitude and phase of each sinusoid will be given by the complex value corresponding to its FFT result bin.
You can sum an increasing number of these sinusoids, starting with 1, to get your converging waveforms.