Java - read WAV file as a float array [duplicate] - java

This question already has answers here:
How do I use audio sample data from Java Sound?
(2 answers)
Closed 7 years ago.
I'm trying to read a WAV file (and in the future also MP3 and Ogg) file as an array of floats in Java, much like libsndfile in C.
I'm looking at the various documentation in the javax.sampled packages, but I can't find a clear explanation on how to do that; I'd like to avoid recurring to external libraries if possible.
Any suggestion?

JavaSound will only give you "raw" access to the sound data embedded in the WAV file, e.g. 8 bit unsigned PCM or 16 bit signed PCM data (if your WAV files are PCM encoded). In either case, the value of each sample is in the range 0 to 255 or -32768 to 32767 and you simply have to shift the range to fit in -1f to 1f.
You should start with AudioSystem.getAudioInputStream(...) using an appropriate source (File, InputStream or URL), examine the format to check the sample size and the number of channels and then you can read and convert the PCM encoded data from the AudioInputStream accordingly.

found this page:
http://www.jsresources.org/faq_audio.html
see in particular 9.4: How can I reconstruct sample values from a byte array?
It works as expected, by comparing java's output with c++ (libsndfile) and matlab (wavread).
I'm going to put the code on m github account when I have time

Related

How can I convert a .wav file to a float array ( edit the float array e.g. adding two signals) and back to a .wav file without whitenoise

I am trying to program an auralization via Ray-Tracing in processing. To edit a sample over the information from the Ray Tracer, i need to convert a .wav File (File-Format: PCM-signed,16bit,stereo,2 bytes/frame, little endian) to an Float Array.
I converted the audio via an audioInputStream and a DataInputStream, where I am loading the audio into an byte Array.
Then I convert the byte Array to a float array like this.
byte[] samples;
float[] audio_data = float(samples);
When I convert the float Array back to a .wav File, I'm getting the sound of the original Audio-File.
But when I'm adding another Float Array to the Original signal and convert it back to a. wav file via the method above(even if I'm adding the same signal), i get a white noise signal instead of the wanted signal (I can hear the original signal under the white noise modulated, but very very silent).
I read about this problem before, that there can be problems by the conversion from the float array to a byte array. That's because float is a 32bit datatype and byte (in java) is only 16 bits and somehow the bytes get mixed together wrong so the white noise is the result. In Processing there is a data type with signed 16bit integers (named: "short") but i can't modify the amplitude anymore, because therefore i need float values, which i can't convert to short.
I also tried to handle the overflow (amplitude) in the float array by modulating the signal from 16 bit values (-32768/32767) to values from -1/1 and back again after mixing (adding) the signals. The result gave me white noise. When i added more than 2 signals it gaves me nothing (nothing to hear).
The concrete Problem I want to solve is to add many signals (more than 1000 with a decent delay to create a kind of reverbation) in the form of float Arrays. Then I want to combine them to one Float Array that i want to save as an audio file without white noise.
I hope you guys can help me.
If you have true PCM data points, there should be no problem using simple addition. The only issue is that on rare occasions (assuming your audio is not too hot to begin with) the values will go out of range. This will tend create a harsh distortion, not white noise. The fact that you are getting white noise suggests to me that maybe you are not converting your PCM sums back to bytes correctly for the format that you are outputting.
Here is some code I use in AudioCue to convert PCM back to bytes. The format is assumed to be 16-bit, 44100 fps, stereo, little-endian. I'm working with PCM as normalized floats. This algorithm does the conversion for a buffer's worth of data at a time.
for (int i = 0, n = buffer.length; i < n; i++)
{
buffer[i] *= 32767;
audioBytes[i*2] = (byte) buffer[i];
audioBytes[i*2 + 1] = (byte)((int)buffer[i] >> 8 );
}
Sometimes, a function like Math.min(Math.max(audioval, -1), 1) or Math.min(Math.max(audioval, -32767), 32767) is used to keep the values in range. More sophisticated limiters or compressor algorithms will scale the volume to fit. But still, if this is not handled, the result should be distortion, not white noise.
If the error is happening at another stage, we will need to see more of your code.
All this said, I wish you luck with the 1000-point echo array reverb. I hadn't heard of this approach working. Maybe there are processors that can handle the computational load now? (Are you trying to do this in real time?) My only success with coding real-time reverberation has been to use the Schroeder method, plugging the structure and values from the CCMRA Freeberb, working off of code from Craig Lindley's now ancient (copyright 2001) book "Digital Audio with Java". Most of that book deals with obsolete GUI code (pre-Swing!), but the code he gives for AllPass and Comb filters is still valid.
I recall when I was working on this that I tracked down references a better reverb to try and code, but I would have to do some real digging to try and find my notes. I was feeling over my head at the time, as the algorithm was presented via block diagrams not coding details or even pseudo-code. Would like to work on this again though and get a better reverb than the Shroeder-type to work. The Schoeder was passable for sounds that were not too percussive.
Getting a solution for real-time ray tracing would be a valuable accomplishment. Many applications in AR/VR and games.

PCM Byte Array Addition

