3D Sound System

I was trying your lib using sounds I found in several existing games.

Sometimes I was getting a weird issue, indeed rendering of certain sounds were not subjected to indicated position.

By this I mean that when my listener was at (0,0,0) and a sound was playing at (1000,1000,1000) and I was getting it as if I was right at my listener’s position.



So here is what I tried to do :



I downloaded the code source of your Helicopter Bounding Ball sample, I did the changes to get it launching in application mode, removed all sounds except the “boing” sound and finally changed the number of ball to 1 so it’s easier to notice ball location thanks to sound.



After all this change I can track the ball using sound, until there everything was fine.



Then I added a sound (also a wav) I found in Counter Strike data folder, just changed the filename in the quickPlay function and launched it again. With this sound I got the bug I explained above.



If you want to check it yourself here are 2 zipped wav that involve this strange behavior : http://87.98.157.161/Sounds.zip



Quick Edit : I wanted to add that I tried to play with volume and rolloff attenuation values, modifying volume is ok while modifying rolloff attenuation didn’t involved any “hear able” modification.

I assume you are using one of the OpenAL library plug-ins.  If that is the case, then your sound effects have to be mono for 3D effects to work.  Stereo sounds can not be used for 3D effects.



The reason for this "bug" is that stereo sound already has differences between left and right speakers and the developers of OpenAL were not ambitious enough to solve the trigonometry that would be required to play stereo sounds correctly in 3D.  Stereo sounds will kind of work in my JavaSound library plug-in, although not the way one would expect (I was too lazy to solve this problem either).



The solution is to convert your sound effects into mono if you want to use them as point sources.  Obviously, this doesn't apply to ambient sources (like background music) which don't require 3D effects - you can keep those in stereo.

Is there a tool programm that can help me to find out if a sound is stereo or mono?

Empire Phoenix said:

Is there a tool programm that can help me to find out if a sound is stereo or mono?


You could probably use a free sound editor program such as Audacity to determine if your sounds are mono or stereo, and it could also be used to convert them to mono if necessary.

I've discovered that the latest release of LWJGL breaks the LibraryLWJGLOpenAL library plug-in, because they no longer support indirect buffers.  I've already figured out a solution and I am in the process of implementing it.  I've been really busy lately so it might be a while before I'm ready to post an update.  This probably won't affect anyone here anyway, since any JME users that are using the SoundSystem are probably using the LibraryJOAL plug-in rather than LibraryLWJGLOpenAL for OpenAL access.

Thanks for the update, Paul.  We're currently using your SoundSystem with a good deal of success… I had a feeling that the change in LWJGL would affect your project… we had to update the jME audio system as well :slight_smile:

is there a plan to ship this new sound system with jme3 and jme2?

Both jME3 and jME2 have their own sound system… But nothing is stopping you from using this one.

Well actually i prefer tihs one kinda, since it seems absolutly reliable, workd one very computer i encounterd so far perfectly, also it runs threaded , amking sure your music is not lagging in a loading screen ^^

Only thing I still wait for is the doppler implementation :slight_smile:

Empire Phoenix said:

Well actually i prefer tihs one kinda, since it seems absolutly reliable, workd one very computer i encounterd so far perfectly, also it runs threaded , amking sure your music is not lagging in a loading screen ^^
Only thing I still wait for is the doppler implementation :)


I haven't used the jME3 sound system at all yet, but this beats the pants off of what's in jME2 IMO.  I also very much like the way it was written...  It was very easy to keep modular but still be closely related to jME's spatial coordinates
Empire Phoenix said:

Only thing I still wait for is the doppler implementation :)


This feature will be in the upcoming release - I just need to run a few more extreme tests to make sure it is working properly. 


BitBlt said:

Only the helicopter demo produces sound for me (running ubuntu here). Should they all work?

PaulLamb said:

It appears that JavaSound may not be compatible on your machine but OpenAL is (usually it is the other way around).


