Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

High Performance-Usage of the Update-Runner-Thread on Embedded-Systems #12

Open
meschie opened this issue Sep 24, 2012 · 1 comment
Open

Comments

@meschie
Copy link

meschie commented Sep 24, 2012

On some embedded-systems there is a load of aproximatly 30-40% producted by the Thread.sleep(1) in this class.

The Issue presents itself in case of starting the SoundEngine on Application-Startup, while shuting it down at the end.

The Problem here is that many embedded-cpu's (e.g. AMD G-T56N, Intel Atom D525) don't have that much throught, so the sleep is not called that often.

The following Patch is kind of a solution for me, because starting the sound without latency is not a problem for me. While playback the sleep must not be bigger than 15ms, but while waiting for something to do, we can sleep a quater second. This reduces CPU-load to about 4%.


Test-Systems:

  • Ubuntu 12.04 LTS 3.2.0-29-generic-pae AMD G-T56N 2GB RAM
  • Ubuntu 10.04 LTS 2.6.32-40-generic Intel Atom D525 2GB RAM
  • Environment: JAVA-AWT-Application with sound on Button-Interaction

On a Ubuntu 10.04 LTS 2.6.32-43-generic with Intel Core2 Quad Q6600 4GB RAM the Problem doesn't occur.

The Problem exists with JAVA-Versions:

  • OpenJDK Runtime Environment (IcedTea6 1.9.13)
  • OpenJDK Runtime Environment (IcedTea7 2.3.2)
  • Java(TM) SE Runtime Environment (build 1.6.0_32-b05)

Index: src/kuusisto/tinysound/TinySound.java
===================================================================
--- src/kuusisto/tinysound/TinySound.java   (revision 409)
+++ src/kuusisto/tinysound/TinySound.java   (working copy)
@@ -145,6 +145,9 @@
        } catch (Exception e) {}
        TinySound.inited = true;
        updateThread.start();
+       
+       mixer.setUpdateRunner(updateThread);
+       
        //yield to potentially give the updater a chance
        Thread.yield();
    }
Index: src/kuusisto/tinysound/internal/Mixer.java
===================================================================
--- src/kuusisto/tinysound/internal/Mixer.java  (revision 409)
+++ src/kuusisto/tinysound/internal/Mixer.java  (working copy)
@@ -40,7 +40,12 @@
    private List<MusicReference> musics;
    private List<SoundReference> sounds;
    private int[] dataBuf; //buffer for reading sound data
+   private Thread updateRunner;

+   public void setUpdateRunner(Thread pUpdateRunner) {
+       this.updateRunner = pUpdateRunner;
+   }
+
    /**
     * Construct a new Mixer for TinySound system.
     */
@@ -55,6 +60,7 @@
     * @param music MusicReference to be registered
     */
    public synchronized void registerMusicReference(MusicReference music) {
+       this.updateRunner.interrupt();
        this.musics.add(music);
    }

@@ -63,6 +69,7 @@
     * @param sound SoundReference to be registered
     */
    public synchronized void registerSoundReference(SoundReference sound) {
+       this.updateRunner.interrupt();
        this.sounds.add(sound);
    }

@@ -71,6 +78,7 @@
     * @param music MusicReference to be unregistered
     */
    public synchronized void unRegisterMusicReference(MusicReference music) {
+       this.updateRunner.interrupt();
        this.musics.remove(music);
    }

Index: src/kuusisto/tinysound/internal/UpdateRunner.java
===================================================================
--- src/kuusisto/tinysound/internal/UpdateRunner.java   (revision 409)
+++ src/kuusisto/tinysound/internal/UpdateRunner.java   (working copy)
@@ -71,62 +71,88 @@
            int bufSize = (int)TinySound.FORMAT.getFrameRate() *
                TinySound.FORMAT.getFrameSize();
            byte[] audioBuffer = new byte[bufSize];
+           
+           //init fallback buffer
+           byte[] audioBuffer2 = new byte[bufSize];
+           for (int i=0; i<audioBuffer2.length; i++) {
+               audioBuffer2[i] = 0;
+           }
+           
            //only buffer some maximum number of frames each update (25ms)
            int maxFramesPerUpdate = 
                (int)((TinySound.FORMAT.getFrameRate() / 1000) * 25);
            int numBytesRead = 0;
            double framesAccrued = 0;
            long lastUpdate = System.nanoTime();
