Reputation: 25297
I'm playing around with audio in Java. I discovered that a commonly used AudioFormat
uses two bytes. However, I couldn't figure out how to put the bytes together into a single int. So I tried doing it in reverse:
public class SineWave {
public static void main(String[] args) throws LineUnavailableException {
int hz = 440;
int samplerate = 16384;
int amplitude = 127;
AudioFormat format = new AudioFormat((float) samplerate, 16, 1, true, true);
SourceDataLine sdl = AudioSystem.getSourceDataLine(format);
sdl.open(format, samplerate * 2);
sdl.start();
while (true) {
byte[] toWrite = new byte[samplerate * 2];
for (int x = 0; x < samplerate; x++) {
int y = (int) Math.round(amplitude * Math.sin(2 * Math.PI * x * hz / samplerate));
byte b1 = (byte) (y & 0xFF);
byte b2 = (byte) ((y >> 8) & 0xFF);
toWrite[2 * x] = b1;
toWrite[2 * x + 1] = b2;
// System.out.printf("%d %d%n", b1, b2);
}
sdl.write(toWrite, 0, toWrite.length);
}
}
}
However, this only works up to an amplitude of 127
. When the System.out.printf
is uncommented, it is clear that this amplitude uses only 1 byte. When I go up to 128
, I get outputs like this (and ugly sounds):
0 0
21 0
42 0
62 0
80 0
96 0
109 0
118 0
125 0
-128 0
127 0
123 0
115 0
104 0
90 0
73 0
55 0
35 0
13 0
Negative values are similar, without the change in sign, and the second byte is always -1
I have deduced that this is because of signed bytes and two's complement, but I still can't figure out what I can do to fix this.
How does Java compose its audio?
Upvotes: 1
Views: 128
Reputation: 9341
You are on the right track, although you might have your byte ordering backwards (try swapping b1 and b2 in the toWrite assignments to see if that makes things sound better). That could explain why things sound bad. Also, an amplitude of 127 is very small so you should try increasing it to the max (32767).
The way you are printing the bytes out is probably adding to the confusion. Splitting a signed 16-bit number into 2 signed 8-bit numbers doesn't really make any sense. Consider when the 16-bit number is -1 (0xffff), you print out two signed bytes and you get -1 (0xff) and -1 (0xff). You'd be better off printing the bytes out as hex values and dealing with the sign in your head.
Upvotes: 1