How to convert a byte array into an AudioInputStream in Java - java

I want to convert a byte array into an AudioInputStream. The byte array was filled from a *.wav file before. I have the following code:
public static AudioInputStream writeBytesBackToStream(byte[] bytes) {
ByteArrayInputStream baiut = new ByteArrayInputStream(bytes);
AudioInputStream stream = null;
try {
stream = AudioSystem.getAudioInputStream(baiut);
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if(stream.equals(null) || stream == null) {
System.out.println("WARNING: Stream read by byte array is null!");
}
return stream;
}
Now I only want to convert this byte array into an AudioInputStream, but an UnsupportedAudioFileException is thrown:
javax.sound.sampled.UnsupportedAudioFileException: could not get audio input stream from input stream
Has anyone got an idea?

Another way to do this, if the data is actually PCM, is to set the AudioFormat parameters manually. Here is an example to create an A minor.
byte[] b = new byte[64000];
//lets make a 440hz tone for 1s at 32kbps, and 523.25hz.
for(int i = 0; i<b.length/2; i++){
b[i*2+1] = (byte)(127*Math.sin(4*Math.PI*440.0/b.length*i));
b[i*2] = (byte)(127*Math.sin(4*Math.PI*523.25/b.length*i));
}
AudioInputStream stream = new AudioInputStream(
new ByteArrayInputStream(b),
new AudioFormat(16000, 8, 2, true, false),
64000
);
Also be careful if you're making bytes into sounds and you have earphones in.

If you properly read the WAV file, the byte array will just contain raw PCM data. Consequently the AudioSystem can not identify the format of the stream and throws the exception.
This can not work by design. You need to provide a complete audio format image in your stream to have AudioSystem recognize what format the stream is, not just raw data.

Related

Input Streams.read() How does it work exactly?

I'm having the following code :
public static void main(String[] args) throws Exception {
FileInputStream inputStream = new FileInputStream("c:/data.txt");
FileOutputStream outputStream = new FileOutputStream("c:/result.txt");
while (inputStream.available() > 0) {
int data = inputStream.read();
outputStream.write(data);
}
inputStream.close();
outputStream.close();
}
I dont get my head around the following line:
int data = inputStream.read();
Get the bytes of the file c:/data.txt, read byte by byte, and then get concatenated automatically within the variable data or does inputStream.read() read the file c:/data.txt all at once and assign everything to the data variable?
From JavaDoc:
A FileInputStream obtains input bytes from a file in a file system.
FileInputStream is meant for reading streams of raw bytes such as image data.
For reading streams of characters, consider using FileReader
Question: Get the bytes of the file c:/data.txt, read byte by byte, and then get concatenated automatically within the variable
data or does inputStream.read() read the file c:/data.txt all at
once and assign everything to the data variable?
To Answer this lets take example:
try {
FileInputStream fin = new FileInputStream("c:/data.txt");
int i = fin.read();
System.out.print((char) i);
fin.close();
} catch (Exception e) {
System.out.println(e);
}
Before running the above program a data.txt file was created with text: Welcome to Stackoverflow.
After the execution of above program the console prints single
character from the file which is 87 (in byte form), clearly
indicating that FileInputStream#read is used to read the byte of
data from the input stream.
So , FileInputStream reads data byte by byte.

Java byte array compression

I'm trying to use the java DeflaterOutputStream and InflaterOutputStream classes to compress a byte array, but both appear to not be working correctly. I assume I'm incorrectly implementing them.
public static byte[] compress(byte[] in) {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DeflaterOutputStream defl = new DeflaterOutputStream(out);
defl.write(in);
defl.flush();
defl.close();
return out.toByteArray();
} catch (Exception e) {
e.printStackTrace();
System.exit(150);
return null;
}
}
public static byte[] decompress(byte[] in) {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
InflaterOutputStream infl = new InflaterOutputStream(out);
infl.write(in);
infl.flush();
infl.close();
return out.toByteArray();
} catch (Exception e) {
e.printStackTrace();
System.exit(150);
return null;
}
}
Here's the two methods I'm using to compress and decompress the byte array. Most implementations I've seen online use a fixed size buffer array for the decompression portion, but I'd prefer to avoid that if possible, because I'd need to make that buffer array have a size of one if I want to have any significant compression.
If anyone can explain to me what I'm doing wrong it would be appreciated. Also, to explain why I know these methods aren't working correctly: The "compressed" byte array that it outputs is always larger than the uncompressed one, no matter what size byte array I attempt to provide it.
This will depend on the data you are compressing. For example if we take an array of 0 bytes it compresses well:
byte[] plain = new byte[10000];
byte[] compressed = compress(plain);
System.out.println(compressed.length); // 33
byte[] result = decompress(compressed);
System.out.println(result.length); // 10000
Compression always has overhead to allow for future decompression. If the compression produced no reduction in length (the data was unique or nearly unique) then the output file could be longer than the input file

Java Decompressing byte array - incorrect data check

