All Downloads are FREE. Search and download functionalities are using the official Maven repository.

playn.android.AndroidAudio Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
/**
 * Copyright 2011 The PlayN Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package playn.android;

import java.io.File;
import java.io.FileInputStream;
import java.util.HashSet;
import java.util.Set;

import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer;

import playn.core.AudioImpl;

class AndroidAudio extends AudioImpl {

  interface Resolver {
    void resolve(AndroidSound sound);
  }

  private final Set> playing = new HashSet>();
  private final AndroidPlatform platform;

  public AndroidAudio(AndroidPlatform platform) {
    super(platform);
    this.platform = platform;
  }

  AndroidSound createSound(final String path) {
    // MediaPlayer should really be used to play compressed sounds and other file formats
    // AudioTrack cannot handle. However, the MediaPlayer implementation is currently the only
    // version of AndroidSound we have written, so we'll use it here regardless of format.
    return new AndroidCompressedSound(this, new Resolver() {
      // this cache file will get cleaned up in our activity's onDestroy
      private String extension = path.substring(path.lastIndexOf('.'));
      private File cachedFile;

      public void resolve (final AndroidSound sound) {
        // we need to create the media player before starting the background task because the media
        // player will dispatch callbacks based on the looper associated with the thread on which
        // it was created; resolve() will be called from the main PlayN thread and we want
        // callbacks to be dispatched on that same thread
        final MediaPlayer mp = new MediaPlayer();
        platform.invokeAsync(new Runnable() {
          public void run () {
            try {
              // lazily create our cached audio file
              if (cachedFile == null) {
                cachedFile = platform.assets().cacheAsset(
                  path, "sound-" + Integer.toHexString(hashCode()) + extension);
              }
              FileInputStream ins = new FileInputStream(cachedFile);
              try {
                mp.setDataSource(ins.getFD());
                mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                  @Override
                  public void onPrepared(final MediaPlayer mp) {
                    dispatchLoaded(sound, mp);
                  }
                });
                mp.setOnErrorListener(new OnErrorListener() {
                  @Override
                  public boolean onError(MediaPlayer mp, int what, int extra) {
                    sound.onLoadError(new Exception("MediaPlayer prepare failure [what=" + what +
                                                    ", extra=" + extra + "]"));
                    return false;
                  }
                });
                mp.prepareAsync();
              } finally {
                ins.close();
              }
            } catch (Exception e) {
              dispatchLoadError(sound, e);
            }
          }
        });
      }
    });
  }

  public void onPause() {
    if (!playing.isEmpty())
      AndroidPlatform.debugLog("Pausing " + playing.size() + " playing sounds.");
    for (AndroidSound sound : playing) {
      sound.onPause();
    }
  }

  public void onResume() {
    // copy and clear out the playing set, the playing sounds will re-add themselves once they are
    // (asynchronously) re-resolved and resume playing
    Set> wasPlaying = new HashSet>(playing);
    playing.clear();
    if (!wasPlaying.isEmpty())
      AndroidPlatform.debugLog("Resuming " + wasPlaying.size() + " playing sounds.");
    for (AndroidSound sound : wasPlaying) {
      sound.onResume();
    }
  }

  public void onDestroy() {
    for (AndroidSound sound : playing) {
      sound.onDestroy();
    }
    playing.clear();
  }

  void onPlaying(AndroidSound sound) {
    AndroidPlatform.debugLog("Playing " + sound);
    playing.add(sound);
  }

  void onStopped(AndroidSound sound) {
    AndroidPlatform.debugLog("Stopped " + sound);
    playing.remove(sound);
  }
}