3D Sound Engine

BTW, do you notice the problem with normal or streaming sources, or both? And which library plug-in, LWJGLOpenAL or JavaSound or both?

Sound System, Bug Fixes, New Plug-in

Sound System
Sound System jPCT

JavaSound library pluggin
LWJGL OpenAL library pluggin
JOAL library pluggin

WAV codec pluggin
JOgg codec pluggin
JOrbis codec pluggin
IBXM codec pluggin
JSpeex codec pluggin

JavaDoc

I fixed the new bug I introduced by trying to fix the IBXM reverse-byte-order bug. While this is a relatively minor fix, I ended up having to make small changes to nearly every package, so be sure to download all the new versions (in other words, mix-and-match at your own peril).

Additionally, I finished the JSpeex codec plug-in. Speex-encoded files are generally wrapped into a .ogg container file. (This is what is produced by the binaries you can download from http://www.speex.org/downloads/). While I was digging around, I noticed that there is an “experimental” capability for JSpeex to wrap the data into .wav files as well. Since the .wav format is quite simple to read from (much easier to understand than .ogg), I went ahead and made this codec plug-in useable with either format. By default, it assumes a .ogg container, but I added a method you can call to use speex-encoded .wav files if you prefer instead. Just a reminder - these aren’t normal .ogg or .wav files (just the header information is), so obviously they will not work with the other codec plug-ins. I would recommend using a different file extension (like .spx) so you don’t get them mixed up with any other non-speex files.

One more note: Speex can also save into “raw” format (i.e., no header information). I didn’t make the codec plug-in support this format because it requires the user to define the header information manually, which doesn’t mesh well with SoundSystem. However, if using the raw format is a requirement for you, let me know and I can look into it more closely to see if there is a way to support it in my codec plug-in.

As always, let me know if you run into any problems, and I will work on correcting them as soon as possible.

I noticed a weird bug in the LibraryJavaSound plug-in today, where if the source and listener have exactly the same position, the source is silent regardless of its intended volume. This is on my PC running Linux and 64-bit Sun Java (a combination which has a multitude of other problems), so I’ll verify that this is happening on “normal” computers as well when I get home this evening (if anyone wants to test this for me, try playing a source from position 0,0,0 without moving the listener, and see if you hear anything).

–EDIT–
I’ve verified that this is a 64-bit Sun Java Linux thing (works fine in other OSs and JREs). It is really odd - everything indicates that the source is playing, but no sound. I think maybe I’ll play around with the gain controls next (maybe it’s assuming incorrectly that a gain of “0” is silent instead of initial gain).

Hi!

Your fix for the problem of webcam does not work:

Starting up SoundSystem...
Initializing Java Sound
    (The Java Sound API.  For more information, see http://java.sun.com/products/java-media/sound/)
JavaSound initialized.

Error in class 'ChannelJava Sound'
    Unable to attach buffer to clip in method 'attachBuffer'
    ERROR MESSAGE:
        Audio Device Unavailable
    STACK TRACE:
        com.sun.media.sound.HeadspaceMixer.nResume(Native Method)
        com.sun.media.sound.HeadspaceMixer.implOpen(HeadspaceMixer.java:346)
        com.sun.media.sound.AbstractMixer.open(AbstractMixer.java:286)
        com.sun.media.sound.AbstractMixer.open(AbstractMixer.java:323)
        com.sun.media.sound.MixerClip.open(MixerClip.java:162)
        paulscode.sound.libraries.ChannelJavaSound.attachBuffer(ChannelJavaSound.java:278)
        paulscode.sound.libraries.SourceJavaSound.play(SourceJavaSound.java:311)
        paulscode.sound.Library.play(Library.java:706)
        paulscode.sound.Library.play(Library.java:675)
        paulscode.sound.SoundSystem.CommandPlay(SoundSystem.java:2076)
        paulscode.sound.SoundSystem.CommandQueue(SoundSystem.java:2599)
        paulscode.sound.CommandThread.run(CommandThread.java:121)

Man, I thought for sure… Let me upload a version with some debug messages for you to run. Give me a few minutes…

OK, it should work now (I just wasn’t aggressive enough in the ranker). I am now running exactly the same code as ChannelJavaSound, and if I catch the same exception i give the Mixer a rank of 0. This should remove the webcam as an option, forcing LibraryJavaSound to pick a different Mixer with a higher rank.

JavaSound library pluggin

Please post the output you receive, and whether or not it works. If all is well, I’ll commit the changes to the official version.

Ok it works, thank you very much:

Starting up SoundSystem...
Initializing Java Sound
    (The Java Sound API.  For more information, see http://java.sun.com/products/java-media/sound/)
Found Java Sound Audio Engine, attempting to rank...
Calculating a rank for Mixer 'Java Sound Audio Engine'
    1) Mixer exists
    2) Minimum sample-rate is ok
    3) Maximum sample-rate is ok
