2013-04-04 177 views
9

我有一个应用程序,我在流模式下使用AudioTrack播放动态生成的音频。该应用程序不需要立即对输入做出响应,因此延迟问题不会影响到该程序的这一方面。您如何确定Android上的音频延迟(AudioTrack)?

问题是我有一个动画需要与音频尽可能精确地'同步',并且似乎不同的设备在AudioTrack停止阻止write()呼叫时有不同的时间长度并询问获取更多数据,以及何时从扬声器播放音频。

我目前的解决方案让我获得了大部分途径 - 我计算了迄今为止传递给AudioTrack的帧数,并将其与getPlaybackHeadPosition()进行比较。它基本上是这样:

long currentTimeInFrames = 0; 
while(playingAudio) { 
    currentTimeInFrames += numberOfFramesToWrite; 
    long delayInFrames = (currentTimeInFrames - audioTrack.getPlaybackHeadPosition()); 
    audioTrack.write(frameBuffer,0,sampleSize); 
    doAnimationAfterDelay(delayInFrames); 
} 

然而,还是有一些延迟是getPlaybackHeadPosition()似乎并没有考虑通过设备而异。

有没有一种方法来轮询系统的AudioTrack的延迟?

回答

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毫秒。 在计算中使用结果。

+0

请注意,在某些设备上抛出'MethodNotFoundException' – zella 2015-09-10 08:21:00

0

您应该考虑您传递给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; 
+0

嗯..现在我再次想到它。如果最小缓冲区大小没有在头部位置中计算,那将会很奇怪。 – 2013-09-10 13:28:22

0

好的,这是关键。首先,您需要扩展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(); 
    } 
} 
1

API级别19在AudioTrack中增加了一个名为getTimeStamp()的方法。从文档:

轮询时间戳的需求。

如果您需要在初始预热期间或路由或模式更改后跟踪时间戳,您应该定期请求新的时间戳,直到报告的时间戳显示帧位置正在前进,或直到明确时间戳不可用这条路线。

您指定AudioTimestamp对象作为函数的参数,它会在与纳秒的“估计”时间戳以及最近“提出的”框的位置继续进行。纳秒值对应于由SystemClock.uptimeMillis()返回的毫秒值。

然后,您可以确定延迟时间,方法是确定何时将该特定帧写入AudioTrackgetTimestamp()是否认为它实际存在。我发现这种方法比上面提到的其他方法更准确。

你必须小心,虽然。文档说getTimeStamp()不支持所有平台或所有路由。您可以通过检查boolean返回值来确定呼叫是否成功。我已经找到了我测试过的设备,直到音频开始播放,该函数才返回false,然后后续调用返回true。我只用AudioTrackSTREAM_MUSIC模式下进行了测试。你的旅费可能会改变。