Opóźnienie dźwięku

Czas oczekiwania to czas potrzebny na przesłanie sygnału przez system. Są to najczęstsze typy opóźnień związanych z aplikacjami audio:

  • Opóźnienie na wyjściu audio to czas upływający między wygenerowaniem próbki audio przez a próbkę odtwarzaną przez gniazdo słuchawek lub wbudowany głośnik.
  • Opóźnienie wejścia audio to czas upływający między odebraniem sygnału audio przez wejścia urządzenia audio, np. mikrofonu, oraz tych samych danych dźwiękowych dostępnych dla .
  • Opóźnienie w obie strony to suma czasu oczekiwania na sygnał wejściowe, czasu przetwarzania aplikacji oraz opóźnienia na wyjściu.

  • Opóźnienie dotknięcia to czas, jaki upływa od momentu, gdy użytkownik dotknie ekranu zdarzenie dotknięcia odebrane przez aplikację.
  • Czas oczekiwania na rozgrzewanie to czas potrzebny na pierwsze uruchomienie potoku audio. czas umieszczenia danych w kolejce w buforze.

Na tej stronie dowiesz się, jak opracować aplikację audio z wejściem i wyjściem o małym opóźnieniu oraz jak tego uniknąć czas oczekiwania na rozgrzewkę.

Mierz czas oczekiwania

Pomiar wejścia audio i opóźnienia wyjściowego dźwięku w izolacji jest trudny, ponieważ wymaga dokładnej wiedzy. gdy do ścieżki audio wysyłana jest pierwsza próbka (chociaż można to zrobić za pomocą metody obwód świetlny i oscyloskop). Jeśli znasz opóźnienie dźwięku w obie strony, możesz użyj ogólnej zasady: opóźnienie wejścia (i wyjścia) dźwięku jest o połowę mniejsze od opóźnienia dźwięku w obie strony. w ścieżkach bez przetwarzania sygnału.

Opóźnienie dźwięku w obie strony jest bardzo różne w zależności od modelu urządzenia i Kompilacja Androida. Możesz uzyskać ogólne pojęcie o podróży w obie strony, na urządzeniach Nexus. opublikowanych pomiarów.

Możesz zmierzyć opóźnienie dźwięku w obie strony, tworząc aplikację, która generuje sygnał audio. nasłuchuje tego sygnału, a potem mierzy czas między jego wysłaniem a odebraniem.

Najmniejsze opóźnienie jest osiągane na ścieżkach dźwiękowych z minimalnym przetwarzaniem sygnału, też użyć funkcji Wtyczka audio sprzężenia zwrotnego audio, która umożliwia przeprowadzenie testu przez złącze zestawu słuchawkowego.

Sprawdzone metody minimalizowania opóźnień

Sprawdzanie wydajności reklam audio

Dokument CDD (Android Compatibility Definition Document) wymienia sprzęt i oprogramowanie. zgodnego urządzenia z Androidem. Zobacz zgodności z Androidem, aby uzyskać więcej informacji o ogólnym programie zgodności. CDD dla rzeczywistego dokumentu CDD.

Zgodnie z dokumentem CDD opóźnienie w obie strony wynosi 20 ms lub mniej (mimo że muzycy zwykle wymagają 10 ms). Wynika to z faktu, że istnieją ważne przypadki użycia, które są włączone przez 20 ms.

Obecnie nie ma interfejsu API do określania opóźnienia dźwięku na dowolnej ścieżce na urządzeniu z Androidem w środowisku wykonawczym. Możesz jednak użyć poniższych flag funkcji sprzętowych, aby sprawdzić, czy urządzenie gwarantuje opóźnienia:

Kryteria zgłaszania tych zgłoszeń są określone w CDD w sekcjach 5.6 Opóźnienie dźwięku oraz 5.10 Profesjonalna ścieżka dźwiękowa.

Aby sprawdzić te funkcje w Javie:

Kotlin

val hasLowLatencyFeature: Boolean =
        packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY)

val hasProFeature: Boolean =
        packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO)

Java

boolean hasLowLatencyFeature =
    getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);

boolean hasProFeature =
    getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO);

Jeśli chodzi o relację funkcji audio, android.hardware.audio.low_latency jest wymagany do korzystania z android.hardware.audio.pro. Urządzenie może zaimplementować android.hardware.audio.low_latency, a nie android.hardware.audio.pro, ale nie i na odwrót.

Nie należy przyjmować żadnych założeń dotyczących wydajności audio.

