How can I split/combine bytes into audio? - java

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?

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.

Related

Pack 3 ints into short

I am trying to pack 3 ints into short and then get the values back; I am using a test code with the following values:
160, 71, 50
But when I try to unpack the values, ​​x & z are wrong, I get 15, 71, 50 (please, note 15 when 160 is expected).
Why is this? thanks. My code is
public static void main(String[] args) {
int[] testValue = new int[]{160, 71, 50};
short packed = pack(testValue[0], testValue[1], testValue[2]);
int[] output = unpack(packed);
System.out.println(output[0]);
System.out.println(output[1]);
System.out.println(output[2]);
}
public static short pack(int x, int y, int z) {
return (short) (x << 12 | z << 8 | y);
}
public static int[] unpack(short packed) {
int x = packed >> 12 & 0xF;
int y = packed & 0xFF;
int z = packed >> 8 & 0xF;
return new int[]{x, y, z};
}
Well, it's impossible; let's see why. We have 3 values to pack 160, 71, 50:
160 == 10100000 (binary), 8 bits long
71 == 1000111 (binary), 7 bits long
50 == 110010 (binary), 6 bits long
Please, note, that
160 is 8 bits long
71 is 7 bits long
50 is 6 bits long
------------------
21 bits for all 3 numbers
we need at least 21 bits (8 + 7 + 6) to store all three numbers when char as well as short is 16 bit value only.
You don't have enough bits in a 16 bit short to pack those three values.
Here is a breakdown of your bits:
160 << 12 = 0x0AA000
50 << 8 = 0x003200
71 << 0 = 0x000047
ORing these together results in:
0xA3247
Which can be stored in 3 bytes not two.
When casting to a short you end up with
0x3247
You could fit three values in the short if you could store each number in 5 bits individually.
Each number ranging from 0 - 31 for unsigned values. With only one value being able to take up 6 bits. This would yield 5 + 5 + 6 = 16 bits.

Combine two 3 byte integers, and one 2 byte integer into one 8 byte integer

Trying to store three integers into one to use for a hash, and decode back into their original values.
The variables:
x = 3 byte integer (Can be negative)
z = 3 byte integer (Can be negative)
y = 2 byte integer (Cannot be negative)
My current code - doesn't work with negatives:
long combined = (y) | (((long) z) << 16) | ((((long) x)) << 40);
int newX = (int) (combined >> 40); // Trim off 40 bits, leaving the heading 24
int newZ = (int) ((combined << 24) >> (40)); // Trim off 24 bits left, and the 16 bits to the right
int newY = (int) ((combined << 48) >> 48); // Trim off all bits other then the first 16
It doesn't work for negatives because your "3 byte integer" or "2 byte integer" is actually a regular 4-byte int. If the number is negative, all the highest bits will be set to "1"; if you binary-or the numbers together, these high 1 bits will overwrite the bits from the other numbers.
You can use bit-masking to encode the number correctly:
long combined = (y & 0xffff) | (((long) z & 0xffffff) << 16) | ((((long) x & 0xffffff)) << 40);
This will cut off the high-bits outside the 16 or 24 bit range that you're interested in.
The decoding already works fine, because the bit-shifting that you perform takes care of sign-extension.

How to change 37 bits in a byte array to all complement of existing value

I have a byte array myByteArray[82]
I want to change exactly 37 bits in this array to the complement value. ie. if bit0 has a '1' I want to change it to '0'. I need to change first 37 such bits to introduce error in this byte array.
Please suggest how to do this
It's not entirely clear what you are trying to do. My best understanding is that you have an array of 82 bytes and you want to invert the lowest 37 bits of the array. Since a byte is 8 bits, so you can do this:
byte[] myByteArray = new byte[82];
// invert lowest 32 bits, 8 at a time
for (int i = 0; i < 4; ++i) {
myByteArray[i] = (byte)(~myByteArray[i]);
}
// invert next five bits
myByteArray[4] = (byte) (
(myByteArray[4] & 0xE0) // top 3 bits unchanged
|
((~myByteArray[4)) & 0x1F) // bottom 5 bits inverted
);
try
byte[] a82 = ...
Set<Integer> set = new HashSet<Integer>();
while (set.size() < 37) {
set.add((int) (Math.random() * 82));
}
for (int i : set) {
int ibyte = i / 8;
int ibit = i % 8;
int m = 1 << ibit;
a[ibyte] ^= m;
}
This works:
int nBits = 37;
int i = 0;
for (; i<nBits / 8; ++i)
myByteArray[i] = (byte)((byte) myByteArray[i] ^ 0xFF);
myByteArray[i] = (byte)(myByteArray[i] ^ ((0xFF >>> 5) ^ 0xFF));
Whether or not you do the final XOR with the 0xFF on the last line depends on whether you consider the most significant bit to be the first bit (then use it) or the last bit (then omit it);
To invert a specific 37 bits, randomly chosen:
// array of 82 bytes with 37 selected bits set to 1, all the rest zero
// you could generate this programmatically as well if you need a different
// set of bits each time, but your question implies you don't
byte[] mask = { 0x00, 0x01, 0x02, 0x80, .... 0x00 };
for (int i=0; i<myByteArray.length; i++)
{
myByteArray[i] ^= mask[i];
}
Use the bit-exclusive-or operator ^. The truth table for xor is
M a s k
| 0 | 1
D -+---+---
a 0| 0 | 1
t -+---+---
a 1| 1 | 0
Wherever there's a 1 in the mask the corresponding bit in the data will be flipped.