I have a little problem: I decompress byte array and everything is ok with following code but sometimes with some data it throws DataFormatException with incorrect data check. Any ideas?
private byte[] decompress(byte[] compressed) throws DecoderException {
Inflater decompressor = new Inflater();
decompressor.setInput(compressed);
ByteArrayOutputStream outPutStream = new ByteArrayOutputStream(compressed.length);
byte temp [] = new byte[8196];
while (!decompressor.finished()) {
try {
int count = decompressor.inflate(temp);
logger.info("count = " + count);
outPutStream.write(temp, 0, count);
}
catch (DataFormatException e) {
logger.info(e.getMessage());
throw new DecoderException("Wrong format", e);
}
}
try {
outPutStream.close();
} catch (IOException e) {
throw new DecoderException("Cant close outPutStream ", e);
}
return outPutStream.toByteArray();
}
Try with a different compression level or using the nowrap options
1 Some warning: do you use the same algorithm in both sides ?
do you use bytes ? (not String)
your arrays have the good sizes ?
2
I suggest you check step by step, catching exceptions, checking sizes, null, and comparing bytes.
like this: Using Java Deflater/Inflater with custom dictionary causes IllegalArgumentException
Take your input
Compress it
copy your bytes
decompress them
compare output with input
3 if you cant find, take another example which works, and modify it step by step
hope it helps
I found out why its happening
byte temp [] = new byte[8196];
its too big, it must be exactly size of decompressed array cause it was earlier Base64 encoded, how i can get this size before decompressing it?

The difference between DataLine and (Output|Input)Stream?

I am working on sound processing with the use of Java now. Within my project, I have to deal with the stream. So I have a lot of staffs to do with DataLine and OutputStream or InputStream.
But to me, they are too similar:(
Is there someone who can help me with this question? Thanks in advance!
Here are some code I used :
TargetDataLine line;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int frameSizeInBytes = format.getFrameSize();
int bufferLengthInFrames = line.getBufferSize() / 8;
int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
byte[] data = new byte[bufferLengthInBytes];
int numBytesRead;
try {
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format, line.getBufferSize());
} catch (LineUnavailableException ex) {
shutDown("Unable to open the line: " + ex);
return;
} catch (SecurityException ex) {
shutDown(ex.toString());
return;
} catch (Exception ex) {
shutDown(ex.toString());
return;
}
line.start();
while (thread != null) {
if ((numBytesRead = line.read(data, 0, bufferLengthInBytes)) == -1) {
break;
}
out.write(data, 0, numBytesRead);
}
I have read the documentation of the class TargetDataLine, it is said :"'read(byte[] b, int off, int len)' Reads audio data from the data line's input buffer."
But where do we define it?
Also the line of type TargetDataLine has not been attached to any mixer, so how can we know for which mixer it is for???
A DataLine is an interface related to handling sampled sound (a.k.a PCM data) in Java. I don't really know a lot of that.
An OutputStream is an interface that represents anything that can get bytes written to it. A simple sample of an OutputStream is a FileOutputStream: all bytes written to that stream will be written to the file it was opened for.
An InputStream is the other end: it's an interface that represents anything from which bytes can be read. A simple sample of an InputStream is a FileInputStream: it can be used to read the data from a file.
So if you were to read audio data from the hard disk, you'd eventually use a FileInputStream to read the data. If you manipulate it and later want to write the resulting data back to the hard disk, you'd use a FileOutputStream to do the actual writing.
An InputStream represents a stream of bytes, where we can read bytes one be one (or in blocks) until it is empty. An OutputStream is the other direction - we write bytes one be one (or in blocks) until we have nothing more to write.
Streams are used to send or receive unstructured byte data.
DataLine handles audio data, in other words, bytes with a special meaning. And it offers some special methods to control the line (start/stop), get the actual format of the audio data and some other characteristics.

Java - converting byte array of audio into integer array

I need to pass audio data into a 3rd party system as a "16bit integer array" (from the limited documentation I have).
This is what I've tried so far (the system reads it in from the resulting bytes.dat file).
AudioInputStream inputStream = AudioSystem.getAudioInputStream(new File("c:\\all.wav"));
int numBytes = inputStream.available();
byte[] buffer = new byte[numBytes];
inputStream.read(buffer, 0, numBytes);
BufferedWriter fileOut = new BufferedWriter(new FileWriter(new File("c:\\temp\\bytes.dat")));
ByteBuffer bb = ByteBuffer.wrap(buffer);
while (bb.remaining() > 1) {
short current = bb.getShort();
fileOut.write(String.valueOf(current));
fileOut.newLine();
}
This doesn't seem to work - the 3rd party system doesn't recognise it and I also can't import the file into Audacity as raw audio.
Is there anything obvious I'm doing wrong, or is there a better way to do it?
Extra info: the wave file is 16bit, 44100Hz, mono.
I've just managed to sort this out.
I had to add this line after creating the ByteBuffer.
bb.order(ByteOrder.LITTLE_ENDIAN);
Edit 2:
I rarely use AudioInputStream but the way you write out the raw data seems to be rather complicated. A file is just a bunch of subsequent bytes so you could write your audio byte array with one single FileOutputStream.write() call. The system might use big endian format whereas the WAV file is stored in little endian (?). Then your audio might play but extremely silently for example.
Edit 3
Removed the code sample.
Is there a reason you are writing the audio bytes as strings into the file with newlines?
I would think the system expects the audio data in binary format, not in string format.
AudioFileFormat audioFileFormat;
try {
File file = new File("path/to/wav/file");
audioFileFormat = AudioSystem.getAudioFileFormat(file);
int intervalMSec = 10; // 20 or 30
byte[] buffer = new byte[160]; // 320 or 480.
AudioInputStream audioInputStream = new AudioInputStream(new FileInputStream(file),
audioFileFormat.getFormat(), (long) audioFileFormat.getFrameLength());
int off = 0;
while (audioInputStream.available() > 0) {
audioInputStream.read(buffer, off, 160);
off += 160;
intervalMSec += 10;
ByteBuffer wrap = ByteBuffer.wrap(buffer);
int[] array = wrap.asIntBuffer().array();
}
audioInputStream.close();
} catch (UnsupportedAudioFileException | IOException e) {
e.printStackTrace();
}

Categories

Resources