diff --git a/engine/src/android/com/jme3/app/AndroidHarness.java b/engine/src/android/com/jme3/app/AndroidHarness.java
index de2c45cd3..6d09b8c5d 100644
--- a/engine/src/android/com/jme3/app/AndroidHarness.java
+++ b/engine/src/android/com/jme3/app/AndroidHarness.java
@@ -8,15 +8,13 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.View;
import android.view.ViewGroup.LayoutParams;
-import android.view.Window;
-import android.view.WindowManager;
+import android.view.*;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.audio.android.AndroidAudioRenderer;
import com.jme3.input.android.AndroidInput;
import com.jme3.input.controls.TouchListener;
import com.jme3.input.event.TouchEvent;
@@ -26,7 +24,6 @@ import com.jme3.system.android.AndroidConfigChooser.ConfigType;
import com.jme3.system.android.JmeAndroidSystem;
import com.jme3.system.android.OGLESContext;
import com.jme3.util.JmeFormatter;
-import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Handler;
@@ -43,76 +40,63 @@ import java.util.logging.Logger;
public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener {
protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName());
-
/**
* The application class to start
*/
protected String appClass = "jme3test.android.Test";
-
/**
* The jme3 application object
*/
protected Application app = null;
-
/**
* ConfigType.FASTEST is RGB565, GLSurfaceView default ConfigType.BEST is
* RGBA8888 or better if supported by the hardware
*/
protected ConfigType eglConfigType = ConfigType.FASTEST;
-
/**
* If true all valid and not valid egl configs are logged
*/
protected boolean eglConfigVerboseLogging = false;
-
/**
* If true MouseEvents are generated from TouchEvents
*/
protected boolean mouseEventsEnabled = true;
-
/**
* Flip X axis
*/
protected boolean mouseEventsInvertX = true;
-
/**
* Flip Y axis
*/
protected boolean mouseEventsInvertY = true;
-
/**
* Title of the exit dialog, default is "Do you want to exit?"
*/
protected String exitDialogTitle = "Do you want to exit?";
-
/**
* Message of the exit dialog, default is "Use your home key to bring this
* app into the background or exit to terminate it."
*/
protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it.";
/**
- * Set the screen window mode.
- * If screenFullSize is true, then the notification bar and title bar are
- * removed and the screen covers the entire display.
- * If screenFullSize is false, then the notification bar remains visible if
- * screenShowTitle is true while screenFullScreen is false,
- * then the title bar is also displayed under the notification bar.
+ * Set the screen window mode. If screenFullSize is true, then the
+ * notification bar and title bar are removed and the screen covers the
+ * entire display. If screenFullSize is false, then the notification bar
+ * remains visible if screenShowTitle is true while screenFullScreen is
+ * false, then the title bar is also displayed under the notification bar.
*/
protected boolean screenFullScreen = true;
-
/**
* if screenShowTitle is true while screenFullScreen is false, then the
* title bar is also displayed under the notification bar
*/
protected boolean screenShowTitle = true;
-
/**
* Splash Screen picture Resource ID. If a Splash Screen is desired, set
* splashPicID to the value of the Resource ID (i.e. R.drawable.picname). If
* splashPicID = 0, then no splash screen will be displayed.
*/
protected int splashPicID = 0;
-
/**
* Set the screen orientation, default is SENSOR
* ActivityInfo.SCREEN_ORIENTATION_* constants package
@@ -216,6 +200,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
if (app != null) {
app.restart();
}
+
logger.info("onRestart");
}
@@ -231,6 +216,14 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
if (view != null) {
view.onResume();
}
+
+ //resume the audio
+ AudioRenderer result = app.getAudioRenderer();
+ if (result instanceof AndroidAudioRenderer) {
+ AndroidAudioRenderer renderer = (AndroidAudioRenderer) result;
+ renderer.resumeAll();
+ }
+
isGLThreadPaused = false;
logger.info("onResume");
}
@@ -241,6 +234,15 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
if (view != null) {
view.onPause();
}
+
+ //pause the audio
+ AudioRenderer result = app.getAudioRenderer();
+ logger.info("pause: " + result.getClass().getSimpleName());
+ if (result instanceof AndroidAudioRenderer) {
+ AndroidAudioRenderer renderer = (AndroidAudioRenderer) result;
+ renderer.pauseAll();
+ }
+
isGLThreadPaused = true;
logger.info("onPause");
}
@@ -248,6 +250,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
@Override
protected void onStop() {
super.onStop();
+
logger.info("onStop");
}
@@ -256,6 +259,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
if (app != null) {
app.stop(!isGLThreadPaused);
}
+
logger.info("onDestroy");
super.onDestroy();
}
@@ -265,35 +269,35 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
}
/**
- * Called when an error has occurred.
- * By default, will show an error message to the user
- * and print the exception/error to the log.
+ * Called when an error has occurred. By default, will show an error message
+ * to the user and print the exception/error to the log.
*/
public void handleError(final String errorMsg, final Throwable t) {
String stackTrace = "";
String title = "Error";
-
- if (t != null){
+
+ if (t != null) {
// Convert exception to string
StringWriter sw = new StringWriter(100);
t.printStackTrace(new PrintWriter(sw));
stackTrace = sw.toString();
title = t.toString();
}
-
- final String finalTitle = title;
- final String finalMsg = (errorMsg != null ? errorMsg : "Uncaught Exception")
- + "\n" + stackTrace;
+
+ final String finalTitle = title;
+ final String finalMsg = (errorMsg != null ? errorMsg : "Uncaught Exception")
+ + "\n" + stackTrace;
logger.log(Level.SEVERE, finalMsg);
runOnUiThread(new Runnable() {
+
@Override
public void run() {
AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
- .setTitle(finalTitle)
- .setPositiveButton("Kill", AndroidHarness.this)
- .setMessage(finalMsg).create();
+ .setTitle(finalTitle)
+ .setPositiveButton("Kill", AndroidHarness.this)
+ .setMessage(finalMsg).create();
dialog.show();
}
});
@@ -324,6 +328,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
switch (evt.getType()) {
case KEY_UP:
runOnUiThread(new Runnable() {
+
@Override
public void run() {
AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon)
diff --git a/engine/src/android/com/jme3/asset/AndroidAssetManager.java b/engine/src/android/com/jme3/asset/AndroidAssetManager.java
index 59e9ca5df..c6f0b1704 100644
--- a/engine/src/android/com/jme3/asset/AndroidAssetManager.java
+++ b/engine/src/android/com/jme3/asset/AndroidAssetManager.java
@@ -1,115 +1,115 @@
-/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.asset;
-
-import com.jme3.asset.plugins.AndroidLocator;
-import com.jme3.asset.plugins.ClasspathLocator;
-import com.jme3.audio.plugins.AndroidAudioLoader;
-import com.jme3.texture.Texture;
-import com.jme3.texture.plugins.AndroidImageLoader;
-import java.net.URL;
-import java.util.logging.Logger;
-
-/**
- * AndroidAssetManager is an implementation of DesktopAssetManager for Android
- *
- * @author larynx
- */
-public class AndroidAssetManager extends DesktopAssetManager {
-
- private static final Logger logger = Logger.getLogger(AndroidAssetManager.class.getName());
-
- public AndroidAssetManager() {
- this(null);
- }
-
- @Deprecated
- public AndroidAssetManager(boolean loadDefaults) {
- //this(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Android.cfg"));
- this(null);
- }
-
- /**
- * AndroidAssetManager constructor
- * If URL == null then a default list of locators and loaders for android is set
- * @param configFile
- */
- public AndroidAssetManager(URL configFile) {
- System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver");
-
- // Set Default Android config
- this.registerLocator("", AndroidLocator.class);
- this.registerLocator("", ClasspathLocator.class);
- this.registerLoader(AndroidImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg");
- this.registerLoader(AndroidAudioLoader.class, "ogg", "mp3");
- this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3m");
- this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3md");
- this.registerLoader(com.jme3.font.plugins.BitmapFontLoader.class, "fnt");
- this.registerLoader(com.jme3.texture.plugins.DDSLoader.class, "dds");
- this.registerLoader(com.jme3.texture.plugins.PFMLoader.class, "pfm");
- this.registerLoader(com.jme3.texture.plugins.HDRLoader.class, "hdr");
- this.registerLoader(com.jme3.texture.plugins.TGALoader.class, "tga");
- this.registerLoader(com.jme3.export.binary.BinaryImporter.class, "j3o");
- this.registerLoader(com.jme3.scene.plugins.OBJLoader.class, "obj");
- this.registerLoader(com.jme3.scene.plugins.MTLLoader.class, "mtl");
- this.registerLoader(com.jme3.scene.plugins.ogre.MeshLoader.class, "meshxml", "mesh.xml");
- this.registerLoader(com.jme3.scene.plugins.ogre.SkeletonLoader.class, "skeletonxml", "skeleton.xml");
- this.registerLoader(com.jme3.scene.plugins.ogre.MaterialLoader.class, "material");
- this.registerLoader(com.jme3.scene.plugins.ogre.SceneLoader.class, "scene");
- this.registerLoader(com.jme3.shader.plugins.GLSLLoader.class, "vert", "frag", "glsl", "glsllib");
-
- logger.info("AndroidAssetManager created.");
- }
-
- /**
- * Loads a texture.
- *
- * @return
- */
- @Override
- public Texture loadTexture(TextureKey key) {
- Texture tex = (Texture) loadAsset(key);
-
- // XXX: This will improve performance on some really
- // low end GPUs (e.g. ones with OpenGL ES 1 support only)
- // but otherwise won't help on the higher ones.
- // Strongly consider removing this.
- tex.setMagFilter(Texture.MagFilter.Nearest);
- tex.setAnisotropicFilter(0);
- if (tex.getMinFilter().usesMipMapLevels()) {
- tex.setMinFilter(Texture.MinFilter.NearestNearestMipMap);
- } else {
- tex.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
- }
- return tex;
- }
-}
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.asset;
+
+import com.jme3.asset.plugins.AndroidLocator;
+import com.jme3.asset.plugins.ClasspathLocator;
+import com.jme3.audio.plugins.AndroidAudioLoader;
+import com.jme3.texture.Texture;
+import com.jme3.texture.plugins.AndroidImageLoader;
+import java.net.URL;
+import java.util.logging.Logger;
+
+/**
+ * AndroidAssetManager is an implementation of DesktopAssetManager for Android
+ *
+ * @author larynx
+ */
+public class AndroidAssetManager extends DesktopAssetManager {
+
+ private static final Logger logger = Logger.getLogger(AndroidAssetManager.class.getName());
+
+ public AndroidAssetManager() {
+ this(null);
+ }
+
+ @Deprecated
+ public AndroidAssetManager(boolean loadDefaults) {
+ //this(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Android.cfg"));
+ this(null);
+ }
+
+ /**
+ * AndroidAssetManager constructor
+ * If URL == null then a default list of locators and loaders for android is set
+ * @param configFile
+ */
+ public AndroidAssetManager(URL configFile) {
+ System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver");
+
+ // Set Default Android config
+ this.registerLocator("", AndroidLocator.class);
+ this.registerLocator("", ClasspathLocator.class);
+ this.registerLoader(AndroidImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg");
+ this.registerLoader(AndroidAudioLoader.class, "ogg", "mp3", "wav");
+ this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3m");
+ this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3md");
+ this.registerLoader(com.jme3.font.plugins.BitmapFontLoader.class, "fnt");
+ this.registerLoader(com.jme3.texture.plugins.DDSLoader.class, "dds");
+ this.registerLoader(com.jme3.texture.plugins.PFMLoader.class, "pfm");
+ this.registerLoader(com.jme3.texture.plugins.HDRLoader.class, "hdr");
+ this.registerLoader(com.jme3.texture.plugins.TGALoader.class, "tga");
+ this.registerLoader(com.jme3.export.binary.BinaryImporter.class, "j3o");
+ this.registerLoader(com.jme3.scene.plugins.OBJLoader.class, "obj");
+ this.registerLoader(com.jme3.scene.plugins.MTLLoader.class, "mtl");
+ this.registerLoader(com.jme3.scene.plugins.ogre.MeshLoader.class, "meshxml", "mesh.xml");
+ this.registerLoader(com.jme3.scene.plugins.ogre.SkeletonLoader.class, "skeletonxml", "skeleton.xml");
+ this.registerLoader(com.jme3.scene.plugins.ogre.MaterialLoader.class, "material");
+ this.registerLoader(com.jme3.scene.plugins.ogre.SceneLoader.class, "scene");
+ this.registerLoader(com.jme3.shader.plugins.GLSLLoader.class, "vert", "frag", "glsl", "glsllib");
+
+ logger.info("AndroidAssetManager created.");
+ }
+
+ /**
+ * Loads a texture.
+ *
+ * @return
+ */
+ @Override
+ public Texture loadTexture(TextureKey key) {
+ Texture tex = (Texture) loadAsset(key);
+
+ // XXX: This will improve performance on some really
+ // low end GPUs (e.g. ones with OpenGL ES 1 support only)
+ // but otherwise won't help on the higher ones.
+ // Strongly consider removing this.
+ tex.setMagFilter(Texture.MagFilter.Nearest);
+ tex.setAnisotropicFilter(0);
+ if (tex.getMinFilter().usesMipMapLevels()) {
+ tex.setMinFilter(Texture.MinFilter.NearestNearestMipMap);
+ } else {
+ tex.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
+ }
+ return tex;
+ }
+}
diff --git a/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java b/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java
index f31b597a6..2b39c30c4 100644
--- a/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java
+++ b/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java
@@ -1,478 +1,499 @@
-/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.audio.android;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.media.SoundPool;
-import android.util.Log;
-
-import com.jme3.asset.AssetKey;
-import com.jme3.audio.AudioNode.Status;
-import com.jme3.audio.*;
-import com.jme3.math.FastMath;
-import com.jme3.math.Vector3f;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * This class is the android implementation for {@link AudioRenderer}
- *
- * @author larynx
- * @author plan_rich
- */
-public class AndroidAudioRenderer implements AudioRenderer,
- SoundPool.OnLoadCompleteListener, MediaPlayer.OnCompletionListener {
-
- private static final Logger logger = Logger
- .getLogger(AndroidAudioRenderer.class.getName());
- private final static int MAX_NUM_CHANNELS = 16;
- private final HashMap musicPlaying = new HashMap();
- private SoundPool soundPool = null;
-
- private final Vector3f listenerPosition = new Vector3f();
- // For temp use
- private final Vector3f distanceVector = new Vector3f();
- private final Context context;
- private final AssetManager assetManager;
- private HashMap soundpoolStillLoading = new HashMap();
- private Listener listener;
- private boolean audioDisabled = false;
-
- private final AudioManager manager;
-
- public AndroidAudioRenderer(Activity context) {
- this.context = context;
- manager = (AudioManager) context
- .getSystemService(Context.AUDIO_SERVICE);
- context.setVolumeControlStream(AudioManager.STREAM_MUSIC);
- assetManager = context.getAssets();
- }
-
- @Override
- public void initialize() {
- soundPool = new SoundPool(MAX_NUM_CHANNELS, AudioManager.STREAM_MUSIC,
- 0);
- soundPool.setOnLoadCompleteListener(this);
- }
-
- @Override
- public void updateSourceParam(AudioNode src, AudioParam param) {
- // logger.log(Level.INFO, "updateSourceParam " + param);
-
- if (audioDisabled) {
- return;
- }
-
- if (src.getChannel() < 0) {
- return;
- }
-
- switch (param) {
- case Position:
- if (!src.isPositional()) {
- return;
- }
-
- Vector3f pos = src.getWorldTranslation();
- break;
- case Velocity:
- if (!src.isPositional()) {
- return;
- }
-
- Vector3f vel = src.getVelocity();
- break;
- case MaxDistance:
- if (!src.isPositional()) {
- return;
- }
- break;
- case RefDistance:
- if (!src.isPositional()) {
- return;
- }
- break;
- case ReverbFilter:
- if (!src.isPositional() || !src.isReverbEnabled()) {
- return;
- }
- break;
- case ReverbEnabled:
- if (!src.isPositional()) {
- return;
- }
-
- if (src.isReverbEnabled()) {
- updateSourceParam(src, AudioParam.ReverbFilter);
- }
- break;
- case IsPositional:
- break;
- case Direction:
- if (!src.isDirectional()) {
- return;
- }
-
- Vector3f dir = src.getDirection();
- break;
- case InnerAngle:
- if (!src.isDirectional()) {
- return;
- }
- break;
- case OuterAngle:
- if (!src.isDirectional()) {
- return;
- }
- break;
- case IsDirectional:
- if (src.isDirectional()) {
- updateSourceParam(src, AudioParam.Direction);
- updateSourceParam(src, AudioParam.InnerAngle);
- updateSourceParam(src, AudioParam.OuterAngle);
- } else {
- }
- break;
- case DryFilter:
- if (src.getDryFilter() != null) {
- Filter f = src.getDryFilter();
- if (f.isUpdateNeeded()) {
- // updateFilter(f);
- }
- }
- break;
- case Looping:
- if (src.isLooping()) {
- }
- break;
- case Volume:
-
- soundPool.setVolume(src.getChannel(), src.getVolume(),
- src.getVolume());
-
- break;
- case Pitch:
-
- break;
- }
-
- }
-
- @Override
- public void updateListenerParam(Listener listener, ListenerParam param) {
- // logger.log(Level.INFO, "updateListenerParam " + param);
- if (audioDisabled) {
- return;
- }
-
- switch (param) {
- case Position:
- listenerPosition.set(listener.getLocation());
-
- break;
- case Rotation:
- Vector3f dir = listener.getDirection();
- Vector3f up = listener.getUp();
-
- break;
- case Velocity:
- Vector3f vel = listener.getVelocity();
-
- break;
- case Volume:
- // alListenerf(AL_GAIN, listener.getVolume());
- break;
- }
-
- }
-
- @Override
- public void update(float tpf) {
- float distance;
- float volume;
-
- // Loop over all mediaplayers
- for (AudioNode src : musicPlaying.keySet()) {
-
- MediaPlayer mp = musicPlaying.get(src);
- {
- // Calc the distance to the listener
- distanceVector.set(listenerPosition);
- distanceVector.subtractLocal(src.getLocalTranslation());
- distance = FastMath.abs(distanceVector.length());
-
- if (distance < src.getRefDistance()) {
- distance = src.getRefDistance();
- }
- if (distance > src.getMaxDistance()) {
- distance = src.getMaxDistance();
- }
- volume = src.getRefDistance() / distance;
-
- AndroidAudioData audioData = (AndroidAudioData) src
- .getAudioData();
-
- if (FastMath.abs(audioData.getCurrentVolume() - volume) > FastMath.FLT_EPSILON) {
- // Left / Right channel get the same volume by now, only
- // positional
- mp.setVolume(volume, volume);
-
- audioData.setCurrentVolume(volume);
- }
- }
- }
- }
-
- public void setListener(Listener listener) {
- if (audioDisabled) {
- return;
- }
-
- if (this.listener != null) {
- // previous listener no longer associated with current
- // renderer
- this.listener.setRenderer(null);
- }
-
- this.listener = listener;
- this.listener.setRenderer(this);
-
- }
-
- @Override
- public void cleanup() {
- // Cleanup sound pool
- if (soundPool != null) {
- soundPool.release();
- soundPool = null;
- }
-
- // Cleanup media player
- for (AudioNode src : musicPlaying.keySet()) {
- MediaPlayer mp = musicPlaying.get(src);
- {
- mp.stop();
- mp.release();
- src.setStatus(Status.Stopped);
- }
- }
- musicPlaying.clear();
- }
-
- @Override
- public void onCompletion(MediaPlayer mp) {
- mp.seekTo(0);
- mp.stop();
- // XXX: This has bad performance -> maybe change overall structure of
- // mediaplayer in this audiorenderer?
- for (AudioNode src : musicPlaying.keySet()) {
- if (musicPlaying.get(src) == mp) {
- src.setStatus(Status.Stopped);
- break;
- }
- }
- }
-
- /**
- * Plays using the {@link SoundPool} of Android. Due to hard limitation of
- * the SoundPool: After playing more instances of the sound you only have
- * the channel of the last played instance.
- *
- * It is not possible to get information about the state of the soundpool of
- * a specific streamid, so removing is not possilbe -> noone knows when
- * sound finished.
- */
- public void playSourceInstance(AudioNode src) {
- if (audioDisabled) {
- return;
- }
-
- AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
-
- if (!(audioData.getAssetKey() instanceof AudioKey)) {
- throw new IllegalArgumentException("Asset is not a AudioKey");
- }
-
- AudioKey assetKey = (AudioKey) audioData.getAssetKey();
-
- try {
- if (audioData.getId() < 0) { // found something to load
- int soundId = soundPool.load(
- assetManager.openFd(assetKey.getName()), 1);
- audioData.setId(soundId);
- }
-
- int channel = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
-
- if (channel == 0) {
- soundpoolStillLoading.put(audioData.getId(), src);
- } else {
- src.setChannel(channel); // receive a channel at the last
- // playing at least
- }
- } catch (IOException e) {
- logger.log(Level.SEVERE,
- "Failed to load sound " + assetKey.getName(), e);
- audioData.setId(-1);
- }
- }
-
- @Override
- public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
- AudioNode src = soundpoolStillLoading.remove(sampleId);
-
- if (src == null) {
- logger.warning("Something went terribly wrong! onLoadComplete"
- + " had sampleId which was not in the HashMap of loading items");
- return;
- }
-
- AudioData audioData = src.getAudioData();
-
- if (status == 0) // load was successfull
- {
- int channelIndex;
- channelIndex = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
- src.setChannel(channelIndex);
- }
- }
-
- public void playSource(AudioNode src) {
- if (audioDisabled) {
- return;
- }
-
- AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
-
- MediaPlayer mp = musicPlaying.get(src);
- if (mp == null) {
- mp = new MediaPlayer();
- mp.setOnCompletionListener(this);
- mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
- }
-
- try {
- AssetKey> key = audioData.getAssetKey();
-
- AssetFileDescriptor afd = assetManager.openFd(key.getName()); // assetKey.getName()
- mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
- afd.getLength());
- mp.prepare();
- mp.setLooping(src.isLooping());
- mp.start();
- src.setChannel(0);
- src.setStatus(Status.Playing);
- musicPlaying.put(src, mp);
-
- } catch (IllegalStateException e) {
- e.printStackTrace();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public void pauseSource(AudioNode src) {
- if (audioDisabled) {
- return;
- }
-
- MediaPlayer mp = musicPlaying.get(src);
- if (mp != null) {
- mp.pause();
- src.setStatus(Status.Paused);
- } else {
- int channel = src.getChannel();
- if (channel != -1)
- soundPool.pause(channel); // is not very likley to make
- // something useful :)
- }
- }
-
- public void stopSource(AudioNode src) {
- if (audioDisabled) {
- return;
- }
-
- // can be stream or buffer -> so try to get mediaplayer
- // if there is non try to stop soundpool
- MediaPlayer mp = musicPlaying.get(src);
- if (mp != null) {
- mp.stop();
- src.setStatus(Status.Paused);
- } else {
- int channel = src.getChannel();
- if (channel != -1) {
- soundPool.pause(channel); // is not very likley to make
- // something useful :)
- }
- }
-
- }
-
- @Override
- public void deleteAudioData(AudioData ad) {
-
- for (AudioNode src : musicPlaying.keySet()) {
- if (src.getAudioData() == ad) {
- MediaPlayer mp = musicPlaying.remove(src);
- mp.stop();
- mp.release();
- src.setStatus(Status.Stopped);
- src.setChannel(-1);
- ad.setId(-1);
- break;
- }
- }
-
- if (ad.getId() > 0) {
- soundPool.unload(ad.getId());
- ad.setId(-1);
- }
- }
-
- @Override
- public void setEnvironment(Environment env) {
- //not yet supported
- }
-
- @Override
- public void deleteFilter(Filter filter) {
- }
-}
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.audio.android;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.SoundPool;
+import android.util.Log;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.audio.AudioNode.Status;
+import com.jme3.audio.*;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class is the android implementation for {@link AudioRenderer}
+ *
+ * @author larynx
+ * @author plan_rich
+ */
+public class AndroidAudioRenderer implements AudioRenderer,
+ SoundPool.OnLoadCompleteListener, MediaPlayer.OnCompletionListener {
+
+ private static final Logger logger = Logger
+ .getLogger(AndroidAudioRenderer.class.getName());
+ private final static int MAX_NUM_CHANNELS = 16;
+ private final HashMap musicPlaying = new HashMap();
+ private SoundPool soundPool = null;
+
+ private final Vector3f listenerPosition = new Vector3f();
+ // For temp use
+ private final Vector3f distanceVector = new Vector3f();
+ private final Context context;
+ private final AssetManager assetManager;
+ private HashMap soundpoolStillLoading = new HashMap();
+ private Listener listener;
+ private boolean audioDisabled = false;
+
+ private final AudioManager manager;
+
+ public AndroidAudioRenderer(Activity context) {
+ this.context = context;
+ manager = (AudioManager) context
+ .getSystemService(Context.AUDIO_SERVICE);
+ context.setVolumeControlStream(AudioManager.STREAM_MUSIC);
+ assetManager = context.getAssets();
+ }
+
+ @Override
+ public void initialize() {
+ soundPool = new SoundPool(MAX_NUM_CHANNELS, AudioManager.STREAM_MUSIC,
+ 0);
+ soundPool.setOnLoadCompleteListener(this);
+ }
+
+ @Override
+ public void updateSourceParam(AudioNode src, AudioParam param) {
+ // logger.log(Level.INFO, "updateSourceParam " + param);
+
+ if (audioDisabled) {
+ return;
+ }
+
+ if (src.getChannel() < 0) {
+ return;
+ }
+
+ switch (param) {
+ case Position:
+ if (!src.isPositional()) {
+ return;
+ }
+
+ Vector3f pos = src.getWorldTranslation();
+ break;
+ case Velocity:
+ if (!src.isPositional()) {
+ return;
+ }
+
+ Vector3f vel = src.getVelocity();
+ break;
+ case MaxDistance:
+ if (!src.isPositional()) {
+ return;
+ }
+ break;
+ case RefDistance:
+ if (!src.isPositional()) {
+ return;
+ }
+ break;
+ case ReverbFilter:
+ if (!src.isPositional() || !src.isReverbEnabled()) {
+ return;
+ }
+ break;
+ case ReverbEnabled:
+ if (!src.isPositional()) {
+ return;
+ }
+
+ if (src.isReverbEnabled()) {
+ updateSourceParam(src, AudioParam.ReverbFilter);
+ }
+ break;
+ case IsPositional:
+ break;
+ case Direction:
+ if (!src.isDirectional()) {
+ return;
+ }
+
+ Vector3f dir = src.getDirection();
+ break;
+ case InnerAngle:
+ if (!src.isDirectional()) {
+ return;
+ }
+ break;
+ case OuterAngle:
+ if (!src.isDirectional()) {
+ return;
+ }
+ break;
+ case IsDirectional:
+ if (src.isDirectional()) {
+ updateSourceParam(src, AudioParam.Direction);
+ updateSourceParam(src, AudioParam.InnerAngle);
+ updateSourceParam(src, AudioParam.OuterAngle);
+ } else {
+ }
+ break;
+ case DryFilter:
+ if (src.getDryFilter() != null) {
+ Filter f = src.getDryFilter();
+ if (f.isUpdateNeeded()) {
+ // updateFilter(f);
+ }
+ }
+ break;
+ case Looping:
+ if (src.isLooping()) {
+ }
+ break;
+ case Volume:
+
+ soundPool.setVolume(src.getChannel(), src.getVolume(),
+ src.getVolume());
+
+ break;
+ case Pitch:
+
+ break;
+ }
+
+ }
+
+ @Override
+ public void updateListenerParam(Listener listener, ListenerParam param) {
+ // logger.log(Level.INFO, "updateListenerParam " + param);
+ if (audioDisabled) {
+ return;
+ }
+
+ switch (param) {
+ case Position:
+ listenerPosition.set(listener.getLocation());
+
+ break;
+ case Rotation:
+ Vector3f dir = listener.getDirection();
+ Vector3f up = listener.getUp();
+
+ break;
+ case Velocity:
+ Vector3f vel = listener.getVelocity();
+
+ break;
+ case Volume:
+ // alListenerf(AL_GAIN, listener.getVolume());
+ break;
+ }
+
+ }
+
+ @Override
+ public void update(float tpf) {
+ float distance;
+ float volume;
+
+ // Loop over all mediaplayers
+ for (AudioNode src : musicPlaying.keySet()) {
+
+ MediaPlayer mp = musicPlaying.get(src);
+ {
+ // Calc the distance to the listener
+ distanceVector.set(listenerPosition);
+ distanceVector.subtractLocal(src.getLocalTranslation());
+ distance = FastMath.abs(distanceVector.length());
+
+ if (distance < src.getRefDistance()) {
+ distance = src.getRefDistance();
+ }
+ if (distance > src.getMaxDistance()) {
+ distance = src.getMaxDistance();
+ }
+ volume = src.getRefDistance() / distance;
+
+ AndroidAudioData audioData = (AndroidAudioData) src
+ .getAudioData();
+
+ if (FastMath.abs(audioData.getCurrentVolume() - volume) > FastMath.FLT_EPSILON) {
+ // Left / Right channel get the same volume by now, only
+ // positional
+ mp.setVolume(volume, volume);
+
+ audioData.setCurrentVolume(volume);
+ }
+ }
+ }
+ }
+
+ public void setListener(Listener listener) {
+ if (audioDisabled) {
+ return;
+ }
+
+ if (this.listener != null) {
+ // previous listener no longer associated with current
+ // renderer
+ this.listener.setRenderer(null);
+ }
+
+ this.listener = listener;
+ this.listener.setRenderer(this);
+
+ }
+
+ @Override
+ public void cleanup() {
+ // Cleanup sound pool
+ if (soundPool != null) {
+ soundPool.release();
+ soundPool = null;
+ }
+
+ // Cleanup media player
+ for (AudioNode src : musicPlaying.keySet()) {
+ MediaPlayer mp = musicPlaying.get(src);
+ {
+ mp.stop();
+ mp.release();
+ src.setStatus(Status.Stopped);
+ }
+ }
+ musicPlaying.clear();
+ }
+
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ mp.seekTo(0);
+ mp.stop();
+ // XXX: This has bad performance -> maybe change overall structure of
+ // mediaplayer in this audiorenderer?
+ for (AudioNode src : musicPlaying.keySet()) {
+ if (musicPlaying.get(src) == mp) {
+ src.setStatus(Status.Stopped);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Plays using the {@link SoundPool} of Android. Due to hard limitation of
+ * the SoundPool: After playing more instances of the sound you only have
+ * the channel of the last played instance.
+ *
+ * It is not possible to get information about the state of the soundpool of
+ * a specific streamid, so removing is not possilbe -> noone knows when
+ * sound finished.
+ */
+ public void playSourceInstance(AudioNode src) {
+ if (audioDisabled) {
+ return;
+ }
+
+ AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
+
+ if (!(audioData.getAssetKey() instanceof AudioKey)) {
+ throw new IllegalArgumentException("Asset is not a AudioKey");
+ }
+
+ AudioKey assetKey = (AudioKey) audioData.getAssetKey();
+
+ try {
+ if (audioData.getId() < 0) { // found something to load
+ int soundId = soundPool.load(
+ assetManager.openFd(assetKey.getName()), 1);
+ audioData.setId(soundId);
+ }
+
+ int channel = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
+
+ if (channel == 0) {
+ soundpoolStillLoading.put(audioData.getId(), src);
+ } else {
+ src.setChannel(channel); // receive a channel at the last
+ // playing at least
+ }
+ } catch (IOException e) {
+ logger.log(Level.SEVERE,
+ "Failed to load sound " + assetKey.getName(), e);
+ audioData.setId(-1);
+ }
+ }
+
+ @Override
+ public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
+ AudioNode src = soundpoolStillLoading.remove(sampleId);
+
+ if (src == null) {
+ logger.warning("Something went terribly wrong! onLoadComplete"
+ + " had sampleId which was not in the HashMap of loading items");
+ return;
+ }
+
+ AudioData audioData = src.getAudioData();
+
+ if (status == 0) // load was successfull
+ {
+ int channelIndex;
+ channelIndex = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
+ src.setChannel(channelIndex);
+ }
+ }
+
+ public void playSource(AudioNode src) {
+ if (audioDisabled) {
+ return;
+ }
+
+ AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
+
+ MediaPlayer mp = musicPlaying.get(src);
+ if (mp == null) {
+ mp = new MediaPlayer();
+ mp.setOnCompletionListener(this);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ }
+
+ try {
+ AssetKey> key = audioData.getAssetKey();
+
+ AssetFileDescriptor afd = assetManager.openFd(key.getName()); // assetKey.getName()
+ mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
+ afd.getLength());
+ mp.prepare();
+ mp.setLooping(src.isLooping());
+ mp.start();
+ src.setChannel(0);
+ src.setStatus(Status.Playing);
+ musicPlaying.put(src, mp);
+
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Pause the current playing sounds. Both from the {@link SoundPool} and the
+ * active {@link MediaPlayer}s
+ */
+ public void pauseAll() {
+ soundPool.autoPause();
+ for (MediaPlayer mp : musicPlaying.values()) {
+ mp.pause();
+ }
+ }
+
+ /**
+ * Resume all paused sounds.
+ */
+ public void resumeAll() {
+ soundPool.autoResume();
+ for (MediaPlayer mp : musicPlaying.values()) {
+ mp.start(); //no resume -> api says call start to resume
+ }
+ }
+
+ public void pauseSource(AudioNode src) {
+ if (audioDisabled) {
+ return;
+ }
+
+ MediaPlayer mp = musicPlaying.get(src);
+ if (mp != null) {
+ mp.pause();
+ src.setStatus(Status.Paused);
+ } else {
+ int channel = src.getChannel();
+ if (channel != -1)
+ soundPool.pause(channel); // is not very likley to make
+ // something useful :)
+ }
+ }
+
+ public void stopSource(AudioNode src) {
+ if (audioDisabled) {
+ return;
+ }
+
+ // can be stream or buffer -> so try to get mediaplayer
+ // if there is non try to stop soundpool
+ MediaPlayer mp = musicPlaying.get(src);
+ if (mp != null) {
+ mp.stop();
+ src.setStatus(Status.Paused);
+ } else {
+ int channel = src.getChannel();
+ if (channel != -1) {
+ soundPool.pause(channel); // is not very likley to make
+ // something useful :)
+ }
+ }
+
+ }
+
+ @Override
+ public void deleteAudioData(AudioData ad) {
+
+ for (AudioNode src : musicPlaying.keySet()) {
+ if (src.getAudioData() == ad) {
+ MediaPlayer mp = musicPlaying.remove(src);
+ mp.stop();
+ mp.release();
+ src.setStatus(Status.Stopped);
+ src.setChannel(-1);
+ ad.setId(-1);
+ break;
+ }
+ }
+
+ if (ad.getId() > 0) {
+ soundPool.unload(ad.getId());
+ ad.setId(-1);
+ }
+ }
+
+ @Override
+ public void setEnvironment(Environment env) {
+ // not yet supported
+ }
+
+ @Override
+ public void deleteFilter(Filter filter) {
+ }
+}
diff --git a/engine/src/android/jme3test/android/SimpleSoundTest.java b/engine/src/android/jme3test/android/SimpleSoundTest.java
new file mode 100644
index 000000000..9503bca0e
--- /dev/null
+++ b/engine/src/android/jme3test/android/SimpleSoundTest.java
@@ -0,0 +1,40 @@
+package jme3test.android;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.audio.AudioNode;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.InputListener;
+import com.jme3.input.controls.MouseButtonTrigger;
+import com.jme3.math.Vector3f;
+
+public class SimpleSoundTest extends SimpleApplication implements InputListener {
+
+ private AudioNode gun;
+ private AudioNode nature;
+
+ @Override
+ public void simpleInitApp() {
+ gun = new AudioNode(assetManager, "Sound/Effects/Gun.wav");
+ gun.setPositional(true);
+ gun.setLocalTranslation(new Vector3f(0, 0, 0));
+ gun.setMaxDistance(100);
+ gun.setRefDistance(5);
+
+ nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", true);
+ nature.setVolume(3);
+ nature.setLooping(true);
+ nature.play();
+
+ inputManager.addMapping("click", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+ inputManager.addListener(this, "click");
+
+ rootNode.attachChild(gun);
+ rootNode.attachChild(nature);
+ }
+
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (name.equals("click") && isPressed) {
+ gun.playInstance();
+ }
+ }
+}
diff --git a/engine/src/android/jme3tools/android/Fixed.java b/engine/src/android/jme3tools/android/Fixed.java
index c13563a9b..4420999e9 100644
--- a/engine/src/android/jme3tools/android/Fixed.java
+++ b/engine/src/android/jme3tools/android/Fixed.java
@@ -13,7 +13,11 @@ import java.util.Random;
*
* @version 1.0
* @author CW
+ *
+ * @deprecated Most devices with OpenGL ES 2.0 have an FPU. Please use
+ * floats instead of this class for decimal math.
*/
+@Deprecated
public final class Fixed {
/**