Drawing and audio not working together in Java Android App - java

I am currenty doing a small game. But my application does not work properly. Program does not stop with error, but firstly it playes part of track, and after that (around 5-10 seconds) it stops and drawing begins.
Without audio added, code works absolutely fine.
I also tested app on several different devices and emulators, but issue keeps occuring.
Here are some methods from class "Game.java", where I am trying to implement audio.
public class Game {
Plane background;
Plane playfield;
Context context;
List<SingleNote> notes = new ArrayList<SingleNote>();
List<SingleNote> notes_to_compute = new ArrayList<SingleNote>();
Date date = new Date();
long time_initial;
float[] touch_coord = new float[2];
private int mStreamId;
public Game(Context context){
this.context = context;
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
public void load(){
background = new Plane(context, R.raw.vertex_shader, R.raw.fragment_shader, R.drawable._map1);
playfield = new Plane(context, R.raw.vertex_shader, R.raw.fragment_shader, R.drawable.s_playfield);
InitNotes(R.raw._map1);
time_initial = date.getTime();
MyThread m = new MyThread();
m.context = context;
m.start();
}
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
public void draw(){
background.draw();
playfield.draw();
update();
}
}
class MyThread extends Thread {
Context context;
public void run(){
SoundManagement.playSoundPool(context, R.raw._map1_a);
}
}
And static PlaySondPool method in class
public static void playSoundPool(Context context, int soundID) {
int MAX_STREAMS = 20;
int REPEAT = 0;
SoundPool soundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, REPEAT);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int soundId, int status) {
int priority = 0;
int repeat = 0;
float rate = 1.f; // Frequency Rate can be from .5 to 2.0
// Set volume
AudioManager mgr = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
float streamVolumeCurrent =
mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
float streamVolumeMax =
mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
float volume = streamVolumeCurrent / streamVolumeMax;
// Play it
soundPool.play(soundId, volume, volume, priority, repeat, rate);
}
});
soundPool.load(context, soundID, 1);
}
I thought using separate thread would help, but same issue occurs.
What should I do in order to avoid this error? Maybe use another library, or play sound from other class?
Whole code here: https://github.com/arthur100500/AndroidProject
Additional debug ingormation from android studio
Logcat: https://pastebin.com/MQSaWw8R
Run: https://pastebin.com/5D4AGuHZ
How it looks: https://youtu.be/X7IBquHs1jA

I think there is every likelihood that the choice of SoundPool for playback is the source of the problem you are experiencing. SoundPool is designed for the handling of audio files that are short (a couple seconds) and can be held in memory. You've indicated that you are trying to use it to play a 5-minute long file. According to the documentation in the API, only the first MB of the 5-minute file is being handled.
Soundpool sounds are expected to be short as they are predecoded into
memory. Each decoded sound is internally limited to one megabyte
storage, which represents approximately 5.6 seconds at 44.1kHz stereo
(the duration is proportionally longer at lower sample rates or a
channel mask of mono). A decoded audio sound will be truncated if it
would exceed the per-sound one megabyte storage space.
Streaming via an AudioTrack should be a more fitting alternative for this sound file.
An AudioTrack instance can operate under two modes: static or
streaming. In Streaming mode, the application writes a continuous
stream of data to the AudioTrack, using one of the write() methods.
These are blocking and return when the data has been transferred from
the Java layer to the native layer and queued for playback. The
streaming mode is most useful when playing blocks of audio data that
for instance are:
too big to fit in memory because of the duration of the sound to play,
too big to fit in memory because of the characteristics of the audio data (high sampling rate, bits per sample ...)
received or generated while previously queued audio is playing.

Related

JavaFX Creates Daemon Threads called "Timer-XX"

