O'Reilly logo

Swing Hacks by Chris Adamson, Joshua Marinacci

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

380
|
Chapter 10, Audio
#75 Build an Audio Waveform Display
HACK
Frame
A frame is a cross section of samples across all channels in the audio file.
So, a 16-bit stereo (two channel) audio file will have 32-bit frames (16
bits per sample * 2 channels per frame = 32 bits per frame).
Load the Raw Data
Java reads raw audio data in 8-bit bytes, but most audio has a higher sam-
ple size. So, in order to represent the audio, you’ll have to combine multiple
bytes to create samples in the audio format. But first, you’ll need to load all
of the audio into a
buffer
before you combine the bytes into samples.
Start by getting an audio stream from a file:
File file = new File(filename);
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new
BufferedInputStream (new FileInputStream (file)));
Now that you have the AudioInputStream, you can read in the audio data.
AudioInputStream has a read( ) method that takes an unpopulated byte[]
and reads in data the length of the byte[]. To read in the entire audio file in
one shot, create a
byte[] the length of the entire audio file. The complete
length of the file in bytes is:
total number of bytes = bytes per frame * total number of frames
You can get the number of frames for the whole file (frameLength) and the
size of the frame (
frameSize) from the AudioInputStream:
int frameLength = (int) audioInputStream.getFrameLength( );
int frameSize = (int) audioInputStream.getFormat().getFrameSize( );
You can create the byte[] with the length set to frameLength * frameSize:
byte[] bytes = new byte[frameLength * frameSize];
Finally, you can read in the audio, passing the AudioInputStream the empty
byte[] and catching the appropriate exceptions:
int result = 0;
try {
result = audioInputStream.read(bytes);
} catch (Exception e) {
e.printStackTrace( );
}
Convert to Samples and Channels
The raw audio data isn’t very useful. It needs to be broken up into channels
and samples. From there, it’s easy to paint the samples.
Build an Audio Waveform Display #75
Chapter 10, Audio
|
381
HACK
The bytes will be converted to samples and represented as ints. You’ll need
a container to store the samples across all channels. So, create a two dimen-
sional
int[][]
referencing the channel and samples per channel. You’ve
already seen how to get the frame length from the
AuduioInputStream
, and
you can get the number of channels the same way. Here is the code to ini-
tialize the
int[][]
:
int numChannels = audioInputStream.getFormat().getChannels( );
int frameLength = (int) audioInputStream.getFrameLength( );
int[][] toReturn = new int[numChannels][frameLength];
Now, you need to iterate through the byte[], convert the bytes to samples,
and place the sample in the appropriate channel in the
int[][]
. The
byte[]
is organized by frames, meaning that you’ll read in a sample for every chan-
nel rather than all of the samples for a specific channel in a row. So, the flow
is to loop through the channels and add samples until the
byte[] has been
iterated completely:
int sampleIndex = 0;
for (int t = 0; t < eightBitByteArray.length;) {
for (int channel = 0; channel < numChannels; channel++) {
int low = (int) eightBitByteArray[t];
t++;
int high = (int) eightBitByteArray[t];
t++;
int sample = getSixteenBitSample(high, low);
toReturn[channel][sampleIndex] = sample;
}
sampleIndex++;
}
This hack is going to deal exclusively with 16-bit samples.
They are by far the most common. Plus, you can get an idea
for how sample conversion works while still keeping things
pretty straightforward. This code gets much trickier with
multiple dynamic sample sizes.
Now for the getSixteenBitSample( ) method. You can’t simply add the bytes
together using regular addition because the bits are displaced—in a 16-bit
sample the high byte represents bits 0 through 7, and the low byte repre-
sents bits 8 through 15. It’s more like concatenation, so the type of math
shown here won’t work:
1010 1101 (high byte)
+ 0011 0010 (low byte)
-----------
1101 1111

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required