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.
Related
I am using mp3spi and Triton, and this code will handle exclusively 192kbps mp3 files.
The problem I am facing is that the first second of hz is mostly made up of exclusively:
0,0,0,0 or 255,255,255,255
I do believe I might not be skipping the header correct, in which case the frequencies are not a true depiction of the mp3 at that specific ms. Does anyone see anything wrong with the way im skipping the header, or how im adding up the bytes to the array?
In other words, I want it so the array at position [0] is equal to the mp3 at position 00:00:00, and the array at position [44100] is equal to the song at exactly 1 second in.
This is the code I use for reading the bytes from the mp3 file, adding it to the arraylist bytes.
import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
public class ReadMP3 {
private ArrayList<Integer> bytes = new ArrayList<>();
private AudioFormat decodedFormat;
public ReadMP3() throws UnsupportedAudioFileException, IOException {
String filename = new ReadFiles().getFile();
File file = new File(filename);
AudioInputStream in = AudioSystem.getAudioInputStream(file);
AudioInputStream din = null;
AudioFormat baseFormat = in.getFormat();
AudioFormat decodedFormat = new
AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
baseFormat.getSampleRate(),
16,
baseFormat.getChannels(),
baseFormat.getChannels() * 2,
baseFormat.getSampleRate(),
false);
din = AudioSystem.getAudioInputStream(decodedFormat, in);
this.decodedFormat = decodedFormat;
int i = 0;
while(true){
int currentByte = din.read();
if (currentByte == -1) {break;}
bytes.add(i, currentByte);
i++;
}
din.close();
in.close();
}
This is the second part of my code, where I add 4 bytes to each index of the array, such that array.length / 44100 is equal to the length of the song in seconds. This implies that each array[i][4] is equal to 1hz.
and array[0][4] up to array[44100][4] is the first second of the song.
public class AnalyzeMP3 {
//adds 4 bytes to offset[i], where each i represents 1hz,
//and 44100hz=1sec
public static int[][] calculate(ReadMP3 mp3) {
//calculates and prints how long the song is
double seconds = mp3.getBytes().size() /
mp3.getDecodedFormat().getFrameRate() / 4;
System.out.println("Length of song: " + (int)seconds + "s");
//adds 4 values to i through the whole song
int[][] offset = new int[mp3.getBytes().size()/4][4];
for(int i = 0; i < mp3.getBytes().size()/4; i++) {
for(int j = 0; j < 4; j++) {
offset[i][j] = mp3.getBytes().get(i+j);
}
}
return offset;
}
}
Thanks Brad and VC.One for making me realize my own mistakes.
To begin with I had to add the correct values to the PCM-signed encoding like this:
AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
(float)44.1, //samplerate
16, //sampleSizeInBits
2, //channels
626, //frameSize
(float)38.4615385, //frameRate
false); //bigEndian
Then I needed to accurately represent the 2 channels in an array. How I did it above in the class AnalyzeMP3 is wrong, and it should be added like this:
//adds 4 values to i through the whole song
int[][] offset = new int[mp3.getBytes().size()/4][4];
int counter = 0;
for(int i = 0; i < mp3.getBytes().size()/4;i++) {
for(int j = 0; j < 4; j++) {
offset[i][j] = mp3.getBytes().get(counter);
counter++;
}
}
After making these changes the array is 4351104 in size. 4351104 / 44100 is equal to the song length in seconds. And there is no header or anything I have to skip, the array is now an accurate representation of the whole song with 44100 frequencies each second. Which can easily be transformed to represent 10ms as 441 frequencies, etc.
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?
Today I was working with the Xuggler library and I tried capturing my screen which worked flawless. But I also wanted to add audio from my microphone to the video file I captured. This was not as easy as I had expected, and now I'm stuck with this strange NullPointerException.
This is my code (abbreviated):
AudioFormat format = new AudioFormat(8000.0F, 16, 1, true, false);
writer.addAudioStream(1, 0, 1, (int) format.getSampleRate());
TargetDataLine line = getTargetDataLineForRecord(format);
final int frameSizeInBytes = format.getFrameSize();
final int bufferLengthInFrames = line.getBufferSize() / 8;
final int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
final byte[] buf = new byte[bufferLengthInBytes];
final long startTime = System.nanoTime();
...
while (recording) {
int numBytesRead = 0;
numBytesRead = line.read(buf, 0, bufferLengthInBytes);
int numSamplesRead = numBytesRead / 2;
short[] audioSamples = new short[numSamplesRead];
if (format.isBigEndian()) {
for (int i = 0; i < numSamplesRead; i++) {
audioSamples[i] = (short) ((buf[2 * i] << 8) | buf[2 * i + 1]);
}
} else {
for (int i = 0; i < numSamplesRead; i++) {
audioSamples[i] = (short) ((buf[2 * i + 1] << 8) | buf[2 * i]);
}
}
writer.encodeAudio(1, audioSamples, System.nanoTime() - startTime, TimeUnit.NANOSECONDS); // CaptureScreen.java:118
}
writer.close();
And here is the NullPointerException:
java.lang.NullPointerException
at com.xuggle.mediatool.MediaWriter.encodeAudio(MediaWriter.java:923)
at exe.media.CaptureScreen.captureScreen(CaptureScreen.java:118)
at exe.media.CaptureScreen.main(CaptureScreen.java:43)
The problem I'm having is at this line (118):
writer.encodeAudio(1, audioSamples, System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
For some reason when I try to encode the audio samples, xuggle throws a NullPointerException, I'm not sure if this is a bug or just me doing something stupid but I am unable to solve it anyway.
For better understanding I have posted all the code on pastebin and this includes code for capturing my screen and also this code where I try to record the audio.
These are the jars I have included:
commons-cli-1.2.jar
logback-classic-1.1.2.jar
logback-core-1.1.2.jar
xuggle-xuggler-arch-x86_x64-w64-mingw32.jar*
xuggle-xuggler-noarch-5.4.jar*
(The '*' means I didn't download the jar from it's primary location.
Thanks in advance and remember ANY helpful answer will be rewarded the 50 rep bounty!
I assume that you have line.open(format); and line.start(); somewhere?
You may need to make sure that you have audiosamples: if (audioSamples.length > 0) writer.encodeAudio ...
Also, you may want to add slf4j-api-1.6.4.jar and slf4j-simple-1.6.4.jar to get more details on the errors from Xuggler.
I am developing an application that uses mp3 encoding/decoding. While in principle it behaves correctly for most of the files, there are some exceptions. I detected that these files have a missing header. I get an array out of bound exception when attempting to decode. I used two approaches but both failed.
The first:
DecodedMpegAudioInputStream dais = (DecodedMpegAudioInputStream) AudioSystem.getAudioInputStream(daisf, ais);
byte[] audioData = IOUtils.toByteArray(dais);//exception here
And the second:
ByteOutputStream bos = new ByteOutputStream();
// Get the decoded stream.
byte[] byteData = new byte[1];
int nBytesRead = 0;
int offset = 0;
int cnt=1;
while (nBytesRead != -1) {
System.out.println("cnt="+cnt);
nBytesRead = dais.read(byteData, offset, byteData.length);//exception here at first loop
if (nBytesRead != -1) {
int numShorts = nBytesRead >> 1;
for (int j = 0; j < numShorts; j++) {
bos.write(byteData[j]);
}
}
cnt+=1;
}
byte[] audioData = bos.getBytes();
It seems that there is an issue with the headers or the structure of it, because the dais stream has content/bytes. However it can be opened with audacity and ffplay so I believe there should be a workaround. Any ideas how to counter it?
You could use code redundancy to improve reliability. Look into alternative libraries, such as Xuggler or JLayer.
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.