I just recognized that my program creates a Daemon Thread every time I play a MP3. And they're are Named "Timer-XX" as everybody suggest yes XX is a increasing integer starting with zero.
Ok I can understand that JFX needs them to play a MP3 but actually the MP3 is just 3 sec long and some of them stay Running for a couple of minutes or longer, while others normaly run out (i just suggest it should be like this).
I would like to know why they get created in the first place? Why they stay Running and how I can get ride of them because as far as I know Daemon dose not mean stopped so it still eats Memory and CPU what I can't give away like candy in the current state of my project.
Here is the code that plays the MP3s:
public class EffectPlayer {
private final String MP3_SUFFIX = ".mp3";
private final static int MAX_VOLUME = 100;
private MediaPlayer mediaPlayer;
private int masterVolume = 50;
private int effectVolume = 50;
public EffectPlayer(File f){
if(isMP3(f)){
Media mp3 = new Media(f.toURI().toString());
mediaPlayer = new MediaPlayer(mp3);
effectVolume = Integer.parseInt(GetConfig.getVar("USER_OPTION_VOLUME_EFFECT"));
masterVolume = Integer.parseInt(GetConfig.getVar("USER_OPTION_VOLUME_MASTER"));
if(effectVolume > masterVolume) effectVolume = masterVolume;
mediaPlayer.setVolume(1 - (float)(Math.log(MAX_VOLUME - effectVolume) / Math.log(MAX_VOLUME)));
mediaPlayer.setCycleCount(1);
mediaPlayer.play();
}
}
private boolean isMP3(File f) {
return f.getName().toLowerCase().endsWith(MP3_SUFFIX);
}
}
The JFX Panel gets created in the class that Creates a EffectPlayer Object
#SuppressWarnings("unused")
public static JFXPanel fxPanel = new JFXPanel();

Frequently repeated threads in Android

I'm building a barcode scanner which, different from other implementations, does the scanning part continuously in the background rather than waiting the user to trigger the process.
Now, the most (or what I think is the most) obvious way to achieve this is to process the scanning part in another thread to make sure that the main thread won't be interrupted. So that the user won't be bothered with UI lags, stutters, and whatnot.
I'm not the brightest guy when it comes to concurrency. But I've did my homework and done some research about it which, in turn has lead me to write this:
...
mScannerExecutor = Executors.newFixedThreadPool(3);
...
Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = parameters.getPreviewSize();
final Image barcode = new Image(size.width, size.height, "Y800");
barcode.setData(data);
Runnable scan = new Runnable() {
#Override
public void run() {
int result = mBarcodeScanner.scanImage(barcode);
if (result != 0) {
if(isInPreview) {
isInPreview = false;
mCamera.stopPreview();
}
SymbolSet symbolSet = mBarcodeScanner.getResults();
mListener.onBarcodeScanned(symbolSet.iterator().next());
if (enableRepeatedScanning) {
new Handler().postDelayed(restartPreview, mRescanIntervalMillis);
}
}
}
};
mScannerExecutor.execute(scan);
}
};
But the above code has been causing a lot of error in its execution. I can't even keep the app running for more than a mere couple of seconds. The error message varies from time to time, but this below was shown the most:
Fatal signal 8 (SIGFPE), code -6, fault addr 0x17b8 in tid 6410 (pool-1-thread-1)
I have a strong feeling that this design in general is heavily flawed. Thus the constant crashing.
What can I do to make this right? Did I miss something really important here?
p.s., The previewCallback defined above will be called very frequently; once every 2000ms (2 secs).

android: taking pictures in task or thread at regular interval?

