Reputation: 11
Here is my code. I am using javax.sound.sampled.* for the audio classes.
byte[] myAudioByteArray = new byte[]{0, 127, 127, 127, -127, 127};
ByteArrayInputStream bais = new ByteArrayInputStream(myAudioByteArray);
AudioInputStream stream = new AudioInputStream(bais, new AudioFormat(SampleRate.R_44100, 24, 1, true, false), myAudioByteArray.length)
The amplitude is bitdepth, so how does java allow you to set the bitdepth of a each sample? I have set the bit depth of the resulting file to be 24 bit depth, so in theory I should be able to set the samples at the specific depth range (16,777,216). The problem is I don't know how you do this with the bytes you feed into the ByteArrayInputStream object that then goes into the AudioInputStream object because they are signed. I thought maybe you could convert the bytes to their binary representation and then combine 3 of them to get 3 bytes for the 16,777,216 range, but that theory is incorrect from what I can tell.
Upvotes: 1
Views: 388
Reputation: 5310
I thought maybe you could convert the bytes to their binary representation and then combine 3 of them to get 3 bytes for the 16,777,216 range, but that theory is incorrect from what I can tell.
Not sure, why this didn't work for you.
Each sample has to be encoded, either big endian or little endian. Here are some methods from the audio processing library jipes that show how you can decode bytes to samples from either little or big endian to int
:
private static int byteToIntLittleEndian(final byte[] buf, final int offset, final int bytesPerSample) {
int sample = 0;
for (int byteIndex = 0; byteIndex < bytesPerSample; byteIndex++) {
final int aByte = buf[offset + byteIndex] & 0xff;
sample += aByte << 8 * (byteIndex);
}
return sample;
}
private static int byteToIntBigEndian(final byte[] buf, final int offset, final int bytesPerSample) {
int sample = 0;
for (int byteIndex = 0; byteIndex < bytesPerSample; byteIndex++) {
final int aByte = buf[offset + byteIndex] & 0xff;
sample += aByte << (8 * (bytesPerSample - byteIndex - 1));
}
return sample;
}
Once the value is decoded you still have to cast to your target interval. If you were interested in a 2 bytes format, i.e. bit depth of 16, you'd simply do this:
final int sample = byteToIntLittleEndian(buf, sampleOffset, bytesPerChannel);
final short shortSample = (short)sample;
The cast takes care of the overflow.
With 3 bytes it's a little more complicated. You need to know the value range first. Note that because you are interested in signed values, it's not [0,2^24]
, but:
/**
* A constant holding the minimum value a <code>signed24bit</code> can
* have, -2<sup>22</sup>.
*/
private static final int MIN_VALUE_24BIT = -2 << 22;
/**
* A constant holding the maximum value a <code>signed24bit</code> can
* have, 2<sup>22</sup>-1.
*/
private static final int MAX_VALUE_24BIT = -MIN_VALUE_24BIT-1;
Then you use that to convert to a signed sample value like this:
final int sample = byteToIntLittleEndian(bud, sampleOffset, bytesPerChannel);
final int threeByteSample = sample > MAX_VALUE_24BIT ? sample + MIN_VALUE_24BIT + MIN_VALUE_24BIT : sample;
Upvotes: 1