I have an application in which I'm trying to send UDP messages using TSLv5. The protocol is somewhat complicated, but to do it I need to write 16 bit values as little-endian and then send that via UDP.
Here's my code doing just that:
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putShort(SCREEN_POS, screen);
buffer.putShort(INDEX_POS, index);
ByteBuffer text = ByteBuffer.wrap(value.getBytes());
short textLength = (short) value.getBytes().length;
buffer.putShort(LENGTH_POS, textLength);
ByteBuffer combined = ByteBufferUtils.concat(buffer, text);
short control = 0x00;
control |= rhTally << 0;
control |= textTally << 2;
control |= lhTally << 4;
control |= brightness << 6;
combined.putShort(CONTROL_POS, control);
short msgLength = (short) (combined.array().length - 2);
combined.putShort(PBC_POS, msgLength);
return new DatagramPacket(combined.array(), combined.array().length, ip, 9000);
This mostly works, but the problem is when I have values that are greater than 127.
For example, my index is 148 and when all is said and done, my control comes out to be 193. When I write those values to the ByteBuffer they become -108 and -63, respectively.
I know why this happens, a ByteBuffer is an array of bytes and bytes can't be greater than 127. What I don't know is how I can achieve this? The protocol does not work if I send signed values, it has to be the exact number.
I can assure that a signed java byte will be read correctly in the two bytes of a short. I have simplified the code, writing the fields one after the other in linear fashion, with message fields in front. Also just used one ByteBuffer.
(Maybe there is some small error like a wrong offset.)
Also I send the text bytes as being in UTF-8. You used the implicit platform encoding, which may differ on every computer.
byte[] text = value.getBytes(StandardCharsets.UTF_8);
int textLength = text.length;
int length = 2 + 2 + 2 + 2 + 2 + textLength;
ByteBuffer buffer = ByteBuffer.allocate(length)
.order(ByteOrder.LITTLE_ENDIAN);
short control = 0x00;
control |= rhTally << 0;
control |= textTally << 2;
control |= lhTally << 4;
control |= brightness << 6;
buffer.putShort(/*CONTROL_POS,*/ control);
short msgLength = (short) (length - 2);
buffer.putShort(/*PBC_POS,*/ msgLength);
buffer.putShort(/*SCREEN_POS,*/ screen);
buffer.putShort(/*INDEX_POS,*/ index);
buffer.putShort(/*LENGTH_POS,*/ (short)textLength);
buffer.put(text, 0, textLength);
return new DatagramPacket(buffer.array(), length, ip, 9000);
Related
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.
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
The protocol I'm using requires sending back the current position in a file as a "unsigned, 4 byte integer in network byte order". There are several questions on this, but they are assuming I'm using Integers, not Longs
I am attempting to port this to NIO's ByteBuffer so it can be sent in the socket channel:
long bytesTransfered = ... some number of bytes transfered...
//TODO: What does this actually do?
outBuffer[0] = (byte) ((bytesTransfered >> 24) & 0xff);
outBuffer[1] = (byte) ((bytesTransfered >> 16) & 0xff);
outBuffer[2] = (byte) ((bytesTransfered >> 8) & 0xff);
//TODO: Why does netbeans say this does nothing?
outBuffer[3] = (byte) ((bytesTransfered >> 0) & 0xff);
Are their any methods in ByteBuffer that accomplish this? Hopefully in a more obvious, self-descriptive way then the bit-shifting magic above?
Whether signed or unsigned, the bits are the same.
If you cast a long to an int, the JVM discards the high-order bits. The issue comes when promoting an int to a long: Java will sign-extend the value, filling in the high-order bits of the long with the most-significant bit of the int.
To resolve this problem, simply apply a mask to the long. The following should make this clear:
long value = Integer.MAX_VALUE + 1234L;
System.out.println("original value = " + value);
int iValue = (int)value;
System.out.println("value as int = " + iValue);
byte[] array = new byte[4];
ByteBuffer buf = ByteBuffer.wrap(array);
buf.putInt(0, iValue);
int iRetrieved = buf.getInt(0);
System.out.println("int from buf = " + iRetrieved);
long retrieved = iRetrieved;
System.out.println("converted to long = " + retrieved);
retrieved = retrieved & 0xFFFFFFFFL;
System.out.println("high bytes masked = " + retrieved);
However, be aware that you still have only 32 bits. If the filesizes is greater than 4Gb you won't be able to fit it into 4 bytes (and if you have to worry about files > 2G, then you should worry about files > 4G).
That's exactly what ByteBuffer.putInt() is for. You say you're using long but you also only want to write four bytes, so you'll have to cast your long to int. Or else use putLong() and get 8 bytes.
I have some problems trying yo convert short value to byte[2]. I'm using this to make some transformations on some audio data buffer(applying gain to buffer). First I load the audio buffer like this:
mRecorder.read(buffer, 0, buffer.length);
where buffer is
private byte[] buffer;
Than, I get the sample (the recording is in 16bit sample size), like this:
short sample = getShort(buffer[i*2], buffer[i*2+1]);
The getShort is define like this:
/*
*
* Converts a byte[2] to a short, in LITTLE_ENDIAN format
*
*/
private short getShort(byte argB1, byte argB2)
{
return (short)(argB1 | (argB2 << 8));
}
Then I apply gain to the sample:
sample *= rGain;
After this, I try to get back the byte array from the multiplied sample:
byte[] a = getByteFromShort(sample);
But this fails, because the sound has a lot of noise even if the gain is 1.
Below is the getByteFromShort method definion:
private byte[] getByteFromShort(short x){
//variant 1 - noise
byte[] a = new byte[2];
a[0] = (byte)(x & 0xff);
a[1] = (byte)((x >> 8) & 0xff);
//variant 2 - noise and almost broke my ears - very loud
// ByteBuffer buffer = ByteBuffer.allocate(2);
// buffer.putShort(x);
// buffer.flip();
return a;
}
So the problem is when converting the short value to byte[2]. When the gain was 1.0, the sound was fill with noise.
Below is the full gain applying method:
for (int i=0; i<buffer.length/2; i++)
{ // 16bit sample size
short curSample = getShort(buffer[i*2], buffer[i*2+1]);
if(rGain != 1){
//apply gain
curSample *= rGain;
//convert back from short sample that was "gained" to byte data
byte[] a = getByteFromShort(curSample);
//modify buffer to contain the gained sample
buffer[i*2] = a[0];
buffer[i*2 + 1] = a[1];
}
}
Could you guys please take a look over getByteFromShort method and tell me where I'm wrong?
Thanks.
getByteFromShort() seems OK.
getShort(byte argB1, byte argB2) is wrong. It produces incorrect result when argB1 is negative.
It should be
return (short)((argB1 & 0xff) | (argB2 << 8));
Use the following code:
ret[0] = (byte)(x & 0xff);
ret[1] = (byte)((x >> 8) & 0xff);
I would use ByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(8*1024);
mRecorder.read(buffer.array(), 0, buffer.capacity());
// using NIO
mRecorder.read(buffer);
while(buffer.remaining() > 1) {
short s = bb.getShort(x);
// do something with s
}
ByteBuffer and its cohorts in java.nio can help with this. Basically, you will create a ByteBuffer backed by an array with your data ByteBuffer.wrap(array). You can then set the endianness of the buffer with ByteBuffer.order() and use functions like get/put Int/Short/byte... to manipulate data in the underlying array.
I do some learning of using voip over udp in a small network. I know there are bundles of libraries ready to do and overdo everything I ever need with a few method calls, but as I said I am learning, so need to reinvent the wheel to see how it works.
I am currently investigating the DatagramPacket class and I've noticed that there is no method that would set header information(ie packet order sequence number which I need to know to do interleaving) in DatagramPacket class.
A little code to reflect the environment:
byte[] block;
DatagramPacket packet; // UDP packet
/* x Bytes per block , y blocks per second,
z ms time block playback duration */
block = recorder.getBlock(); // assume I have class that handles audio
// recording and returns speech in a
// uncompressed form of bytes
packet = new DatagramPacket(block, block.length, clientIP, PORT);
Firstly, I assume that because it is UDP, the sender doesnt really care anything whatsoever besides the simple fact that he throws packets somewhere. So that is why there is no such method inside.
Secondly, I assume that I need to do it myself - add extra bytes to the byte block to be sent , which would contain a sequence number of a packet? However am also concerned that if I do that, then how do I recognize if bytes are header bytes not audio bytes? I can make assumption that first byte represents a number, however we know that byte can only represent 258 numbers. I've never really worked on byte level before. Or there maybe other techniques?
Shortly saying, to do interleaving I need to know how to set up packet sequence number as I can't order unordered packets :-)
Thank You,
You'll need to serialize/deserialize data types your program uses onto a byte array.
Lets assume you're talking about RTP, and you'd want to send a packet with these fields - look at chapter 5 in the RTP specs:
Version = 2
padding = 0
extension = 0
CSRC count = 1
marker = 0
payload type = 8 (G711 alaw)
sequence number = 1234
timestamp = 1
one CSRC = 4321
Lets put these into some variables, using integers for ease, or long when we need to deal with an unsigned 32 bit value:
int version = 2;
int padding = 0;
int extension = 0;
int csrcCount = 1;
int marker = 0;
int payloadType = 8;
int sequenceNumber = 1234;
long timestamp = 1;
long ourCsrc = 4321;
byte buf[] = ...; //allocate this big enough to hold the RTP header + audio data
//assemble the first bytes according to the RTP spec (note, the spec marks version as bit 0 and 1, but
//this is really the high bits of the first byte ...
buf[0] = (byte) ((version & 0x3) << 6 | (padding & 0x1) << 5 | (extension & 0x1) << 4 | (csrcCount & 0xf));
//2.byte
buf[1] = (byte)((marker & 0x1) << 7 | payloadType & 0x7f);
//squence number, 2 bytes, in big endian format. So the MSB first, then the LSB.
buf[2] = (byte)((sequenceNumber & 0xff00) >> 8);
buf[3] = (byte)(sequenceNumber & 0x00ff);
//packet timestamp , 4 bytes in big endian format
buf[4] = (byte)((timestamp & 0xff000000) >> 24);
buf[5] = (byte)((timestamp & 0x00ff0000) >> 16);
buf[6] = (byte)((timestamp & 0x0000ff00) >> 8);
buf[7] = (byte) (timestamp & 0x000000ff);
//our CSRC , 4 bytes in big endian format
buf[ 8] = (byte)((sequenceNumber & 0xff000000) >> 24);
buf[ 9] = (byte)((sequenceNumber & 0x00ff0000) >> 16);
buf[10] = (byte)((sequenceNumber & 0x0000ff00) >> 8);
buf[11] = (byte) (sequenceNumber & 0x000000ff);
That's the header, now you can copy the audio bytes into buf, starting at buf[12] and send buf as one packet.
Now, the above is ofcourse just to show the principles, an actual serializer for a RTP packet would have to deal with much more, in accordance to the RTP specificaion (e.g. you might need some extension headers, you might need more than one CSRC, you need the correct payload type according to the format of the audio data you have, you need to packetize and schedule those audio data correctly - e.g. for G.711Alaw you'll should fill each RTP packet with 160 bytes of audio data and send one packet every 20 milisecond.
I am trying to find the value of the first 2 bytes in a UDP packet which corresponds to the length of the remaining payload. What is the best method to find this value in Java given that I know the first 2 bytes? Would java.nio.ByteBuffer be of any use?
Thanks
I usually use something like this:
static public int buildShort(byte high, byte low)
{
return ((0xFF & (int) high) * 256) + ((0xFF & (int) low));
}
Then you take first two bytes of your DatagramPacket:
int length = buildShort(packet.getData()[0], packet.getData()[1]);
Mind that I used length as an int because also short data type (as everyone) is signed in Java, so you need a larger space.
Using a ByteBuffer is convenient, just don't get tripped up by Java signed 16-bit values:
byte[] data = new byte[MAX_LEN];
ByteBuffer buf = ByteBuffer.wrap(data);
DatagramPacket pkt = new DatagramPacket(data, data.length);
⋮
while (connected) {
socket.receive(pkt);
int len = buf.getShort() & 0xFFFF;
⋮
}
If you don't want to use ByteBuffer, the conversion is still fairly easy. The equivalent multiplication and addition can be used, but I see bit operators used more frequently:
int len = (data[0] & 0xFF) << 8 | data[1] & 0xFF;
You can indeed make use of java.nio.ByteBuffer. Here's a kickoff example:
ByteBuffer buffer = ByteBuffer.allocate(2);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.put(byte1);
buffer.put(byte2);
int length = buffer.getShort(0) & 0xFFFF; // Get rid of sign.
Using ByteBuffer would only be of value if you are reading the UDP packets (using nio). You can create a utility method:
static final int getLength(DatagramPacket packet) {
byte data[] = DatagramPacket.getData();
return (int)((0xFF & (int)data[0]) << 8) | (0xFF & (int)data[1]));
}