I'm writing an android app which should take pictures in a user-defined interval (20 sec - 1 min). It should take the pictures even while it is running in background or while the device is sleeping. The app will run for a very long time period. If it is necessary to wake up the device, it should put back to sleep as soon as possible to save batterie life. After taking a picture the app will process some additional work (comparison of two pictures).
I read some stuff about sheduling alarms (http://developer.android.com/training/scheduling/alarms.htm), creating Services (also # android training) and Android AsyncTasks, Java threads (http://www.mergeconflict.net/2012/05/java-threads-vs-android-asynctask-which.html)
... but I'm still not sure what is the best way to achieve this.
My questions are:
Should I use thread or a task to take the pictures in background?
(the comparison of the two pictures might take longer than a few
milliseconds but i don't know anything about the cpu load of this
operation)
Should I use an alarm to wake the device up or are there any alternative solutions?
How can both (alarms and thread/task) work together? (Include the Alarm in the Task/Thread?)
Many thanks for your help in advance.
As to our question I know I can help get started with the aspect of repeating the picture taking task at a user defined time interval. For such a task you can user a Timer to achieve this. The code would look something like this:
mTmr = new Timer();
mTsk = new TimerTask() {
#Override
public void run() {
//Take picture or do whatever you want
}
};
mTmr.schedule(mTsk, 0, USER_DEFINED_EXECUTION_INTERVAL);
schedule begins the timer. The first parameter of schedule used here is the task to run which is mTsk. The second parameter is the delay until the first execution (in milliseconds), in this case no delay. The third parameter is what you'll want to manipulate which is the interval of executions. The parameter is the time between executions so if it were 20 seconds you'd pass in 20,000. If it were a minute it would be 60,000. You can get this value from the user using any method you'd like.
To keep the timer running make sure you don't call mTmr.cancel in onPause because for your case you want to keep the timer running while the user isn't on the app. Not calling cancel means the timer will hold it's resources until the app is closed by the user.
OR you can look at this How to schedule a periodic task in Java? If you'd like to use ScheduledExecutorService instead of a Timer.
I have made this app - Lenx. It uses Camera extensively and I am processing image in the background. I have used AsyncTask to process the image and it has never given any problems. The app also has a timer which starts the process after certain interval. The logic that I have used is very simple.
I have not used Camera2 API yet, so the code might be deprecated. I created CameraPreview class which implements Camera.PreivewCallback.
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (data == null) {
return;
}
int expectedBytes = previewWidth * previewHeight *
ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8;
if (expectedBytes != data.length) {
Log.e(TAG, "Mismatched size of buffer! Expected ");
mState = STATE_NO_CALLBACKS;
mCamera.setPreviewCallbackWithBuffer(null);
return;
}
if (mProcessInProgress || mState == STATE_PROCESS_IN_PROGRESS) {
mCamera.addCallbackBuffer(data);
return;
}
if (mState == STATE_PROCESS) {
mProcessInProgress = true;
processDataTask = new ProcessDataTask();
processDataTask.execute(data);
}
}
public void startProcessing() {
mState = STATE_PROCESS;
}
And my AsyncTask is something like this
private class ProcessDataTask
extends
AsyncTask<byte[], Void, Boolean> {
#Override
protected Boolean doInBackground(byte[]... datas) {
mState = STATE_PROCESS_IN_PROGRESS;
Log.i(TAG, "background process started");
byte[] data = datas[0];
long t1 = java.lang.System.currentTimeMillis();
// process your data
long t2 = java.lang.System.currentTimeMillis();
Log.i(TAG, "processing time = " + String.valueOf(t2 - t1));
mCamera.addCallbackBuffer(data);
mProcessInProgress = false;
return true;
}
#Override
protected void onPostExecute(Boolean result) {
mState = STATE_PROCESS_WAIT;
}
}
onPreviewFrame() will always get called as long as the camera preview is running. You need to take the data and process it only when you trigger something. So simply change the state of a variable, in this case, mState, and based on the state, call your AsyncTask.

Android Battery information issue

