I'm working on an Android application (in Java, obviously) and I recently updated my UDP reader code. In both versions, I set up some buffers and receive a UDP packet:
byte[] buf = new byte[10000];
short[] soundData = new short[1000];
DatagramPacket packet = new DatagramPacket (buf, buf.length);
socket.receive (packet);
In the initial version, I put the data back together one byte at a time (it's actually 16 PCM audio data):
for (int i = 0; i < count; i++)
soundData[i] = (short) (((buf[k++]&0xff) << 8) + (buf[k++]&0xff));
In the updated version, I used some cool Java tools I didn't know about when I started:
bBuffer = ByteBuffer.wrap (buf);
sBuffer = bBuffer.asShortBuffer();
sBuffer.get (soundData, 0, count);
In both cases, "count" is being populated correctly (I checked). However, there appear to be new problems with my streaming audio -- perhaps it isn't being handled fast enough -- which doesn't make any sense to me. Obviously, the buffer code is compiling into a lot more than three statements of JVM code, but it sure seemed like a reasonable assumption when I start this that the 2nd version would be faster than the 1st.
Patently, I'm not insisting that my code HAS to use Java NIO buffers, but at first glance at least, it DOES seem like a mo' betta' to go about this.
Anybody got any recommendations for a fast, simple Java UDP reader and whether there is a generally accepted "best way"??
Thanks, R.
ByteBuffer holds a sequence of integer values to be used in an I/O operation. The ByteBuffer class provides the following four categories of operations upon long buffers: Absolute and relative get method that read single bytes. Absolute and relative put methods that write single bytes.
wrap. Wraps a byte array into a buffer. The new buffer will be backed by the given byte array; that is, modifications to the buffer will cause the array to be modified and vice versa. The new buffer's capacity will be array.
By default, the order of a ByteBuffer object is BIG_ENDIAN. If a byte order is passed as a parameter to the order method, it modifies the byte order of the buffer and returns the buffer itself. The new byte order may be either LITTLE_ENDIAN or BIG_ENDIAN.
The capacity and limit of the new buffer are given by the number of remaining bytes divided by two. The capacity is the index of the last byte in the buffer. The limit is the index of the first index that should not be read from the buffer. The mark will be undefined.
Your code would be more efficient if instead of reading a packet into a byte array (copying the data from a native buffer into the array) and then wrapping it in a new ByteBuffer (creating a new object) and converting to a ShortBuffer (creating a new object) you set up your objects only once and avoided the copy.
You can do this by using DatagramChannel.socket() to create your socket, then connecting it as usual and usuing socket.getChannel() to get a DatagramChannel object. This object will allow you to read packets directly into an existing ByteBuffer (which you should create with ByteBuffer.allocateDirect for maximum efficiency). You can then us asShortBuffer() just once to create a view of your data as shorts, and read from that ShortBuffer after every time you refill the ByteBuffer.
The code therefore looks like this:
DatagramSocket socket = DatagramChannel.socket();
// code to connect socket
DatagramChannel channel = socket.getChannel();
ByteBuffer buffer = ByteBuffer.allocateDirect (10000);
// you may want to invoke buffer.order(...) here to tell it what byte order to use
ShortBuffer shortBuf = buffer.asShortBuffer();
// in your receive loop:
buffer.clear();
channel.receive(buffer);
shortBuf.position(0).limit(buffer.position()/2); // may ignore a byte if odd number received
shortBuf.get(soundBuf,0,shortBuf.limit());
You should find this is much more efficient than your previous code because it avoids an entire copy of the data and the format conversion is handled by hand-optimized code rather than compiler generated byte manipulation which may be suboptimal. It will be somewhat more efficient if you use the platform-native byte order (I believe Android uses little-endian byte order on all platforms it is available for, and your code above seems to be big-endian, so this may not be possible for you), in which case shortBuf.get() becomes a direct memory copy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With