Pamiętaj o tych założeniach, aby uniknąć problemów z opóźnieniami:

  • Nie zakładaj, że głośniki i mikrofony używane w urządzeniach mobilnych zwykle działają dobrze. akustykę. Ze względu na niewielki rozmiar akustyka jest ogólnie słaba, więc przetwarzanie sygnału jest aby poprawić jakość dźwięku. Przetwarzanie sygnału zwiększa opóźnienie.
  • Nie zakładaj, że wywołania zwrotne wejściowe i wyjściowe są zsynchronizowane. Do jednoczesnego wprowadzania danych dla każdej strony są używane oddzielne moduły obsługi kolejki bufora i dane wyjściowe. Brak gwarancja względnej kolejności wywołań zwrotnych lub synchronizacji zegarów audio, nawet wtedy, gdy obie strony używają tej samej częstotliwości próbkowania. Aplikacja powinna buforować dane za pomocą: prawidłową synchronizację bufora.
  • Nie zakładaj, że rzeczywista częstotliwość próbkowania jest dokładnie taka sama jak nominalna częstotliwość próbkowania. Dla: Jeśli na przykład nominalna częstotliwość próbkowania wynosi 48 000 Hz, to normalne, że zegar audio przesuwa się szybciej. z nieco inną szybkością niż system operacyjny CLOCK_MONOTONIC. Dzieje się tak, ponieważ zegary dźwiękowe i zegary systemowe mogą pochodzić z różnych kryształów.
  • Nie zakładaj, że rzeczywista częstotliwość próbkowania podczas odtwarzania jest taka sama jak zwłaszcza wtedy, gdy punkty końcowe znajdują się w osobnych ścieżkach. Na przykład jeśli przechwytujesz z mikrofon na urządzeniu z nominalną częstotliwością próbkowania 48 000 Hz i odtwarzany przez USB przy nominalnej częstotliwości próbkowania 48 000 Hz rzeczywiste częstotliwości próbkowania mogą się nieznacznie różnić od siebie nawzajem.

Konsekwencją potencjalnie niezależnych zegarów audio jest konieczność stosowania asynchronicznej częstotliwości próbkowania. konwersji. Prosta technika (choć nie idealna dla jakości audio) umożliwiająca asynchroniczną częstotliwość próbkowania konwersji polega na duplikowaniu lub usuwaniu próbek w miarę potrzeb w pobliżu punktu przecięcia. Więcej i bardziej złożone konwersje.

Minimalizuj opóźnienie sygnału wejściowego

W tej sekcji znajdziesz sugestie, które pomogą Ci zmniejszyć opóźnienie sygnału wejściowego dźwięku podczas nagrywania z użyciem wbudowany mikrofon lub zewnętrzny zestaw słuchawkowy z mikrofonem.

  • Jeśli aplikacja monitoruje wejście, zasugeruj użytkownikom korzystanie z zestawu słuchawkowego (np. przez wyświetlanie ekranu Najlepsze ze słuchawkami przy pierwszym uruchomieniu). Notatka że samo użycie zestawu słuchawkowego nie gwarantuje najniższego możliwego opóźnienia. Może być konieczne wykonaj inne czynności, by usunąć przetwarzanie niechcianych sygnałów ze ścieżki audio, np. przez za pomocą funkcji VOICE_RECOGNITION gotowe podczas nagrywania.
  • Przygotuj się na obsługę nominalnych częstotliwości próbkowania 44 100 i 48 000 Hz według raportu getproperty(String) dla PROPERTY_OUTPUT_SAMPLE_RATE. Inne częstotliwości próbkowania są możliwe, ale są one rzadkie.
  • Przygotuj się na obsługę rozmiaru bufora zgłaszanego przez getproperty(String) dla PROPERTY_OUTPUT_FRAMES_PER_BUFFER. Typowe rozmiary bufora to 96, 128, 160, 192, 240, 256, lub 512, ale możliwe są też inne wartości.

Minimalizowanie opóźnienia wyjściowego

Użyj optymalnej częstotliwości próbkowania podczas tworzenia odtwarzacza dźwięku

Aby uzyskać jak najkrótszy czas oczekiwania, należy dostarczyć dane audio pasujące do optymalnej częstotliwości próbkowania i rozmiaru bufora. Więcej informacji: Projektowanie z myślą o krótszym czasie oczekiwania.

W Javie można uzyskać optymalną częstotliwość próbkowania z modułu AudioManager: przykładowy kod:

Kotlin

val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val sampleRateStr: String? = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE)
var sampleRate: Int = sampleRateStr?.let { str ->
    Integer.parseInt(str).takeUnless { it == 0 }
} ?: 44100 // Use a default value if property not found

Java

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

Po ustaleniu optymalnej częstotliwości próbkowania możesz ją podać podczas tworzenia odtwarzacza. W tym przykładzie użyto 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;
   ...
}

