package de.tu.darmstadt.seemoo.ansian.control.threads;
import java.util.concurrent.CopyOnWriteArrayList;
import android.util.Log;
import de.tu.darmstadt.seemoo.ansian.gui.views.MySurfaceView;
import de.tu.darmstadt.seemoo.ansian.model.preferences.MiscPreferences;
import de.tu.darmstadt.seemoo.ansian.model.preferences.Preferences;
/**
* <h1>AnSiAn - SurfaceUpdateThread</h1>
*
* Module: SurfaceUpdateThread.java Description: This thread takes care of
* updating the GUI components
*
* @author Dennis Mantz
* @author Markus Grau
* @author Steffen Kreis
*
* Copyright (C) 2014 Dennis Mantz License:
* http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
public class SurfaceUpdateThread extends Thread {
private int fftSize = 0; // Size of the FFT
private static int frameRate = 50; // Frames per Second
private static double load = 0; // Time_for_processing_and_drawing /
// Time_per_Frame
private boolean dynamicFrameRate = false; // Turns on and off the automatic
// frame rate control
private boolean stopRequested = true; // Will stop the thread when set to
// true
private static CopyOnWriteArrayList<MySurfaceView> views = new CopyOnWriteArrayList<MySurfaceView>();
private boolean running = true;
private int drawCounter = 0;
private static final String LOGTAG = "SurfaceUpdateThread";
private static final int MAX_FRAMERATE = 30; // Upper limit for the
// automatic frame rate
// control
// at every load value below this threshold we increase the frame rate
private static final double LOW_THRESHOLD = 0.65;
// at every load value above this threshold we decrease the frame rate
private static final double HIGH_THRESHOLD = 0.85;
/**
* Constructor. Will initialize the member attributes.
*
* @param view
* reference to the AnalyzerSurface for drawing
* @param fftSize
* Size of the FFT
* @param arrayBlockingQueue
* queue that delivers sample packets
*/
public SurfaceUpdateThread() {
setPriority(MIN_PRIORITY);
MiscPreferences preferences = Preferences.MISC_PREFERENCE;
this.fftSize = preferences.getFFTSize();
frameRate = Preferences.GUI_PREFERENCE.getFramerate();
dynamicFrameRate = Preferences.GUI_PREFERENCE.isDynamicFrameRate();
// Check if fftSize is a power of 2
int order = (int) (Math.log(fftSize) / Math.log(2));
if (fftSize != (1 << order))
throw new IllegalArgumentException("FFT size must be power of 2");
}
public int getFftSize() {
return fftSize;
}
/**
* Will start the processing loop
*/
@Override
public void start() {
this.stopRequested = false;
super.start();
}
/**
* Will set the stopRequested flag so that the processing loop will
* terminate
*/
public void stopLoop() {
this.stopRequested = true;
clearGui();
}
/**
* @return true if loop is running; false if not.
*/
public boolean isRunning() {
return !stopRequested;
}
@Override
public void run() {
Log.i(LOGTAG, "Processing loop started. (Thread: " + this.getName() + ")");
long startTime; // timestamp when signal processing is started
long sleepTime; // time (in ms) to sleep before the next run to meet the
// frame rate
while (!stopRequested) {
if (running) {
// store the current timestamp
startTime = System.currentTimeMillis();
sleepTime = (1000 / frameRate) - (System.currentTimeMillis() - startTime);
drawGui();
try {
if (sleepTime > 0) {
// load = processing_time / frame_duration
load = (System.currentTimeMillis() - startTime) / (1000.0 / frameRate);
// Log.d(LOGTAG, "drawing framerate: " + frameRate + "/
// load: " + load);
// Automatic frame rate control:
if (dynamicFrameRate && load < LOW_THRESHOLD && frameRate < MAX_FRAMERATE)
frameRate++;
if (dynamicFrameRate && load > HIGH_THRESHOLD && frameRate > 1)
frameRate--;
sleep(sleepTime);
} else {
// Automatic frame rate control:
if (dynamicFrameRate && frameRate > 1)
frameRate--;
// Log.d(LOGTAG, "Couldn't meet requested frame rate!");
load = 1;
}
} catch (Exception e) {
Log.e(LOGTAG, "Error while calling sleep()");
}
}
}
this.stopRequested = true;
Log.i(LOGTAG, "Processing loop stopped. (Thread: " + this.getName() + ")");
}
public void drawGui() {
drawCounter++;
for (MySurfaceView view : views) {
if (view.isShown() && drawCounter % view.getDrawDivisor() == 0)
view.draw();
}
}
public void clearGui() {
for (MySurfaceView view : views) {
view.clear();
}
}
public static double getLoad() {
return load;
}
public static int getFrameRate() {
return frameRate;
}
public static void registerView(MySurfaceView mySurfaceView) {
views.add(mySurfaceView);
}
public static void unregisterView(MySurfaceView mySurfaceView) {
views.remove(mySurfaceView);
}
}