Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (517)
Games in Android Showcase (123)
games submitted by our members
Games in WIP (578)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  My OggStreamer ALWAYS works running from IDE, but only 1/3 of the time from jar  (Read 4120 times)
0 Members and 1 Guest are viewing this topic.
Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Posted 2012-03-27 17:22:10 »

I have a runnable Game.class and a runnable OggStreamer.class, to allow the music to run in its own separate thread, which I send as a parameter for the Game-class to use. When I run the game from my IDE the OggStreamer always works, but when I've exported it to a .jar-file, it only works 1/3 of the times I start it up. And it's not like the first piece of music doesn't start, and then the next piece of music started will play..it doesn't work at all, until I've started the game a few times.

Has any of you good people had a similar problem? I could understand it if it didn't work at all, which could indicate there was something wrong with the file-references to the music inside the jar-file...but it DOES work, just not consistently outside the IDE. I've seen similar posts around the web, but none have any definitive answers to them. I'm using vorbisspi 1.0.3, jorbis 0.0.15, tritonus_share and jogg 0.0.7 as referenced libraries for playing the ogg-files, if that helps.

EDIT: The ogg-files are running a sample rate of 44100Hz, Stereo, encoded with variable bitrate 128-256 Kbps.
They are between 1 and 15mb in filesize.

I'm running Windows 7 64-bit.
IDE: IBM Rational Software Architect (Eclipse)

NOTE: This is my first attempt at Game-programming, and I know it's not very pretty and that I am a sort of newbie Smiley There are many things I'd change about the general design, but I'm using this as a project to help me understand the problems I'll encounter when I sit down to design a real framework for my next game. I'm on the third semester of Computer Science, so I've coded quite a lot, just never any games. Mostly apps.

Game.class uses the OggStreamer-class as such:
1  
2  
3  
4  
if(musicStreamer!=null && !musicStreamer.getFilename().equals("/snm/sound/oggs/Prelude.ogg")){
            if(!musicStreamer.isStop())musicStreamer.stopLoop();
            musicStreamer.startLoop("/snm/sound/oggs/Prelude.ogg");
}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
public class Start {
    public static void main(String[] args) throws InterruptedException{
       
       Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());
       
   OggStreamer musicStreamer = new OggStreamer();
       ExecutorService threadExecutor = Executors.newCachedThreadPool();
       
   // Give Game a running and ready musicStreamer thread
   Game game = new Game(musicStreamer);
        game.init();
       
   // start game thread
        threadExecutor.execute( musicStreamer ); // start task1
        threadExecutor.execute( game ); // start task2
       
        threadExecutor.shutdown();
    }
}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
public class OggStreamer implements Runnable{

   private URL url;
   private AudioInputStream stream;
   private AudioInputStream decodedStream;
   private AudioFormat format;
   private AudioFormat decodedFormat;
   private boolean stop, running;
   String filename = "";
   SourceDataLine line = null;

   public OggStreamer() {
      this.stop = true;
      this.running = true;
      this.url = null;
   }

