Android AudioTrack缓冲问题

好的,我有一个频率发生器,它使用AudioTrack将PCM数据发送到硬件。这是我正在使用的代码:

private class playSoundTask extends AsyncTask<Void, Void, Void> {

float frequency;

float increment;

float angle = 0;

short samples[] = new short[1024];

@Override

protected void onPreExecute() {

int minSize = AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT );

track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100,

AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,

minSize, AudioTrack.MODE_STREAM);

track.play();

}

@Override

protected Void doInBackground(Void... params) {

while( Main.this.isPlaying)

{

for( int i = 0; i < samples.length; i++ )

{

frequency = (float)Main.this.slider.getProgress();

increment = (float)(2*Math.PI) * frequency / 44100;

samples[i] = (short)((float)Math.sin( angle )*Short.MAX_VALUE);

angle += increment;

}

track.write(samples, 0, samples.length);

}

return null;

}

}

频率与滑动条相关,并且在样品生成循环中报告了正确的值。当我启动应用程序时,一切都很好。当您沿着滑动条拖动手指时,您会听到清晰的声音。但是经过大约10秒钟的播放之后,音频开始变得跳动。它不是平滑的扫描,而是交错的,并且仅在大约每1000 Hz左右更改音调。关于什么可能导致此的任何想法?

如果问题出在其他地方,这里是所有代码:

    public class Main extends Activity implements OnClickListener, OnSeekBarChangeListener {

AudioTrack track;

SeekBar slider;

ImageButton playButton;

TextView display;

boolean isPlaying=false;

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

display = (TextView) findViewById(R.id.display);

display.setText("5000 Hz");

slider = (SeekBar) findViewById(R.id.slider);

slider.setMax(20000);

slider.setProgress(5000);

slider.setOnSeekBarChangeListener(this);

playButton = (ImageButton) findViewById(R.id.play);

playButton.setOnClickListener(this);

}

private class playSoundTask extends AsyncTask<Void, Void, Void> {

float frequency;

float increment;

float angle = 0;

short samples[] = new short[1024];

@Override

protected void onPreExecute() {

int minSize = AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT );

track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100,

AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,

minSize, AudioTrack.MODE_STREAM);

track.play();

}

@Override

protected Void doInBackground(Void... params) {

while( Main.this.isPlaying)

{

for( int i = 0; i < samples.length; i++ )

{

frequency = (float)Main.this.slider.getProgress();

increment = (float)(2*Math.PI) * frequency / 44100;

samples[i] = (short)((float)Math.sin( angle )*Short.MAX_VALUE);

angle += increment;

}

track.write(samples, 0, samples.length);

}

return null;

}

}

@Override

public void onProgressChanged(SeekBar seekBar, int progress,

boolean fromUser) {

display.setText(""+progress+" Hz");

}

public void onClick(View v) {

if (isPlaying) {

stop();

} else {

start();

}

}

public void stop() {

isPlaying=false;

playButton.setImageResource(R.drawable.play);

}

public void start() {

isPlaying=true;

playButton.setImageResource(R.drawable.stop);

new playSoundTask().execute();

}

@Override

protected void onResume() {

super.onResume();

}

@Override

protected void onStop() {

super.onStop();

//Store state

stop();

}

@Override

public void onStartTrackingTouch(SeekBar seekBar) {

// TODO Auto-generated method stub

}

@Override

public void onStopTrackingTouch(SeekBar seekBar) {

// TODO Auto-generated method stub

}

}

回答:

我找到原因了!

问题是AudioTrack缓冲区的大小。如果无法足够快地生成样本,样本将用完,回放会暂停,然后在再次有足够的样本时继续播放。

唯一的解决方案是确保可以足够快的速度生成样本(44100 / s)。作为快速解决方案,请尝试将采样率降低到22000(或增加缓冲区的大小)。

至少这对我来说是这样的-当我优化样本生成例程时,跳跃消失了。

(增加缓冲区的大小会使声音稍后开始播放,因为有一些储备正在等待-直到缓冲区填满。但是仍然,如果您没有足够快地生成样本,最终样本会用完)。

哦,您应该将不变变量放在循环之外!并可能使样本数组更大,以便循环运行更长的时间。

short samples[] = new short[4*1024];

...

frequency = (float)Main.this.slider.getProgress();

increment = (float)(2 * Math.PI) * frequency / 44100;

for(int i = 0; i < samples.length; i++)

{

samples[i] = (short)((float)Math.sin(angle) * Short.MAX_VALUE);

angle += increment;

}

您还可以以10Hz为步长,对200Hz-8000Hz频率的所有正弦值进行预计算。或者按需执行此操作:当用户选择频率时,请使用您的方法生成样本一段时间,然后将其保存到数组中。当您生成足够的采样以产生一个完整的正弦波时,您可以继续在数组上循环(因为sin是周期函数)。实际上,由于您从未完全碰到一个周期,所以声音中可能会存在一些小的不一致之处(因为您将角度增加1,并且一个完整的正弦波的长度是sampleRate / (double)frequency采样)。但是,选择正确的时间段倍数会使不一致现象不明显。

另外,请参见http://developer.android.com/guide/practices/design/performance.html

以上是 Android AudioTrack缓冲问题 的全部内容, 来源链接: utcz.com/qa/419017.html

回到顶部