I've discovered the cause for this problem.  It occurs whenever non-Sun Java builds are being used (such as the one packaged with 64-bit versions of Ubuntu).  These do not have the "Java Sound Mixer", which is used by the LibraryJavaSound plug-in.  The easiest solution for the developer is to have the end-user install Sun Java (plus the browser plug-in for applets).  I understand that this may not be an option in every situation (especially since Sun does not currently have a stable Java plug-in for 64-bit browsers, requiring the user to install an alternate 32-bit browser).  Because of this, I am adding a bit better support for manually specifying an alternate Mixer and determining which controls are compatible for it whenever "Java Sound Mixer" is unavailable.  These features will also be available in the upcoming release.
PaulLamb said:

I've discovered that the latest release of LWJGL breaks the LibraryLWJGLOpenAL library plug-in, because they no longer support indirect buffers.  I've already figured out a solution and I am in the process of implementing it.  I've been really busy lately so it might be a while before I'm ready to post an update.  This probably won't affect anyone here anyway, since any JME users that are using the SoundSystem are probably using the LibraryJOAL plug-in rather than LibraryLWJGLOpenAL for OpenAL access.


Hi,
I found this thread just few hours ago and tried your library. I was able to run it on MacOSX, congratz:) I was able to run JavaSound, LWJGL and JOAL plugins.. could someone tell me why use JOAL instead of LWJGL? In post I quoted you suggest that jme users probably use JOAL instead of LWJGL, but why? Jme users mostly use LWJGL renderer so using your LWJGL plugin need not any additional library.. Is there any benefit of using JOAL and having to link additional libraries?
Thanks for any reply.. As I said I know nearly nothing about your sound library yet:)
gorgor said:
Jme users mostly use LWJGL renderer so using your LWJGL plugin need not any additional library.

This was a mistype - I am playing around with four different 3D libraries at the moment, and I mixed jME up with another one that links with JOGL (in which case JOAL would be the obvious chose for audio).  You are correct - LibraryLWJGLOpenAL would be the obvious choice for jME since the LWJGL library would likely already be linked for the rendering.
PaulLamb said:

This was a mistype - I am playing around with four different 3D libraries at the moment, and I mixed jME up with another one that links with JOGL (in which case JOAL would be the obvious chose for audio).  You are correct - LibraryLWJGLOpenAL would be the obvious choice for jME since the LWJGL library would likely already be linked for the rendering.


Now here is a persistent man :)
PaulLamb said:

gorgor said:
Jme users mostly use LWJGL renderer so using your LWJGL plugin need not any additional library.

This was a mistype - I am playing around with four different 3D libraries at the moment, and I mixed jME up with another one that links with JOGL (in which case JOAL would be the obvious chose for audio).  You are correct - LibraryLWJGLOpenAL would be the obvious choice for jME since the LWJGL library would likely already be linked for the rendering.


Thanks, that clarifies everything:)

I tried your library a little and I have two things to ask..

1) resource location (again:))
I'm sorry to ask again because similar questions were asked here before, but..
I'm trying to load .ogg files from the resource directory besides the application directory.. let's say I have following structure:
dir/main/SoundTest.class
dir/data/fire.ogg

As I understood, for such a case I must use URLs.. ok.. no problem..
So I tried it and was successfull! Here is the final result which works in my case:


