Я заметил, что 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'
}