问题
I have a requirement that I need to display a dial pad like screen and I have to generate 1khz tone (not DTMF tone) whenever user presses on a dial pad buttons.
I have used the code from the below link to generate 1 khz tone:
Playing an arbitrary tone with Android
When I started dialing buttons on my dialpad screen till 21 presses it is successfully generating tone but after 22nd attempt I am getting Application Not Responding (ANR) error and I need to close the app.
Below is my code:
final float duration = 0.3f; // seconds
final int sampleRate = 4000;
final int numSamples = (int)duration * sampleRate + 100;
final double sample[] = new double[numSamples];
final double freqOfTone = 1000; // hz
final byte generatedSnd[] = new byte[2 * numSamples];
final Handler handler = new Handler();
public void onClick(View v) {
// TODO Auto-generated method stub
int id = v.getId();
playSound();
}
private void playSound()
{
final Thread thread = new Thread(new Runnable() {
public void run() {
genTone();
handler.post(new Runnable() {
public void run() {
playSound1();
}
});
}
});
thread.start();
}
void playSound1(){
final AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, numSamples,
AudioTrack.MODE_STATIC);
audioTrack.write(generatedSnd, 0, generatedSnd.length);
audioTrack.play();
}
void genTone(){
// fill out the array
for (int i = 0; i < numSamples; ++i) {
sample[i] = Math.sin(2 * Math.PI * i / (sampleRate/freqOfTone));
}
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalised.
int idx = 0;
for (final double dVal : sample) {
// scale to maximum amplitude
final short val = (short) ((dVal * 32767));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
}
onClick() method will be called each time when I press buttons on the dialpad.
with the above code I am getting this output in Logcat:
05.500 E/AudioFlinger( 85): no more track names available
09-21 05:20:05.500 E/AudioTrack( 133): AudioFlinger could not create track, status: -12
09-21 05:20:05.503 E/SoundPool( 133): Error creating AudioTrack
09-21 05:20:05.535 E/AudioFlinger( 85): no more track names available
09-21 05:20:05.535 E/AudioTrack( 6080): AudioFlinger could not create track, status: -12
09-21 05:20:05.535 E/AudioTrack-JNI( 6080): Error initializing AudioTrack
09-21 05:20:05.535 E/AudioTrack-Java( 6080): [ android.media.AudioTrack ] Error code -20 when initializing AudioTrack.
09-21 05:20:05.535 D/AndroidRuntime( 6080): Shutting down VM
09-21 05:20:05.535 W/dalvikvm( 6080): threadid=1: thread exiting with uncaught exception (group=0x40015578)
09-21 05:20:05.539 E/AndroidRuntime( 6080): FATAL EXCEPTION: main
09-21 05:20:05.539 E/AndroidRuntime( 6080): java.lang.IllegalStateException: play() called on uninitialized AudioTrack.
09-21 05:20:05.539 E/AndroidRuntime( 6080): at android.media.AudioTrack.play(AudioTrack.java:824)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at com.android.dial.DialPadScreen.playSound1(DialPadScreen.java:274)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at com.android.dial.DialPadScreen$1$1.run(DialPadScreen.java:248)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at android.os.Handler.handleCallback(Handler.java:587)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at android.os.Handler.dispatchMessage(Handler.java:92)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at android.os.Looper.loop(Looper.java:123)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at android.app.ActivityThread.main(ActivityThread.java:3687)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at java.lang.reflect.Method.invokeNative(Native Method)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at java.lang.reflect.Method.invoke(Method.java:507)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at dalvik.system.NativeStart.main(Native Method)
09-21 05:20:05.542 E/ ( 133): Dumpstate > /data/log/dumpstate_app_error
09-21 05:20:05.542 W/ActivityManager( 133): Force finishing activity com.android.dial/.DialPadScreen
I am printing the state of the "audioTrack" till 21th time I am getting value 1 (STATE_INITIALIZED) after that I am getting value '0' (STATE_UNINITIALIZED). Dont know why state is getting changed.
Please help me what are the changes I need to do prevent this froce close issue in my app. or Please suggest if there's any alternative to do this.
回答1:
About the number of 21 successfully generated tones and next fails, I think you have to free the previously created instances invoking audioTrack.release()
method.
In order to avoid the IllegalStateException
:
After created an instance of AudioTrack
you have to test if it is well initialized.
Using MODE_STATIC
mode, if it does not correctly initialize, the state will be: STATE_UNINITIALIZED
,
instead, if correctly initialized: STATE_NO_STATIC_DATA
, when you invoke write method the state will be changed to STATE_INITIALIZED
.
Using MODE_STREAM
mode, if it does not correctly initialize, the state will be: STATE_UNINITIALIZED
,
instead, if correctly initialized: STATE_INITIALIZED
.
Reading on source of AudioTrack
I seen that. I think this is very strange because the object returned by constructor is not usable because it is not possible to initialize after construction.
回答2:
You could create one single AudioTrack
in the onCreate
of your app and then call audioTrack.play()
on app start, and then just write data into it on button presses.
Also, in my experience with AudioTrack
, if I try to play more than one AudioTrack simultaneously, it creates a really bad, choppy sound and there is lag.
You could consider pre-recording sounds and using Sound Pool because that is able to play multiple sounds simultaneously.
Or, since API level 9, you can assign a session ID to AudioTrack, which should allow you to play Audio Tracks with SoundPool if I understand correctly, and then you would ideally want to create STATIC AudioTracks
回答3:
Change AudioTrack.MODE_STATIC to AudioTrack.MODE_STREAM. That should do it :)
回答4:
I had a similar problem and I solved it by adding this permission to the manifest file:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
来源:https://stackoverflow.com/questions/7497598/uninitialized-audiotrack-exception-when-i-try-to-generate-tone-on-22nd-time