Android Main thread cant handle output and working to hard - android

I'm trying to draw some shapes but when i load the app there is just a black screen. I've checked the logcat and it says that the output is to much and that the main thread may be working to hard. how do i deal with this. whenn my app first runs the logcat prints what i want but after a while it just prints called unimplemented OpenGL ES API over and over again. How to stop main thread from working so hard.
#Override
public void onDrawFrame(final GL10 gl) {
// Clears the screen and depth buffer.
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();//replace the current matrix with the identity matrix
main.runOnUiThread(new Runnable() {
public void run() {
Random r = new Random();
if(System.currentTimeMillis() >= (now+delay)){
if(starAdded < starTotal) {
if ((r.nextInt() % 2) == 0) {
star[starAdded] = new Star();
starAdded++;
Log.i("MyGLRenderer", "3 star added");
}
else {
star[starAdded] = new fourStar();
starAdded++;
Log.i("MyGLRenderer", "4 star added");
}
now = System.currentTimeMillis();
}
}
for(int a = 0; a < starAdded; a++) {
star[a].draw(gl);
}}});
}

Use a game thread.. e.g
public class GameThread extends Thread{
private static int MAX_FRAME_SKIPS;
private static int FRAME_PERIOD;
private final Game game;
private final GameViewRenderer gameView;
private boolean gameRunning = true;
private boolean running = false;
Handler handler = new Handler();
public GameThread(Game game, GameViewRenderer gameView, int maxFPS, int maxFrameSkips) {
this.game = game;
this.gameView = gameView;
MAX_FRAME_SKIPS = maxFrameSkips;
FRAME_PERIOD = 1000 / maxFPS;
}
#Override
public void run() {
long beginTime;
long timeDiff;
int sleepTime;
int framesSkipped;
beginTime = System.currentTimeMillis();
framesSkipped = 0;
gameRunning = game.isRunning();
game.updateState(); // Update game
gameView.postInvalidate(); // Render the game
timeDiff = System.currentTimeMillis() - beginTime; // Calculate cycle length
sleepTime = (int)(FRAME_PERIOD - timeDiff); // Calculate time available to sleep
// Checks if got time to sleep, either sleeps thread or catches up
if (sleepTime > 0) {
if(gameRunning && running){
handler.postDelayed(this, sleepTime);
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
game.updateState(); // Update without rendering
sleepTime += FRAME_PERIOD; // Add frame period to check if in next frame
framesSkipped++;
if(gameRunning && running) {
this.run(); // No time to wait! RUN! CATCH UP!
}
}
}
}
public void setRunning(boolean running) {
this.running = running;
}
}
Or decrease the fps

Related

How to increase FPS in Android Game?

