音頻延遲時間:緩衝區大小html
在 Android 上打造出色的多媒體體驗java
延遲時間是指信號在系統中傳輸所需的時間。下面是與音頻應用相關的常見類型的延遲時間:android
往返延遲時間是指輸入延遲時間、應用處理時間和輸出延遲時間的總和。git
本頁面將介紹如何在開發您的音頻應用時保證低輸入和輸出延遲時間以及如何避免出現預熱延遲時間。github
僅在使用 OpenSL ES™ API 的 Android 實現和 Android NDK 時,才支持低延遲時間音頻。shell
很難單獨測量音頻輸入和輸出延遲時間,由於須要準確地瞭解第一個樣本什麼時候傳送入音頻路徑(儘管可使用光檢測電路和示波器完成)。 若是您瞭解往返音頻延遲時間,則可使用通常經驗法則:音頻輸入(和輸出)延遲時間是通過無信號處理路徑的往返音頻延遲時間的一半。app
往返音頻延遲時間根據設備型號和 Android 版本號的不一樣而大不相同。 您能夠經過閱讀已發佈的測量值大略瞭解 Nexus 設備的往返延遲時間。異步
您能夠經過建立應用測量往返音頻延遲時間,該應用可以生成音頻信號、偵聽此音頻信號和測量發送與接收信號之間的時間。或者,您也能夠安裝此延遲時間測試應用。 此應用使用拉森測試執行往返延遲時間測試。 您也能夠查看延遲時間測試應用的源代碼。ide
因爲最低延遲時間是在信號處理最少的音頻路徑上得到的,您可能還想使用迴環音頻適配器,它讓測試可以經過耳麥鏈接器運行。oop
Android 兼容性定義文檔 (CDD) 枚舉了兼容 Android 設備的硬件和軟件要求。請參閱 Android 兼容性瞭解與總體兼容性計劃有關的詳細信息,參閱 CDD 瞭解實際的 CDD 文檔。
在 CDD 中,往返延遲時間被指定爲 20 毫秒或更低(而樂師一般須要 10 毫秒)。 這是由於 20 毫秒能夠實現一些重要的用例。
當前沒有 API 能夠在運行時肯定 Android 設備上經過任何路徑的音頻延遲時間。 不過,您可使用下列硬件功能標記了解設備是否能爲延遲時間提供任何保證:
android.hardware.audio.low_latency
指示 45 毫秒或更短的持續輸出延遲時間。android.hardware.audio.pro
指示 20 毫秒或更短的持續往返延遲時間。報告這些標記的標準在 CDD 的 5.6 音頻延遲時間和 5.10 專業音頻部分中定義。
如下是如何在 Java 中檢查這些功能:
boolean hasLowLatencyFeature =
getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);
boolean hasProFeature =
getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO);
對於各音頻功能的關係,android.hardware.audio.low_latency
功能是 android.hardware.audio.pro
的先決條件。 設備能夠實現 android.hardware.audio.low_latency
而不能實現 android.hardware.audio.pro
,但反之則否則。
請注意有助於避免延遲時間問題的下列假設:
CLOCK_MONOTONIC
稍微不一樣的採樣率計時。 這是由於音頻和系統時鐘由不一樣的晶體制成。潛在獨立的音頻時鐘的一個結果是須要異步採樣率轉換。 異步採樣率轉換的簡單(儘管音頻質量不理想)方法是根據須要在接近過零點的位置重複或減小樣本。 也能夠進行更復雜的轉換。
此部分會提供建議,幫助您在使用內置麥克風或外部耳麥麥克風錄音時減小音頻輸入延遲時間。
VOICE_RECOGNITION
預設值)。要得到最低延遲時間,您必須提供與設備的最佳採樣率和緩衝區大小匹配的音頻數據。 如需瞭解詳細信息,請參閱面向更低的延遲時間設計。
以下列代碼示例中所示,在 Java 中,您能夠從 AudioManager 得到最佳採樣率:
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String sampleRateStr = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
int sampleRate = Integer.parseInt(sampleRateStr);
if (sampleRate == 0) sampleRate = 44100; // Use a default value if property not found
知道最佳採樣率後,您能夠在使用 OpenSL ES 建立您的播放器時提供具體數值:
// create buffer queue audio player
void Java_com_example_audio_generatetone_MainActivity_createBufferQueueAudioPlayer
(JNIEnv* env, jclass clazz, jint sampleRate, jint framesPerBuffer)
{
...
// specify the audio source format
SLDataFormat_PCM format_pcm;
format_pcm.numChannels = 2;
format_pcm.samplesPerSec = (SLuint32) sampleRate * 1000;
...
}
注:samplesPerSec
指的是每一個通道的採樣率,單位爲毫赫(1 Hz = 1000 mHz)。
您能夠經過 AudioManager API 採用與得到最佳採樣率類似的方式得到最佳緩衝區大小:
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
int framesPerBufferInt = Integer.parseInt(framesPerBuffer);
if (framesPerBufferInt == 0) framesPerBufferInt = 256; // Use default
PROPERTY_OUTPUT_FRAMES_PER_BUFFER
屬性表示 HAL(硬件抽象層)緩衝區能夠容納的音頻幀數量。 您應構建音頻緩衝區,使其能夠容納這個數量的確切倍數。 若是使用準確數量的音頻幀,會按期出現回調,這將減小抖動。
使用 API 而不是硬編碼值來肯定緩衝區大小相當重要,由於在不一樣的設備和 Android 版本號中,HAL 緩衝區大小會有所不一樣。
快速混合器僅支持下列這些接口:
不支持如下這些接口,由於它們涉及信號處理,且會致使快速音軌的請求被拒絕:
建立播放器時,請確保僅添加快速接口,如如下示例所示:
const SLInterfaceID interface_ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };
完成下列這些步驟以驗證您是否已成功得到低延遲時間音軌:
adb shell ps | grep your_app_name
adb shell dumpsys media.audio_flinger
第一次將音頻數據加入隊列時,設備音頻電路須要較短、但仍十分重要的一段時間來預熱。 要避免這種預熱延遲時間,您能夠將包含無聲的音頻數據緩衝區加入隊列,如如下代碼示例所示:
#define CHANNELS 1
static short* silenceBuffer;
int numSamples = frames * CHANNELS;
silenceBuffer = malloc(sizeof(*silenceBuffer) * numSamples);
for (i = 0; i < numSamples; i++) {
silenceBuffer[i] = 0;
}
須要生成音頻時,您能夠將包含真實音頻數據的緩衝區加入隊列。
注:持續輸出音頻將消耗大量的電量。請確保您在 onPause() 方法中中止輸出。另外,請考慮在用戶無活動的一段時間後暫停無聲輸出。