382
|
Chapter 10, Audio
#75 Build an Audio Waveform Display
HACK
What you want is more like this:
1010 1101 (high byte)
+ 0011 0010 (low byte)
---------------------
1010 1101 0011 0010
And in order to get this to work with standard addition, you need to add
two 16-bit bytes with bits shifted and placeholder
0s added where neces-
sary. Then you get something like this:
1010 1101 0000 0000 (high byte)
+ 0000 0000 0011 0010 (low byte)
---------------------
1010 1101 0011 0010
The high byte needs to be bit shifted. Bit shifting, the process of sliding bits
around, is typically a big no-no in Java—as a result, you’ve probably never
seen the bit-shifting operator before (it’s
<< or >> depending on the direction
followed by the number of bits to shift in either direction). However, here it
is necessary to use bit shifting, so you will bit shift the high byte 8 bits to the
left:
high << 8
Now, you need to prepend the leading 0s onto the low byte. You can do this
using the bit
AND operator and using a 16-bit byte consisting of all 0s. It
works like this:
0000 0000 0000 0000 (all 0's bytes)
+ 0011 0010 (low byte)
---------------------
0000 0000 0011 0010
Here is the code for the sample conversion:
private int getSixteenBitSample(int high, int low) {
return (high << 8) + (low & 0x00ff);
}
Creating a Single Waveform Display
Now that you have the audio sample data organized by channels, it’s time to
get to painting. To keep everything modular, create a class called
SingleWaveformPanel to paint one channel of audio data. In the next section,
you’ll write a
WaveformPanelContainer to use multiple SingleWaveformPanels
to handle multi-channel audio.
The waveform painting is going to be drawn by plotting points scaled to the
sample data and drawing lines between them. This is simplistic, but it yields
good results. Figures 10-4 and 10-5 show the same waveform in Audacity
and the simulator for this hack; they’re pretty close.
Build an Audio Waveform Display #75
Chapter 10, Audio
|
383
HACK
I’m going to gloss over the scaling code because I really want to concentrate
on the conversion from audio information to visualization. But to under-
stand why scaling is necessary, remember that CD quality audio has 44,100
samples per second. So, without scaling, you would need 44,100 horizontal
pixels for every second of your audio file. Obviously, this is impractical. So,
if you dig into the source code for this hack, you can see the scaling and how
the scales are determined. Meanwhile, just assume that the waveform is
always scaled to fit in the panel.
Start by drawing the center line at
0
:
g.setColor(REFERENCE_LINE_COLOR);
g.drawLine(0, lineHeight, (int)getWidth( ), lineHeight);
Next, mark the origin to start drawing at 0,0:
int oldX = 0;
int oldY = (int) (getHeight( ) / 2);
int xIndex = 0;
Now, you need to figure out the incremental jump between samples to
adjust for the scale factor. This works out to be:
number of samples / (number of samples * horizontal scale factor)
The following code grabs the increment and paints a line from the origin to
the first sample:
int increment = getIncrement( )
g.setColor(WAVEFORM_COLOR);
int t = 0;
for (t = 0; t < increment; t += increment) {
g.drawLine(oldX, oldY, xIndex, oldY);
xIndex++;
oldX = xIndex;
}
Finish up by iterating through the audio and drawing lines to the scaled
samples:
for (; t < samples.length; t += increment) {
double scaleFactor = getYScaleFactor( );
double scaledSample = samples[t] * scaleFactor;
int y = (int) ((getHeight( ) / 2) - (scaledSample));
g.drawLine(oldX, oldY, xIndex, y);
xIndex++;
oldX = xIndex;
oldY = y;
}
}

Get Swing Hacks now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.