Reputation: 669
For a peer-to-peer audio client, I need to have the ability to change the output volume to a desired level. In my case, the volume is a floating point number between zero and one.
I modify the audio stream this way:
void play(byte[] buf)
{
for (int i = 0; i < buf.length; i += 2)
{
// sample size is 2 bytes, so convert to int and then back
int data = ((buf[i + 1] & 0xFF) << 8) | (buf[i] & 0xFF);
data = (int) (data * outputVolume);
buf[i] = (byte) (data & 0xFF);
buf[i + 1] = (byte) (data >> 8);
}
line.write(buf, 0, Math.min(buf.length, line.available()));
}
Now, when outputVolume
is set to 0, the output is silent. When it is set to 1, it behaves normal and quality is fine (as it is not modified). But any numbers between 0 and 1 produce a horrible noise which is louder than the expected stream itself. At 0.5, the noise reaches it's loudest point.
I don't want to use the controls of the audio mixer itself (like gain control or volume control) because I had compatibility problems this way and later on, I want to modify the byte stream even more so I have to iterate through the stream anyway.
Upvotes: 0
Views: 443
Reputation: 37835
Assuming the audio data is signed (because I think it would be pretty unusual to have unsigned 16-bit samples), there is a mistake in that code, because you also need to sign extend the sample.
You can remove the & 0xFF
from the high byte which will let sign extension happen automatically:
int data = (buf[i + 1] << 8) | (buf[i] & 0xFF);
If for some reason you couldn't modify the and-shift-or expression, you could do sign extension like this:
int data = ((buf[i + 1] & 0xFF) << 8) | (buf[i] & 0xFF);
data = (data << 16) >> 16;
The result of the shifting expression is equivalent to this:
if (data > 32767)
data -= 65536;
And this:
if ((i & 0x80_00) != 0)
i |= 0xFF_FF_00_00;
(Those would also work.)
However, in your case you can just remove the & 0xFF
from the high byte.
For a quick explanation, if you had some 16-bit sample like this (which is -1):
11111111_11111111
If you just convert to 32-bit without sign extending, you would get:
00000000_00000000_11111111_11111111
But that's 65536, not -1. Sign extension fills the upper bits with 1s if the MSB in the 16-bit value was set:
11111111_11111111_11111111_11111111
Upvotes: 2