Java Sound Audio Engine didn't rank so well, checking for something better...
Calculating a rank for Mixer 'Camera [plughw:0,0]'
    1) Mixer exists
    2) Minimum sample-rate is ok
    3) Maximum sample-rate is ok
Calculating a rank for Mixer 'V8237 [plughw:1,0]'
    1) Mixer exists
    2) Minimum sample-rate is ok
    3) Maximum sample-rate is ok
    4) Maximum line-count is ok
    RANK: 9
Calculating a rank for Mixer 'V8237 [plughw:1,1]'
    1) Mixer exists
    2) Minimum sample-rate is ok
    3) Maximum sample-rate is ok
    4) Maximum line-count is ok
    RANK: 9
Calculating a rank for Mixer 'Java Sound Audio Engine'
    1) Mixer exists
    2) Minimum sample-rate is ok
    3) Maximum sample-rate is ok
Calculating a rank for Mixer 'Port Camera [hw:0]'
    1) Mixer exists
    2) Minimum sample-rate is ok
    3) Maximum sample-rate is ok
Calculating a rank for Mixer 'Port V8237 [hw:1]'
    1) Mixer exists
    2) Minimum sample-rate is ok
    3) Maximum sample-rate is ok
Using the best ranked mixer, which is... 'V8237 [plughw:1,0]'
JavaSound initialized.

25 août 2010 23:29:04 com.ardor3d.renderer.jogl.JoglRenderer updateTexSubImage
ATTENTION: Attempting to update a texture that is not currently on the card.

SoundSystem shutting down...
    Author: Paul Lamb, www.paulscode.com

I think it is not your fault, the mixer that is selected only succeeds in playing 2 sounds and does not work anymore after that (it is unable to play the same sounds once more). I advise you not to investigate such a bug as when Java Sound Audio Engine works reliably, the sounds are played correctly. Good night.

I think this is worth looking into, since there are probably a lot of Linux users out there with webcams. I assume the Mixer that works is ‘V8237 [plughw:1,1]’, correct? I’ll look into what those numbers represent - there might be a logical reason why this one works better than ‘V8237 [plughw:1,0]’.

Hi!

You’re right. In France, lots of people bought EEE PCs with Linux & an integrated webcam with a microphone.

1.1 and 1.0 seem to match with the version of ALSA or OSS. V8237 [plughw:1,0] is used, not V8237 [plughw:1,1]. Maybe I can try to force the second one to see if there is any difference.

I think you’ll find these aren’t version numbers, they’re [card, device] indexes, starting from 0. You’ll need to fiddle in the ALSA configuration files to change what gets indexed and in what order.

Only link I can find quickly is http://en.wikipedia.org/wiki/Advanced_Linux_Sound_Architecture#Concepts

Hope that’s a help.

Best wishes, Neil

[quote=“gouessej,post:89,topic:33341”]
Let me know if it works any better than the other one. If so, I’ll try and set up some test machines with Linux and webcams to see if there is any way to predict which one works. If neither work, then it really won’t be solvable until I finish writing my software mixer.

[quote=“gouessej,post:87,topic:33341”]
Give this version a try and let me know if it works any better:

JavaSound library pluggin

Ok I’m going to give it a try when I’m back home in some hours. However, it seems to be something really wrong with my webcam; when it is plugged, some other USB devices do not work anymore…

I get this now:

