неинициализированное исключение AudioTrack, когда я пытаюсь сгенерировать тон в 22-й раз - PullRequest
2 голосов
/ 21 сентября 2011

У меня есть требование, что мне нужно отображать экранную панель, подобную экрану, и я должен генерировать сигнал 1 кГц (не сигнал DTMF) всякий раз, когда пользователь нажимает на кнопки панели набора.

Я использовал код из приведенной ниже ссылки для генерации тона 1 кГц:

Воспроизведение произвольного тона с Android

Когда я начал набирать кнопки на экране своей клавиатуры до нажатия 21, он успешно генерирует тональный сигнал, но после 22-й попытки я получаю сообщение об ошибке «Приложение не отвечает» (ANR), и мне нужно закрыть приложение.

Ниже мой код:

 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 () будет вызываться каждый раз, когда я нажимаю кнопки на панели набора.

с приведенным выше кодом, я получаю этот вывод в 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

Я печатаю состояние «audioTrack» до 21-го раза, когда я получаю значение 1 (STATE_INITIALIZED), после чего я получаю значение «0» (STATE_UNINITIALIZED). Не знаю, почему состояние меняется.

Пожалуйста, помогите мне, какие изменения мне нужно сделать, чтобы эта проблема не закрывалась в моем приложении. или, пожалуйста, предложите, если есть какая-либо альтернатива для этого.

Ответы [ 4 ]

4 голосов
/ 13 января 2014

Насчет числа 21 успешно сгенерированных тонов и следующих сбоев, я думаю, вам нужно освободить ранее созданные экземпляры, используя метод audioTrack.release().

Чтобы избежать IllegalStateException:
После создания экземпляра AudioTrack необходимо проверить, правильно ли он инициализирован.
При использовании режима MODE_STATIC, если он неправильно инициализируется, состояние будет: STATE_UNINITIALIZED, вместо этого, если правильно инициализировано: STATE_NO_STATIC_DATA, при вызове метода записи состояние будет изменено на STATE_INITIALIZED.
В режиме MODE_STREAM, если он неправильно инициализируется, состояние будет: STATE_UNINITIALIZED, вместо этого, если правильно инициализировано: STATE_INITIALIZED.
Читая по источнику AudioTrack Я видел это. Я думаю, что это очень странно, потому что объект, возвращаемый конструктором, не может быть использован, потому что его невозможно инициализировать после построения.

2 голосов
/ 26 сентября 2011

Вы можете создать один AudioTrack в onCreate вашего приложения и затем вызвать audioTrack.play() при запуске приложения, а затем просто записать в него данные при нажатии кнопки.

Кроме того, по моему опыту с AudioTrack, если я пытаюсь воспроизвести более одного AudioTrack одновременно, он создает действительно плохой, прерывистый звук и задержка.

Вы можете рассмотреть возможность предварительной записи звуков и использовать Sound Pool , потому что он способен воспроизводить несколько звуков одновременно.

Или, начиная с уровня API 9, вы можете назначить идентификатор сеанса для AudioTrack , который должен позволять вам воспроизводить аудиодорожки с помощью SoundPool, если я правильно понимаю, и тогда вам в идеале нужно создать STATIC AudioTracks

1 голос
/ 15 октября 2012

У меня была похожая проблема, и я решил ее, добавив это разрешение в файл манифеста:

    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
1 голос
/ 30 сентября 2011

Измените AudioTrack.MODE_STATIC на AudioTrack.MODE_STREAM.Это должно сделать это:)

...