AudioTrack 采样率不一致

Posted

技术标签:

【中文标题】AudioTrack 采样率不一致【英文标题】:AudioTrack samplerate inconsistencies 【发布时间】:2015-07-11 01:40:45 【问题描述】:

使用 AudioTrack 进行播放时,有时我需要重新采样不符合 AudioTrack 支持的采样率的音频。为此,我需要确定当前设备下、当前音频配置下 AudioTrack 支持的最大采样率。

由于 AudioTrack 允许的采样率记录不充分,我决定窥探 AudioTrack 的源代码,发现这条惊人的线:

private static final int SAMPLE_RATE_HZ_MAX = 96000;

无论设备的实际播放能力如何,AudioTrack 实例似乎都在应用 96 KHz 的硬限制。

更令人困惑的是AudioFormat 类,我在其中传递给AudioTrack 的构造函数(API 21),其中包含以下行:

if ((sampleRate <= 0) || (sampleRate > 192000)) 

在它的 setSampleRate() 方法中。现在这是 192 KHz 的硬性限制。因此,将 > 192 KHz 传递到 AudioFormat(或其构建器)将导致 IllegalArgumentException 来自 AudioFormat 并将配置的 192 KHz IllegalArgumentException。


到目前为止,我发现最令人困惑的是 AudioTrack 中的方法 getNativeOutputSampleRate(),它实际上确实返回了正确的输出采样率(好吧,鉴于它直接从本机层运行,这并不奇怪,但如此不一致)。

最重要的是,setPlaybackRate() 方法声称:

有效的采样率范围是从 1 Hz 到 getNativeOutputSampleRate(int) 返回值的两倍。

确实,我确实尝试过,它有效吗?考虑以下 sn-p:

int nativeRate = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);

android.util.Log.i("UI", "Native stream rate: " + nativeRate + " Hz");

// Build audio attributes

AudioAttributes.Builder attribBuilder = new AudioAttributes.Builder();

attribBuilder.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC);
attribBuilder.setUsage(AudioAttributes.USAGE_MEDIA);

AudioAttributes attrib = attribBuilder.build();

// Build audio format

AudioFormat.Builder afBuilder = new AudioFormat.Builder();

afBuilder.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO);
afBuilder.setEncoding(AudioFormat.ENCODING_PCM_16BIT);
afBuilder.setSampleRate(nativeRate);

try
    AudioTrack trackTest = new AudioTrack(attrib, afBuilder.build(), nativeRate, AudioTrack.MODE_STREAM, 0);

    android.util.Log.i("UI", "Track created successfully (direct)");
catch(Exception ex)
    android.util.Log.w("UI", "Failed to create AudioTrack at native rate!");

    // Use a random supported samplerate to get pass constructor
    afBuilder.setSampleRate(48000);

    try
        AudioTrack trackTest = new AudioTrack(attrib, afBuilder.build(), nativeRate, AudioTrack.MODE_STREAM, 0);

        trackTest.setPlaybackRate(nativeRate);

        android.util.Log.i("UI", "Track created successfully (indirect)");
    catch(Exception e)
        android.util.Log.w("UI", "Failed to create AudioTrack at 48 KHz");
    

按照程序流程,当原生采样率为时,代码打印出来:

原生流率:48000 Hz 轨道创建成功(直接)

但是,当我连接一个播放能力高达 192 KHz 的外部 DAC 时,我得到:

原生流率:192000 Hz 无法以原生速率创建 AudioTrack! 轨道创建成功(间接)

这些不一致是怎么回事?而且,setPlaybackRate() 是否与传递给构造函数的采样率相同?

【问题讨论】:

您可以考虑向code.google.com/p/android/issues/list 提交关于这些不一致的问题(我可以确认,这些不一致也存在于 API 22 的源代码中)。关于setPlaybackRate(),它显然绕过了您提到的(不一致的)检查,并直接在本机端设置播放。另外,看看 [core/jni/android_media_AudioTrack.cpp][1] [1] 的历史可能会很有趣:android.googlesource.com/platform/frameworks/base/+/88e209d%5E! @AladinQ 感谢您提供指向源的链接,我不知道他们包含本地层源,这应该有助于调试问题。考虑到可能没有任何问题(只是文档错误或未记录的行为),我将推迟将其列为问题。以前发生过……我会看看我能从原生资源中得到什么。 【参考方案1】:

目前市场上的大多数安卓手机只支持一种采样率。我相信某些三星以 48kHz 播放,而几乎所有其他三星以 44.1kHz 播放。这些值是由硬件决定的,虽然有一个工具可以改变原始速率,但它的功能是次要的未来证明,但主要是 2. 在运行时在软件中重新采样所有音频。这是一项昂贵的任务,而且还有些破坏性。在 192kHz (= 2 * 96kHz) 处存在硬限制的原因可能是因为发送超过两倍的最大频率 (96kHz) 会浪费大量资源,因为您不妨丢弃每秒采样一次以有效地进行下采样2 倍,直到你在那个范围内。

最好避免指定非本地采样率。它将在软件中重新采样,充其量是一种资源浪费,最坏的情况是造成延迟。

Or hear it from a Google engineer

【讨论】:

欣赏链接,我会尽快观看。但是,正如我所提到的,Android 设备能够将音频投射到外部源,例如 USB 外部 DAC。我拥有这样一个 DAC,当我连接它时,Android 会协商 192 KHz 的 native 速率(通过系统日志以及我的 DAC 上的采样率指示器指示)。确定本机速率的努力是为了在不必要时最小化重新采样的性能成本。 96 KHz+ 的源文件应尽可能保持原样(即直接通过而不重新采样),以最大限度地延长电池寿命。 啊,当您谈论 USB 时,我确实认为我可能漏掉了一些东西。 gitlab.com/SaberMod/pa-android-frameworks-base/commit/…哈哈,48kHz 的限制是去年“固定”的,固定为 96kHz 我想 Aladin Q 的评论是正确的答案!正因为如此:if (sampleRateInHz &lt; SAMPLE_RATE_HZ_MIN || sampleRateInHz &gt; SAMPLE_RATE_HZ_MAX) throw new IllegalArgumentException(sampleRateInHz + "Hz is not a supported sample rate.");

以上是关于AudioTrack 采样率不一致的主要内容,如果未能解决你的问题,请参考以下文章

音频重采样

Android 深入系统完全讲解(25)

Android 深入系统完全讲解(25)

更改 mp3 文件的采样率

如何使用 AVFoundation 以正确的音高播放不同采样率的音频文件?

采样率改变正弦波的音高,sound() 函数,MATLAB