[quote]Error in class ‘ChannelJava Sound’
Unable to attach buffer to clip in method ‘attachBuffer’
ERROR MESSAGE:
line with format PCM_SIGNED 22050.0 Hz, 16 bit, mono, 2 bytes/frame, little-endian not supported.
STACK TRACE:
com.sun.media.sound.DirectAudioDevice$DirectDL.implOpen(DirectAudioDevice.java:494)
com.sun.media.sound.DirectAudioDevice$DirectClip.implOpen(DirectAudioDevice.java:1280)
com.sun.media.sound.AbstractDataLine.open(AbstractDataLine.java:107)
com.sun.media.sound.DirectAudioDevice$DirectClip.open(DirectAudioDevice.java:1061)
com.sun.media.sound.DirectAudioDevice$DirectClip.open(DirectAudioDevice.java:1028)
paulscode.sound.libraries.ChannelJavaSound.attachBuffer(ChannelJavaSound.java:278)
paulscode.sound.libraries.SourceJavaSound.play(SourceJavaSound.java:311)
paulscode.sound.Library.play(Library.java:706)
paulscode.sound.Library.play(Library.java:675)
paulscode.sound.SoundSystem.CommandPlay(SoundSystem.java:2076)
paulscode.sound.SoundSystem.CommandQueue(SoundSystem.java:2599)
paulscode.sound.CommandThread.run(CommandThread.java:121)
[/quote]
On my view, don’t waste any time in fixing this bug as I have not checked whether it is reproducible on any other Linux machines.

That’s odd, I wouldn’t have expected it to get into ChannelJavaSound.attachBuffer without printing some of the debug messages from the Mixer selection process first.

Anyway, what is the brand/model of your webcam, and the Linux version? I’m going to set up some Linux test environments and want to see if I can reproduce the problem here. I’m not placing a high priority on this bug, but it is something I’ll continue to look at among other things.

Mandriva Linux 2010 (one of the most famous Linux distro just behind Ubuntu).
Logitech, Inc. QuickCam Messenger

Hi!

I am using you library for my latest game Gunslinger 2:

http://www.java-gaming.org/index.php/topic,22854.0.html

It works very good! However, even if my game is not 3D I still want the player to know if the sound comes from the left or right.

Since it is java2D my coordinate system have 0,0 at the top left corner of the map. I use this to set the initial values:


        soundSystem.setListenerAngle(0);
        soundSystem.setListenerOrientation(0, -1, 0, 0, 0, 1);

If I understand it correctly it should make the player stand at the bottom of the screen and look up (to the top of the screen). His head points out of the screen. This way, his right ear is to the right and his left ear is to the left. Correct? I have tried to experiment, but am not sure that it sounds right.

Of course, each frame I update the player position. I never need to update these other vectors, right?

I use mostly quickPlay since that allows a sound to overlap itself (I don’t want to keep track of multiple sources from the same object). Will 30 calls per second to quickPlay be a problem?

A design note, I would have preferred if a source wasn’t tightly bound to a sample. Instead, I would like to have a source: “enemy_1” or “door_2” and then those sources could play different samples. As it is now, I have to have lots of sources that always are at the same position. A door can open and close, so it must have two sources. A creature can walk, attack, take damage and die, that means at least 4 sources. In the end, I just use quickPlay for almost everything.

The call to setListenerAngle is not necessary (zero is already the default value, and it would be overwritten by setListenerOrientation anyway). The best way to visualize the orientation you wrote, you are laying face-down flat on your stomach (looking in the -y direction) with your feet pointing into the screen (up is in the +z direction, toward the player). Thus, if you were to position the listener in the center of the screen, then things on the left side of the screen will sound like they are to the right and things in the right side of the screen will sound like they are to the left (and if you have an OpenAL version designed for surround-sound system, things that are at the top of the screen would sound like they are behind you and things that are at the bottom of the screen would sound like they are in front of you). A better orientation might be (0, 1, 0, 0, 0, -1).

Notice, however, that you must also change the listener’s position, not just his angle. Without changing position, the listener will be at the top-left (0, 0), and everything will sound like it is to one side.

Next, you must consider the fact that if (0, 0) is the top-left, this means that +y is down in your coordinate system, but it is up in SoundSystem’s coordinate system. So to keep things correct, you would want to reverse the sign of any y-coordinates you pass to SoundSystem.

