调节 Android AudioTrack 播放速度

Regulate Android AudioTrack playback speed

我目前正在尝试使用 AudioTrack 播放音频。通过网络接收音频,应用程序不断读取数据并添加到内部缓冲区。一个单独的线程正在消耗数据并使用 AudioTrack 进行播放。

问题:

  1. 音频播放不断波动(感觉音频有规律地下降),导致听不清。
  2. 播放速度太快或太慢,不真实。

为了避免网络延迟和其他因素,我让应用程序等待直到它读取足够的数据并在最后播放。

这使得音频播放速度非常快。这是我使用的基本逻辑示例。

    sampleRate = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
    audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
                    AudioFormat.CHANNEL_OUT_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT),
                    AudioTrack.MODE_STREAM);
audioTrack.play();

short shortBuffer[] = new short[AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT)];
while (!stopRequested){
    readData(shortBuffer);
    audioTrack.write(shortBuffer, 0, shortBuffer.length, AudioTrack.WRITE_BLOCKING);
}

Android AudiTrack class 没有内置功能来根据环境条件控制音频播放,这样说对吗?如果是这样,是否有更好的库提供简化的音频播放方式?

正如用户@pskink 所说,

Most likely your sampleRate (or any other parameter passed to the AudioTrack constructor) is invalid.

所以我将从检查您实际设置采样率的值开始。

供参考,也可以调用setPlayBackParams方法设置AudioTrack的速度:

public void setPlaybackParams (PlaybackParams params)

如果勾选输出音频的AudioTrack docs, you can see the PlaybackParams docs and can set the speed and pitch。然后可以传递此对象以在 AudioTrack 对象中设置播放参数。

但是,如果您唯一的问题是原始构造函数 sampleRate(因为我们看不到变量 sampleRate 的来源),您不太可能需要使用它。

我看到的第一个问题,是任意采样率。

AudioTrack.getNativeOutputSampleRate 将 return 声音系统使用的采样率。它可能是 44100、48000、96000、192000 或其他任何值。但看起来你有来自某个独立来源的音频数据,它以非常精确的采样率生成数据。

假设来自源的音频数据以每秒 44100 个样本进行采样。如果您以 96000 开始播放它,它将加速并提高音调。

因此,使用来源提供的采样率设置以及通道数、采样格式等,而不依赖于系统默认值。

第二个:你确定readData程序总是足够快以成功填充缓冲区,无论缓冲区有多小,并且return 后退速度比缓冲区播放速度快?

您已创建 AudioTrack with AudioTrack.getMinBufferSize 作为 bufferSizeInBytes 参数传递。

getMinBufferSize 函数 returns 可以在此参数上使用的缓冲区的最小可能大小。假设它 returned 对应于 10ms 长度的缓冲区的大小。 这意味着新数据应该在这个时间间隔内准备好。 IE。前一个 write returned 控制和新 write 执行之间的时间间隔应小于缓冲区的时间大小。

因此,如果 readData 功能可能由于某种原因延迟超过该时间间隔,播放将暂停一段时间,您会听到播放中的小间隙。

readData可能延迟的原因可能是多种多样的:如果是从文件中读取数据,则可能会延迟等待IO操作;如果它分配 java 个对象,它可能会遇到垃圾收集器的延迟;如果它使用另一种使用自己缓冲的音频源的某种解码器,它可能会定期延迟重新填充缓冲区。

但是无论如何,如果您不是在创建某种应尽快对用户输入做出反应的实时合成器,请始终使用相当大的缓冲区大小,但不要小于 getMinBufferSize return编辑。即:

sampleRate = 44100;// sampling rate of the source

int bufSize = sampleRate * 4; // 1 second length; 4 - is the frame size: 2 chanels * 2 bytes per each sample
bufSize = max(bufSize, AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT)); // Not less than getMinBufferSize returns
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
                AudioFormat.CHANNEL_OUT_STEREO,
                AudioFormat.ENCODING_PCM_16BIT,
                bufSize,
                AudioTrack.MODE_STREAM);