* Applied audio fix patch (thanks prich)
- Pause audio when jME3 app is paused - Add WAV to supported formats - Add Android audio test * Minor javadoc fixes in AndroidHarness * Made jme3tools.android.Fixed class deprecated git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9152 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
4721c944a2
commit
b89b2428af
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
* <code>AndroidAssetManager</code> 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;
|
||||
|
||||
/**
|
||||
* <code>AndroidAssetManager</code> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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<AudioNode, MediaPlayer> musicPlaying = new HashMap<AudioNode, MediaPlayer>();
|
||||
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<Integer, AudioNode> soundpoolStillLoading = new HashMap<Integer, AudioNode>();
|
||||
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<AudioNode, MediaPlayer> musicPlaying = new HashMap<AudioNode, MediaPlayer>();
|
||||
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<Integer, AudioNode> soundpoolStillLoading = new HashMap<Integer, AudioNode>();
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
40
engine/src/android/jme3test/android/SimpleSoundTest.java
Normal file
40
engine/src/android/jme3test/android/SimpleSoundTest.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user