   public void run() {
      while(running){
            while (!this.stop) {
               System.out.println("Playing Loop");
               try {
                  // Get AudioInputStream from given file.
                  this.stream = AudioSystem.getAudioInputStream(this.url);
                  this.decodedStream = null;
                  if (this.stream != null) {
                     this.format = this.stream.getFormat();
                     this.decodedFormat = new AudioFormat(
                           AudioFormat.Encoding.PCM_SIGNED,
                           this.format.getSampleRate(), 16,
                           this.format.getChannels(),
                           this.format.getChannels() * 2,
                           this.format.getSampleRate(), false);
                     // Get AudioInputStream that will be decoded by underlying
                     // VorbisSPI
                     this.decodedStream = AudioSystem.getAudioInputStream(
                           this.decodedFormat, this.stream);
                  }else{
                     JOptionPane.showMessageDialog(null, "Stream = null!");
                  }
               } catch (Exception e) {
                  // Do nothing
                  System.out.println("Could not get or decode audiostream");
               }
               line = null;
               try {
                  line = this.getSourceDataLine(this.decodedFormat);
                  FloatControl volume = (FloatControl)line.getControl(FloatControl.Type.MASTER_GAIN);
                  volume.setValue(1);
               } catch (LineUnavailableException lue) {
                  // Do nothing
                  JOptionPane.showMessageDialog(null, "Line is unavailable!");
               }
               if (line != null) {
                  try {
                     byte[] data = new byte[4096];
                     // Start
                     line.start();
                     int nBytesRead = 0;
     
                     while (nBytesRead != -1) {
                        nBytesRead = this.decodedStream.read(data, 0,
                              data.length);
                        if (nBytesRead != -1) {
                           line.write(data, 0, nBytesRead);
                        }
                        if (this.stop) {
                           break;
                        }
                     }
     
                     // Stop
                     line.drain();
                     line.stop();
                     line.close();
                  } catch (IOException io) {
                     // Do nothing
                     JOptionPane.showMessageDialog(null, "Line cannot start!");
                  }
               }
            }
      }
   }

   private SourceDataLine getSourceDataLine(AudioFormat audioFormat)
         throws LineUnavailableException {
      SourceDataLine res = null;
      DataLine.Info info = new DataLine.Info(SourceDataLine.class,
            audioFormat);
      res = (SourceDataLine) AudioSystem.getLine(info);
      res.open(audioFormat);
      return res;
   }


   public void startLoop(String filenameString) {
      this.filename = filenameString;
      System.out.println("Starting loop with: "+filenameString);
      this.url = this.getClass().getResource(filenameString);
      this.stop = false;
   }

   public void stopLoop() {
      System.out.println("Stopping loop");
      try {
         if(this.decodedStream!=null)this.decodedStream.close();
         if(this.stream!=null)this.stream.close();
      } catch (IOException e) {
      }
      this.stop = true;
      this.url = null;
   }
   
   public boolean isStop() {
      return stop;
   }

   public void setStop(boolean stop) {
      this.stop = stop;
   }

   public URL getUrl() {
      return url;
   }

   public void setUrl(String string) {
      this.filename = string;
      this.url = this.getClass().getResource(string);
   }

   public String getFilename() {
      return filename;
   }
}

- Jonas
Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #1 - Posted 2012-04-10 02:49:10 »

Wow, 110 reads and no one has a clue. Guess I'll have to read some more and start over Sad

- Jonas
Offline ra4king

JGO Kernel


Medals: 353
Projects: 3
Exp: 5 years


I'm the King!


« Reply #2 - Posted 2012-04-10 03:14:38 »

Hmmm must have missed this thread Tongue

If it works in IDE but doesn't in JAR, then this sounds like a path issue. However you mentioned it sometimes works, which is interesting. I myself have a game that uses OggVorbis and very similar code for my sounds, and it always works on all platforms.

Do the SOPs show that it should play, even though it doesn't? Do you have any more information?

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline 65K
« Reply #3 - Posted 2012-04-10 07:15:13 »

When do you start call the streamer ?
1  
2  
threadExecutor.execute( musicStreamer ); // start task1
threadExecutor.execute( game ); // start task2

It is not guaranteed that the streamer thread is actually running before the game thread, and thus is ready to work.

Also the inner streamer loop looks suspicious, as it plays the the given url again and again.
Threading problem(s), I would say.

Then, modifying the stop flag from outside need some synchronization, or at least the stop boolean must be made volatile.

Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #4 - Posted 2012-04-14 03:04:04 »

Do the SOPs show that it should play, even though it doesn't? Do you have any more information?
Yes, all the SOPs tell me it's working fine, but frankly they only give info about when I call the methods to start and stop the streamer. Nothing useful about what the problem might be. As you can see, I've tried putting in some JOptionPanes to abruptly tell me if something is wrong with the execution of the code, like not getting a Line, but I've never seen any of them pop out. I'll remove them.