I'm using the following code to get the voltage of a battery at two different times (t1,t2). t1 is before the execution of a task and t2 is after the execution of a task. So by rule, t2 must be smaller than t1.
However, in execution it is not true. I am getting multiple values, that are greater, smaller and equal to t1. How could this be possible? Even in android battery monitoring tools, I have noticed that sometimes the total battery mAh value increases few points without plugging the charger.
public void onCreate() {
BroadcastReceiver batteryReceiver = new BroadcastReceiver() {
int scale = -1;
int level = -1;
int voltage = -1;
int temp = -1;
#Override
public void onReceive(Context context, Intent intent) {
level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
temp = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1);
voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1);
Log.e("BatteryManager", "level is "+level+"/"+scale+", temp is "+temp+", voltage is "+voltage);
}
};
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(batteryReceiver, filter);
}
Android framework continously get information from its power_supply subsystem. So after an update, it will send out an BATTERY_STATE_CHANGED intent.
private void updateLocked() {
if (!mUpdatesStopped) {
// Update the values of mAcOnline, et. all.
native_update();
// Process the new values. Sendout the intent
processValuesLocked();
}
}
But it just update the information after a period. So it might contains some extent of inaccuracy.
In the implementation of the native_update. Android simply reads out the content of some files under /sys/class/power_supply/battery
setBooleanField(env, obj, gPaths.acOnlinePath, gFieldIds.mAcOnline);
setBooleanField(env, obj, gPaths.usbOnlinePath, gFieldIds.mUsbOnline);
setBooleanField(env, obj, gPaths.wirelessOnlinePath, gFieldIds.mWirelessOnline);
setBooleanField(env, obj, gPaths.batteryPresentPath, gFieldIds.mBatteryPresent);
setIntField(env, obj, gPaths.batteryCapacityPath, gFieldIds.mBatteryLevel);
setVoltageField(env, obj, gPaths.batteryVoltagePath, gFieldIds.mBatteryVoltage);
setIntField(env, obj, gPaths.batteryTemperaturePath, gFieldIds.mBatteryTemperature);
So if you want the voltage information, you can simply:
$cat /sys/class/power_supply/battery/voltage_now
$cat /sys/class/power_supply/battery/batt_vol
Note that voltage_now is in microvolts, not millivolts.
You can also read it programatically if you want.
However, it may still not accurate enough since the content of these data is updated by the opearting system(might be something like a power driver), so for really accurate stats, you may have to try hardware approach.:)
How could this be possible?
The battery mAh value is measured as voltage from the battery. But this voltage varies from time to time. It drops a little when there is a high current draw and goes back when, for example the processor is idle. Also it changes on temperature change. So overall it is very inaccurate.

Playing sounds simultaneously Android

I am creating a game on Android and I have kind of put this problem off for a while, and am now just coming back to it. In my game I have a background beat, gun sounds, explosions... etc. and I need to be able to play them simultaneously. Right now when I call play on the SoundPool class, the currently playing sound gets interrupted and the new one starts to play. My SoundManager class is below as well as the usage. Any help would be much appreciated as this is really my first game where I have needed this many sound effects. Thanks!
public class SoundManager {
private SoundPool mSoundPool;
private HashMap<Integer, Integer> mSoundPoolMap;
private AudioManager mAudioManager;
private Context mContext;
public SoundManager(Context theContext) {
mContext = theContext;
mSoundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
mSoundPoolMap = new HashMap<Integer, Integer>();
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
}
public void addSound(int index, int SoundID) {
mSoundPoolMap.put(index, mSoundPool.load(mContext, SoundID, 1));
}
public void playSound(int index) {
float streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_RING);
streamVolume = streamVolume / mAudioManager.getStreamMaxVolume(AudioManager.STREAM_RING);
mSoundPool.play((Integer) mSoundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f);
}
public void playLoopedSound(int index) {
float streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
streamVolume = streamVolume / mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
mSoundPool.play((Integer) mSoundPoolMap.get(index), streamVolume, streamVolume, 1, -1, 1f);
}
}
... and here is an example of how I use the class.
SoundManager sm = new SoundManager(this);
sm.addSound(0, R.raw.explosion);
sm.playSound(0);
... So with this style I add all my sounds to the SoundPool on load and then based on user input I just want to play the sound. Does this look correct? Or should I try and go about this a different way?
Well I did end up figuring this out in case anyone else wants to know. The problem wasn't that it couldn't play more than one sound at a time it was that it was only able to play 4 sounds at a time which gave me the impression that sounds were stopping and starting. In the constructor this line
mSoundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
needed to be changed to allow more streams to play at the same time. So by changing the first argument from a 4 to say, a 20, you could then play 20 sounds at the same time. The game sounds much better now haha. Hope this helps someone.

Categories

Resources