I need to perform spectral analysis of a simple wav file.
The things I have already done :
Read file into byte array :
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead = 0;
while ((bytesRead = audioStream.read(buffer)) != -1) {
baos.write(buffer, 0, bytesRead);
}
fileByteArray = baos.toByteArray();
Then I transform it to the real values (doubles).
I've got sample values stored in double[] array.
How can I make FFT of those samples + estimate fundamental frequency?
Using JTranforms library I tried something like this :
DoubleFFT_1D fft = new DoubleFFT_1D(reader.getSpectrum().getYvalues().length);
double[] x = reader.getSpectrum().getYvalues();
double[] frequencyArray = new double[x.lenght/2];
double[] amplitudeArray = new double[x.lenght/2];
fft.realForward(x);
int i=0;
for (int j = 0; j < x.length-2; j += 2) {
i++;
this.frequencyArray[i] = i;
this.amplitudeArray[i] = Math.sqrt(Math.pow(doub[j],2) + Math.pow(doub[j + 1],2));
}
Is it correct?
All suggestions are appreciated ;)
You should use autocorrelation which can be computed efficiently with FFT:
DoubleFFT_1D fft = new DoubleFFT_1D(reader.getSpectrum().getYvalues().length);
DoubleFFT_1D ifft = new DoubleFFT_1D(reader.getSpectrum().getYvalues().length);
fft.realForward(x);
for (int i = 0; i < x.length/2; i++) {
x[2*i] = Math.sqrt(Math.pow(x[2*i],2) + Math.pow(x[2*i+1],2));
x[2*i+1] = 0;
}
ifft.realInverse(x);
for (int i = 1; i < x.length; i++)
x[i] /= x[0];
x[0] = 1.0;
This code gives you a list of values for which:
x[i]: corelation with i shifts
So, for example if you have a high value (close to 1) for x[n], that means you have a fundemental signal period that is: n*(1000/sampleRateHz) msecs. This is equivalent to a frequency of: sampleRateHz/(1000*n)
The values in the frequency array need to be related to the sample rate and the length of the FFT.
You will still need to solve the problem of determining the fundamental frequency versus the peak frequencies. For that, you may want to use a pitch detection/estimation algorithm, of which there are many (look for research papers on the topic).
Related
Now I can get only array of bytes from audio file. But I need frequency of sound. How I can get it. I'm trying the fft. But after that I get very big numbers and it's not frequency. Of course, I can't to mult to i, because this is Java
private static double[] fft(byte[] bytes) {
double[] fft = new double[bytes.length];
for (int k = 0; k < bytes.length; k++) {
for (int n = 0; n < bytes.length; n++) {
fft[k] += bytes[n] * Math.pow(Math.E, -2 * Math.PI * k * n / bytes.length);
}
}
return fft;
}
I would suggest using a complex type for your FFT calculations, it will make everything simpler (or at least more readable) and doesn't add a lot of overhead.
I am not a java person, it doesnt seem like JDK has a built in complex type BUT implementations like this exist:
https://introcs.cs.princeton.edu/java/97data/Complex.java.html
Your FFT could then be something like this (a bit unoptimized pseudo code!):
private static Complex[] fft(byte[] bytes) {
Complex[] fft = new Complex[bytes.length];
for (int k = 0; k < bytes.length; k++) {
for (int n = 0; n < bytes.length; n++) {
Complex temp = new Complex (0,-2 * Math.PI * k * n/bytes.length);
fft[k] += bytes[n] * Complex.exp(temp);
}
}
return fft;
}
You can get the magnitudes with something like
Complex.abs(fft[k])
I would also look at your outer loop (k), this is the size of your FFT and it will currently be the length of the input. This may or may not be what you want, I would suggest looking at signal windowing.
I am trying to read and parse a few pieces of information from a file that will be stored in sequential order. ,a char[] of size 8 ,an int ,an int[] of size 8 and finally ,an int.
So, I am reading 56 bytes of information. I am using RandomAccessFile and was wondering if I needed to seek() after preforming each operation of readChar() and readInt() or if I can just call these methods one after the other. I guess this is more of a question about whether the file pointer will reset after each operation completes or if it's safe so assume that the fp will follows it's last location until the file is closed.
Here is what I've written:
int currentOffset = 128;
for(int i = 0; i<16; i++){
//Initialize nodes.
readDisk.seek(currentOffset);
char[] name = new char[8];
for(int j = 0; j<8; j++){
name[i] = readDisk.readChar();
}
int size = readDisk.readInt();
int[] blockPointers = new int[8];
for(int j = 0; j<8; j++){
blockPointers[i] = readDisk.readInt();
}
int used = readDisk.readInt();
Will the fp be at 156 after these operations? Thank you! Sorry if this is a silly question.
Yes, read moves file pointer by number of bytes read, try this
RandomAccessFile f = new RandomAccessFile("1.txt", "r");
f.readChar();
System.out.println(f.getFilePointer());
output
2
I am a beginner in using OpenCV for JAVA. I want to access individual pixel values of an image matrix. Since, JAVA jar for OpenCV doesn't offer nice functions like C++, I ran into some trouble. After lot of searching, I found out two different methods to do that though they are not explained properly (not even in documentation). We can do that either using get() and put() functions or by converting the mat data into a primitive java type such as arrays. I tried both but getting different output results! Please help explaining what am I doing wrong. Am I using them wrong or some other silly problem. I am still a newbie so please forgive if its a stupid question. :)
CASE 1: Using get() function
Mat A = Highgui.imread(image_addr); \\"image_addr" is the address of the image
Mat C = A.clone();
Size sizeA = A.size();
for (int i = 0; i < sizeA.height; i++)
for (int j = 0; j < sizeA.width; j++) {
double[] data = A.get(i, j);
data[0] = data[0] / 2;
data[1] = data[1] / 2;
data[2] = data[2] / 2;
C.put(i, j, data);
}
CASE 2: Using Array
Mat A = Highgui.imread(image_addr); \\"image_addr" is the address of the image
Mat C = A.clone();
int size = (int) (A.total() * A.channels());
byte[] temp = new byte[size];
A.get(0, 0, temp);
for (int i = 0; i < size; i++)
temp[i] = (byte) (temp[i] / 2);
C.put(0, 0, temp);
Now according to my understanding they both should do the same thing. They both access the individual pixel values (all 3 channels) and making it half. I am getting no error after running. But, the output image I am getting is different in these two cases. Can someone please explain what is the issue? May be I don't understand exactly how get() function works? Is it because of the byte() casting? Please help.
Thanks!
It was happening because of byte() casting. I changed the data type of mat image in second case to *CV_64FC3* so that I can use double[] instead of byte[] and it solved the problem.
Mat A = Highgui.imread(image_addr); //"image_addr" is the address of the image
Mat C = A.clone();
A.convertTo(A, CvType.CV_64FC3); // New line added.
int size = (int) (A.total() * A.channels());
double[] temp = new double[size]; // use double[] instead of byte[]
A.get(0, 0, temp);
for (int i = 0; i < size; i++)
temp[i] = (temp[i] / 2); // no more casting required.
C.put(0, 0, temp);
FYI, I also did some time measurement and using second method is way faster than first method.
Found a simple and working solution after a lot of searching-
Mat img = Highgui.imread("Input.jpg"); //Reads image from the file system and puts into matrix
int rows = img.rows(); //Calculates number of rows
int cols = img.cols(); //Calculates number of columns
int ch = img.channels(); //Calculates number of channels (Grayscale: 1, RGB: 3, etc.)
for (int i=0; i<rows; i++)
{
for (int j=0; j<cols; j++)
{
double[] data = img.get(i, j); //Stores element in an array
for (int k = 0; k < ch; k++) //Runs for the available number of channels
{
data[k] = data[k] * 2; //Pixel modification done here
}
img.put(i, j, data); //Puts element back into matrix
}
}
Highgui.imwrite("Output.jpg", img); //Writes image back to the file system using values of the modified matrix
Note: An important point that has not been mentioned anywhere online is that the method put does not write pixels onto Input.jpg. It merely updates the values of the matrix img. Therefore, the above code does not alter anything in the input image. For producing a visible output, the matrix img needs to be written onto a file i.e., Output.jpg in this case. Also, using img.get(i, j) seems to be a better way of handling the matrix elements rather than using the accepted solution above as this helps in visualizing and working with the image matrix in a better way and does not require a large contiguous memory allocation.
I'd really like assessing if any of you could point me towards the most optimized and computetionally quick linear algebra library in terms of Cholesky factorization.
So far I've been using the Apache Commons Math library, but perhaps there are more robust and better-enhanced options already available.
For instance, would PColt, EJML or ojAlgo better choices? The most urgent concerns is mainly one: I need to iteratively calculate (within a 2048 elements for loop generally) the lower triangular Cholesky factor for up to three different matrices; the largest size the matrices will reach is about 2000x2000.
Cholesky factorisation is quite a simple algorithm. Here's the (unoptimised) C# code that I use. C# and Java are quite similar, so should be an easy job for you to convert to Java and make whatever improvements you deem necessary.
public class CholeskyDecomposition {
public static double[,] Do(double[,] input) {
int size = input.GetLength(0);
if (input.GetLength(1) != size)
throw new Exception("Input matrix must be square");
double[] p = new double[size];
double[,] result = new double[size, size];
Array.Copy(input, result, input.Length);
for (int i = 0; i < size; i++) {
for (int j = i; j < size; j++) {
double sum = result[i, j];
for (int k = i - 1; k >= 0; k--)
sum -= result[i, k] * result[j, k];
if (i == j) {
if (sum < 0.0)
throw new Exception("Matrix is not positive definite");
p[i] = System.Math.Sqrt(sum);
} else
result[j, i] = sum / p[i];
}
}
for (int r = 0; r < size; r++) {
result[r, r] = p[r];
for (int c = r + 1; c < size; c++)
result[r, c] = 0;
}
return result;
}
}
Have a look at the Java Matrix Benchmark. The "Inver Symm" case test inverting a matrix using the cholesky decomposition. If you get the source code for the benchmark there is also a pure cholesky decomposition test that you can turn on.
Here's another comparison of various matrix decompositions between ojAlgo and JAMA
I have two-dimensional array of integers. First index indicates the number of channels. The second one indicates the number of sample in the channel. How can I save this array into the audio file? I know, I have to convert it to byte array, but I have no idea how to do that.
// edit
More info. I already have a class for drawing a waveform. It is here:
http://javafaq.nu/java-example-code-716.html
now I want to cut part of this wave and save it to the new file. So I have to cut part of int[][] samplesContainer, convert it to byte array (I don't know how) and then save it to file with the same format as audioInputStream.
// edit
OK. So the biggest problem is to write inverted function to this one:
protected int[][] getSampleArray(byte[] eightBitByteArray) {
int[][] toReturn = new int[getNumberOfChannels()][eightBitByteArray.length / (2 * getNumberOfChannels())];
int index = 0;
//loop through the byte[]
for (int t = 0; t < eightBitByteArray.length;) {
//for each iteration, loop through the channels
for (int a = 0; a < getNumberOfChannels(); a++) {
//do the byte to sample conversion
//see AmplitudeEditor for more info
int low = (int) eightBitByteArray[t];
t++;
int high = (int) eightBitByteArray[t];
t++;
int sample = (high << 8) + (low & 0x00ff);
if (sample < sampleMin) {
sampleMin = sample;
} else if (sample > sampleMax) {
sampleMax = sample;
}
//set the value.
toReturn[a][index] = sample;
}
index++;
}
return toReturn;
}
I don't understand why there is second incrementation of t, after high. I also have no idea how can i get high and low from sample.
The code you posted reads a sample stream, byte by byte, into the samples array. The code assumes that, in the stream, every two 8-bit bytes form a 16-bit sample, and that there is one sample for each of the NumOfChannels channels.
So, given an array of samples like the one returned by that code,
int[][] samples;
and a byte array for streaming,
byte[] stream;
you might build the converse stream of bytes this way
for (int i=0; i<NumOfSamples; i++) {
for (int j=0; j<NumOfChannels; j++) {
int sample=samples[i][j];
byte low = (byte) (sample & 0xff) ;
byte high = (byte) ((sample & 0xff00 ) >> 8);
stream[((i*NumOfChannels)+j)*2] = low;
stream[(((i*NumOfChannels)+j)*2)+1] = high;
}
}