When do you start call the streamer ?
1  
2  
threadExecutor.execute( musicStreamer ); // start task1
threadExecutor.execute( game ); // start task2

It is not guaranteed that the streamer thread is actually running before the game thread, and thus is ready to work.

Also the inner streamer loop looks suspicious, as it plays the the given url again and again.
Threading problem(s), I would say.

Then, modifying the stop flag from outside need some synchronization, or at least the stop boolean must be made volatile.
A threading-issue could very well be the culprit. I'm just very concerned that it always works from the IDE. So strange!
Game.init() does nothing with it. The streamer is started almost instantly inside the gameloop, because it plays music on the title-screen.

How would I go about ensuring the musicstreamer-thread is running, before I execute the game-thread? It seems to be running just fine, because if it wasn't, I'd get an error when trying to use it, because I've sent it as a parameter for the game-thread, right?

The thing about the looping...well, I want the music to loop, until it gets another piece of music to play, which is done by calling stop(), and then startLoop(String filenameString), where filenameString is the full Jar-path. This works very well, assuming the streamer works initially.

I will make the stop-boolean volatile. I doubt it'll solve this particular issue, but it will probably solve some issues I'd run into later on. Seems legit Smiley

- Jonas
Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #5 - Posted 2012-04-14 03:29:52 »

Erhm...I must admit. I can't replicate it anymore. Maybe they fixed it in the newest Java-update or something. I don't understand. I've had this problem for 2½-3 months (ever since I put music in the game), and now all of my old versions seem to work too (I keep a library of Jars from each update). I've even tried overloading the system, playing all sorts of things in the background and running benchmarks while starting the game, and either way the music works. That's so weird! I'll test it on some more systems tomorrow, but for now, the problem has fixed itself.

I would still like to know how to ensure that one thread is running properly, if you would be so kind.

Well, thank you very much for your help. It has been most appreciated Smiley

- Jonas
Offline 65K
« Reply #6 - Posted 2012-04-14 08:19:17 »

From Executor#execute(): "Executes the given command at some time in the future"
Take a look at Thread#isAlive().

stopLoop() can break your streamer. Don't modify private internals from other threads, just switch the stop flag. Make sure to clean up after leaving the outer Runnable loop.

Btw: did you test your error handling ?

Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #7 - Posted 2012-04-15 14:27:26 »

From Executor#execute(): "Executes the given command at some time in the future"
Take a look at Thread#isAlive().

stopLoop() can break your streamer. Don't modify private internals from other threads, just switch the stop flag. Make sure to clean up after leaving the outer Runnable loop.

Btw: did you test your error handling ?

Thread.isAlive(), of course Smiley I had been pondering about that one, I just wasn't sure.

My first stopLoop() did do what you're suggesting, and the rest of parameters were reset after end of the while(!this.isStop)-loop. Only problem with that, was that the music would be running for a little while before changing. I guess that's because it plays the rest of the buffer before stopping, and what I did to the stopLoop() (as it is now) stops it imediately. But if it might break the streamer, I guess I have to live with that.

What should I clean up after leaving the outer Runnable loop?

About the error handling, what do you mean test? If I catch any exceptions, they're sent to my ErrorHandler, which prints them in an errorlog-file for me. Do you mean if I've tested that I also catch the exceptions in the streamer, and not just the Game-class?

- Jonas
Offline 65K
« Reply #8 - Posted 2012-04-15 17:32:21 »

But if it might break the streamer, I guess I have to live with that.
No, you dont have to. Multithreading just takes more effort.

What should I clean up after leaving the outer Runnable loop?
All resources you opened like Streams, close audio devices, etc.
In case of clean ending and in case of exception handling. A crashed application keeping on playing is no fun...

About the error handling, what do you mean test? If I catch any exceptions, they're sent to my ErrorHandler, which prints them in an errorlog-file for me. Do you mean if I've tested that I also catch the exceptions in the streamer, and not just the Game-class?
Well, test, just means.... test your stuff  Grin
Verify with invalid URLs, with unavailable audio devices, etc., show proper error messages.

