3D Sound System

Started by paulscode, March 11, 2008, 02:38:51 AM

Previous topic - Next topic

paulscode

Don't really know.  He is obviously doing something different with his applet than I did with mine.  He may have put his own signature on the applet  -  I just used the signature that was already on the appletloader jar, so if you ever ran another appletloader program before mine, then you already got the warning and chose to trust that signature.  That is just a guess, though.

paulscode

I haven't gotten any response on the lwjgl issue in a couple of days.  I guess nobody can figure out what I did wrong with my applet.  This is an issue I need to solve, because I am trying to collect all the pieces I'll need to develop a browser-based game, and I want it to be compatible with as many computers as possible.  So I've decided to spend a lot of time this weekend putting a real effort into solving this problem.  Some places I am going to start are:

1)  I will start with the most basic sound program possible, so I am going to copy and paste the first OpenAl demo program on the lwjgl site, and put it into an applet.  I will not add any more complexity to it than necessary so I know for sure that there is not a problem with the Java code.  Get that working on my test machines, and then take what I learn, and apply it to a more complicated applet.

2)  I will spend some time searching the internet.  The lwjgl binding of OpenAL is not well doccumented (seems that most people with OpenAl lwjgl experience are not creating applets).  Because of this, I have been having trouble finding any useful information related to my problem.  What I really need to find is some source code or a guide, so that I can compare and see what I am doing wrong.  I highly doubt I am the only person who has had this problem, so I just need to find another person who had solved it.

3)  I will try recreating the native jars and resigning them to see if that helps at all.

If anyone can think of some more suggestions for me to try, let me know!

JavaMan

