(Извините, что исправляю это снова и снова.)
Я хочу воспроизвести новый поток сразу после остановки AudioTrack.
Однако, если вы не запускаете воспроизведение через несколько миллисекунд после остановка воспроизведения с помощью stop () , OnMarkerReached обратный вызов не может быть вызван.
Я написал пример кода.
Нажмите кнопку, чтобы воспроизвести звук .
При нажатии кнопки данные за 0,5 секунды записываются в AudioTrack, а через 0,25 секунды сигнал в течение следующих 0,5 секунд записывается в AudioTrack с помощью onMarkerReached . Звук воспроизводится в течение 2 секунд.
Если вы нажмете кнопку во время воспроизведения звука в течение 2 секунд, он будет stop () , flu sh () и начните следующее воспроизведение. В настоящее время OnMarkerReached может не вызываться, если перед воспроизведением не вставлено дополнительное время.
Проверка была выполнена на двух android устройствах.
OnMarkerReached вызывается, даже если устройство A имеет 0 секунд дополнительного времени. Однако OnMarkerReached не вызывался, даже если у устройства B было дополнительное время 10 мс.
Почему onMarkerReached иногда не вызывается?
Вот Пример кода.
MainActivity. java
package com.example.audiotrackexample;
import androidx.appcompat.app.AppCompatActivity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements AudioTrack.OnPlaybackPositionUpdateListener {
final String TAG = MainActivity.class.getSimpleName();
Button playBtn;
Button queryBtn;
TextView curPosTv;
TextView curPosTv2;
public class SinGenerator {
float freq;
int samplingRate;
int sampleCount;
public SinGenerator(float freq, int samplingRate){
this.freq = freq;
this.samplingRate = samplingRate;
this.sampleCount = 0;
}
public short generate(){
this.sampleCount++;
double t = (double)freq * sampleCount / samplingRate;
double sin = Math.sin(2.0 * Math.PI * t);
return (short) (sin*Short.MAX_VALUE);
}
}
static final int SAMPLING_RATE = 16000;
static final int AUDIO_DATA_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
static final int CHANNEL = AudioFormat.CHANNEL_OUT_MONO;
static final int READ_WAVE_BUFFER_SIZE = SAMPLING_RATE / 2; // 0.5s
static final int AUDIO_TRACK_MIN_BUFFER_SIZE = SAMPLING_RATE;
int audioTrackBufferSize;
AudioTrack audioTrack;
SinGenerator sinGenerator = new SinGenerator(300, SAMPLING_RATE);
short[] wave;
short[] readWaveBuff = new short[READ_WAVE_BUFFER_SIZE];
int waveReadLen = 0;
Handler handler = new Handler(Looper.myLooper());
void setNextWave(){
System.arraycopy( wave, waveReadLen, readWaveBuff, 0, READ_WAVE_BUFFER_SIZE );
waveReadLen += READ_WAVE_BUFFER_SIZE;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
playBtn = findViewById(R.id.playBtn);
queryBtn = findViewById(R.id.queryBtn);
curPosTv = findViewById(R.id.curPosTv);
curPosTv2 = findViewById(R.id.curPosTv2);
queryBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "curPlaybackPos : " + audioTrack.getPlaybackHeadPosition());
curPosTv2.setText("curPlaybackPos : " + audioTrack.getPlaybackHeadPosition());
}
});
audioTrackBufferSize = AudioTrack.getMinBufferSize(
SAMPLING_RATE,
CHANNEL,
AUDIO_DATA_FORMAT);
if (audioTrackBufferSize < AUDIO_TRACK_MIN_BUFFER_SIZE) {
audioTrackBufferSize = AUDIO_TRACK_MIN_BUFFER_SIZE;
}
Log.d(TAG, "audioTrackBufferSize : " + audioTrackBufferSize);
audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
SAMPLING_RATE,
CHANNEL,
AUDIO_DATA_FORMAT,
audioTrackBufferSize,
AudioTrack.MODE_STREAM);
audioTrack.setPlaybackPositionUpdateListener(MainActivity.this);
wave = new short[READ_WAVE_BUFFER_SIZE * 4];
for (int i = 0; i < 4; i++) {
sinGenerator = new SinGenerator(300 + i * 100, SAMPLING_RATE);
for (int j = 0; j < READ_WAVE_BUFFER_SIZE; j++) {
wave[i * READ_WAVE_BUFFER_SIZE + j] = sinGenerator.generate();
}
}
playBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
audioTrack.stop();
audioTrack.flush();
handler.postDelayed(new Runnable() {
@Override
public void run() {
waveReadLen = 0;
setNextWave();
audioTrack.setNotificationMarkerPosition(READ_WAVE_BUFFER_SIZE / 2);
audioTrack.write(readWaveBuff, 0, READ_WAVE_BUFFER_SIZE);
audioTrack.play();
curPosTv.setText("curPlaybackPos : " + audioTrack.getPlaybackHeadPosition());
}
},0); // device A is onMarkerReached called. but device B is not.
// }, 10); // device A is onMarkerReached called. but device B is not.
// }, 100); // Devices A and B call onMarkerReached.
}
});
}
@Override
public void onMarkerReached(AudioTrack track) {
curPosTv.setText("curPlaybackPos : " + audioTrack.getPlaybackHeadPosition());
if(waveReadLen == wave.length) {
audioTrack.stop();
audioTrack.flush();
Log.d(TAG, "finish playing");
} else {
setNextWave();
audioTrack.write(readWaveBuff, 0, READ_WAVE_BUFFER_SIZE);
int newMarkerPosition = audioTrack.getNotificationMarkerPosition();
if (waveReadLen == wave.length) {
newMarkerPosition += (READ_WAVE_BUFFER_SIZE / 2) + READ_WAVE_BUFFER_SIZE;
} else {
newMarkerPosition += READ_WAVE_BUFFER_SIZE;
}
audioTrack.setNotificationMarkerPosition(newMarkerPosition);
Log.d(TAG, "curPos : " + audioTrack.getPlaybackHeadPosition() + " markerPos : " + audioTrack.getNotificationMarkerPosition());
}
}
@Override
public void onPeriodicNotification(AudioTrack track) {
}
}
activity_main. xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/playBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Audio Play">
</Button>
<TextView
android:id="@+id/curPosTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
<Button
android:id="@+id/queryBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Query marker position">
</Button>
<TextView
android:id="@+id/curPosTv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
</LinearLayout>
build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.audiotrackexample"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}