v0.7.4b: Initial LibGDX commit! more details below:

Large sections of game logic are now working through libgdx instead of
android libraries. There is still work to do but this is the first
major step. Big changes include:
- Graphics code is now through LibGDX (except for text rendering)
- Initialization and screen-handling logic is now mostly through LibGDX
- Audio is now through LibGDX
- Input handling is now through LibGDX
- Most misc functions are now through LibGDX
This commit is contained in:
Evan Debenham
2019-07-30 16:50:40 -04:00
parent f10be84a10
commit 2a523f2ea2
42 changed files with 828 additions and 972 deletions
@@ -21,8 +21,7 @@
package com.watabou.noosa;
import android.graphics.Bitmap;
import com.badlogic.gdx.graphics.Pixmap;
import com.watabou.gltextures.SmartTexture;
import com.watabou.gltextures.TextureCache;
import com.watabou.glwrap.Matrix;
@@ -264,8 +263,8 @@ public class BitmapText extends Visual {
lineHeight = baseLine = height;
}
protected void splitBy( Bitmap bitmap, int height, int color, String chars ) {
protected void splitBy( Pixmap bitmap, int height, int color, String chars ) {
int length = chars.length();
int width = bitmap.getWidth();
@@ -302,7 +301,7 @@ public class BitmapText extends Visual {
}
found = false;
for (int j=line; j < line + height; j++) {
if (bitmap.getPixel( separator, j ) != color) {
if (colorNotMatch( bitmap, separator, j, color)) {
found = true;
break;
}
@@ -320,7 +319,7 @@ public class BitmapText extends Visual {
}
found = true;
for (int j=line; j < line + height; j++) {
if (bitmap.getPixel( separator, j ) != color) {
if (colorNotMatch( bitmap, separator, j, color)) {
found = false;
break;
}
@@ -335,13 +334,22 @@ public class BitmapText extends Visual {
lineHeight = baseLine = height( frames.get( chars.charAt( 0 ) ) );
}
public static Font colorMarked( Bitmap bmp, int color, String chars ) {
//FIXME
private boolean colorNotMatch(Pixmap pixmap, int x, int y, int color) {
int pixel = pixmap.getPixel(x, y);
if ((pixel & 0xFF) == 0) {
return color != 0;
}
return pixel != color;
}
public static Font colorMarked( Pixmap bmp, int color, String chars ) {
Font font = new Font( TextureCache.get( bmp ) );
font.splitBy( bmp, bmp.getHeight(), color, chars );
return font;
}
public static Font colorMarked( Bitmap bmp, int height, int color, String chars ) {
public static Font colorMarked( Pixmap bmp, int height, int color, String chars ) {
Font font = new Font( TextureCache.get( bmp ) );
font.splitBy( bmp, height, color, chars );
return font;
@@ -21,42 +21,30 @@
package com.watabou.noosa;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.PackageManager.NameNotFoundException;
import android.media.AudioManager;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.Vibrator;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
import com.watabou.glscripts.Script;
import com.watabou.gltextures.TextureCache;
import com.watabou.glwrap.Blending;
import com.watabou.glwrap.ScreenConfigChooser;
import com.watabou.glwrap.Vertexbuffer;
import com.watabou.input.InputHandler;
import com.watabou.input.Keys;
import com.watabou.input.Touchscreen;
import com.watabou.noosa.audio.Music;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.BitmapCache;
import com.watabou.utils.DeviceCompat;
import com.watabou.utils.SystemTime;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class Game extends Activity implements GLSurfaceView.Renderer, View.OnTouchListener {
public class Game extends AndroidApplication implements ApplicationListener {
public static Game instance;
@@ -95,13 +83,9 @@ public class Game extends Activity implements GLSurfaceView.Renderer, View.OnTou
public static float timeTotal = 0f;
protected GLSurfaceView view;
protected SurfaceHolder holder;
//protected SurfaceHolder holder;
// Accumulated touch events
protected ArrayList<MotionEvent> motionEvents = new ArrayList<MotionEvent>();
// Accumulated key events
protected ArrayList<KeyEvent> keysEvents = new ArrayList<KeyEvent>();
protected InputHandler inputHandler;
public Game( Class<? extends Scene> c ) {
super();
@@ -112,17 +96,9 @@ public class Game extends Activity implements GLSurfaceView.Renderer, View.OnTou
protected void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
BitmapCache.context = TextureCache.context = instance = this;
DisplayMetrics m = new DisplayMetrics();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
getWindowManager().getDefaultDisplay().getRealMetrics( m );
else
getWindowManager().getDefaultDisplay().getMetrics( m );
density = m.density;
dispHeight = m.heightPixels;
dispWidth = m.widthPixels;
instance = this;
//FIXME this should be moved into a separate class, once we start to move to multiplatform
try {
version = getPackageManager().getPackageInfo( getPackageName(), 0 ).versionName;
} catch (NameNotFoundException e) {
@@ -134,20 +110,33 @@ public class Game extends Activity implements GLSurfaceView.Renderer, View.OnTou
versionCode = 0;
}
setVolumeControlStream( AudioManager.STREAM_MUSIC );
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
config.depth = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
//use rgb888 on more modern devices for better visuals
config.r = config.g = config.b = 8;
} else {
//and rgb565 (default) on older ones for better performance
}
view = new GLSurfaceView( this );
view.setEGLContextClientVersion( 2 );
//Older devices are forced to RGB 565 for performance reasons.
//Otherwise try to use RGB888 for best quality, but use RGB565 if it is what's available.
view.setEGLConfigChooser( new ScreenConfigChooser(
DeviceCompat.legacyDevice(),
false ));
view.setRenderer( this );
view.setOnTouchListener( this );
setContentView( view );
config.useCompass = false;
config.useAccelerometer = false;
//TODO consider the following additional options, might be better than setting manually
//config.hideStatusBar
//config.useImmersiveMode
initialize(this, config);
//FIXME shouldn't have a reference to the view here, remove things which access this
view = (GLSurfaceView)graphics.getView();
inputHandler = new InputHandler();
Gdx.input.setInputProcessor(inputHandler);
Gdx.input.setCatchKey(Keys.BACK, true);
Gdx.input.setCatchKey(Keys.MENU, true);
//FIXME this doesn't seem to work quite right. That might not be due to LibGDX though.
Music.setMuteListener();
//so first call to onstart/onresume calls correct logic.
paused = true;
@@ -155,138 +144,46 @@ public class Game extends Activity implements GLSurfaceView.Renderer, View.OnTou
private boolean paused;
//Starting with honeycomb, android's lifecycle management changes slightly
@Override
public void onStart() {
super.onStart();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
resumeGame();
}
}
@Override
protected void onResume() {
super.onResume();
if (scene != null) {
scene.onResume();
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
resumeGame();
}
}
@Override
protected void onPause() {
super.onPause();
if (scene != null) {
scene.onPause();
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
pauseGame();
}
}
@Override
public void onStop() {
super.onStop();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
pauseGame();
}
}
public void pauseGame(){
if (paused) return;
paused = true;
view.onPause();
Script.reset();
Music.INSTANCE.pause();
Sample.INSTANCE.pause();
}
public void resumeGame(){
if (!paused) return;
now = 0;
paused = false;
view.onResume();
Music.INSTANCE.resume();
Sample.INSTANCE.resume();
}
public boolean isPaused(){
return paused;
}
@Override
public void onDestroy() {
super.onDestroy();
destroyGame();
public void create() {
density = Gdx.graphics.getDensity();
dispHeight = Gdx.graphics.getDisplayMode().height;
dispWidth = Gdx.graphics.getDisplayMode().width;
Music.INSTANCE.mute();
Sample.INSTANCE.reset();
}
@SuppressLint({ "Recycle", "ClickableViewAccessibility" })
@Override
public boolean onTouch( View view, MotionEvent event ) {
synchronized (motionEvents) {
motionEvents.add( MotionEvent.obtain( event ) );
}
return true;
Blending.useDefault();
//refreshes texture and vertex data stored on the gpu
TextureCache.reload();
RenderedText.reloadCache();
Vertexbuffer.refreshAllBuffers();
}
@Override
public boolean onKeyDown( int keyCode, KeyEvent event ) {
public void resize(int width, int height) {
Gdx.gl.glViewport(0, 0, width, height);
if (keyCode != Keys.BACK &&
keyCode != Keys.MENU) {
return false;
if (height != Game.height || width != Game.width) {
Game.width = width;
Game.height = height;
resetScene();
}
synchronized (motionEvents) {
keysEvents.add( event );
}
return true;
}
@Override
public boolean onKeyUp( int keyCode, KeyEvent event ) {
if (keyCode != Keys.BACK &&
keyCode != Keys.MENU) {
return false;
}
synchronized (motionEvents) {
keysEvents.add( event );
}
return true;
}
@Override
public void onDrawFrame( GL10 gl ) {
if (width == 0 || height == 0) {
return;
}
public void render() {
NoosaScript.get().resetCamera();
NoosaScriptNoLighting.get().resetCamera();
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
Gdx.gl.glDisable(Gdx.gl.GL_SCISSOR_TEST);
Gdx.gl.glClear(Gdx.gl.GL_COLOR_BUFFER_BIT);
draw();
GLES20.glFlush();
Gdx.gl.glFlush();
SystemTime.tick();
long rightNow = SystemClock.elapsedRealtime();
@@ -295,29 +192,39 @@ public class Game extends Activity implements GLSurfaceView.Renderer, View.OnTou
step();
}
@Override
public void onSurfaceChanged( GL10 gl, int width, int height ) {
GLES20.glViewport(0, 0, width, height);
if (height != Game.height || width != Game.width) {
Game.width = width;
Game.height = height;
resetScene();
public void pause() {
paused = true;
if (scene != null) {
scene.onPause();
}
//view.onPause();
Script.reset();
//Music.INSTANCE.pause();
//Sample.INSTANCE.pause();
}
@Override
public void onSurfaceCreated( GL10 gl, EGLConfig config ) {
Blending.useDefault();
//refreshes texture and vertex data stored on the gpu
TextureCache.reload();
RenderedText.reloadCache();
Vertexbuffer.refreshAllBuffers();
public void resume() {
paused = false;
now = 0;
//view.onResume();
//Music.INSTANCE.resume();
//Sample.INSTANCE.resume();
}
@Override
public void dispose() {
destroyGame();
//Music.INSTANCE.mute();
//Sample.INSTANCE.reset();
}
protected void destroyGame() {
@@ -392,14 +299,7 @@ public class Game extends Activity implements GLSurfaceView.Renderer, View.OnTou
Game.elapsed = Game.timeScale * step * 0.001f;
Game.timeTotal += Game.elapsed;
synchronized (motionEvents) {
Touchscreen.processTouchEvents( motionEvents );
motionEvents.clear();
}
synchronized (keysEvents) {
Keys.processTouchEvents( keysEvents );
keysEvents.clear();
}
inputHandler.processAllEvents();
scene.update();
Camera.updateAll();
@@ -410,11 +310,15 @@ public class Game extends Activity implements GLSurfaceView.Renderer, View.OnTou
}
protected void logException( Throwable tr ){
Log.e("GAME", Log.getStackTraceString(tr));
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
tr.printStackTrace(pw);
pw.flush();
Gdx.app.error("GAME", sw.toString());
}
public static void vibrate( int milliseconds ) {
((Vibrator)instance.getSystemService( VIBRATOR_SERVICE )).vibrate( milliseconds );
Gdx.input.vibrate(milliseconds);
}
public interface SceneChangeCallback{
@@ -21,10 +21,7 @@
package com.watabou.noosa;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import com.badlogic.gdx.graphics.Pixmap;
import com.watabou.gltextures.SmartTexture;
import com.watabou.gltextures.TextureCache;
@@ -41,14 +38,12 @@ public class Halo extends Image {
super();
if (!TextureCache.contains( CACHE_KEY )) {
Bitmap bmp = Bitmap.createBitmap( RADIUS * 2, RADIUS * 2, Bitmap.Config.ARGB_8888 );
Canvas canvas = new Canvas( bmp );
Paint paint = new Paint();
paint.setColor( 0x0AFFFFFF );
Pixmap pixmap = new Pixmap(RADIUS * 2, RADIUS * 2, Pixmap.Format.RGBA8888);
pixmap.setColor( 0xFFFFFF0A );
for (int i = 0; i < 50; i++) {
canvas.drawCircle(RADIUS, RADIUS, RADIUS * (i+1)/50f, paint);
pixmap.fillCircle(RADIUS, RADIUS, (int)(RADIUS * (i+1)/50f));
}
TextureCache.add( CACHE_KEY, new SmartTexture( bmp ) );
TextureCache.add( CACHE_KEY, new SmartTexture( pixmap ) );
}
texture( CACHE_KEY );
@@ -21,8 +21,7 @@
package com.watabou.noosa;
import android.opengl.GLES20;
import com.badlogic.gdx.Gdx;
import com.watabou.glscripts.Script;
import com.watabou.glwrap.Attribute;
import com.watabou.glwrap.Quad;
@@ -81,7 +80,7 @@ public class NoosaScript extends Script {
aUV.vertexPointer( 2, 4, vertices );
Quad.releaseIndices();
GLES20.glDrawElements( GLES20.GL_TRIANGLES, size, GLES20.GL_UNSIGNED_SHORT, indices );
Gdx.gl20.glDrawElements( Gdx.gl20.GL_TRIANGLES, size, Gdx.gl20.GL_UNSIGNED_SHORT, indices );
Quad.bindIndices();
}
@@ -92,8 +91,8 @@ public class NoosaScript extends Script {
vertices.position( 2 );
aUV.vertexPointer( 2, 4, vertices );
GLES20.glDrawElements( GLES20.GL_TRIANGLES, Quad.SIZE, GLES20.GL_UNSIGNED_SHORT, 0 );
Gdx.gl20.glDrawElements( Gdx.gl20.GL_TRIANGLES, Quad.SIZE, Gdx.gl20.GL_UNSIGNED_SHORT, 0 );
}
public void drawQuad( Vertexbuffer buffer ) {
@@ -106,8 +105,8 @@ public class NoosaScript extends Script {
aUV.vertexBuffer( 2, 4, 2 );
buffer.release();
GLES20.glDrawElements( GLES20.GL_TRIANGLES, Quad.SIZE, GLES20.GL_UNSIGNED_SHORT, 0 );
Gdx.gl20.glDrawElements( Gdx.gl20.GL_TRIANGLES, Quad.SIZE, Gdx.gl20.GL_UNSIGNED_SHORT, 0 );
}
public void drawQuadSet( FloatBuffer vertices, int size ) {
@@ -121,8 +120,8 @@ public class NoosaScript extends Script {
vertices.position( 2 );
aUV.vertexPointer( 2, 4, vertices );
GLES20.glDrawElements( GLES20.GL_TRIANGLES, Quad.SIZE * size, GLES20.GL_UNSIGNED_SHORT, 0 );
Gdx.gl20.glDrawElements( Gdx.gl20.GL_TRIANGLES, Quad.SIZE * size, Gdx.gl20.GL_UNSIGNED_SHORT, 0 );
}
public void drawQuadSet( Vertexbuffer buffer, int length, int offset ){
@@ -139,8 +138,8 @@ public class NoosaScript extends Script {
aUV.vertexBuffer( 2, 4, 2 );
buffer.release();
GLES20.glDrawElements( GLES20.GL_TRIANGLES, Quad.SIZE * length, GLES20.GL_UNSIGNED_SHORT, Quad.SIZE * Short.SIZE/8 * offset );
Gdx.gl20.glDrawElements( Gdx.gl20.GL_TRIANGLES, Quad.SIZE * length, Gdx.gl20.GL_UNSIGNED_SHORT, Quad.SIZE * Short.SIZE/8 * offset );
}
public void lighting( float rm, float gm, float bm, float am, float ra, float ga, float ba, float aa ) {
@@ -161,14 +160,14 @@ public class NoosaScript extends Script {
uCamera.valueM4( camera.matrix );
if (!camera.fullScreen) {
GLES20.glEnable( GLES20.GL_SCISSOR_TEST );
GLES20.glScissor(
Gdx.gl20.glEnable( Gdx.gl20.GL_SCISSOR_TEST );
Gdx.gl20.glScissor(
camera.x,
Game.height - camera.screenHeight - camera.y,
camera.screenWidth,
camera.screenHeight);
} else {
GLES20.glDisable( GLES20.GL_SCISSOR_TEST );
Gdx.gl20.glDisable( Gdx.gl20.GL_SCISSOR_TEST );
}
}
}
@@ -184,23 +183,34 @@ public class NoosaScript extends Script {
private static final String SHADER =
"uniform mat4 uCamera;" +
"uniform mat4 uModel;" +
"attribute vec4 aXYZW;" +
"attribute vec2 aUV;" +
"varying vec2 vUV;" +
"void main() {" +
" gl_Position = uCamera * uModel * aXYZW;" +
" vUV = aUV;" +
"}" +
//vertex shader
"uniform mat4 uCamera;\n" +
"uniform mat4 uModel;\n" +
"attribute vec4 aXYZW;\n" +
"attribute vec2 aUV;\n" +
"varying vec2 vUV;\n" +
"void main() {\n" +
" gl_Position = uCamera * uModel * aXYZW;\n" +
" vUV = aUV;\n" +
"}\n" +
//this symbol separates the vertex and fragment shaders (see Script.compile)
"//\n" +
"varying mediump vec2 vUV;" +
"uniform lowp sampler2D uTex;" +
"uniform lowp vec4 uColorM;" +
"uniform lowp vec4 uColorA;" +
"void main() {" +
" gl_FragColor = texture2D( uTex, vUV ) * uColorM + uColorA;" +
"}";
//fragment shader
//preprocessor directives let us define precision on GLES platforms, and ignore it elsewhere
"#ifdef GL_ES\n" +
" #define LOW lowp\n" +
" #define MED mediump\n" +
"#else\n" +
" #define LOW\n" +
" #define MED\n" +
"#endif\n" +
"varying MED vec2 vUV;\n" +
"uniform LOW sampler2D uTex;\n" +
"uniform LOW vec4 uColorM;\n" +
"uniform LOW vec4 uColorA;\n" +
"void main() {\n" +
" gl_FragColor = texture2D( uTex, vUV ) * uColorM + uColorA;\n" +
"}\n";
}
@@ -45,22 +45,33 @@ public class NoosaScriptNoLighting extends NoosaScript {
}
private static final String SHADER =
"uniform mat4 uCamera;" +
"uniform mat4 uModel;" +
"attribute vec4 aXYZW;" +
"attribute vec2 aUV;" +
"varying vec2 vUV;" +
"void main() {" +
" gl_Position = uCamera * uModel * aXYZW;" +
" vUV = aUV;" +
"}" +
"//\n" +
"varying mediump vec2 vUV;" +
"uniform lowp sampler2D uTex;" +
"void main() {" +
" gl_FragColor = texture2D( uTex, vUV );" +
"}";
//vertex shader
"uniform mat4 uCamera;\n" +
"uniform mat4 uModel;\n" +
"attribute vec4 aXYZW;\n" +
"attribute vec2 aUV;\n" +
"varying vec2 vUV;\n" +
"void main() {\n" +
" gl_Position = uCamera * uModel * aXYZW;\n" +
" vUV = aUV;\n" +
"}\n" +
//this symbol separates the vertex and fragment shaders (see Script.compile)
"//\n" +
//fragment shader
//preprocessor directives let us define precision on GLES platforms, and ignore it elsewhere
"#ifdef GL_ES\n" +
" #define LOW lowp\n" +
" #define MED mediump\n" +
"#else\n" +
" #define LOW\n" +
" #define MED\n" +
"#endif\n" +
"varying MED vec2 vUV;\n" +
"uniform LOW sampler2D uTex;\n" +
"void main() {\n" +
" gl_FragColor = texture2D( uTex, vUV );\n" +
"}\n";
}
@@ -26,6 +26,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Typeface;
import com.badlogic.gdx.graphics.Pixmap;
import com.watabou.gltextures.SmartTexture;
import com.watabou.glwrap.Matrix;
import com.watabou.glwrap.Texture;
@@ -169,7 +170,18 @@ public class RenderedText extends Image {
canvas.drawText(r.text, (r.size/10f), r.size, painter);
r.texture = new SmartTexture(bitmap, Texture.NEAREST, Texture.CLAMP, true);
//FIXME really ugly and slow conversion between android bitmap and gdx pixmap
int[] pixels = new int[bitmap.getWidth()*bitmap.getHeight()];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
// Convert from ARGB to RGBA
for (int i = 0; i< pixels.length; i++) {
int pixel = pixels[i];
pixels[i] = (pixel << 8) | ((pixel >> 24) & 0xFF);
}
Pixmap pixmap = new Pixmap(bitmap.getWidth(), bitmap.getHeight(), Pixmap.Format.RGBA8888);
pixmap.getPixels().asIntBuffer().put(pixels);
r.texture = new SmartTexture(pixmap, Texture.NEAREST, Texture.CLAMP, true);
bitmap.recycle();
RectF rect = r.texture.uvRect(0, 0, r.width, r.height);
r.frame(rect);
@@ -22,20 +22,18 @@
package com.watabou.noosa.audio;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Build;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import com.badlogic.gdx.Gdx;
import com.watabou.noosa.Game;
public enum Music {
INSTANCE;
private MediaPlayer player;
private com.badlogic.gdx.audio.Music player;
private String lastPlayed;
private boolean looping;
@@ -58,25 +56,11 @@ public enum Music {
return;
}
try {
AssetFileDescriptor afd = Game.instance.getAssets().openFd( assetName );
MediaPlayer mp = new MediaPlayer();
mp.setAudioStreamType( AudioManager.STREAM_MUSIC );
mp.setDataSource( afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength() );
mp.prepare();
player = mp;
player.start();
player.setLooping(looping);
player.setVolume(volume, volume);
} catch (Exception e) {
Game.reportException(e);
player = null;
}
player = Gdx.audio.newMusic(Gdx.files.internal(assetName));
player.setLooping(looping);
player.setVolume(volume);
player.play();
}
public void mute() {
@@ -92,19 +76,15 @@ public enum Music {
public void resume() {
if (player != null) {
player.start();
player.play();
player.setLooping(looping);
}
}
public void stop() {
if (player != null) {
try {
player.stop();
player.release();
} catch ( Exception e ){
Game.reportException(e);
}
player.stop();
player.dispose();
player = null;
}
}
@@ -112,7 +92,7 @@ public enum Music {
public void volume( float value ) {
volume = value;
if (player != null) {
player.setVolume( value, value );
player.setVolume( value );
}
}
@@ -134,30 +114,29 @@ public enum Music {
return enabled;
}
public static final PhoneStateListener callMute = new PhoneStateListener(){
@Override
public void onCallStateChanged(int state, String incomingNumber)
{
if( state == TelephonyManager.CALL_STATE_RINGING ) {
INSTANCE.pause();
} else if( state == TelephonyManager.CALL_STATE_IDLE ) {
if (!Game.instance.isPaused()) {
INSTANCE.resume();
}
}
super.onCallStateChanged(state, incomingNumber);
}
};
//FIXME android-specific code, that is also broken by being part of this class.
public static void setMuteListener(){
//versions lower than this require READ_PHONE_STATE permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
TelephonyManager mgr =
(TelephonyManager) Game.instance.getSystemService(Activity.TELEPHONY_SERVICE);
mgr.listen(Music.callMute, PhoneStateListener.LISTEN_CALL_STATE);
mgr.listen(new PhoneStateListener(){
@Override
public void onCallStateChanged(int state, String incomingNumber)
{
if( state == TelephonyManager.CALL_STATE_RINGING ) {
INSTANCE.pause();
} else if( state == TelephonyManager.CALL_STATE_IDLE ) {
if (!Game.instance.isPaused()) {
INSTANCE.resume();
}
}
super.onCallStateChanged(state, incomingNumber);
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}
}
}
@@ -21,113 +21,79 @@
package com.watabou.noosa.audio;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.media.AudioManager;
import android.media.SoundPool;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Sound;
import com.watabou.noosa.Game;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
public enum Sample implements SoundPool.OnLoadCompleteListener {
public enum Sample {
INSTANCE;
public static final int MAX_STREAMS = 8;
protected SoundPool pool =
new SoundPool( MAX_STREAMS, AudioManager.STREAM_MUSIC, 0 );
protected HashMap<Object, Integer> ids =
new HashMap<>();
protected HashMap<Object, Sound> ids = new HashMap<>();
private boolean enabled = true;
private float volume = 1f;
private LinkedList<String> loadingQueue = new LinkedList<>();
private float globalVolume = 1f;
public void reset() {
for (Sound sound : ids.values()){
sound.dispose();
}
ids.clear();
loadingQueue = new LinkedList<>();
pool.release();
pool = new SoundPool( MAX_STREAMS, AudioManager.STREAM_MUSIC, 0 );
pool.setOnLoadCompleteListener( this );
}
public void pause() {
if (pool != null) {
pool.autoPause();
for (Sound sound : ids.values()) {
sound.pause();
}
}
public void resume() {
if (pool != null) {
pool.autoResume();
for (Sound sound : ids.values()) {
sound.resume();
}
}
public void load( String... assets ) {
for (String asset : assets) {
loadingQueue.add( asset );
}
loadNext();
}
private void loadNext() {
final String asset = loadingQueue.poll();
if (asset != null) {
if (!ids.containsKey( asset )) {
try {
pool.setOnLoadCompleteListener( new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
loadNext();
}
} );
AssetManager manager = Game.instance.getAssets();
AssetFileDescriptor fd = manager.openFd( asset );
int streamID = pool.load( fd, 1 ) ;
ids.put( asset, streamID );
fd.close();
} catch (IOException e) {
loadNext();
} catch (NullPointerException e) {
// Do nothing (stop loading sounds)
}
} else {
loadNext();
//FIXME there used to be a queue here so that assets were loaded async.
//This was to prevent hanging on specific android versions (implement in vanilla v1.7.5)
//Maybe LibGDX already handles this?
for (String asset : assets){
if (!ids.containsKey(asset)){
ids.put(asset, Gdx.audio.newSound(Gdx.files.internal(asset)));
}
}
}
public void unload( Object src ) {
if (ids.containsKey( src )) {
pool.unload( ids.get( src ) );
ids.get( src ).dispose();
ids.remove( src );
}
}
public int play( Object id ) {
public long play( Object id ) {
return play( id, 1 );
}
public int play( Object id, float volume ) {
public long play( Object id, float volume ) {
return play( id, volume, volume, 1 );
}
public int play( Object id, float leftVolume, float rightVolume, float rate ) {
public long play( Object id, float volume, float pitch ) {
return play( id, volume, volume, pitch );
}
public long play( Object id, float leftVolume, float rightVolume, float pitch ) {
float volume = Math.max(leftVolume, rightVolume);
float pan = rightVolume - leftVolume;
if (enabled && ids.containsKey( id )) {
return pool.play( ids.get( id ), leftVolume*volume, rightVolume*volume, 0, 0, rate );
return ids.get(id).play( globalVolume*volume, pitch, pan );
} else {
return -1;
}
@@ -138,14 +104,11 @@ public enum Sample implements SoundPool.OnLoadCompleteListener {
}
public void volume( float value ) {
this.volume = value;
globalVolume = value;
}
public boolean isEnabled() {
return enabled;
}
@Override
public void onLoadComplete( SoundPool soundPool, int sampleId, int status ) {
}
}