我有一个应用程序,我在流模式下使用AudioTrack
来播放动态生成的音频。该应用程序不必立即响应输入,因此延迟问题不会打扰我的程序。
问题是我的动画需要与音频尽可能精确地“同步”,并且似乎不同的设备在AudioTrack
停止阻止{之间的时间间隔不同{1}}呼叫并要求提供更多数据,以及何时从扬声器播放音频。
我当前的解决方案让我大部分都在那里 - 我计算到目前为止我传递给write()
的帧数,并将其与AudioTrack
进行比较。它看起来基本上像:
getPlaybackHeadPosition()
但是,long currentTimeInFrames = 0;
while(playingAudio) {
currentTimeInFrames += numberOfFramesToWrite;
long delayInFrames = (currentTimeInFrames - audioTrack.getPlaybackHeadPosition());
audioTrack.write(frameBuffer,0,sampleSize);
doAnimationAfterDelay(delayInFrames);
}
似乎没有考虑因设备而异的延迟。
有没有办法在系统中轮询AudioTrack的延迟?
答案 0 :(得分:4)
API级别19在AudioTrack
中添加了一个名为getTimeStamp()的方法。来自文档:
根据需要轮询时间戳。
如果您需要在初始预热期间或路由或模式更改后跟踪时间戳,您应该定期请求新的时间戳,直到报告的时间戳显示帧位置正在前进,或者直到明确表示时间戳不可用为止路由。
您指定一个AudioTimestamp对象作为函数的参数,它将填充最近“呈现”的帧位置及其“估计”时间戳(以纳秒为单位)。纳秒值对应于SystemClock.uptimeMillis()返回的毫秒值。
然后,您可以通过确定何时将该特定帧写入AudioTrack
与getTimestamp()
认为它实际呈现的时间来确定延迟。我发现这种方法比上面提到的其他方法更准确。
但你必须要小心。文档说明并非所有平台或所有路由都支持getTimeStamp()
。您可以通过检查boolean
返回值来确定呼叫是否成功。我发现我测试的设备函数返回false,直到音频开始呈现,然后后续调用返回true。我只在AudioTrack
模式下使用STREAM_MUSIC
进行了测试。您的里程可能会有所不同。
答案 1 :(得分:1)
考虑驱动程序的延迟。这里有隐藏的函数AudioManager.getOutputLatency(int)来获取它。
这样称呼:
AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
try{
Method m = am.getClass().getMethod("getOutputLatency", int.class);
latency = (Integer)m.invoke(am, AudioManager.STREAM_MUSIC);
}catch(Exception e){
}
我在不同的设备上大约需要45到50毫秒。 在计算中使用结果。
答案 2 :(得分:-1)
您应该考虑传递给AudioTrack创建的缓冲区。
final int minBufSize = AudioTrack.getMinBufferSize(Application.PLAYRATE,
AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
out=new AudioTrack(AudioManager.STREAM_MUSIC, Application.PLAYRATE,
AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufSize,
AudioTrack.MODE_STREAM);
extraLatencyFrames = minBufSize/4;
答案 3 :(得分:-1)
好的,这是关键。首先,您需要扩展Audiotrack类,然后使用getNativeFrameCount来近似处理本机方面所涉及的延迟。
class MyAudioTrack extends AudioTrack
{
public MyAudioTrack(int streamType, int sampleRateInHz, int channelConfig,
int audioFormat, int bufferSizeInBytes, int mode)
throws IllegalArgumentException {
super(streamType, sampleRateInHz, channelConfig, audioFormat,
bufferSizeInBytes, mode);
System.out.println("Native framecount "+getNativeFrameCount());
}
public int getFrameCount()
{
return getNativeFrameCount();
}
}