uninitialized AudioTrack exception when I try to generate tone on 22nd time

前提是你 提交于 2019-12-24 00:25:01

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!