Android AudioTrack.onPlaybackPositionUpdateListener не всегда срабатывает вовремя - PullRequest
0 голосов
/ 17 мая 2018

Я заметил, что AudioTrack.setPlaybackPositionUpdateListener () иногда не работает должным образом (по крайней мере, в эмуляторах Android Studio из API 19-22).

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

AudioTrack должен перезвонить с помощью onPeriodicNotification () , который, в свою очередь, меняет цвет фона действия и создает журнал.

Ожидаемое поведение:

Уведомление отправляется и принимается более или менее точно каждую секунду после нажатия кнопки запуска.

Это происходит в большинстве случаев, но иногда(кажется, в основном по API 19-22):

Первое (после одной секунды) уведомление пропускается / откладывается, и вместо этого мы получаем два одновременных уведомления, которые происходят через две секунды.

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

Единственная причина, о которой я могу подумать, это, возможно, проблема с масштабированием частоты процессора, как описано около отметки 19:30 для этого видео.

MainActivity:

public class MainActivity extends Activity {

    View rootView;
    Button startButton;
    int initialBackgroundColor;
    boolean colorFlipper = false;
    boolean isPlaying = false;

    // AUDIO -------------
    AudioTrack audioTrack;
    int sampleRateInHz = 44100;
    int bufferSizeInBytes = 44100;
    byte[] silenceArray;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        rootView = findViewById(R.id.rootView);
        startButton = findViewById(R.id.startButton);
        initialBackgroundColor = rootView.getDrawingCacheBackgroundColor();

        // AUDIO -------------
        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, AudioFormat.CHANNEL_OUT_MONO,
                AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes, AudioTrack.MODE_STREAM);
        audioTrack.setPositionNotificationPeriod(44100); // this amounts to one second


        // create "dummy" (silent) sound... one second long
        silenceArray = new byte[bufferSizeInBytes];
        for (int i = 0; i < (bufferSizeInBytes-1); i++)
            silenceArray[i] = 0;

    }

    public void startPressed(View v) {
        boolean wasPlayingWhenPressed = startButton.isSelected();
        startButton.setSelected(!startButton.isSelected());
        if (wasPlayingWhenPressed) {
            stop();
            startButton.setText("START");
        } else {
            start();
            startButton.setText("STOP");
        }
    }

    private void start() {
        isPlaying = true;
        audioTrack.reloadStaticData();
        audioTrack.play();

        Runnable r = new Runnable() {
            public void run() {

                audioTrack.setPlaybackPositionUpdateListener(new AudioTrack.OnPlaybackPositionUpdateListener(){
                    @Override
                    public void onMarkerReached(AudioTrack arg0) {
                    }
                    @Override
                    public void onPeriodicNotification(AudioTrack arg0) {
                        // this *should* be called every second after play is pressed,
                        // but sometimes the first call is postponed and comes out
                        // simultaneously with the second call
                        Log.i("XXX","onPeriodicNotification() was called");
                        flipBackgroundColor();
                    }
                });

                while(isPlaying) {
                    audioTrack.write(silenceArray,0,silenceArray.length);
                }
            }
        };

        Thread backround_thread = new Thread(r);
        backround_thread.start();
    }

    private void stop() {

        isPlaying = false;
        audioTrack.stop();

    }

    private void flipBackgroundColor(){

        colorFlipper = !colorFlipper;
        if (colorFlipper) {
            rootView.setBackgroundColor(Color.RED);
        } else {
            rootView.setBackgroundColor(initialBackgroundColor);
        }

    }

}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rootView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/startButton"
        android:onClick="startPressed"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:text="start"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.example.boober.stackqaudiotrackcallback"
        minSdkVersion 19
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support.constraint:constraint-layout:1.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
...