3D Sound

java wav wave 3d sound

This example shows a way to make sound appear from a direction (like in a game) and is based on Wave audio files. It the concept applies to all audio formats, though one should always keep compression in mind.

Wave files have three parts to them, a tiny first one which essentially says that this is in fact a wave file. A second with information about the audio, and finally the raw audio data itself. One suggest giving a quick glance at the spec.

Unfortunately there is no easy way to do this in Java so one has to do it themselves byte by byte.

First let us load all the mono bytes and create a new buffer, almost twice the size.

// SOUND_DATA_OFFSET is the header size and is not always the same but usually it's 44 (bytes) ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int nRead; byte[] data = new byte[4096]; while ((nRead = is.read(data, 0, data.length)) != -1) buffer.write(data, 0, nRead); buffer.flush(); byte[] monoBytes = buffer.toByteArray(); byte[] stereoBytes = new byte[(monoBytes.length - SOUND_DATA_OFFSET) * 2 + SOUND_DATA_OFFSET]; int filesize = stereoBytes.length - 8; // ret of the code }

Now one has to change the header

// chunk size writeInt(filesize, monoBytes, 4, 4); // subchunk size writeInt(filesize - WAVE_HEADER_SIZE, monoBytes, 74, 4); // NumChannels (2b) writeInt(2, monoBytes, 22, 2); // ByteRate (4b) writeInt(22050 * 16 / 8, monoBytes, 28, 4); // BlockAlign (2b) writeInt(2 * 16 / 8, monoBytes, 32, 2); System.arraycopy(monoBytes, 0, stereoBytes, 0, SOUND_DATA_OFFSET);

And now one can create the Stereo sound

for (int i = SOUND_DATA_OFFSET; i < stereoBytes.length; i += 4) { // get as int int val = (short) ( ((monoBytes[(i - SOUND_DATA_OFFSET) / 2 + SOUND_DATA_OFFSET + 1] & 0xff) << 8) | (monoBytes[(i - SOUND_DATA_OFFSET) / 2 + SOUND_DATA_OFFSET] & 0xff) ); // do maths int left = val; int right = val; // angleMultiplier = (angle, RIGHT_EAR) -> Math.abs(Math.sin(Math.toRadians((angle + offset) / 2))); right *= Math.max(0.2, angleMultiplier(angle, RIGHT_EAR)); // RIGHT_EAR = 90 left *= Math.max(0.2, angleMultiplier(angle, LEFT_EAR)); // LEFT_EAR = -90 // do what you think is right right *= distMulti; left *= distMulti; // left channel left = Math.max(-32_760, Math.min(32_760, left)); stereoBytes[i + 0] = (byte) (left & 0xFF); stereoBytes[i + 1] = (byte) ((left >> 8) & 0xFF); // right channel right = Math.max(-32_760, Math.min(32_760, right)); stereoBytes[i + 2] = (byte) (right & 0xFF); stereoBytes[i + 3] = (byte) ((right >> 8) & 0xFF); }

As writing Integers as bytes is tedious, here you go

private static void writeInt(int value, byte[] array, int offset, int length) { for (int i = 0; i < length; i++) array[offset + i] = (byte) (value >>> (8 * i)); }

Reference

No known

History Apr 06, 2018