I'm wondering how I may obtain musical information such as amplitude from a audio file?
Suppose we have a raw audio file, what I want to extract data from the file which allows me to generate a curve such as http://www1.icsi.berkeley.edu/Speech/mr/nearfar.html. Once I have obtained this curve, I will perform FFT, etc.
I have been trying to search for solutions in Java Sound, but what I only know so far is that I can pour the data into byte[] using AudioInputStream.
But how do I translate that byte[] into a double[] which contains actual information about the sound? In this case, the values in the double[] represent amplitude.
Could anyone please provide a solution to solve this problem in Java?
Typical wav file is 16 bit little endian, so you can take each two consecutive bytes, b1 and b2, and get the amplitude as follows:
(double) (b2 << 8 | b1 & 0xFF) / 32767.0;
if the input is stereo, first two bytes would be left channel, then two bytes for right channel.
Related
I am learning how MIDI works and am wondering how to merge two bytes. There is a paragraph that states the following
“The status and channel Bytes are merged into one byte (00-FF) Because these messages have an MSB (Most Significant Byte) of 1 the command statuses actually begin at 80 hexadecimal (128 and up to 255) The LSB (Least Significant Byte takes a value of 0-F Hexadecimal (0 to 15) to specify which MIDI channel the command will be sent to. A command message tells the MIDI gear to perform certain types of things like play a note, change the volume, add effects, and other types of things. This table shows the different command status and what they do.”
I am not 100% sure what it means by merging Bytes into a byte. Any help would be appreciated. If you could provide an example of how to do this in java with an explanation I’d be even more grateful.
So I found the answer to the problem.
Byte b1 = 0xF;
Byte b2 = 0xF;
Byte merged = (b1 >> 4 | b2);
Byte 1 is shifted four bits to the left to guarantee it is the most significant bit while byte 2 is bitwise or as the least significant bits. The important thing is that the paragraph i posted above says you may merge two bytes together. Both bytes must be at a maximum of 4 bits. If they exceed 8 bits total the result is a short or int based on how large the result is.
I know that using a hexadecimal editor, one can edit binary files and change 4 bits with each hexadecimal value, But I am kind of thinking of a project that requires to modify a single bit rather than 4-bits.
So Is there a way to read something (e.g. ASCII coded plain text-file) in bits and manipulate single bits in e.g. Java?
As a noob, I can think of loading each bytes and generating a string containing each 8-bit representation of each byte, but that is kind of quite a complex way and will waste a lot of space. Also, this approach would require me to keep a list containing each available byte's 8-bit representation to look it up.
Others have already hinted that your question is broad and can probably be better answered by other sites, ressources or search.
However I'd like to show you some small snippet with which you can start.
// Read a file into a byte array, we are not interested
// in interpreting encodings, just plain bytes
final byte[] fileContent = Files.readAllBytes(Paths.get("pathToMyFile"));
// Iterate the content and display one byte per line
for (final byte data : fileContent) {
// Convert to a regular 8-bit representation
System.out.println(Integer.toBinaryString(data & 255 | 256).substring(1));
}
You can also easily manipulate the bytes and also the bits by simply accessing the array contents and using simple bit operators like &, |, ^.
Here is a snippet showing you how to set and unset a bit at a given position:
byte data = ...;
// Set it (to 1)
data = data | (1 << pos);
// Unset it (to 0)
data = data & ~(1 << pos);
The conversion gets explained here: how to get the binary values of the bytes stored in byte array
The bit manipulation here: Set specific bit in byte
Here are some relevant Java-Docs: Files#readAllBytes and Integer#toBinaryString
Note that from a view of efficiency the smallest you can go in Java is byte, there is no bit data type. However in practice you will probably not see any difference, the CPU already loads the whole neighboring bits and bytes into the cache regardless of you want to use them or not. Thus you can just use the byte data type and use them to manipulate single bits.
I'm trying to play an array in a Java program.
So far I was able to play byte arrays with the following code:
AudioFormat audioFormat = new AudioFormat(samplingFreq, bps, 1, true, true);
SourceDataLine sdline = AudioSystem.getSourceDataLine(audioFormat);
line.open(audioFormat);
sdline.start();
sdline.write(playArray, 0, playArray.length);
sdline.drain();
sdline.close();
However, with this I'm only able to play byte arrays since the write method only accepts byte arrays as argument. I want to be able to play 16/32 bits per sample arrays as well.
Are there any ways to play integer arrays or even doubles using AudioSystem(or any other class).
With the help of the comment from #greg-449, I was able to solve the problem. To play higher bits per second, you just need to increase the bps argument and send bytes one after another.
As an example, if we want to send 1867 as a sample, we need 16 bits which are as follows :
0000 0111 0100 1011
The first 8 bits are 7 in decimal and the second eight bits are 75 in decimal, so since we're using big endian(last argument of the AudioFormat argument), the first element of our byte array should be 7 and the second element should be 75. So for 16 bits, we just need two bytes for each sample.
I'm trying to play some .m4a files, and I understand that JAAD only supports decoding AAC, but there are songs that I am able to get the sourceDataLine from, and then when I go to try to play them, I get behavior like this:
We read: 1024 bytes.
We read: 512 bytes.
We read: -1 bytes.
When running this:
// read from the input
bytesRead = audioInputStream.read(tempBuffer, 0, tempBuffer.length);
System.out.println("We read: " + bytesRead + " bytes.");
until bytesRead == -1
For this particular file, I'm getting the AudioFormat baseformat to be this:
MPEG1L1 48000.0 Hz, unknown bits per sample, mono, unknown frame size, 125.0 frames/second.
Then the AudioFormat decodedFormat to be this:
PCM_SIGNED 48000.0 Hz, 16 bit, mono, 2 bytes/frame, little-endian
I use these line of code to make the conversion:
AudioFormat baseFormat = audioInputStream.getFormat();
AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
baseFormat.getSampleRate(),
16,
baseFormat.getChannels(),
baseFormat.getChannels() * 2,
baseFormat.getSampleRate(),
false);
Am I doing something wrong here? I don't fully understand that that second line really does, but it's been working just fine for decoding MP3 files using the MP3SPI.
I'd really appreciate any guidance here.
Is the problem that your file is only showing 1024 + 512 bytes in length? (That would be awfully short for an audio file!) Or is the question about File Formats? I've seen some people run into a problem when they try to decode an mp3 of a .wav file that happens to be incompatible with the range of wav file formats supported by Java.
I'm assuming the "second line" you refer to is the creation of a new FileFormat, yes? That is simply the act of making a new Format based upon the one being decoded from your inputstream. Presumably, you will use the new format in your playback.
The point of the new format is probably to ensure the data will be played with a format compatible with your system. It says:
(1) that no matter the incoming encoding format, Signed PCM will be used.
(2) the same sample rate will be used (my system only supports 44100, am surprised to see yours allowing 48000).
(3) 16-bits will be the new number of bits per sample, regardless of the number of bits used in the original original file.
(4) the same number of channels will be used as the original file.
(5) the number of channels * 2 will be deemed to be the frame size (makes sense, given 16 bits per sample)
(6) the same frames per second rate
(7) the byte order will be little-endian, regardless of the input file order.
You can look into the API of this constructor (some good documentation) under "Constructor Detail" at this link: http://docs.oracle.com/javase/6/docs/api/javax/sound/sampled/AudioFormat.html
If you need to convert some of the audio data in order to allow playback, then things get more involved. Have you read through this tutorial on audio file & format conversion?
http://docs.oracle.com/javase/tutorial/sound/converters.html
I hope this is at least of partial help. I haven't used JAAD myself, so I can understand if this post isn't very helpful.
I'm working on an application that processes audio data.
I'm using java (I've added MP3SPI, Jlayer, and Tritonus). I'm extracting the audio data from a .wav file to a byte array. The audio data samples I'm working with are 16 bits stereo.
According to what I've read the format for one sample is:
AABBCCDD
where AABB represents left channel and CCDD rigth channel (2 bytes for each channel).
I'd need to convert this sample into a double value type. I've reading about data format. Java uses Big endian, .wav files use little endian. I'm a little bit confused. Could you please help me with the conversion process?
Thanks you all
Warning: integers and bytes are signed. Maybe you need to mask the low bytes when packing them together:
for (int i =0; i < length; i += 4) {
double left = (double)((bytes [i] & 0xff) | (bytes[i + 1] << 8));
double right = (double)((bytes [i + 2] & 0xff) | (bytes[i + 3] << 8));
... your code here ...
}
When you use the ByteBuffer (java.nio.ByteBuffer) you can use the method order;
[order]
public final ByteBuffer order(ByteOrder bo)
Modifies this buffer's byte order.
Parameters:
bo - The new byte order, either BIG_ENDIAN or LITTLE_ENDIAN
Returns:
This buffer
After this you can get the above mentioned values with;
getChar()
getShort()
getInt()
getFloat()
getDouble()
What a great language is Java ;-)
Little Endian means that the data is in the form BBAA and DDCC. You would just swap it around.
From the beginning of the frame:
int left = (bytes[i+1] << 8) + bytes[i];
int right = (bytes[i+3] << 8) + bytes[i+2];
where i is your the index of your sample.
I would personally look for a library that does the endian swapping for you. Each audio file format has assumptions about the endianness for you and getting this right is tricky for all the bit depths/datatypes wave files support:
8bit - uint8
16bit - int16
24bit - int32
32bit - int32 as float
32bit - float
64bit - double
If you want to support most common types of wave files you'll need endian conversions for all of these datatypes.
I would look at ByteSwapper, which will give you byteswapping for most of the types listed above.
Its too bad Java doesn't have an endianness field in their File IO classes. Being able to simply open a file whos edianness is big or little is a much easier solution to this issue.