...
SoundSystem mySoundSystem = new SoundSystem();
mySoundSystem.newSource( false,"fire",getClass().getResource("/data/fire.ogg"), "fire.ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());
mySoundSystem.play( "fire" );
...



The question is what is "filename" argument (4th) good for?.. I can use this and it works as well


mySoundSystem.newSource( false,"fire",getClass().getResource("/data/fire.ogg"), "WHATEVER_I_WANT.ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());
mySoundSystem.play( "fire" );



2nd argument (sourcename) "fire" is used in .play() method to identify the sound I wanna play.. the URL 3rd argument specifies where the file in the HDD is.. and the 4th? I observed it has to have .ogg extension for identifying which codec to use I suppose but is there any other reason to not use just "ogg" for all my sounds and musics?
Now I play sound and background music in the following way:


SoundSystem mySoundSystem = new SoundSystem();
mySoundSystem.backgroundMusic( "music", getClass().getResource("/data/music.ogg"), "ogg", true );
mySoundSystem.newSource( false,"fire",getClass().getResource("/data/fire.ogg"), "ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());
mySoundSystem.play( "fire" );



Is it correct? Am i not doing something terribly wrong?

2) background music queue
From this thread I figured out that there is no way how to listen on finishing music.. so how can I implement something like queue for background musics? Let's say I wanna 5 musics to be used as a background music.. play the first one and when it finishes play the second one. Then the third and so on.. and when the last one finishes then start from the first one again.. simple enough and common as hell, isn't it?
Is there any support for this in your library? Or should I implement some cyclic queue myself? And if so, how can I react on finishing the music so to start another one? By polling .isPlaying() method regularly? (NOTE: I don't even know if there is any .isPlaying() method by the time I'm writing this)

Thanks for any advice..


gorgor said:

The question is what is "filename" argument (4th) good for?.. I can use this and it works as well


...
"WHATEVER_I_WANT.ogg"
...


2nd argument (sourcename) "fire" is used in .play() method to identify the sound I wanna play.. the URL 3rd argument specifies where the file in the HDD is.. and the 4th? I observed it has to have .ogg extension for identifying which codec to use I suppose but is there any other reason to not use just "ogg" for all my sounds and musics?

Yes, this has been confusing for a number of people when they start using my sound library and URL's.  This parameter is an identifier for the file represented by your URL.  The reason I didn't use something acquired from the URL instance itself, is because there are numerous ways to create a URL instance, and there is not necessarily a unique string identifier associated with every type of URL.  This identifier is used to reference the sample loaded from a particular loaded file/URL, so you don't have to reload the sample every time you create a source from it.  That is why you would not want to just use "ogg" for everything.  If you tried that, only the first one would actually read the sample, and any other ones would use that loaded data.

Consider this for example:


// BAD CODE!!
mySoundSystem.newSource( false,"fire1",getClass().getResource("/data/fire.ogg"), "ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());
mySoundSystem.newSource( false,"fire2",getClass().getResource("/data/fire.ogg"), "ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());
mySoundSystem.newSource( false,"explosion",getClass().getResource("/data/explosion.ogg"), "ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());
// BAD CODE!!


In the above example, I used "ogg" as the identifier for all three sources.  Look at the third parameter, though - I wanted the first two sources to come from the URL pointing to "/data/fire.ogg" and the third to come from "/data/explosion.ogg".  But since I used only one identifier for all three sources, SoundSystem will load the sample from "/data/fire.ogg" for the first source, and use that loaded sample to create the remaining two sources.  In other words, the third parameter will be ignored because a sample named "ogg" has already been loaded (no need to reload it again).  If you then played source "explosion", it would play the sample loaded from "/data/fire.ogg" instead of the sample you wanted it to play from "/data/explosion.ogg".

The correct way to write the above code would be this:


// CORRECT
mySoundSystem.newSource( false,"fire1",getClass().getResource("/data/fire.ogg"), "fire.ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());
mySoundSystem.newSource( false,"fire2",getClass().getResource("/data/fire.ogg"), "fire.ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());
mySoundSystem.newSource( false,"explosion",getClass().getResource("/data/explosion.ogg"), "explosion.ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());


This time, I used two different identifiers, since I wanted to load samples from two different files/URL's.  So when the first source is created, it loads the sample from "/data/fire.ogg".  When the second source is created, it uses that already loaded sample, which I identified as "fire.ogg".  When the third source is created, it has a different identifier, so SoundSystem loads the sample from "/data/explosion.ogg" for that one.  Any future new sources created using either "fire.ogg" or "explosion.ogg" as the fourth parameter will simply use that pre-loaded sample rather than reading it from the file/URL again.

Also, once you have loaded the sample from a URL, you could simply use NULL for the third parameter, since it is ignored once the sample has already been loaded.  For example, the following would be identical to my previous example:


// ALSO CORRECT
mySoundSystem.loadSound( getClass().getResource("/data/fire.ogg"), "fire.ogg" );
mySoundSystem.loadSound( getClass().getResource("/data/explosion.ogg"), "explosion.ogg" );
mySoundSystem.newSource( false,"fire1",null,"fire.ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());
mySoundSystem.newSource( false,"fire2",null, "fire.ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());
mySoundSystem.newSource( false,"explosion",null, "explosion.ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());



I think the best way to think about it is to just give each of your URL's a filename that corresponds with whatever sample is loaded by that URL.  So if you are using a URL to load from a local file, then use that file's name as the identifier.  Let me know if this was a good enough explanation.

gorgor said:
Now I play sound and background music in the following way:


SoundSystem mySoundSystem = new SoundSystem();
mySoundSystem.backgroundMusic( "music", getClass().getResource("/data/music.ogg"), "ogg", true );
mySoundSystem.newSource( false,"fire",getClass().getResource("/data/fire.ogg"), "ogg",false,0,0,0,SoundSystemConfig.ATTENUATION_ROLLOFF,SoundSystemConfig.getDefaultRolloff());
mySoundSystem.play( "fire" );



Is it correct? Am i not doing something terribly wrong?

Ok, this will actually work, but not for the reason you think.  The reason it works, is because a sample is only pre-loaded for NORMAL sources (newSource and quickPlay methods).  For STREAMING sources (backgroundMusic, newStreamingSource, and quickStream methods), the audio data is both read in and streamed simultaneously -- a copy of the data is NOT kept around in memory (it would be a huge memory hog to try and store it somewhere).  Thus, it would be perfectly OK to use just "ogg" as the identifier for every file/URL used to create STREAMING sources.  However, as I mentioned above, you must give a unique sample identifier to each file/URL used to create one or more NORMAL sources.


gorgor said:

2) background music queue
From this thread I figured out that there is no way how to listen on finishing music..

Music-end events will be implemented in the upcoming release (for STREAMING or MIDI sources ONLY), however...

gorgor said:

so how can I implement something like queue for background musics? Let's say I wanna 5 musics to be used as a background music.. play the first one and when it finishes play the second one. Then the third and so on.. and when the last one finishes then start from the first one again.. simple enough and common as hell, isn't it?  Is there any support for this in your library?

This is already supported by SoundSystem (IMO music-end events are redundant, but a number of people have asked for them).  The method you're looking for is called 'queueSound'.  This method is pretty straightforward just by looking at the parameters, but if you need additional help figuring it out, let me know.  It only works for STREAMING or MIDI sources.  Here's a quick example:

mySoundSystem.backgroundMusic( "music", getClass().getResource("/data/music_leadin.ogg"), "music_leadin.ogg", true );
mySoundSystem.queueSound( "music", getClass().getResource("/data/music_main.ogg"), "music_main.ogg" );


In this example, a new priority, streaming source called "music" would be created.  It would start streaming from "/data/music_leadin.ogg", and when the end of that stream was reached, it would immediately begin streaming from "/data/music_main.ogg".  Since 'true' was used for the 5th parameter, the stream would continuously loop audio from "/data/music_main.ogg" until the source was stopped, or something else was queued, or setLooping( "music", false ) was called.  Note there are also fadeOut and fadeOutIn methods that can be used for music transition (STREAMING or MIDI sources only).

gorgor said:
By polling .isPlaying() method regularly? (NOTE: I don't even know if there is any .isPlaying() method by the time I'm writing this)

There is a 'playing()' method, but I don't recommend using it to continuously poll like this, just because that would require a busy "while" loop.  You are better off using the above methods for music transitions.  The only time continuous polling should ever be needed is if you want to do transitions between sound effects played from a NORMAL (non-streaming, non-MIDI) source.  I can't think of many cases where this would be needed.  Transitions are mostly used for music and speech, which tend to be quite large and should be streamed.

--EDIT--
I may have forgot to mention that the JavaDoc is not very up-to-data, which may explain why people aren't aware of some of the newer methods I added to the SoundSystem.  I've been having problems with NetBeans generating nonsensical error messages.  I just re-installed NetBeans, and hopefully this problem is corrected - I'll try and upload an up-do-date JavaDoc with the next release.

Another thing that tends to confuse developers new to using my SoundSystem is that I created a standard interface to multiple 3rd-party libraries which all use their own unique terminologies ("clip" vs. "source" or "sourcedataline" vs. "stream", for example).  There is no "standard" way to say things when it comes to sound libraries.  This leads to people making false assumptions due to misinterpretation of my terminology (a lot of developers assume that newSource is somehow "better" than quickPlay, for example – actually it is just more work and usually not necessary).  I tried to set up standard definitions in my tutorial guides, so I recommend reading that if you are confused about something I write in my posts here.



The three most important definitions (and they are the ones most often misinterpreted) are what I call "sample", "source", and "channel".  "Samples" refer to the actual audio data contained in sound files.  "Sources" refer to one or more 3D positions where a sample is played from.  "Channels" are links to data ports in the underlying sound library through which audio is played (these are the clip, source, sourcedataline, etc referred to by the underlying library).  So the basic data flow works like this:  When you tell SoundSystem to play a "source", it looks up the correct "sample" and it plays it through an available "channel" using the settings associated with that "source".  So using my terminology, creating a thousand "sources" from a single "sample" doesn't eat up a ton of memory, because there are at most one instance of the actual audio data (the "sample"), and 32 data ports (the "channels") processing audio data at a time.  The "source" itself is merely a string identifier and a few floats which depict its position and behavior.

Thanks a lot for explanation… I read it and it clarifies many things to me… I'll do some tests and I'll be back tomorrow:)

Hi there…

first of all, thanks for such a long and explanatory reply… I think I understand now the difference between "samples", "sources" and "channels"… sources are merely 3D positions emitting noise:) They do not eat memory much and they are used to play the samples. Sample are those which one might want to preload… and which eat memory if non-streamed…

channels are not much of a concern… computer have usually 32… you be default make 4 streamed and 28 non-streamed… and I do not need to care about them much, right?..



Now to the queue problem… I tried the queueSound method and it is the step forward but not exactly what I want.



mySoundSystem.backgroundMusic( "music", getClass().getResource("/data/m1.ogg"), "m1.ogg", true );
mySoundSystem.queueSound( "music", getClass().getResource("/data/m2.ogg"), "m2.ogg" );
mySoundSystem.queueSound( "music", getClass().getResource("/data/m3.ogg"), "m3.ogg" );


this plays:
m1, m2, m3, m3, m3, m3, m3, ...

I would rather to play:
m1, m2, m3, m1, m2, m3, m1, ...

Is there an easy way to accomplish this?.. That listener which come in next version can will be probably useful to detect end of a non-looping queue and enqueue new batch of background music.. but without it?
I can either enqueue a million music to ensure that queue will not be exhausted and everyone quits the game sooner that that:) or I can enqueue another batch of music (e.g. 10 minutes of music) every now and then.. (but sooner then 10 minutes)...
however, neither way satisfies me a lot:(

gorgor said:
channels are not much of a concern.. computer have usually 32.. you be default make 4 streamed and 28 non-streamed.. and I do not need to care about them much, right?..

Right.  You would almost never have to worry about the channels, unless you wanted to stream more than 4 things simultaneously.

gorgor said:
I would rather to play:
m1, m2, m3, m1, m2, m3, m1, ...
Is there an easy way to accomplish this?


You make a good point.  No, currently there is no clean way to accomplish this besides the methods you described (queueing a bunch up front or requeueing every so often).  I suppose there is a good use for music end events after all..  I expect to post the next release sometime later this week.