Assuming you have the listener position and coordinate signs correct, but you are still not hearing what you expect, there are a couple of problems that can be easily adjusted (both can be done if needed):

Problem #1: Panning between left and right speaker either pans too rapidly or too slowly compared to the 2D positions
Solution:
Ignore y and z (use only x coordinates) and adjust the diameter of the circle used to calculate the pan. You would use the default listener orientation for this (where +y is up, -z is into the screen), and when setting the position of the listener and all sources only pass them the x-coordinates. So say you want to have the listener at (5, 7) and to play an explosion at (2, 4):

float diameterFactor = 0.0f;  // increase gradually to achieve the desired panning behavior
soundSystem.setListenerPosition( 5, -diameterFactor, 0 );  // ignore the y-coordinate which was 7.. use the diameter factor instead

...

boolean priority = false;
String filename = "explosion.wav";
boolean loop = false;
float x = 2;
float y = 0;  // ignore the y-coordinate, which was 4
float z = 0;
int aModel = SoundSystemConfig.ATTENUATION_ROLLOFF;
float rFactor = SoundSystemConfig.getDefaultRolloff();
soundSystem.quickPlay( priority, filename, loop, x, y, z, aModel, rFactor );

The good thing about this method is you don’t have to worry about the sign difference in y coordinates, because you are only using the x coordinates.

Problem #2: Far away sounds are either too loud or too quiet based on their distance from the listener
Solution:
Adjust the rolloff factor if using Logarithmic/Rolloff Attenuation (or fade distance if using Linear Attenuation)

For your question about the listener’s orientation, correct you should only need to set it one time. Also, you really only need to update the listener’s position if it changes. It doesn’t hurt to update it every frame but it may not be necessary (for LibraryJavaSound, each update of the listener position requires a loop through all the sources to calculate a new pan/gain, so only calling setListenerPosition when it changes could potentially be a useful optimization if there are a ton of sources to loop through).

For your question about 30 calls per second to quickPlay, in stress tests that is not a problem as far as stability goes, however you should note that there are generally only 28 normal channels available, so sources will be getting cut off if you are playing that many simultaneously. Depending on the speed of the user’s system, there could be a noticeable performance hit as the channels are rapidly being started, stopped, reset, and started again. Also, if a lot of copies of the same sample are playing at close to the same time, you will experience phase-resonance interference (this is true for any sound library, not just SoundSystem). What this sounds like is randomly amongst the numerous playing sources, you will hear a distorted version of the sample played either extremely loud or extremely quiet (this behavior is very noticeable). It is caused by the amplitude values of more than one sample aligning to either amplify or cancel each other out. It happens most noticeably with sound effects that are made up of repeating sample data (such as bells, engine hums, laser pulses, etc). Interestingly, this phenomenon actually happens in the real world as well (for example, some migratory birds have specially-designed in-flight calls that reduce the effect of echo off of mountains by inversely aligning the wave amplitudes as returning sound waves passes back over the oncoming waves, making it easier for them to locate other flocks and coordinate their movements).

As for your design note, true it would be better optimized to allow more than one sample to play from a single source, but it would also create more of a headache for the developer, who would now have to keep track of which sample is currently assigned to each source. Since sources are relatively cheap, I think the potential optimization benefit compared to the “easy to use” benefit falls in favor of easy to use. Additionally, in both of your examples, it is more realistic to play the samples from more than one source, because they may overlap (a door could be slammed shut then rapidly opened again, or a creature could be walking and attacking at the same time or taking damage and dying at the same time, etc.). Just quickPlay the sample where it needs to play, and let SoundSystem worry about creating and getting rid of the sources for you.

Thanks for your elaborate answer (and thanks for writing this engine)!

Bug-Fixes Update

Sound System jPCT
Sound System

JavaSound library pluggin

WAV codec pluggin

[i]What’s New:

  • Updated CodecWav link to current version
  • Improved LibraryJavaSound performance slightly in non-Sun Java versions
  • Handled rare pan-control exception
  • Fixed fadeOutIn bug which caused fade-in effect to be silent[/i]