GZipInputStream .read() insert zeros into buffer

I am having a strange program with a GzipInputStream zero filling part of the buffer. I have the fortune of knowing what the bytes are supposed to look like in the stream and I can see the buffer is being filled with 8 correct bytes and 12 zeros (shouldn't be zero)
BYTES SHOULD LOOK LIKE THIS---->
0
20
82
22
-91
27
-96
65
66
65
88
32
32
32
32
81
32
0
0
0
100
78
BYTES ACTUALLY LOOK LIKE THIS--->
0
20
82
22
-91
27
-96
65
66
65
0
0
0
0
0
0
0
0
0
0
0
0
The first two bytes represent an integer that determine the size of the variable length (in bytes) payload after the first two. So in this example, the first bytes are 0 20 and in BIG_ENDIAN this gives us a subsequent payload size of 20 bytes.
Here is my code for reading
gzipInputStream = new GZIPInputStream(url.openStream());
byte[] payload = new byte[2];
gzipInputStream.read(payload);
for(int i=0;i<payload.length;i++){
System.out.println(payload[i]);
}
int payloadSize = ((payload[0] & 0xFF) << 8) | ((payload[1]) & 0xFF);
//read the next payloadSize bytes
byte[] messageBytes = new byte[payloadSize];
gzipInputStream.read(messageBytes);
So the first two bytes are those in the payload array and the second 20 bytes are those in messageBytes. Can't figure it out
Modified code thanks to NPE
byte[] payloadSizeBytes = new byte[2];
int payloadSizeBytesRead = 0;
while(payloadSizeBytesRead < 2){
int r = gzipInputStream.read(buffer);
if(r>0){
payloadSizeBytes[payloadSizeBytesRead] = buffer[0];
payloadSizeBytesRead++;
}
}
int payloadSize = ((payloadSizeBytes[0] & 0xFF) << 8) | ((payloadSizeBytes[1]) & 0xFF);
//read the next payloadSize bytes
byte[] messageBytes = new byte[payloadSize];
int messageBytesRead = 0;
while(messageBytesRead < payloadSize){
int r = gzipInputStream.read(buffer);
if(r>0){
messageBytes[messageBytesRead] = buffer[0];
messageBytesRead++;
}
}
for(int i=0;i<messageBytes.length;i++){
System.out.println(messageBytes[i]);
}
The contract on read(byte[]) is that it reads some data, and returns how many bytes have been read. As things stand, you are ignoring the return value. Instead, you should examine the return value of read() and keep calling read() until you've read payloadSize bytes.
An easy way to do this is by using read(b, off, len) in a loop:
int payloadSize = ((payload[0] & 0xFF) << 8) | ((payload[1]) & 0xFF);
byte[] messageBytes = new byte[payloadSize];
int bytesRead = 0;
while (bytesRead < payloadSize) {
bytesRead += gzipInputStream.read(messageBytes, bytesRead, payloadSize - bytesRead);
}

Java stripping zeros from function

I'm trying to flip some bytes around in Java and the function I have is working correctly for some bytes and failing for others.
The function I am using is this:
public static int foldInByte(int m, int pos, byte b) {
int tempInt = (b << (pos * 8));
tempInt = tempInt & (0x000000ff << (pos * 8));
m = m | tempInt;
return m;
}
And the code that implements this is:
byte[] bitMaskArray = new byte[]{
byteBuffer.get(inputIndex),
byteBuffer.get(inputIndex + 1),
byteBuffer.get(inputIndex + 2),
byteBuffer.get(inputIndex + 3)};
int tempInt = 0;
tempInt = foldInByte(0, 3, bitMaskArray[3]);
tempInt = foldInByte(tempInt, 2, bitMaskArray[2]);
tempInt = foldInByte(tempInt, 1, bitMaskArray[1]);
tempInt = foldInByte(tempInt, 0, bitMaskArray[0]);
bitMask = tempInt;
The bytes are being read from a ByteBuffer with the byteOrder being Little Endian.
For example, the bytes 00 01 B6 02 set the bitMask to: 2B60100 - which works perfectly in my program.
However, if the bytes are A0 01 30 00, the bitMask is set to: 3001A0 - which has stipped the last zero from the bitmask.
Is there any way I can stop Java from stipping off trailing zeros?
I hope that makes sense.
Thanks
Tony
The zeros are not being stripped -- both examples cited are correct.
00 01 B6 02 is the 4-byte little-endian for 2B60100
A0 01 30 00 is the 4-byte little-endian for 3001A0
The zeros are there, but probably just not being printed. The System.out.print family of calls will not print leading zero digits.
I might mention that your method is needlessly complex. Here is a single method that computes the same value:
static int extractLittleEndian4(byte[] buf, int index)
{
int a = buf[index+0]&0xff, b = buf[index+1]&0xff, c = buf[index+2]&0xff, d = buf[index+3]&0xff;
return a | (b << 8) | (c << 16) | (d << 24);
}
It looks like you have a ByteBuffer filled with your bytes already. Why don't you let the ByteBuffer reverse the bytes for you? Just add the bytes to the buffer (BIG_ENDIAN is the default if you want to add an integer instead of bytes) and then change the order before reading the integer.
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
int output = byteBuffer.getInt(0);
If all you're doing is reversing the byte order, let the library do the work for you. If you happened to start with an integer value, you can even just do this:
int input = ...;
int output = Integer.reverseBytes(input);

Categories