title: Audio performance with Javafx for Android (MediaPlayer and NativeAudioService)
body: <p>I have created, with JavaFX, a game on desktop that works fine (20000 Java lines. As it is a game, the Real Time constraint is important (response time of player's actions). </p>
  
  <p>The final aim is to run this application with Android. I have almost finished to "transfer the Java code" from PC to Android, even if I have encountered some real time trouble. I think almost all of them are solved now.</p>
  
  <p>For instance, I have minimized the CPU time (consumption) of Shape or Rectangle.intersect(node1, node2) calls that are used for detecting impacts between two mobiles. Thus, the real time has been divided by 3. Great!</p>
  
  <p>For testing this Android version, I use Eclipse + Neon2, JavaFX, JavaFXports + gluon and my phone (Archos Diamond S).</p>
  
  <hr>
  
  <p><strong>But, for Android phones, I had a real time problem related to the sounds that are generated with MediaPlayer and NativeAudioSrvice.</strong></p>
  
  <p>Yet, I have followed this advice that suggests the synchronous mode:
  <a href="https://stackoverflow.com/questions/38419634/javafxports-how-to-call-android-native-media-player/42160145">javafxports how to call android native Media Player</a></p>
  
  <p>1st question:</p>
  
  <p><strong>Does it exist an asynchronous mode with this Mediaplayer class?I think that would solve this latency problem?</strong>
  In practice, I have tried the asynchronous solution ... without success: the real time problem due to the audio generation with MediaPlayer stays: an audio generation costs from 50 ms to 80 ms whereas the main cyclic processing runs each 110 ms. Each audio generation can interfer with the main processing execution.</p>
  
  <p><strong>And, in each periodic task (rate: 110 ms), I can play several sounds like that.</strong> And, in a trace, there was up to six sound activations that take (together) about 300 ms (against the 110 ms of the main cyclic task )</p>
  
  <p><strong>QUESTION:</strong> </p>
  
  <p><strong>How to improve the performance of NativeAudio class (especially, the method play() with its calls that create the real time problem: setDataSource(...), prepare() and start() )?</strong></p>
  
  <hr>
  
  <p><strong>THE SOLUTION</strong></p>
  
  <p>The main processing must be a "synchronized" method to be sure that this complete processing will be run, without any audio interruption.</p>
  
  <p>More, each complete processing for generating a sound is under a dedicated thread, defined with a Thread.MIN_PRIORITY priority.</p>
  
  <p>Now, the main processing is run each 110 ms and, when it begins, it cannot be disturbed by any audio generation. The display is very "soft" (no more jerky moving).</p>
  
  <p>There is just a minor problem: when an audio seDataSource(), a start() or a prepare() method has begun, it seems to be that the next main processing shall wait the end of the method before beginning (TBC)</p>
  
  <p>I hope this solution could help another people. It is applicable in any case of audio generations with MediaPlayer.</p>
  
  <hr>
  
  <p><strong>JAVA code of the solution</strong></p>
  
  <p>The main processing is defined like that:</p>
  
  <pre><code>public static ***synchronized*** void mainProcessing() {
  // the method handles the impacts, explosions, sounds, movings, ... , in other words almost the entiere game  .. in a CRITICAL SECTION
  }
  
  /****************************************************/
  </code></pre>
  
  <p>In the NativeAudio class that implements "NativeAudioService":</p>
  
  <pre><code>    @Override
          public void play() {
              if (bSon) {
                  Task&lt;Void&gt; taskSound = new Task&lt;Void&gt;() {
  
                  @Override
                  protected Void call() throws Exception {
                  generateSound(); 
                  return null;
                  }}; 
  
                Thread threadSound = new Thread(taskSound);
                threadSound.setPriority(Thread.MIN_PRIORITY);
                threadSound.start();
              } 
          }
  
  
    /****************************************************/    
    private void generateSound() {
          currentPosition = 0;
          nbTask++;
          noTask = nbTask;
          try {
              if (mediaPlayer != null) {
                  stop();
              }
              mediaPlayer = new MediaPlayer();
  
              AssetFileDescriptor afd = FXActivity.getInstance().getAssets().openFd(audioFileName);
  
              mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());      
  
              mediaPlayer.setAudioStreamType(AudioManager.STREAM_RING);
  
              float floatLevel = (float) audioLevel;
              mediaPlayer.setVolume(floatLevel, floatLevel);
  
              mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
                  @Override
                  public void onCompletion(MediaPlayer mediaPlayer) {
                      if (nbCyclesAudio &gt;= 1) {
                                mediaPlayer.start();
                            nbCyclesAudio--;
                          } else {
                          mediaPlayer.stop(); 
                          mediaPlayer.release(); // for freeing the resource - useful for the phone codec 
                          mediaPlayer = null;
                          }
                  }
              });
  
              mediaPlayer.prepare();
  
              mediaPlayer.start();
              nbCyclesAudio--;
  
          } catch (IOException e) {
              }
        }
  </code></pre>