Here, the error causes are not shown, that is quite bad (Microsoft style)
The streamer should not mess with UI stuff like JOptionPane.
In case of exception in the lower loop, it keeps on running, opens a new stream, and produces another and another message boxe, If I don't miss anything.


Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #9 - Posted 2012-04-17 13:13:25 »

But if it might break the streamer, I guess I have to live with that.
No, you dont have to. Multithreading just takes more effort.
Oh, I didn't mean I'd have to live with the fact that it's breaking. I meant I'd have to live with the fact that it plays for a little while before stopping. Meaning, I'll fix the thing Smiley It would be possible to make it run in smaller increments, meaning it'll play less per loop, right?
Like, instead of this: byte[] data = new byte[4096];
Do this: byte[] data = new byte[2048];

Or am I breaking some rules then?
EDIT: Well, doesn't make a difference in the waiting-period with the modifications posted below.


What should I clean up after leaving the outer Runnable loop?
All resources you opened like Streams, close audio devices, etc.
In case of clean ending and in case of exception handling. A crashed application keeping on playing is no fun...
Indeed! That's what I thought. I'll get right on it.

About the error handling, what do you mean test? If I catch any exceptions, they're sent to my ErrorHandler, which prints them in an errorlog-file for me. Do you mean if I've tested that I also catch the exceptions in the streamer, and not just the Game-class?
Well, test, just means.... test your stuff  Grin
Verify with invalid URLs, with unavailable audio devices, etc., show proper error messages.

Here, the error causes are not shown, that is quite bad (Microsoft style)
Ouch...I thought there was a policy on using swear-words Wink

The streamer should not mess with UI stuff like JOptionPane.
In case of exception in the lower loop, it keeps on running, opens a new stream, and produces another and another message box, If I don't miss anything.
Of course you're right. I only put those in there because I was desperate to be thrown from the game when an error occurred, since nothing was ever caught in my previous tries with exceptions. I was so sure the problem was in the streamer-script, that I was oblivious to the fact that my streamer-thread might not be ready to work.

Thank you so very much for your help! Even though it seems to have fixed itself, the main problem anyway, I'll implement the changes you've suggested. It's for my own good, so when it does break for real, I'll know what broke.
You're a gem! Here's another appreciation Smiley

