Using apache's FFT to convert a .wav file to freq array - java

Here's what i have so far:
InputStream is = AudioSystem.getAudioInputStream(new File("C:/users/barnao/desktop/uotm.wav"));
byte[] bytes = IOUtils.toByteArray(is);
short[] shorts = new short[bytes.length/2];
ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);
double[] vals = new double[shorts.length];
for (int i = 0; i < shorts.length; i++)
vals[i] = (double) shorts[i];
FastFourierTransformer fft = new FastFourierTransformer(DftNormalization.STANDARD);
Complex[] freqs = fft.transform(vals, TransformType.FORWARD);
However i'm getting an error message that the input isn't a power of 2...
Exception in thread "main" org.apache.commons.math3.exception.MathIllegalArgumentException: 31,590,166 is not a power of 2, consider padding for fix
How do i fix this?

Related

Extract data from .wav file in Java

I am trying to extract data from .wav file to draw a wave graph, but I am stuck as I get the only a stream of 0s with my code:
AudioInputStream ais = AudioSystem.getAudioInputStream(new File("audio/sine_-06_05_02000.wav"));
AudioFormat format = ais.getFormat();
format = new AudioFormat(format.getFrameRate(), format.getSampleSizeInBits(), format.getChannels(), true, true);
ais = AudioSystem.getAudioInputStream(format, ais);
int sample_size = format.getSampleSizeInBits() / 8;
ArrayList<Long> data = new ArrayList<Long>();
int size = 400;
while (data.size() < size)
{
byte[] buffer = new byte[8];
ais.read(buffer, 8-sample_size, sample_size);
if (buffer[8-sample_size] < 0)
{
for (int i = 0; i < 8 - sample_size; i++)
{
buffer[i] = -1;
}
}
data.add(ByteBuffer.wrap(buffer).getLong());
}
for(long value:data)
{
System.out.println(value);
}
Please tell me why I cannot get the data and where my code is wrong if you could find out. Thank you!
Edit: I figured it out that my audio resource was digital, but the code was for analog audio.

Converting Bytes To BitSets

