调节 Android AudioTrack 播放速度
Posted
技术标签:
【中文标题】调节 Android AudioTrack 播放速度【英文标题】:Regulate Android AudioTrack playback speed 【发布时间】:2018-07-13 06:50:26 【问题描述】:我目前正在尝试使用 AudioTrack 播放音频。音频通过网络接收,应用程序不断读取数据并添加到内部缓冲区。一个单独的线程正在使用数据并使用 AudioTrack 进行播放。
问题:
-
音频播放不断波动(感觉像音频定期下降),使其不清楚。
播放速度太高或太低,不切实际。
为了避免网络延迟和其他因素,我让应用程序等待它读取足够的数据并在最后播放。
这使得音频播放得非常快。这是我使用的一个基本逻辑示例。
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 类没有内置功能来根据环境条件控制音频播放是否正确?如果是这样,是否有更好的库可用于简化音频播放?
【问题讨论】:
从 AudioTrack.getNativeOutputSampleRate() 方法检索采样率(问题已更新)。我尝试将采样率设置为不同的值,但都没有奏效。 在你把信号写出来之前,你根本没有对信号做任何 DSP? 【参考方案1】:我看到的第一个问题是任意采样率。
AudioTrack.getNativeOutputSampleRate
将返回声音系统使用的采样率。它可能是 44100、48000、96000、192000 或其他。但看起来你有来自某个独立来源的音频数据,它会以非常精确的采样率产生数据。
假设来自源的音频数据以每秒 44100 个样本进行采样。如果你从 96000 开始播放,它会加快速度并提高音调。
因此,请使用来源提供的采样率设置以及通道数、采样格式等,而不依赖于系统默认设置。
第二个:你确定readData
过程总是足够快以成功填充缓冲区,无论缓冲区有多小,并且返回的速度比播放缓冲区的速度快吗?
您已经创建了AudioTrack,并将AudioTrack.getMinBufferSize 作为bufferSizeInBytes
参数传递。
getMinBufferSize
函数返回可用于此参数的缓冲区的最小可能大小。假设它返回了对应于 10ms 长度的缓冲区的大小。
这意味着应在此时间间隔内准备新数据。 IE。之前的write
返回控制和执行新的write
之间的时间间隔应该小于缓冲区的时间大小。
因此,如果 readData
函数可能由于某种原因延迟超过该时间间隔,则播放将暂停该时间,您会听到播放中的小间隙。
readData
可能延迟的原因可能是多方面的:如果是从文件中读取数据,那么它可能会延迟等待 IO 操作;如果它分配java对象,它可能会碰到垃圾收集器的延迟;如果它使用另一种音频源的某种解码器,它使用自己的缓冲,它可能会周期性地延迟重新填充缓冲区。
但无论如何,如果您没有创建某种应尽快对用户输入做出反应的实时合成器,请始终使用合理高的缓冲区大小,但不得小于返回的getMinBufferSize
。即:
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);
【讨论】:
非常感谢 AterLux。是的,你是对的,数据是由流媒体设备通过 RTP 以稳定的采样率发送的。但是我没有其配置的详细信息。其次,readData 从数据缓冲区返回数据,并在数据到达移动应用程序时将其添加到缓冲区。只有在读写锁的小时间窗口才获得。除此之外,从/到缓冲区的读取和写入都发生在并行线程中。 查看流媒体设备的编码器设置,我可以看到它使用的是 16000 的采样率。为 16000 采样的数据计算所需缓冲区大小的方法是什么?如果没有足够的数据来填充缓冲区的整个长度,我应该用静音(零)填充剩余长度吗? 不,您应该填写尽可能多的数据,但是在“write”的第三个参数中,而不是shortBuffer.length
,您必须指定readData
返回的确切数据量。因此,如果readData
只填充了缓冲区的一部分,那么它应该以某种方式返回实际填充的数据数。
readData 使用 shortBuffer 长度作为最小长度。如果没有足够的数据,它返回零表示没有数据。如果返回零长度,我将一个具有最小长度的空数组推送到 AudioTrack.write。
然后,您应该修改readData
填充数组的方式。并且绝对不要将空数组传递给write
方法。【参考方案2】:
就像用户@pskink 所说,
很可能是您的 sampleRate(或任何其他传递给 AudioTrack 构造函数)无效。
所以我会首先检查您实际设置的采样率是什么值。
作为参考,你也可以通过调用setPlayBackParams
方法来设置AudioTrack
的速度:
public void setPlaybackParams (PlaybackParams params)
勾选AudioTrack docs,可以看到PlaybackParams docs,可以设置输出音频的speed和pitch。然后可以传递此对象以在您的 AudioTrack 对象中设置播放参数。
但是,如果您唯一的问题是原始构造函数 sampleRate
(因为我们看不到变量 sampleRate
的来源),则不太可能需要使用它。
【讨论】:
从 AudioTrack.getNativeOutputSampleRate() 方法中检索采样率(添加到原始问题中)。我尝试将采样率设置为不同的值,但都没有奏效。以上是关于调节 Android AudioTrack 播放速度的主要内容,如果未能解决你的问题,请参考以下文章