Quote(seems that most people with OpenAl lwjgl experience are not creating applets
Hey, maybe you should try distributing your game with JavaWeb start instead of an applet? That way maybe more people could help you out over at the lwjgl forums.
Just a suggestion.
Jman

paulscode

I have awesome news and lowsy news.

First the awesome news ... I solved the no-sound problem!!  It just occurred to me when I was re-reading this thread, and I got to the recent posts about that SoundManager exception.  It hit me that a similar "thread issue" like the one that was causing that exception could be causing this no-sound problem -- if init(), like paint(), were called from a seperate thread (doesn't seem logical to me that this would be the case, but you never know).  I tried moving all the sound stuff out of the init() method, and sticking it all right before the main game loop.  And it worked!  On all three test machines.

Now for the lowsy news.  I assume everyone creating an applet using jPCT is creating a number of their Object3Ds in the init() method (makes sense, right?).  That would also be the obvious place to instantiate the SoundManager and bind sound sources to the objects.  Problem is, creating a new SoundManager object initializes the sound system ... which can't be initialized in the init() method. (chicken .. egg .. no, chicken ...)

So that leaves me with two options:

FIRST OPTION
Add a new method in SoundManager called something like "initOpenAL()" which the user must call from somewhere outside the init() method of their applet.  The requirement to call this method would carry over to applications as well.  This would make the SoundManager slightly more difficult to use, and users would have to ensure that they called that method before trying to play any sounds.  This option would also require a somewhat significant change to the SoundManager class, since I would want to be able to bind sources to objects before those sources have necessarily been created (some type of queueing would accomplish this).

SECOND OPTION
Make a clear note that SoundManager MUST NOT be instantiated in the init() method, or sound will not work on some computers.  Then let the user figure out how to change their applet to follow that rule.


So which of these options would you prefer, or can you think of another alternative?


For now, I guess the simplest work-around (using option #2) would be to create a new method like "init2()", and move everything from "init()" over to it, then call init2() before starting the main game loop.  Just seems kind of hackish to me.

Quote from: JavaMan on April 05, 2008, 01:08:27 AM
Hey, maybe you should try distributing your game with JavaWeb start instead of an applet? That way maybe more people could help you out over at the lwjgl forums.
That would be the obvious solution, but I kind of have my heart set on the game being an applet.  My idea for the game is to have it inset into a webpage, with things happening outside of the game applet itself (like private chat, trading, etc).  Besides, I want the SoundManager to work well for both applets and applications.

paulscode

Quote from: paulscode on April 05, 2008, 01:59:41 AM
For now, I guess the simplest work-around (using option #2) would be to create a new method like "init2()", and move everything from "init()" over to it, then call init2() before starting the main game loop.  Just seems kind of hackish to me.

ARgh!  I just tried this, forgetting that paint() gets called from a seperate thread..  Another lovely exception!

So I guess Option #1 is it.

fireside

I agree with the applet thing.  I don't know, I just like applets a lot better than web starts.  I hope you'll paste some example code with your manager because I have zero experience with applets right now.  I just know that's how I want my game to eventually run.  Right now I'm more or less following the car demo way of instantiating 3d objects in their own class.
click here->Fireside 7 Games<-

EgonOlsen

Wouldn't it be an option to create the SoundManager anywhere but to ensure that OpenAL will be initialized the first time someone wants to play a sound?

paulscode

Quote from: EgonOlsen on April 05, 2008, 12:02:07 PM
Wouldn't it be an option to create the SoundManager anywhere but to ensure that OpenAL will be initialized the first time someone wants to play a sound?

Aha!  Yes, that is very good.  Then I would just make a note in the doccumentation that sounds can not be played from inside the init() method.  Come to think of it, you already couldn't do that ( the reason I wrote that ugly "run once" loop in the paint() method before).  That was one of the little bugs I was going to see if I could fix -- I guess now I can call it a "feature"  ;D ).

I like that a lot - wouldn't require the user to change anything.  You'd still use SoundManager exactly the way it works now.  Thanks for the great suggestion!

paulscode

While thinking about how to impliment that last idea, I think I know a way to make it so sounds could be played from the init() method or anywhere else.  Assuming the root of the problem is a thread issue (which I am 99% sure it is), all I have to do is make it so all initialization, playing, pausing, stopping, rewinding, etc. is all done on the same thread.  I just need to set up a Command Buffer, which queues up commands from the user.  Then a seperate running thread will do all the work, like initializing the sound system if it hasn't been initialized yet, sorting sources, playing sounds, etc.

Actually, since I was going to do something exactly like this for the new SoundManager class, I think I will start from scratch here rather than trying to change the entire structure of the class I have now.  I could even allow the user to choose which Management Model to use -- source culling or channel monitoring.

So many ideas ... I am firing up NetBeans now!

EgonOlsen

Quote from: paulscode on April 05, 2008, 01:04:25 PM
While thinking about how to impliment that last idea, I think I know a way to make it so sounds could be played from the init() method or anywhere else.  Assuming the root of the problem is a thread issue (which I am 99% sure it is), all I have to do is make it so all initialization, playing, pausing, stopping, rewinding, etc. is all done on the same thread.  I just need to set up a Command Buffer, which queues up commands from the user.  Then a seperate running thread will do all the work, like initializing the sound system if it hasn't been initialized yet, sorting sources, playing sounds, etc.
Sounds exactly like what the AWTGLRenderer does to ensure that everything OpenGL-related happens in the AWT event dispatch thread.

paulscode

I have finished putting together the basic infrastructure for the new Command Buffer.  It is still full of bugs, so I will continue working on it.  The main bug right now is the computer lagging on random frames.  I believe this is most likely a thread communication problem.  I may have to rethink how I am doing this, so any suggestions would be helpful.  The way it works now is, the commandBuffer is a LinkedList containing CommandObjects.  There is a locking mechanism to prevent two threads from accessing the list simultaneously:

Thread #1 (End-user's thread, when they call SoundManager.tick()):
while( commandBufferLocked ){} // wait for other thread to finish

commandBufferLocked = true;  // tell other threads to wait

  // ... queue commands to move sources so they match Object3Ds

commandBufferLocked = false;  // tell other thread we are finished


Thread #2
while( commandBufferLocked ){} // wait for other thread to finish
commandBufferLocked = true;  // tell other threads to wait

  // ... actually move the sources

commandBufferLocked = false;  // tell other thread we are finished


This seems like it should work, but I want to rule it out as the source of the lag problem.  Is there another way to do this without a LinkedList.  I am thinking like some kind of queue you can "pop" in the order it was filled, and be sure whatever you get is whole / not corrupt?

This project is definitely teaching me a lot about threads!

JavaMan

I could imagine a problem with the boolean commandBufferLocked variable. It could be possible that both threads would access the variable at the same time. I suggest some method like this to change the variable.


public synchronized boolean commandBufferLockControl(boolean b, int type){
  if(type==1){
      commandBufferLocked=b;
      return b;
  }
   return commandBufferLocked;
}


This way setting and reading the commandBufferLock can be synchronized. I think though that using a LinkedList is good. I don't know if using an iterator on a HasSet would be any faster.

EgonOlsen

while( commandBufferLocked ){}...this is a busy wait loop. Not a good idea. Just let the thread sleep and wake him up when the command buffer has data to be processed. And as JavaMan said: Use synchronized blocks instead of trying to rebuild your own semaphore logic by using booleans.

paulscode

#43
Quote from: JavaMan on April 06, 2008, 09:25:33 PM

public synchronized boolean commandBufferLockControl(boolean b, int type){
  if(type==1){
      commandBufferLocked=b;
      return b;
  }
   return commandBufferLocked;
}

I am fairly new to Java and have never seen "synchronized" syntax before - I am going to read up on this right now.  Thanks for this tip - exactly the kind of thing I was looking for.

Quote from: EgonOlsen on April 06, 2008, 11:14:46 PM
while( commandBufferLocked ){}...this is a busy wait loop. Not a good idea. Just let the thread sleep and wake him up when the command buffer has data to be processed.
That is an excellent idea.  I was actually trying to think of how to do this with sleep instead of a busy "while" loop.  I was confusing myself by trying to come up with two threads alternately sleeping while the other was using the buffer, then one waking the other up and vice-versa.  I hadn't considered this simple solution of just having one "Command Processer" thread sleep away and the second thread waking it when a new command goes into the list ( :-[ - DUH!).

The other thing I need to do smoothly is re-sorting the sources list when source positions change.  This same concept should work nicely here as well -- have a "Sources Sort" thread awaken whenever distances change.

These are both great ideas, thanks a lot!

paulscode

I have read up on synchronizing threads, and I had a thought about how to do the Command Buffer.  I would create a method like this in SoundManager:

    public synchronized void CommandQueue( CommandObject newCommand )
    {
        if( newCommand == null )
        {
            // execute queued commands
        }
        else
        {
            // queue a new command
        }
    }


This one method should work for both executing commands and queueing new commands, and since it is "synchronized", I can just let Java's built-in logic take care of regulating the threads.  How does this sound to you?