+           int slowDownCounter = 0;
            //keep running until told to stop
            while (this.running.get()) {
-               //check the time
-               long currTime = System.nanoTime();
-               //accrue frames
-               double delta = currTime - lastUpdate;
-               double secDelta = (delta / 1000000000L);
-               framesAccrued += secDelta * TinySound.FORMAT.getFrameRate(); 
-               //read frames if needed
-               int framesToRead = (int)framesAccrued;
-               int framesToSkip = 0;
-               //check if we need to skip frames to catch up
-               if (framesToRead > maxFramesPerUpdate) {
-                   framesToSkip = framesToRead - maxFramesPerUpdate;
-                   framesToRead = maxFramesPerUpdate;
-               }
-               //skip frames
-               if (framesToSkip > 0) {
-                   int bytesToSkip = framesToSkip *
-                       TinySound.FORMAT.getFrameSize();
-                   this.mixer.skip(bytesToSkip);
-               }
-               //read frames
-               if (framesToRead > 0) {
-                   //read from the mixer
-                   int bytesToRead = framesToRead *
-                       TinySound.FORMAT.getFrameSize();
-                   int tmpBytesRead = this.mixer.read(audioBuffer,
-                           numBytesRead, bytesToRead);
-                   numBytesRead += tmpBytesRead; //mark how many read
-                   //fill rest with zeroes
-                   int remaining = bytesToRead - tmpBytesRead;
-                   for (int i = 0; i < remaining; i++) {
-                       audioBuffer[numBytesRead + i] = 0;
+               try {
+                   //check the time
+                   long currTime = System.nanoTime();
+                   //accrue frames
+                   double delta = currTime - lastUpdate;
+                   double secDelta = (delta / 1000000000L);
+                   framesAccrued += secDelta * TinySound.FORMAT.getFrameRate(); 
+                   //read frames if needed
+                   int framesToRead = (int)framesAccrued;
+                   int framesToSkip = 0;
+                   //check if we need to skip frames to catch up
+                   if (framesToRead > maxFramesPerUpdate) {
+                       framesToSkip = framesToRead - maxFramesPerUpdate;
+                       framesToRead = maxFramesPerUpdate;
                    }
-                   numBytesRead += remaining; //mark zeroes read
-               }
-               //mark frames read and skipped
-               framesAccrued -= (framesToRead + framesToSkip);
-               //write to speakers
-               if (numBytesRead > 0) {
-                   this.outLine.write(audioBuffer, 0, numBytesRead);
-                   numBytesRead = 0;
+                   //skip frames
+                   if (framesToSkip > 0) {
+                       int bytesToSkip = framesToSkip *
+                           TinySound.FORMAT.getFrameSize();
+                       this.mixer.skip(bytesToSkip);
+                   }
+                   //read frames
+                   if (framesToRead > 0) {
+                       //read from the mixer
+                       int bytesToRead = framesToRead *
+                           TinySound.FORMAT.getFrameSize();
+                       int tmpBytesRead = this.mixer.read(audioBuffer,
+                               numBytesRead, bytesToRead);
+                       numBytesRead += tmpBytesRead; //mark how many read
+                       
+                       if (tmpBytesRead>0) {
+                           slowDownCounter=0;
+                       }                   
+                       
+                       //fill rest with zeroes
+                       int remaining = bytesToRead - tmpBytesRead;
+                       //this method is faster...
+                       System.arraycopy(audioBuffer2, 0, audioBuffer, numBytesRead, remaining);
+                       
+//                     for (int i = 0; i < remaining; i++) {
+//                         audioBuffer[numBytesRead + i] = 0;
+//                     }
+                       numBytesRead += remaining; //mark zeroes read
+                   }
+                   //mark frames read and skipped
+                   framesAccrued -= (framesToRead + framesToSkip);
+                   //write to speakers
+                   if (numBytesRead > 0) {
+                       this.outLine.write(audioBuffer, 0, numBytesRead);
+                       numBytesRead = 0;
+                   }
+                   //mark last update
+                   lastUpdate = currTime;
+                   slowDownCounter++;
+                   //give the CPU back to the OS for a bit
+               
+                   if (slowDownCounter > 1000) {
+                       slowDownCounter = 1001;                     
+                       Thread.sleep(1000);
+                   } else {
+                       Thread.sleep(5);
+                   }
+               } catch (InterruptedException e) {
+                   //ignore this exception because in case of interrupt, we woke the thread
+                   slowDownCounter=0;
                }
-               //mark last update
-               lastUpdate = currTime;
-               //give the CPU back to the OS for a bit
-               try {
-                   Thread.sleep(1);
-               } catch (InterruptedException e) {}
            }
        }
@finnkuusisto
Copy link
Owner

I'm reluctant to include a change that could introduce so much latency to the start of playback. I appreciate the suggestion and will look try to come up with a solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants