我目前正在尝试使用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类没有内置功能可以根据环境条件控制音频播放是否正确?如果是这样,是否有更好的库以简化的方式播放音频?
答案 0 :(得分:0)
就像@pskink用户所说的
您的sampleRate(或传递给 AudioTrack构造函数)无效。
因此,我将从检查您实际设置采样率的值开始。
作为参考,您还可以通过调用AudioTrack
方法来设置setPlayBackParams
的速度:
public void setPlaybackParams (PlaybackParams params)
如果选中AudioTrack docs,则可以看到PlaybackParams docs,并可以设置输出音频的speed和pitch。然后可以传递该对象以在AudioTrack对象中设置播放参数。
但是,如果唯一的问题是原始构造函数sampleRate
,则不太可能需要使用它(因为我们无法看到变量sampleRate
的来源)。
答案 1 :(得分:0)
我看到的第一个问题是任意采样率。
AudioTrack.getNativeOutputSampleRate
将返回声音系统使用的采样率。它可以是44100、48000、96000、192000或其他任何值。但是看起来您有来自某些独立来源的音频数据,它们以非常精确的采样率生成数据。
比方说,来自源的音频数据以每秒44100个样本进行采样。如果您从96000开始播放,它将加快速度并提高音调。
因此,请使用源提供的采样率设置以及通道数,采样格式等,而不依赖于系统默认值。
第二个:您确定readData
过程始终足够快以成功填充缓冲区,无论缓冲区有多小,并且返回速度都比缓冲区播放的快?
您已经创建了AudioTrack,其中AudioTrack.getMinBufferSize是通过bufferSizeInBytes
参数传递的。
getMinBufferSize
函数返回可用于此参数的缓冲区的最小可能大小。假设它返回的大小对应于10ms长度的缓冲区。
这意味着应在此时间间隔内准备新数据。即先前返回的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);