Uwaga: samplesPerSec dotyczy częstotliwości próbkowania na kanał w ms (1 Hz = 1000 mHz).

Użyj optymalnego rozmiaru bufora do umieszczania danych audio w kolejce

Optymalny rozmiar bufora można uzyskać w podobny sposób jak optymalną częstotliwość próbkowania, stosując metodę Interfejs API AudioManager:

Kotlin

val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val framesPerBuffer: String? = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)
var framesPerBufferInt: Int = framesPerBuffer?.let { str ->
    Integer.parseInt(str).takeUnless { it == 0 }
} ?: 256 // Use default

Java

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

Właściwość PROPERTY_OUTPUT_FRAMES_PER_BUFFER wskazuje liczbę klatek dźwięku. który może przechowywać bufor HAL (Hardware Abstraction Layer). Musisz utworzyć dźwięk jest buforowana tak, aby zawierała dokładną wielokrotność tej liczby. Jeśli użyjesz prawidłowego numeru klatek audio, wywołania zwrotne występują w regularnych odstępach czasu, co pozwala zmniejszyć zakłócenia.

Do określenia rozmiaru bufora należy używać interfejsu API, a nie wartości zakodowanej na stałe, bo rozmiar bufora HAL różni się w zależności od urządzenia i wersji Androida.

Nie dodawaj interfejsów wyjściowych obejmujące przetwarzanie sygnałów

Szybki mikser obsługuje tylko te interfejsy:

  • SL_IID_ANDROIDSIMPLEBUFFERQUEUE
  • SL_IID_VOLUME
  • SL_IID_MUTESOLO

Te interfejsy są niedozwolone, ponieważ wymagają przetwarzania sygnałów i powodują prośba o skrócenie czasu do odrzucenia:

  • SL_IID_BASSBOOST
  • SL_IID_SKUSEND
  • SL_IID_ENVIRONMENTALREVERB
  • SL_IID_EQUALIZER
  • SL_IID_PLAYBACKRATE
  • SL_IID_PRESETREVERB
  • SL_IID_VIRTUALIZER
  • SL_IID_ANDROID
  • SL_IID_ANDROIDSEND

Podczas tworzenia odtwarzacza pamiętaj, aby dodawać tylko szybkie interfejsy, jak pokazano w następujący przykład:

const SLInterfaceID interface_ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };

Sprawdź, czy używasz ścieżki niskiego opóźnienia

Wykonaj te czynności, aby sprawdzić, czy udało Ci się pobrać ścieżkę o małym opóźnieniu:

  1. Uruchom aplikację, a następnie uruchom to polecenie:
  2. adb shell ps | grep your_app_name
    
  3. Zanotuj identyfikator procesu aplikacji.
  4. Teraz włącz dźwięk z aplikacji. Masz około 3 sekund na uruchomienie następujące polecenie z terminala:
  5. adb shell dumpsys media.audio_flinger
    
  6. Zeskanuj, aby znaleźć identyfikator procesu. Jeśli w kolumnie Nazwa widzisz literę F, oznacza to, że znajduje się ona na ścieżka z małym opóźnieniem (F oznacza szybki utwór).

Minimalizuj czas oczekiwania na rozgrzewkę

Dodanie danych dźwiękowych do kolejki po raz pierwszy zajmuje niewiele, czas na rozgrzanie obwodu audio urządzenia. Aby uniknąć tego opóźnienia na rozgrzewkę, możesz: do umieszczenia w kolejce buforów danych audio zawierających ciszę, jak widać w tym przykładowym kodzie:

#define CHANNELS 1
static short* silenceBuffer;
int numSamples = frames * CHANNELS;
silenceBuffer = malloc(sizeof(*silenceBuffer) * numSamples);
    for (i = 0; i<numSamples; i++) {
        silenceBuffer[i] = 0;
    }

W momencie, gdy chcesz nagrać dźwięk, możesz przełączyć się na bufory kolejki zawierające prawdziwych danych dźwiękowych.

Uwaga: ciągłe odtwarzanie dźwięku wiąże się ze znacznym zużyciem energii. Pamiętaj, by zatrzymać w funkcji onPause(). Rozważ też wstrzymanie cichych wyników po określonym czasie bezczynności użytkownika.

Dodatkowy przykładowy kod

Aby pobrać przykładową aplikację, która pokazuje opóźnienie dźwięku, zobacz Próbki NDK.

Więcej informacji

  1. Opóźnienie dźwięku dla deweloperów aplikacji
  2. Opóźnienie dźwięku
  3. Mierzenie opóźnienia dźwięku
  4. Rozgrzewka dźwiękowa
  5. Czas oczekiwania (dźwięk)
  6. Czas opóźnienia w obie strony