I have a few raw PCM audio files. I can successfully read a stream of bytes from these files and play them through an audio playing mechanism which accepts PCM data as input.
When i read data from these files, i store it in byte[]. These tracks have the same size and are complementary in terms of sound (they sound good together). Therefore, I want to add several byte[] containing PCM data into a single byte[] of the same size, representing the final music.
I tried it in an easy thoughtless manner by simply doing it like this:
for(int i=0; i<finalbytes.length; i++)
{
finalbytes[i] = (byte) (music1bytes[i] + music2bytes[i]);
}
It actually wasn't that bad. The final sound is indeed an addition of both tracks. The problem is, when a few tracks are added, sometimes in specific parts of the song, peaks of static noise can be heard. It is probably due to the addition resulting in non-clamped values or something, which I don't know how to solve.
So, how to add two or more byte arrays of PCM data?
I'm assuming the samples of these raw audio files are 8 bit signed.
What's happening is overflow. If both samples add up to greater than 127 or less than -128 you won't get the correct result - you get integer overflow.
You could divide each resulting sample by 2:
finalbytes[i] = (byte) ((music1bytes[i] + music2bytes[i]) / 2);
This way, even if each audio file has a maximum sample value you will not get overflow. Disadvantage is that resulting file might be a bit quiet.
Another option is to clip:
int sample = music1bytes[i] + music2bytes[i];
sample = Math.min(sample, Byte.MAX_VALUE);
sample = Math.max(sample, Byte.MIN_VALUE);
finalbytes[i] = (byte)sample;
If both audio sources are pretty loud then there might be a lot of clipping and it mightn't sound that great.
You could also try using SoftMixingMixer from JavaSound and let it do the mixing for you. Might actually be a lot more work this way since you'd need to define the audio format of the RAW audio files, but it likely will give the best sounding result. With this option, you'll need to use openStream(AudioFormat) with the audio format of the output file and tell the mixer to play your 2 RAW audio files through lines.

How can I determine what parameters to use for AudioFormat?

I have a WAV file here: http://uppit.com/slpmuzpywxhs/202.wav
and I need to construct a Java AudioFormat object based on properties of that sound file using the following the parameters:
float sampleRate, int sampleSizeInBits, int channels, boolean signed, boolean bigEndian.
So my question is: How can I analyze that WAV file in order to determine those values?
EDIT: Solution found thanks to jaket!
You can look at the wave header to determine that the format is:
22050 sample rate
8 bits per sample
endianess - irrelevant for 8-bits but may as well say little endian.
1 channel
I found this info by downloading the file, right clicking and selecting "Get Info" on the mac. There are lots of other ways to find this out.
If I were writing code for this though I probably wouldn't want to hard code the values though. Google found me this:
AudioInputStream stream = AudioSystem.getAudioInputStream("202.wav");
AudioFormat format = stream.getFormat();

How do I work with binary data in Java? [duplicate]

This question already has answers here:
Reading/writing a BINARY File with Strings?
(2 answers)
Closed 8 years ago.
I'm working on a simple VM/interpreter kind of program for a simple toy language I implemented. Currently the compiler emits textual assembly instructions such as push and add to be executed by the VM.
Recently I figured it would be a better idea to actually compile to binary opcodes instead of textual ones, for performance and space. (It doesn't actually matter in this toy project, but this is for learning).
Soon I realized, even though generally I consider myself a decent Java programmer, that I have no idea how to work with binary data in Java. No clue.
Regarding this, I have two questions:
How can I save binary data to a file? For example say I want to save the byte 00000001 and then the byte 00100000 to a file (each byte will be an opcode for the VM) - how can I do that?
How can I read binary data from a file on disk, save it in variables, parse it and manipulate it? Do I use the usual I/0 and parsing techniques I use with regular Strings, or is this something different?
Thanks for your help
You should be able to use any OutputStream to do this, because they all have a write method that takes a byte. For example, you could use a FileOutputStream.
Similarly, you can use an InputStream's read method for reading bytes.
OutputStream and its subtypes have methods to write bytes to files. DataOutputStream has additional methods to write int, long, float, double, etc. if you need those, although it writes them in big endian byte order. The corresponding input streams have equivalent methods for reading.
You may want to try to use ByteArrayInputStream and DataOutputStream
Refer to the Oracle documentation for details.
Also, checkout this similar question: How to output binary data to a file in Java?
Use a FileOutputStream. It has a write(byte[] b) method to write an array of bytes to a file and a write(int b) method to write a single byte to the file.
So, to save 00000001 followed by 00100001, you can do :
FileOutputStream file = new FileOutputStream(new File(path));
file.write(1);
file.write(33);
Similarly you can read FileInputStream for reading from a binary file.

Magic number file checking

I'm attempting to read magic numbers/bytes to check the format of a file. Will reading a file byte by byte work in the same way on a Linux machine?
Edit: The following shows to get the magic bytes from a class file using an int. I'm trying to do the same for a variable number of bytes.
http://www.rgagnon.com/javadetails/java-0544.html
I'm not sure that I understand what you are trying to do, but it sounds like what you are trying to do isn't the same thing as what the code that you are linking to is doing.
The java class format is specified to start with a magic number, so that code can only be used to verify if a file might be a java class or not. You can't use the same logic and apply it to arbritraty file formats.
Edit: .. or do you only want to check for wav files?
Edit2: Everything in Java is in big endian, that means that you can use DataInputStream.readInt to read the first four bytes from the file, and then compare the returned int with 0x52494646 (RIFF as a big endian integer)

Categories

Resources