I am trying to figure out a way of taking data from a file and I want to store every 4 bytes as a bitset(32). I really have no idea of how to do this. I have played about with storing each byte from the file in an array and then tried to covert every 4 bytes to a bitset but I really cannot wrap my head around using bitsets. Any ideas on how to go about this?
FileInputStream data = null;
try
{
data = new FileInputStream(myFile);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int bytesRead;
while ((bytesRead = data.read(b)) != -1)
{
bos.write(b, 0, bytesRead);
}
byte[] bytes = bos.toByteArray();
Ok, you got your byte array. Now what you have to convert each byte to a bitset.
//Is number of bytes divisable by 4
bool divisableByFour = bytes.length % 4 == 0;
//Initialize BitSet array
BitSet[] bitSetArray = new BitSet[bytes.length / 4 + divisableByFour ? 0 : 1];
//Here you convert each 4 bytes to a BitSet
//You will handle the last BitSet later.
int i;
for(i = 0; i < bitSetArray.length-1; i++) {
int bi = i*4;
bitSetArray[i] = BitSet.valueOf(new byte[] { bytes[bi], bytes[bi+1], bytes[bi+2], bytes[bi+3]});
}
//Now handle the last BitSet.
//You do it here there may remain less than 4 bytes for the last BitSet.
byte[] lastBitSet = new byte[bytes.length - i*4];
for(int j = 0; j < lastBitSet.length; j++) {
lastBitSet[i] = bytes[i*4 + j]
}
//Put the last BitSet in your bitSetArray
bitSetArray[i] = BitSet.valueOf(lastBitSet);
I hope this works for you as I have written quickly and did not check if it works. But this gives you the basic idea, which was my intention at the beginning.

Is it possible to perform XOR operation on 4bytes at a time

i need to perform XOR operation on 4 bytes which are represented as a single byte
b[0]=97;
b[1]=98;
b[2]=99;
b[3]=100;
int temp=0;
int temp1=0;
int temp2=0;
int temp3=0;
int temp4=0;
temp1=temp1|b[0];
temp1=temp1<<24;
temp2=temp2|b[1];
temp2=temp2<<16;
temp3=temp3|b[2];
temp3=temp3<<8;
temp4=temp4|b[3];
temp=temp4|temp3|temp2|temp1;
i have used the above code to convert the four bytes into a single integer. Now how can i represent this integer in a byte
// Assuming you have 2 byte arrays that you want to XOR
byte[] ba1 = new byte[4];
byte[] ba2 = new byte[4];
// Just filling up with arb. values
ba1[0] = 99;
ba1[1] = 100;
ba1[2] = 101;
ba1[3] = 102;
ba2[0] = 10;
ba2[1] = 11;
ba2[2] = 12;
ba2[3] = 13;
// To store the XOR in ba1 (or new byte[] if you want)
ba1[0] = ba1[0] ^ ba2[0];
ba1[1] = ba1[1] ^ ba2[1];
ba1[2] = ba1[2] ^ ba2[2];
ba1[3] = ba1[3] ^ ba2[3];
Now you can convert it to an integer again if you want. But from how I understand your question you actually want them in byte[] format.

Java - Mixing audio files generates unwanted white noise

Recently, I've been experimenting with mixing AudioInputStreams together. After reading this post, or more importantly Jason Olson's answer, I came up with this code:
private static AudioInputStream mixAudio(ArrayList audio) throws IOException{
ArrayList<byte[]> byteArrays = new ArrayList();
long size = 0;
int pos = 0;
for(int i = 0; i < audio.size(); i++){
AudioInputStream temp = (AudioInputStream) audio.get(i);
byteArrays.add(convertStream(temp));
if(size < temp.getFrameLength()){
size = temp.getFrameLength();
pos = i;
}
}
byte[] compiledStream = new byte[byteArrays.get(pos).length];
for(int i = 0; i < compiledStream.length; i++){
int byteSum = 0;
for(int j = 0; j < byteArrays.size(); j++){
try{
byteSum += byteArrays.get(j)[i];
}catch(Exception e){
byteArrays.remove(j);
}
}
compiledStream[i] = (byte) (byteSum / byteArrays.size());
}
return new AudioInputStream(new ByteArrayInputStream(compiledStream), ((AudioInputStream)audio.get(pos)).getFormat(), ((AudioInputStream)audio.get(pos)).getFrameLength());
}
private static byte[] convertStream(AudioInputStream stream) throws IOException{
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int numRead;
while((numRead = stream.read(buffer)) != -1){
byteStream.write(buffer, 0, numRead);
}
return byteStream.toByteArray();
}
This code works very well for mixing audio files. However, it seems the more audio files being mixed, the more white noise that appears in the returned AudioInputStream. All of the files being combined are identical when it comes to formatting. If anyone has any suggestions\advice, thanks in advance.
I could be wrong, but I think your problem has to do with the fact that you are messing with the bytes instead of what the bytes mean. For instance, if you are working with a 16 bit sampling rate, 2 bytes form the number that corresponds to the amplitude rather than just 1 byte. So, you end up getting something close but not quite right.

Android audio FFT to retrieve specific frequency magnitude using audiorecord

I am currently trying to implement some code using Android to detect when a number of specific audio frequency ranges are played through the phone's microphone. I have set up the class using the AudioRecord class:
int channel_config = AudioFormat.CHANNEL_CONFIGURATION_MONO;
int format = AudioFormat.ENCODING_PCM_16BIT;
int sampleSize = 8000;
int bufferSize = AudioRecord.getMinBufferSize(sampleSize, channel_config, format);
AudioRecord audioInput = new AudioRecord(AudioSource.MIC, sampleSize, channel_config, format, bufferSize);
The audio is then read in:
short[] audioBuffer = new short[bufferSize];
audioInput.startRecording();
audioInput.read(audioBuffer, 0, bufferSize);
Performing an FFT is where I become stuck, as I have very little experience in this area. I have been trying to use this class:
FFT in Java and Complex class to go with it
I am then sending the following values:
Complex[] fftTempArray = new Complex[bufferSize];
for (int i=0; i<bufferSize; i++)
{
fftTempArray[i] = new Complex(audio[i], 0);
}
Complex[] fftArray = fft(fftTempArray);
This could easily be me misunderstanding how this class is meant to work, but the values returned jump all over the place and aren't representative of a consistent frequency even in silence. Is anyone aware of a way to perform this task, or am I overcomplicating matters to try and grab only a small number of frequency ranges rather than to draw it as a graphical representation?
First you need to ensure that the result you are getting is correctly converted to a float/double. I'm not sure how the short[] version works, but the byte[] version only returns the raw byte version. This byte array then needs to be properly converted to a floating point number. The code for the conversion should look something like this:
double[] micBufferData = new double[<insert-proper-size>];
final int bytesPerSample = 2; // As it is 16bit PCM
final double amplification = 100.0; // choose a number as you like
for (int index = 0, floatIndex = 0; index < bytesRecorded - bytesPerSample + 1; index += bytesPerSample, floatIndex++) {
double sample = 0;
for (int b = 0; b < bytesPerSample; b++) {
int v = bufferData[index + b];
if (b < bytesPerSample - 1 || bytesPerSample == 1) {
v &= 0xFF;
}
sample += v << (b * 8);
}
double sample32 = amplification * (sample / 32768.0);
micBufferData[floatIndex] = sample32;
}
Then you use micBufferData[] to create your input complex array.
Once you get the results, use the magnitudes of the complex numbers in the results. Most of the magnitudes should be close to zero except the frequencies that have actual values.
You need the sampling frequency to convert the array indices to such magnitudes to frequencies:
private double ComputeFrequency(int arrayIndex) {
return ((1.0 * sampleRate) / (1.0 * fftOutWindowSize)) * arrayIndex;
}

Categories

Resources