8

First of all, thank you for this great site, I have found a great deal of information by searching through the questions answered here. I have a problem, though, that I only found one mention of (with no answer).

I am writing an application that uses AudioTrack to loop a runtime-generated sinewave through the headphone jack (to control other electronics). It does this at timed intervals (using Handler.postDelayed), and it works just fine - except if you happen to force close the application. In that case, the sine wave keeps on playing even after the app itself is long gone, and literally the only thing I can do to stop it is to reboot the phone.

The sound stops like it should if the application is closed properly, and even if it crashes.

I have tried:

  • creating an onDestroy() method to stop and release the AudioTrack instance, but unfortunately, it doesn't even get called.
  • using a larger audio buffer for the sound loop (hoping that the small buffer size caused the bug); no difference.
  • starting my app again: now I have two freaking sine waves!
  • uninstalling(!) the app entirely: the sine wave is still there to haunt me from beyond its grave.
  • putting the phone into silent mode: no difference. (I don't want this to happen to my users.)
  • turning the Media Volume all the way down, and waiting 15+ minutes: no difference.
  • changing the sample rate to 11, 22, or 44.1 kHz: no difference.
  • starting and killing the app again and again: I could actually get 8 different immortal sine waves interfering with each other this way. Pretty cool actually. :P

I know it's not 'healthy' to use task killers on Android, but a lot of people still do, and I don't want my users' phones to become unstoppable noise generators in case my application happens to hang and get its arse whooped.

Here is the code that generates the sine wave:

int bufSize = (int)(11025.0 / 60.0); // the number of samples needed for a seamless loop at 60Hz
AT = new AudioTrack(AudioManager.STREAM_MUSIC, 11025, AudioFormat.CHANNEL_CONFIGURATION_MONO,
    AudioFormat.ENCODING_PCM_8BIT, bufSize, AudioTrack.MODE_STATIC);

byte buffer[] = new byte[bufSize];

float angle = 0.0f;
for (int i=0; i < bufSize; i++){
    buffer[i] = (byte)(Math.sin(angle) * 255);
    angle += (float)(2*Math.PI) * 60 / 11025;
}

if (AT.write(buffer, 0, bufSize) != bufSize){
    log("Error: Couldn't write audio buffer!");
} else {
    AT.setLoopPoints(0, bufSize, -1);
    AT.play();
}

Even though I'm pretty sure this bug is in Android itself, I have been desperately searching for a way to detect a force close in order to run one last line of code to stop this from happening before my app dies. I've found no solution, even though I know this is possible somehow (I have an evil alarm app that comes back to life even if you kill it with a task killer). I would be extremely grateful if anyone could enlighten me!

I've also noticed that when my app is running in the background and I return to it (from the "recent apps" menu or through any other way), it seems as if a new instance is created each time. This means you can't stop the other instance playing in the background - except if you use a task killer... I'm sure this must be some trivial beginner's mistake I've made, but I was wondering if it might be related to the Sinewave of Death.

Running Android 2.1.1 on an Xperia X10 mini pro.

nhaarman
  • 98,571
  • 55
  • 246
  • 278
therealdm
  • 91
  • 1
  • 6

1 Answers1

1

Even though I'm pretty sure this bug is in Android itself

It sure seems like it. Any chance I can convince you to post a complete project that demonstrates this problem? Also, what Android OS versions have you tried this on?

I have been desperately searching for a way to detect a force close in order to run one last line of code to stop this from happening before my app dies.

By definition, that's not possible.

I've found no solution, even though I know this is possible somehow

No, it isn't.

(I have an evil alarm app that comes back to life even if you kill it with a task killer).

This does not mean that the "evil alarm app" instance that was force-closed found out about the fact. This means that the "evil alarm app" is using other techniques to find out after the fact that it had died and starts a fresh copy. And, even if you were to try to do this yourself, as you noted, it would not help -- the best you'd get would be your "8 different immortal sine waves" scenario.

I've also noticed that when my app is running in the background

Why would you bother writing a Service to play a sine wave? I can see playing it while your activity is in the foreground, but you should be stopping the sine wave in onPause(), so when you're not in the foreground, the noise stops.

and I return to it (from the "recent apps" menu or through any other way), it seems as if a new instance is created each time

Depending on what "running in the background" means and how you got to the "background", this is normal. Pressing the BACK button will destroy the activity, for example; going back to it by any means will create a new activity instance.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • thank you for the reply! :) because of my Android emulator having very nearly unusable performance, I've only tried this on my actual phone so far, meaning only one Android version. I will create a test project to try and isolate the bug and test it on different versions, and will post it here. By "in the background", I meant what happens when you press the Home button and your app keeps running. I need my app to work like this, because it will be left running for long periods of time. – therealdm Jul 05 '11 at 15:24
  • Also, right now the portion of my app that uses loops ceased to work without _any_ change to the audio code on my part. It plays fine the first time, but the second time it tries to play the sound, nothing happens except the debug log being filled with "buffer out of range" errors. Changing the size of my audio buffer has no effect, it seems like there is nothing I can do. One time after trying too many times, the device kept flooding these errors into the debugger after my application has quit, until it was rebooted. Right now I'm looking into attempting this with SoundPool instead. – therealdm Jul 05 '11 at 15:28
  • @therealdm: "By "in the background", I meant what happens when you press the Home button and your app keeps running. I need my app to work like this, because it will be left running for long periods of time." -- that needs to be a `Service`, then. – CommonsWare Jul 05 '11 at 15:29
  • Are apps like music players Services? Because they're like my app: you start the interface, press the Play button, and they keep playing regardless of whether or not you are still using the interface, using another app, or just turned the screen off. But when you open the "Recent Apps" menu and go back to them, the interface is still there in the same state you left it, so you can manipulate the playback. I think for this case, when the app is only run when the phone is connected to a circuit, making it a Service would be unneccessary. But in any case, I need it to work first! :) – therealdm Jul 05 '11 at 16:04
  • @therealdm: "Are apps like music players Services?" -- absolutely. "Because they're like my app: you start the interface, press the Play button, and they keep playing regardless of whether or not you are still using the interface, using another app, or just turned the screen off." -- that's why you need a service. If the user starts your app and presses BACK, the activity is gone. Kaput. Finito. If you want the sound to keep playing, it needs to be in a service. What you are probably doing now is leaking memory by leaking a thread, which is poor programming. – CommonsWare Jul 05 '11 at 16:11
  • I see. In that case, I will look into making it a Service as soon as I get the core functionality down, thanks for the heads up! :) As simple as my app should be (it's an intervalometer), it's remarkable how difficult it is to get a simple sinewave to play on a device as capable as a smart phone. I'm now quite convinced that there is nothing I can do to make AudioTrack work like it should, so the next thing I'm going to try is SoundPool, even though I've read it's just as buggy... – therealdm Jul 05 '11 at 16:23