One more question, though. In the code below, namely the startLoop() method, is it ok to use Thread.sleep(long) in this case? I didn't think it would work, since I'm putting the thread to sleep, while it's waiting for itself to end the current streaming-process and set the url to null. If I set it to sleep, it works, but if I don't put it to sleep, and have an empty while-loop, it stops working the second it tries to start the second piece of music.
A simple SOP instead of sleep works too, but that just seems weird.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
if (line != null) {
   try {
      byte[] data = new byte[4096];
      // Start
      line.start();
      int nBytesRead = 0;
     
      while (nBytesRead != -1 && !stop) {
      nBytesRead = this.decodedStream.read(data, 0,
            data.length);
      if (nBytesRead != -1) {
         line.write(data, 0, nBytesRead);
      }
   }
     
   // Stop
   line.drain();
   line.stop();
   line.close();
   line=null;
   if(this.decodedStream!=null)this.decodedStream.close();
   if(this.stream!=null)this.stream.close();
   this.decodedStream = null;
   this.stream = null;
   this.url = null;
   } catch (IOException io) {
      System.out.println("Line cannot start!");
   }
}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
public void startLoop(String filenameString) {
      this.filename = filenameString;
      System.out.println("Starting loop with: "+filenameString);
      while(this.getUrl()!=null){
         try {
            Thread.sleep(10);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      this.url = this.getClass().getResource(filenameString);
      this.stop = false;
   }

   public void stopLoop() {
      System.out.println("Stopping loop");
      this.stop = true;
   }

- Jonas
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline 65K
« Reply #10 - Posted 2012-04-18 06:54:25 »

Fighting threading issues with artificial sleeps is bad.
I don't see the intention here, you would implement a blocking start method ?
For immediate stopping, how about checking the boolean stop flag in the read/write loop and call line#stop() ?

Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #11 - Posted 2012-04-18 21:01:04 »

Fighting threading issues with artificial sleeps is bad.
For immediate stopping, how about checking the boolean stop flag in the read/write loop and call line#stop() ?
Well, I did that, and I've changed quite a bit. It seems to be very happy about all my changes. No hick-ups Smiley
I've tried different things to make startLoop() wait for the streamer to be closed, before introducing a new song to it, but while this approach with a SOP in a while-loop (instead of Thread#sleep) works fine, I doubt this is the method I should be using. I can leave the SOP completely empty (""), but it just doesn't seem right. If I remove the while-loop completely, the game keeps running, but without changing the music.

The looping-boolean is there, because I also have a method that starts a piece of music that doesn't loop.

I've added some comments as to my intentions. I hope you can make sense of it. Otherwise I'm more than happy to elaborate.
I've included a read-out of my SOPs at the bottom. Even though they seem to print out quite a lot of lines, the change in music is almost instant.

I don't see the intention here, you would implement a blocking start method ?
I don't want it to block; I just want it to wait until the streamer has stopped, and has relinquished its resources, before it starts a new song.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
   public void run() {
      while(running){
         if(!this.stop){
            System.out.println("Streamer is running...");
            do{
               System.out.println("Playing song: "+this.url.getFile().substring(this.url.getFile().lastIndexOf("/")+1));
               try {
                  // Get AudioInputStream from given file.
                  this.stream = AudioSystem.getAudioInputStream(this.url);
                  this.decodedStream = null;
                  if (this.stream != null) {
                     this.format = this.stream.getFormat();
                     this.decodedFormat = new AudioFormat(
                           AudioFormat.Encoding.PCM_SIGNED,
                           this.format.getSampleRate(), 16,
                           this.format.getChannels(),
                           this.format.getChannels() * 2,
                           this.format.getSampleRate(), false);
                     // Get AudioInputStream that will be decoded by underlying VorbisSPI
                     this.decodedStream = AudioSystem.getAudioInputStream(
                           this.decodedFormat, this.stream);
                  }else{
                     System.out.println("Stream is null");
                  }
               } catch (Exception e) {
                  // Do nothing
                  System.out.println("Could not get or decode audiostream");
               }
               line = null;
               try {
                  line = this.getSourceDataLine(this.decodedFormat);
                  FloatControl volume = (FloatControl)line.getControl(FloatControl.Type.MASTER_GAIN);
                  volume.setValue(1);
               } catch (LineUnavailableException lue) {
                  // Do nothing
                  System.out.println("Line is unavailable!");
               }
               if (line != null) {
                  try {
                     byte[] data = new byte[4096];
                     // Start
                     line.start();
                     int nBytesRead = 0;
     
                     while (nBytesRead != -1) {
                        nBytesRead = this.decodedStream.read(data, 0, data.length);
                        if (nBytesRead != -1) {
                           line.write(data, 0, nBytesRead);
                        }

                        // if we have called this.stop(), we break out of the streaming while-loop
                        if(stop) break;
                     }
                     
                     // if song has ended and we're not looping, make sure to stop the streamer
                     if(!looping) stop = true;
                     
                     // clean-up after stop-call or end of song
                     line.stop();
                     line.close();
                     if(this.decodedStream!=null) this.decodedStream.close();
                     if(this.stream!=null) this.stream.close();
                     
                     // if stop has been called, we make sure it doesn't try to loop,
                     // and set the url to null, so startLoop() and startMusic() know
                     // that we're ready to run a new song.
                     if(stop){
                        looping = false;
                        url = null;
                     }
                  } catch (IOException io) {
                     // Do nothing
                     System.out.println("Line cannot start!");
                  }
               }
            }
            while (this.looping);
         }else{
            System.out.println("Streamer is stopped.");
         }
      }
     
      // clean-up before thread is shut down
      if(line.isRunning()){
         line.stop();
      }
      if(line.isOpen())
         line.close();
      line=null;
      try {
         this.decodedStream.close();
         this.stream.close();
      } catch (IOException e) {
         e.printStackTrace();
      }
      this.decodedStream = null;
      this.stream = null;
      this.looping = false;
      this.url = null;
   }

   public void startLoop(String filenameString) {
      this.filename = filenameString;
      System.out.println("Starting loop with: "+filenameString);
      int i = 0;
      while(this.getUrl()!=null){
         System.out.println("Waiting for streamer to stop "+i);
         i++;
      }
      this.url = this.getClass().getResource(filenameString);
      this.looping = true;
      this.stop = false;
   }

   public synchronized void stop() {
      System.out.println("Stopping streamer");
      this.stop = true;
   }


Streamer is stopped.
Streamer is stopped.
Streamer is stopped.
Starting loop with: /snm/sound/oggs/Prelude.ogg
Streamer is stopped.
Streamer is stopped.
Streamer is stopped.
Streamer is stopped.
Streamer is running...
Playing song: Prelude.ogg

Going to worldmap...
Stopping streamer
Starting loop with: /snm/sound/oggs/Worldmap.ogg
Waiting for streamer to stop 0
...many of these...
Waiting for streamer to stop 2318
Streamer is stopped.
..many of these...
Streamer is stopped.
Streamer is running...
Playing song: Worldmap.ogg

- Jonas
Offline 65K
« Reply #12 - Posted 2012-04-19 06:04:54 »

1  
2  
3  
4  
while(this.getUrl()!=null){
         System.out.println("Waiting for streamer to stop "+i);
         i++;
      }

That's what I meant with blocking. Do not do such loops. Without sleep it will even raise CPU occupation. Here, it might work, but it is bad style. Just pass in your new file and let the streamer decide how to handle it.

1  
2  
3  
4  
5  
 public void run() {
      while(running){
         if(!this.stop){
            System.out.println("Streamer is running...");
            do{

Looks like a candidate for simplification: three all embracing code blocks.
Maybe think about extracting methods: it does not look very easy to understand.

If stop is volatile, there is no need to synchronize stop(). That only keeps multiple threads calling stop() simulataneously, while they could still invoke start() for instance.

Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #13 - Posted 2012-04-19 08:08:13 »

If stop is volatile, there is no need to synchronize stop(). That only keeps multiple threads calling stop() simultaneously, while they could still invoke start() for instance.
Yeah, and since I only have the Game-class using the streamer, that doesn't really do anything. And synchronizing both startLoop() and stop() makes the entire thing break down. Plus it doesn't make sense, really.

Just pass in your new file and let the streamer decide how to handle it.
I'd love to, but when I remove the while-loop in startLoop(), it doesn't change the music, because it doesn't have time to shut down the streamer before starting the next loop (setting stop = false again). The stop-flag isn't true long enough for it to shut down the streamer. I'm not sure how I can correctly get around this problem without waiting at some point.

1  
2  
3  
4  
5  
 public void run() {
      while(running){
         if(!this.stop){
            System.out.println("Streamer is running...");
            do{

Looks like a candidate for simplification: three all embracing code blocks.
Maybe think about extracting methods: it does not look very easy to understand.
Well, the ' if(!this.stop)'  loop was only implemented to tell me when the streamer is stopped and when it's running. Merely a debugging-thing.
The while(running) keeps the thread alive, and the do-while(looping) makes it loop if it is set to loop.

- Jonas
Offline philfrei
« Reply #14 - Posted 2012-04-19 08:45:52 »

Quote
I don't want it to block; I just want it to wait until the streamer has stopped, and has relinquished its resources, before it starts a new song.

Just a thought: why not make another fresh OggStreamer object and use that? Then you don't have to wait for the first to relinquish anything. You can let the first thread die a natural death by running out of code to execute. If you do wish to reuse it, and to wait for the streamer to finish, you could look into adding a LineListener() to notify you when the line is ready. That is a LOT better than trying to manage thread timing with Sleep commands!

65K is also correct, I think, with the advice about your run() routine having too many loops. Along those lines, you might find the example in the Java sound tutorial interesting--the sample code while loop in SourceDataLine is the pertinent section. The OggStreamer has definite similarities in structure to SourceDataLine playback.
http://docs.oracle.com/javase/tutorial/sound/playing.html


"It's after the end of the world! Don't you know that yet?"
Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #15 - Posted 2012-04-19 12:00:57 »

Thank you very much for your thoughts. I think I know what I have to do now.

Right now, it is a sort of mix between a single-shot music streamer (opening and closing everything constantly), and a music-streamer that allows for changing of the playing music. Since I'm planning on having some sort of music running at all times, there's no need to reset the line. I just need to stop it, right? Only the stream, decodedStream and formats need to change when a new piece of music is introduced. That should make it much easier to write, so it can comprehend a change in music.

Then I should be able to use the line-object as described in the Java-tutorial you linked, so I can have a LineListener help me start the music with the correct timing. But I'll still have to use some sort of waiting-mechanism. How else would I make the streamer start a piece of music some time in the future, when it is ready?

I don't see the problem in implementing a blocking/waiting-mechanism in a thread, when it is a sort of wingman-thread doing its own thing, other than the obvious cost in CPU-cycles. Is there really no way to wait?
No one but the Game-class is depending on it. But there has to be some method of making a function await a certain variable-change. Somehow, the streamer HAS to wait until it is ready, before it starts the next song.

If that is truly impossible, then I guess I have to make a one-shot musicStreamer, which starts a new instance every time there's a new piece of music starting. I'm just wondering if they won't intersect somehow, giving me some other problems...I guess I'll have to try it.

- Jonas
Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #16 - Posted 2012-04-19 17:38:34 »

Well, it was a long, hard fight, but I finally did it. Now it can play looping songs, and non-looping songs.
The extra all-embracing loops are gone Smiley

I solved it with a bunch of your brilliant comments. Mainly, the thought about not using any variables that are used in the streaming-loop, in the startLoop(). Instead, I use some temporary variables in startLoop(), and let the while(running)-loop declare when it is ready to play the next song, and when it's ready, it sets the main-variables to the temporary ones for the next song.

stop() is no longer called from outside the streamer-thread. The startLoop()- and startMusic()-methods call stop() before doing anything else, and prep temporary variables for playing the new song. You CAN however still call stop() from outside the streamer-thread, and it will stop the playback correctly.

Thank you all SO very much! I could not have done this without you! I hope this is a viable solution.

EDIT: I should make startLoop() and startMusic() synchronized, right?

Code: http://pastebin.java-gaming.org/54a6b9d64

- Jonas
Offline philfrei
« Reply #17 - Posted 2012-04-19 21:11:31 »

Quote
I don't see the problem in implementing a blocking/waiting-mechanism in a thread, when it is a sort of wingman-thread doing its own thing, other than the obvious cost in CPU-cycles. Is there really no way to wait?

I'm in the process of reading Doug Lea's "Concurrent Programming in Java" and just entered the 3rd chapter where he discusses things like "spin waiting" and "latches" and "guards" and such. The book is a classic reference for design-patterns for concurrent threads, and quite relevant despite being over 10 years old.

But, I'm not convinced you need anything so fancy. If you do have the LineListener working, is there really a need to have the new song cue thread spinning, waiting for it? Instead, the LineListener can initiate, can call a method that launches a "readied" new song cue.

It is possible that you don't need to use synchronization. (I don't when I call sound cues, but I don't know the details of your implementation.) If the issue is the "stopped" boolean (in the tutorial example I cited, both "start" and "stop" methods [presumably], and the while loop in the run method use this variable), then setting it to be volatile should suffice.

And as I reread your last post, I'm thinking the Head First "Design Patterns" book might be a good recommendation. It is a much easier/friendlier read than the Lea book which is more advanced. It talks about the very things you mention, such as the best way to pass parameters between threads. The LineListener idea comes in part as a variation of the Observer or Subscriber pattern which is discussed in that book.

"It's after the end of the world! Don't you know that yet?"
Offline 65K
« Reply #18 - Posted 2012-04-19 21:39:18 »

EDIT: I should make startLoop() and startMusic() synchronized, right?
If a thread enters a synchronized method, it aquires a lock for the whole object. Thus, other threads are blocked from entering any synchronized method of the same object, as well as from locking the object directly.

If you only synchronize startLoop() and startMusic() then it will never be possible for two threads executing those methods at the same time. As those methods are usually not invoked from the streamer thread, it would have no effect on it - not before you work with the same lock from inside the streamer loops.

Or just use a volatile boolean flag.
Or synchronize a (non-volatile) boolean flag in start, stop and in the streamer loop.

Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #19 - Posted 2012-04-19 22:23:51 »

It is possible that you don't need to use synchronization. (I don't when I call sound cues, but I don't know the details of your implementation.) If the issue is the "stopped" boolean (in the tutorial example I cited, both "start" and "stop" methods [presumably], and the while loop in the run method use this variable), then setting it to be volatile should suffice.
It works well, and acts the exact same way, whether I synchronize the start and/or stop-methods or not, so it seems you're right. Thanks for clarifying.

And as I reread your last post, I'm thinking the Head First "Design Patterns" book might be a good recommendation. It is a much easier/friendlier read than the Lea book which is more advanced. It talks about the very things you mention, such as the best way to pass parameters between threads. The LineListener idea comes in part as a variation of the Observer or Subscriber pattern which is discussed in that book.
Thanks a lot. I'll see if I can find some place to buy it. I'm very much into getting to know this whole threading-thing. I'm building a few programs, such as an elevator-simulator, to learn more about it. I'm sure this book will become very handy!

EDIT: I should make startLoop() and startMusic() synchronized, right?
If a thread enters a synchronized method, it aquires a lock for the whole object. Thus, other threads are blocked from entering any synchronized method of the same object, as well as from locking the object directly.

If you only synchronize startLoop() and startMusic() then it will never be possible for two threads executing those methods at the same time. As those methods are usually not invoked from the streamer thread, it would have no effect on it - not before you work with the same lock from inside the streamer loops.

Or just use a volatile boolean flag.
Or synchronize a (non-volatile) boolean flag in start, stop and in the streamer loop.
OK, so I can either do what I've done so far, just have the stop-boolean be volatile, or I could make it non-volatile and synchronize the start and stop methods and the streamer-loop? How would I make it synchronized in the streamer-lopp? Just synchronize the entire run()?

- Jonas
Offline 65K
« Reply #20 - Posted 2012-04-20 06:31:32 »

How would I make it synchronized in the streamer-lopp? Just synchronize the entire run()?
If you synchronize run(), no other thread would ever be able to get into start() or stop(). You would rather synchronize the stop flag before accessing it. But I'd say, stick to your volatile boolean.

Offline Ultroman

JGO Knight


Medals: 25
Projects: 1


Snappin' at snizzes since '83


« Reply #21 - Posted 2012-04-20 08:54:07 »

Quote from: 65K
If you synchronize run(), no other thread would ever be able to get into start() or stop().

I thought so. That cleared up a lot of confusion for me Smiley

Thank you.

- Jonas
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

DarkCart (16 views)
2014-10-31 21:44:48

DarkCart (20 views)
2014-10-31 21:43:57

TehJavaDev (40 views)
2014-10-27 03:28:38

TehJavaDev (30 views)
2014-10-27 03:27:51

DarkCart (44 views)
2014-10-26 19:37:11

Luminem (27 views)
2014-10-26 10:17:50

Luminem (30 views)
2014-10-26 10:14:04

theagentd (36 views)
2014-10-25 15:46:29

Longarmx (64 views)
2014-10-17 03:59:02

Norakomi (62 views)
2014-10-16 15:22:06
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!