I am making an Android Game in which i am only getting in and around 20 FPS. This is the Thread Class i am using:
public class MainThread extends Thread
{
private static final String TAG = MainThread.class.getSimpleName();
// desired fps
private final static int MAX_FPS = 50;
// maximum number of frames to be skipped
private final static int MAX_FRAME_SKIPS = 5;
// the frame period
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
// Stuff for stats */
private DecimalFormat df = new DecimalFormat("0.##"); // 2 dp
// we'll be reading the stats every second
private final static int STAT_INTERVAL = 1000; //ms
// the average will be calculated by storing
// the last n FPSs
private final static int FPS_HISTORY_NR = 10;
// last time the status was stored
private long lastStatusStore = 0;
// the status time counter
private long statusIntervalTimer = 0l;
// number of frames skipped since the game started
private long totalFramesSkipped = 0l;
// number of frames skipped in a store cycle (1 sec)
private long framesSkippedPerStatCycle = 0l;
// number of rendered frames in an interval
private int frameCountPerStatCycle = 0;
private long totalFrameCount = 0l;
// the last FPS values
private double fpsStore[];
// the number of times the stat has been read
private long statsCount = 0;
// the average FPS since the game started
private double averageFps = 0.0;
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private PlayView playView;
// flag to hold game state
private boolean running;
public void setRunning(boolean running)
{
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder, PlayView playView)
{
super();
this.surfaceHolder = surfaceHolder;
this.playView = playView;
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
// initialise timing elements for stat gathering
initTimingElements();
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder)
{
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// update game state
this.playView.update();
// render state to the screen
// draws the canvas on the panel
this.playView.render(canvas);
// calculate how long did the cycle take
timeDiff = System.currentTimeMillis() - beginTime;
// calculate sleep time
sleepTime = (int)(FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS)
{
// we need to catch up
this.playView.update(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check if in next frame
framesSkipped++;
}
if (framesSkipped > 0)
{
Log.d(TAG, "Skipped:" + framesSkipped);
}
// for statistics
framesSkippedPerStatCycle += framesSkipped;
// calling the routine to store the gathered statistics
storeStats();
}
}
finally
{
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null)
{
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
private void storeStats()
{
frameCountPerStatCycle++;
totalFrameCount++;
// check the actual time
statusIntervalTimer += (System.currentTimeMillis() - statusIntervalTimer);
if (statusIntervalTimer >= lastStatusStore + STAT_INTERVAL) {
// calculate the actual frames pers status check interval
double actualFps = (double)(frameCountPerStatCycle / (STAT_INTERVAL / 1000));
//stores the latest fps in the array
fpsStore[(int) statsCount % FPS_HISTORY_NR] = actualFps;
// increase the number of times statistics was calculated
statsCount++;
double totalFps = 0.0;
// sum up the stored fps values
for (int i = 0; i < FPS_HISTORY_NR; i++) {
totalFps += fpsStore[i];
}
// obtain the average
if (statsCount < FPS_HISTORY_NR) {
// in case of the first 10 triggers
averageFps = totalFps / statsCount;
} else {
averageFps = totalFps / FPS_HISTORY_NR;
}
// saving the number of total frames skipped
totalFramesSkipped += framesSkippedPerStatCycle;
// resetting the counters after a status record (1 sec)
framesSkippedPerStatCycle = 0;
statusIntervalTimer = 0;
frameCountPerStatCycle = 0;
statusIntervalTimer = System.currentTimeMillis();
lastStatusStore = statusIntervalTimer;
// Log.d(TAG, "Average FPS:" + df.format(averageFps));
playView.setAvgFps("FPS: " + df.format(averageFps));
}
}
private void initTimingElements()
{
// initialise timing elements
fpsStore = new double[FPS_HISTORY_NR];
for (int i = 0; i < FPS_HISTORY_NR; i++) {
fpsStore[i] = 0.0;
}
Log.d(TAG + ".initTimingElements()", "Timing elements for stats initialised");
}
}
Despite my best efforts to improve the FPS, it is not improving. All suggestion for optimizing this code are welcome.

onDrawFrame, requestRender, and render thread? | OpenGL ES 2.0

i have a problem running my app using an external Thread for game loop in OpenGL ES 2.0.
here is my onCreate in my Starting class:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new MyGLView(this);
System.out.println(running);
setContentView(mGLView);
loop = new GameLoop();
//loop.run(); <- this is supposed to run the loop but when its called,
// nothing really appears on screen. just title of the app.
}
my GameLoop Class
private final static int maxFPS = 30;
private final static int maxFrameSkips = 5;
private final static int framePeriod = 1000 / maxFPS;
public static boolean running;
public final static String TAG = "test";
public GameLoop() {
}
#Override
public void run() {
running = StartPoint.isRunning();
long beginTime;
long timeDiff;
int sleepTime;
int framesSkipped;
sleepTime = 0;
while (running) {
running = StartPoint.isRunning();
beginTime = System.currentTimeMillis();
framesSkipped = 0;
StartPoint.mGLView.requestRender(); // <- supposed to re-render?
timeDiff = System.currentTimeMillis() - beginTime;
sleepTime = (int) (framePeriod - timeDiff);
if (sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime < 0 && framesSkipped < maxFrameSkips) {
sleepTime += framePeriod;
framesSkipped++;
Log.d(TAG, "Frames Skipped");
}
}
}
and finally my renderer class:
private static final String TAG = "Renderer";
BoundsSquare square;
private final float[] mMVPMatrix = new float[16];
private final float[] mProjMatrix = new float[16];
private final float[] mVMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
private int camWidth,camHeight;
Camera2D cam;
Vector3 vec;
public long cycle = 0; // used this to determine how many cycles
//went through, it is stuck on cycle 0, nothing else happens after first render
#Override
public void onDrawFrame(GL10 nope) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
//set camera position
cam.setFrustum(mProjMatrix, mVMatrix, mMVPMatrix);
square.draw(mMVPMatrix);
//square.animate(tick);
//square.setBounds(vec);
System.out.println("Cycle " + cycle + " Ended");
cycle++;
}
#Override
public void onSurfaceChanged(GL10 nope, int width, int height) {
cam.setRatio(width, height);
GLES20.glViewport(0, 0, width, height);
}
public static int loadShader(int type, String shaderCode) {
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
#Override
public void onSurfaceCreated(GL10 gl,
javax.microedition.khronos.egl.EGLConfig config) {
GLES20.glClearColor(0.0f, 0.0f, 1.0f, 0.5f);
camWidth=480;camHeight=320;
cam= new Camera2D(0, 0, camHeight, camWidth);
// initialize a square
vec = new Vector3 (10,10,40,90);
square = new BoundsSquare(vec);
System.out.println("Surface Created");
}
so basically what happens is that if i do not call loop.run(); i get a static picture, which is basically one cycle of onDrawFrame, after its done with this, nothing else pops up on the logCat.
the next thing that happens is if i do call loop.run(), basically the loop goes through forever and everything, but, nothing appears on the screen. i just see a title screen, not the glView.
What am i doing wrong? Is there another way to update the Screen?
Threads must be started with start() not run().

Android Game loop, Memory and animations

I'm new in Android and i'm developing the Insult Swordfighting of Monkey Island 1, but i'm facing some noob problems :)
This is my GameLoop:
public class MainThread extends Thread {
private static final String TAG = MainThread.class.getSimpleName();
// desired fps
private final static int MAX_FPS = 50;
// maximum number of frames to be skipped
private final static int MAX_FRAME_SKIPS = 5;
// the frame period
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private MainGamePanel gamePanel;
// flag to hold game state
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
super();
this.surfaceHolder = surfaceHolder;
this.gamePanel = gamePanel;
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
// initialise timing elements for stat gathering
initTimingElements();
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// update game state
this.gamePanel.update();
// render state to the screen
// draws the canvas on the panel
this.gamePanel.render(canvas);
// calculate how long did the cycle take
timeDiff = System.currentTimeMillis() - beginTime;
// calculate sleep time
sleepTime = (int)(FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// we need to catch up
this.gamePanel.update(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check if in next frame
framesSkipped++;
}
}
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
First noob question:
Is it legit that the background or fixed images (like a finished animation frame) are rendered constantly? Is it a waste of memory?
I'd like to start with a cheap memory logic before to start with a major use of animations.
Second question:
In my render() function I call
canvas.drawBitmap(background, null,new Rect(0, 0,canvas.getWidth(),canvas.getHeight()), paint);
or
Rect dest = new Rect(this.getXpos(), getYpos(), getXpos() + (rclip.right - rclip.left),
getYpos() + (rclip.bottom - rclip.top));
canvas.drawBitmap(tileSheet, rclip, dest, null);
Is it right to call all that "new"? GC cleans those after using them?

How to make sprites in Android

I'm developing a game which includes sprites.
currently I'm loading a bitmap and using Rectangle to get the right frame from the bitmap.
the problem is loading bitmaps takes too much memory and I wont be able to load several sprites at the same time.
what are my alternatives for doing sprite in Android?
Try this one:
import org.cocos2d.layers.CCLayer;
public class GameLayer extends CCLayer{
CCSprite mSprite;
protected GameLayer() {
super();
CGSize winSize = CCDirector.sharedDirector().winSize();
mSprite = new CCSprite("image.png");
mSprite.setPosition(CGPoint.ccp(mSprite.getContentSize().width/2.0f, mSprite.getContentSize().height/2.0f));
addChild(mSprite);
}
/* It is Sprite class */
import android.graphics.*;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
public class Sprite {
int x;
private int y;
private boolean visibility = true;
private Bitmap sprite;
private int verticalFrame,horizontalFrame;
private int frameWidth,frameHeight;
private int sequence[] = new int[1];
private int maxFrame = 0;
private int currentFrame;
private int color;
private Rect src;
private Rect dest;
private int frameCount;
private Paint p = new Paint();
private int spInitX,spInitY;
private int rotate = 0;
private float currentRotateAngle = 0;
//private Graphics g;
public float velocityY;
public float velocityX;
public int height;
public Sprite(Bitmap sprite){
this.sprite = sprite;
this.frameWidth = sprite.getWidth();
this.frameHeight = sprite.getHeight();
setFrameCount();
}
public Sprite(Bitmap sprite,int frameWidth,int frameHeight){
this.sprite = sprite;
this.frameWidth = frameWidth;
this.frameHeight = frameHeight;
setFrameCount();
}
public void rotate(float angle){
currentRotateAngle = angle;
}
public void setImage(Bitmap bm,int frameWidth,int frameHeight){
this.sprite = bm;
this.frameWidth = frameWidth;
this.frameHeight = frameHeight;
}
public Bitmap getBitmap(){
return sprite;
}
public void paint(Canvas canvas){
dest.offsetTo(getX(), getY());
// g.drawImage(sprite, x, y,src.left,src.top,frameWidth,frameHeight);
canvas.drawBitmap(sprite, src, dest, null);
}
public int getMaxFrame(){
return maxFrame;
}
public int getFrameSequenceLength(){
return sequence.length;
}
public void setFrameSequence(int seq[]){
sequence = seq;
}
public void previousFrame(){
if(sequence.length > 1){
if(frameCount > 0){
setFrame(sequence[frameCount]);
frameCount--;
}else{
frameCount = sequence.length - 1;
setFrame(sequence[frameCount]);
}
}else{
setFrame(frameCount);
if(frameCount > 0){
frameCount++;
}else{
frameCount = maxFrame - 1;
}
}
}
public void setPixel(int x,int y){
spInitX = x;
spInitY = y;
}
public void nextFrame(){
if(sequence.length > 1){
if(frameCount < sequence.length){
setFrame(sequence[frameCount]);
frameCount++;
}else{
frameCount = 0;
setFrame(sequence[frameCount]);
}
}else{
setFrame(frameCount);
if(frameCount < maxFrame){
frameCount++;
}else{
frameCount = 0;
}
}
}
public int getFrame(){
return currentFrame;
}
public void setPosition(int x,int y){
this.x = x;
this.y = y;
}
public void setFrameCount(){
verticalFrame = sprite.getHeight() / frameHeight;
horizontalFrame = sprite.getWidth() / frameWidth;
src = new Rect(0,0,frameWidth,frameHeight);
dest = new Rect(0,0,frameWidth,frameHeight);
maxFrame = verticalFrame * horizontalFrame;
}
public void setFrame(int frame){
if(frame < maxFrame){
currentFrame = frame;
}
int hf = currentFrame % horizontalFrame;
int vf = currentFrame / horizontalFrame;
src.left = hf * frameWidth;
src.right = src.left + frameWidth;
src.top = vf * frameHeight;
src.bottom = src.top + frameHeight;
}
public boolean collidesWith(Sprite sp,boolean cl){
int maxHGap = (getWidth() + sp.getWidth())/2;
int maxVGap = (getHeight() + sp.getHeight())/2;
int x = getX() + getWidth()/2;
int y = getY() + getHeight()/2;
int x1 = sp.getX() + sp.getWidth()/2;
int y1 = sp.getY() + sp.getHeight()/2;
if(Math.abs(x - x1) < maxHGap && Math.abs(y - y1) < maxVGap){
return true;
}
return false;
}
public void setVisible(boolean v){
visibility = v;
}
public final boolean isVisible(){
return visibility;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void move(int moveX,int moveY){
setX(getX()+moveX);
setY(getY()+moveY);
//this.y+=y;
//this.x+=x;
}
public final int getWidth(){
return frameWidth;
}
public final int getHeight(){
return frameHeight;
}
public void setEventY(int i) {
// TODO Auto-generated method stub
}
public int getEventY() {
// TODO Auto-generated method stub
return 0;
}
}
/*Create Main Thread Class Also */
import java.text.DecimalFormat;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
public class MainThread extends Thread {
boolean isPaused;
private static final String TAG = MainThread.class.getSimpleName();
// desired fps
private final static int MAX_FPS = 50;
// maximum number of frames to be skipped
private final static int MAX_FRAME_SKIPS = 5;
// the frame period
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
/* Stuff for stats */
private DecimalFormat df = new DecimalFormat("0.##"); // 2 dp
// we'll be reading the stats every second
private final static int STAT_INTERVAL = 1000; // ms
// the average will be calculated by storing
// the last n FPSs
private final static int FPS_HISTORY_NR = 10;
// last time the status was stored
private long lastStatusStore = 0;
// the status time counter
private long statusIntervalTimer = 0l;
// number of frames skipped since the game started
private long totalFramesSkipped = 0l;
// number of frames skipped in a store cycle (1 sec)
private long framesSkippedPerStatCycle = 0l;
// number of rendered frames in an interval
private int frameCountPerStatCycle = 0;
private long totalFrameCount = 0l;
// the last FPS values
private double fpsStore[];
// the number of times the stat has been read
private long statsCount = 0;
// the average FPS since the game started
private double averageFps = 0.0;
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private GameView gv;
// flag to hold game state
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder, GameView gv) {
super();
this.surfaceHolder = surfaceHolder;
this.gv = gv;
}
public MainThread(Setting setting) {
// TODO Auto-generated constructor stub
}
public void setPause(int i) {
synchronized (gv.getHolder()) {
if (i == 0) {
isPaused = false;
}
if (i == 1) {
isPaused = true;
}
}
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
initTimingElements();
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// update game state
this.gv.update();
// render state to the screen
// draws the canvas on the panel
this.gv.render(canvas);
// calculate how long did the cycle take
timeDiff = System.currentTimeMillis() - beginTime;
// calculate sleep time
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// we need to catch up
this.gv.update(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check
// if in next frame
framesSkipped++;
}
if (framesSkipped > 0) {
Log.d(TAG, "Skipped:" + framesSkipped);
}
// for statistics
framesSkippedPerStatCycle += framesSkipped;
// calling the routine to store the gathered statistics
storeStats();
}
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
private void storeStats() {
frameCountPerStatCycle++;
totalFrameCount++;
// assuming that the sleep works each call to storeStats
// happens at 1000/FPS so we just add it up
// statusIntervalTimer += FRAME_PERIOD;
// check the actual time
statusIntervalTimer += (System.currentTimeMillis() - statusIntervalTimer);
if (statusIntervalTimer >= lastStatusStore + STAT_INTERVAL) {
// calculate the actual frames pers status check interval
double actualFps = (double) (frameCountPerStatCycle / (STAT_INTERVAL / 1000));
// stores the latest fps in the array
fpsStore[(int) statsCount % FPS_HISTORY_NR] = actualFps;
// increase the number of times statistics was calculated
statsCount++;
double totalFps = 0.0;
// sum up the stored fps values
for (int i = 0; i < FPS_HISTORY_NR; i++) {
totalFps += fpsStore[i];
}
// obtain the average
if (statsCount < FPS_HISTORY_NR) {
// in case of the first 10 triggers
averageFps = totalFps / statsCount;
} else {
averageFps = totalFps / FPS_HISTORY_NR;
}
// saving the number of total frames skipped
totalFramesSkipped += framesSkippedPerStatCycle;
// resetting the counters after a status record (1 sec)
framesSkippedPerStatCycle = 0;
statusIntervalTimer = 0;
frameCountPerStatCycle = 0;
statusIntervalTimer = System.currentTimeMillis();
lastStatusStore = statusIntervalTimer;
// Log.d(TAG, "Average FPS:" + df.format(averageFps));
gv.setAvgFps("FPS: " + df.format(averageFps));
}
}
private void initTimingElements() {
// initialise timing elements
fpsStore = new double[FPS_HISTORY_NR];
for (int i = 0; i < FPS_HISTORY_NR; i++) {
fpsStore[i] = 0.0;
}
Log.d(TAG + ".initTimingElements()",
"Timing elements for stats initialised");
}
}

Android: How to create fade-in/fade-out sound effects for any music file that my app plays?

The application that I am working on plays music files. If a timer expires I want the music to fade out. How do I do that. I am using MediaPlayer to play music and music files are present in raw folder of my application.
This is my entire handler class for Android MediaPlayer. Look at the play() and pause() functions. Both contain the ability to either fade or not. The updateVolume() function was the key to let the sound increase/decrease linearly.
package com.stackoverflow.utilities;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.media.MediaPlayer;
import android.net.Uri;
public class MusicHandler {
private MediaPlayer mediaPlayer;
private Context context;
private int iVolume;
private final static int INT_VOLUME_MAX = 100;
private final static int INT_VOLUME_MIN = 0;
private final static float FLOAT_VOLUME_MAX = 1;
private final static float FLOAT_VOLUME_MIN = 0;
public MusicHandler(Context context) {
this.context = context;
}
public void load(String path, boolean looping) {
mediaPlayer = MediaPlayer.create(context, Uri.fromFile(new File(path)));
mediaPlayer.setLooping(looping);
}
public void load(int address, boolean looping) {
mediaPlayer = MediaPlayer.create(context, address);
mediaPlayer.setLooping(looping);
}
public void play(int fadeDuration) {
// Set current volume, depending on fade or not
if (fadeDuration > 0)
iVolume = INT_VOLUME_MIN;
else
iVolume = INT_VOLUME_MAX;
updateVolume(0);
// Play music
if (!mediaPlayer.isPlaying())
mediaPlayer.start();
// Start increasing volume in increments
if (fadeDuration > 0) {
final Timer timer = new Timer(true);
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
updateVolume(1);
if (iVolume == INT_VOLUME_MAX) {
timer.cancel();
timer.purge();
}
}
};
// calculate delay, cannot be zero, set to 1 if zero
int delay = fadeDuration / INT_VOLUME_MAX;
if (delay == 0)
delay = 1;
timer.schedule(timerTask, delay, delay);
}
}
public void pause(int fadeDuration) {
// Set current volume, depending on fade or not
if (fadeDuration > 0)
iVolume = INT_VOLUME_MAX;
else
iVolume = INT_VOLUME_MIN;
updateVolume(0);
// Start increasing volume in increments
if (fadeDuration > 0) {
final Timer timer = new Timer(true);
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
updateVolume(-1);
if (iVolume == INT_VOLUME_MIN) {
// Pause music
if (mediaPlayer.isPlaying())
mediaPlayer.pause();
timer.cancel();
timer.purge();
}
}
};
// calculate delay, cannot be zero, set to 1 if zero
int delay = fadeDuration / INT_VOLUME_MAX;
if (delay == 0)
delay = 1;
timer.schedule(timerTask, delay, delay);
}
}
private void updateVolume(int change) {
// increment or decrement depending on type of fade
iVolume = iVolume + change;
// ensure iVolume within boundaries
if (iVolume < INT_VOLUME_MIN)
iVolume = INT_VOLUME_MIN;
else if (iVolume > INT_VOLUME_MAX)
iVolume = INT_VOLUME_MAX;
// convert to float value
float fVolume = 1 - ((float) Math.log(INT_VOLUME_MAX - iVolume) / (float) Math.log(INT_VOLUME_MAX));
// ensure fVolume within boundaries
if (fVolume < FLOAT_VOLUME_MIN)
fVolume = FLOAT_VOLUME_MIN;
else if (fVolume > FLOAT_VOLUME_MAX)
fVolume = FLOAT_VOLUME_MAX;
mediaPlayer.setVolume(fVolume, fVolume);
}
}
One way to do it is to use MediaPlayer.setVolume(right, left) and have these values decrement after every iteration..here is a rough idea
float volume = 1;
float speed = 0.05f;
public void FadeOut(float deltaTime)
{
mediaPlayer.setVolume(volume, volume);
volume -= speed* deltaTime
}
public void FadeIn(float deltaTime)
{
mediaPlayer.setVolume(volume, volume);
volume += speed* deltaTime
}
The FadeIn() or FadeOut() should be called once this timer of yours has expired. The method doesn't need to take the deltaTime, but it's better as it will lower the volume at the same rate across all devices.
It is a very good class sngreco.
To make it more complete I will add stop() function to stop the player with fade, and stopAndRelease() to stop the player and release the resources securely, very useful to use when you call Activity methods like onStop() or onDestroy().
The two methods:
public void stop(int fadeDuration)
{
try {
// Set current volume, depending on fade or not
if (fadeDuration > 0)
iVolume = INT_VOLUME_MAX;
else
iVolume = INT_VOLUME_MIN;
updateVolume(0);
// Start increasing volume in increments
if (fadeDuration > 0)
{
final Timer timer = new Timer(true);
TimerTask timerTask = new TimerTask()
{
#Override
public void run()
{
updateVolume(-1);
if (iVolume == INT_VOLUME_MIN)
{
// Pause music
mediaPlayer.stop();
timer.cancel();
timer.purge();
}
}
};
// calculate delay, cannot be zero, set to 1 if zero
int delay = fadeDuration / INT_VOLUME_MAX;
if (delay == 0)
delay = 1;
timer.schedule(timerTask, delay, delay);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void stopAndRelease(int fadeDuration) {
try {
final Timer timer = new Timer(true);
TimerTask timerTask = new TimerTask()
{
#Override
public void run()
{
updateVolume(-1);
if (iVolume == INT_VOLUME_MIN)
{
// Stop and Release player after Pause music
mediaPlayer.stop();
mediaPlayer.release();
timer.cancel();
timer.purge();
}
}
};
timer.schedule(timerTask, fadeDuration);
} catch (Exception e) {
e.printStackTrace();
}
}
I have been working on this I hope it helps :D :
private static void crossFade() {
MediaPlayerManager.fadeOut(currentPlayer, 2000);
MediaPlayerManager.fadeIn(auxPlayer, 2000);
currentPlayer = auxPlayer;
auxPlayer = null;
}
public static void fadeOut(final MediaPlayer _player, final int duration) {
final float deviceVolume = getDeviceVolume();
final Handler h = new Handler();
h.postDelayed(new Runnable() {
private float time = duration;
private float volume = 0.0f;
#Override
public void run() {
if (!_player.isPlaying())
_player.start();
// can call h again after work!
time -= 100;
volume = (deviceVolume * time) / duration;
_player.setVolume(volume, volume);
if (time > 0)
h.postDelayed(this, 100);
else {
_player.stop();
_player.release();
}
}
}, 100); // 1 second delay (takes millis)
}
public static void fadeIn(final MediaPlayer _player, final int duration) {
final float deviceVolume = getDeviceVolume();
final Handler h = new Handler();
h.postDelayed(new Runnable() {
private float time = 0.0f;
private float volume = 0.0f;
#Override
public void run() {
if (!_player.isPlaying())
_player.start();
// can call h again after work!
time += 100;
volume = (deviceVolume * time) / duration;
_player.setVolume(volume, volume);
if (time < duration)
h.postDelayed(this, 100);
}
}, 100); // 1 second delay (takes millis)
}
public static float getDeviceVolume() {
int volumeLevel = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
return (float) volumeLevel / maxVolume;
}
Here is my simplified adaptation of stock Android Alarm Clock's fade in implementation.
Rather than defining the number of steps/increments and then increasing the volume step by step (as in other answers to this question), it adjusts volume every 50ms (configurable value) working out steps/increments on the scale between -40dB (near silent) and 0dB (max; relative to the stream volume) based on:
Preset effect duration (can be hard-coded or set by user)
Elapsed time since the playback started
See computeVolume() below for the juicy bits.
Full original code can be found here: Google Source
private MediaPlayer mMediaPlayer;
private long mCrescendoDuration = 0;
private long mCrescendoStopTime = 0;
// Default settings
private static final boolean DEFAULT_CRESCENDO = true;
private static final int CRESCENDO_DURATION = 1;
// Internal message codes
private static final int EVENT_VOLUME = 3;
// Create a message Handler
#SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
...
case EVENT_VOLUME:
if (adjustVolume()) {
scheduleVolumeAdjustment();
}
break;
...
}
}
};
// Obtain user preferences
private void getPrefs() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
...
final boolean crescendo = prefs.getBoolean(SettingsActivity.KEY_CRESCENDO, DEFAULT_CRESCENDO);
if (crescendo) {
// Convert mins to millis
mCrescendoDuration = CRESCENDO_DURATION * 1000 * 60;
} else {
mCrescendoDuration = 0;
}
...
}
// Start the playback
private void play(Alarm alarm) {
...
// Check to see if we are already playing
stop();
// Obtain user preferences
getPrefs();
// Check if crescendo is enabled. If it is, set alarm volume to 0.
if (mCrescendoDuration > 0) {
mMediaPlayer.setVolume(0, 0);
}
mMediaPlayer.setDataSource(this, alarm.alert);
startAlarm(mMediaPlayer);
...
}
// Do the common stuff when starting the alarm.
private void startAlarm(MediaPlayer player) throws java.io.IOException, IllegalArgumentException, IllegalStateException {
final AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
// Do not play alarms if stream volume is 0
// (typically because ringer mode is silent).
if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
player.setAudioStreamType(AudioManager.STREAM_ALARM);
player.setLooping(true);
player.prepare();
player.start();
// Schedule volume adjustment
if (mCrescendoDuration > 0) {
mCrescendoStopTime = System.currentTimeMillis() + mCrescendoDuration;
scheduleVolumeAdjustment();
}
}
}
// Stop the playback
public void stop() {
...
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
mCrescendoDuration = 0;
mCrescendoStopTime = 0;
...
}
// Schedule volume adjustment 50ms in the future.
private void scheduleVolumeAdjustment() {
// Ensure we never have more than one volume adjustment queued.
mHandler.removeMessages(EVENT_VOLUME);
// Queue the next volume adjustment.
mHandler.sendMessageDelayed( mHandler.obtainMessage(EVENT_VOLUME, null), 50);
}
// Adjusts the volume of the ringtone being played to create a crescendo effect.
private boolean adjustVolume() {
// If media player is absent or not playing, ignore volume adjustment.
if (mMediaPlayer == null || !mMediaPlayer.isPlaying()) {
mCrescendoDuration = 0;
mCrescendoStopTime = 0;
return false;
}
// If the crescendo is complete set the volume to the maximum; we're done.
final long currentTime = System.currentTimeMillis();
if (currentTime > mCrescendoStopTime) {
mCrescendoDuration = 0;
mCrescendoStopTime = 0;
mMediaPlayer.setVolume(1, 1);
return false;
}
// The current volume of the crescendo is the percentage of the crescendo completed.
final float volume = computeVolume(currentTime, mCrescendoStopTime, mCrescendoDuration);
mMediaPlayer.setVolume(volume, volume);
// Schedule the next volume bump in the crescendo.
return true;
}
/**
* #param currentTime current time of the device
* #param stopTime time at which the crescendo finishes
* #param duration length of time over which the crescendo occurs
* #return the scalar volume value that produces a linear increase in volume (in decibels)
*/
private static float computeVolume(long currentTime, long stopTime, long duration) {
// Compute the percentage of the crescendo that has completed.
final float elapsedCrescendoTime = stopTime - currentTime;
final float fractionComplete = 1 - (elapsedCrescendoTime / duration);
// Use the fraction to compute a target decibel between -40dB (near silent) and 0dB (max).
final float gain = (fractionComplete * 40) - 40;
// Convert the target gain (in decibels) into the corresponding volume scalar.
final float volume = (float) Math.pow(10f, gain/20f);
//LOGGER.v("Ringtone crescendo %,.2f%% complete (scalar: %f, volume: %f dB)", fractionComplete * 100, volume, gain);
return volume;
}

Categories

Resources