Latenza audio

La latenza è il tempo necessario per far viaggiare un segnale attraverso un sistema. Questi sono i casi in cui tipi di latenza relativi alle app audio:

  • La latenza dell'output audio è il tempo che intercorre tra la generazione di un campione audio da parte di una e il Sample riprodotto tramite il jack per cuffie o l'altoparlante integrato.
  • La latenza dell'input audio è il tempo che intercorre tra la ricezione di un segnale audio da parte di l'input audio di un dispositivo, come il microfono, e gli stessi dati audio disponibili per un dell'app.
  • La latenza di round trip è la somma della latenza di input, del tempo di elaborazione dell'app e e la latenza dell'output.

  • La latenza al tocco è il tempo che intercorre tra un utente che tocca lo schermo e quella evento tocco ricevuto da un'app.
  • La latenza di riscaldamento è il tempo necessario per avviare la pipeline audio la prima i dati relativi all'ora in cui vengono accodati in un buffer.

In questa pagina viene descritto come sviluppare l'app audio con input e output a bassa latenza e come evitare latenza di riscaldamento.

Misura la latenza

È difficile misurare la latenza dell'ingresso e dell'uscita audio in modo isolato poiché è necessario conoscere esattamente quando il primo campione viene inviato nel percorso audio (anche se per eseguire questa operazione è possibile di test della luce e un oscilloscopio). Se conosci la latenza audio andata e ritorno, puoi utilizza la regola generale: la latenza dell'input (e dell'output) audio è metà della latenza audio di andata e ritorno su percorsi senza elaborazione degli indicatori.

La latenza audio andata e ritorno varia notevolmente a seconda del modello di dispositivo e Build Android. Puoi farti un'idea approssimativa della risposta per i dispositivi Nexus leggendo misurazioni pubblicate.

Puoi misurare la latenza audio di andata e ritorno creando un'app che genera un segnale audio. ascolta il segnale e misura il tempo che intercorre tra l'invio e la ricezione.

Poiché la latenza più bassa si ottiene su percorsi audio con un'elaborazione minima del segnale, potresti vuoi usare anche Chiavetta di loopback audio, che permette di eseguire il test sul connettore delle cuffie.

Best practice per ridurre al minimo la latenza

Convalida le prestazioni audio

Il Compatibility Definition Document (CDD) di Android elenca le risorse hardware e software i requisiti di un dispositivo Android compatibile. Vedi Compatibilità Android per ulteriori informazioni sul programma di compatibilità generale. . CDD per il documento CDD effettivo.

Nel CDD, la latenza di round trip è specificata come 20 ms o inferiore (anche se i musicisti richiede in genere 10 ms). Questo perché esistono casi d'uso importanti che possono essere 20 ms

Al momento non esiste un'API per determinare la latenza audio su qualsiasi percorso su un dispositivo Android all'indirizzo runtime. Tuttavia, puoi usare i seguenti flag delle funzionalità hardware per scoprire se dispositivo garantisce la latenza:

I criteri per la segnalazione di questi flag sono definiti nella CDD nelle sezioni 5.6 Latenza audio e 5.10 Audio professionale.

Per verificare le seguenti funzionalità in Java:

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);

Per quanto riguarda la relazione delle caratteristiche audio, il android.hardware.audio.low_latency è un prerequisito per android.hardware.audio.pro. Un dispositivo può implementare android.hardware.audio.low_latency e non android.hardware.audio.pro, ma non viceversa.

Non fare ipotesi sulle prestazioni audio

Per evitare problemi di latenza, tieni presente le seguenti ipotesi:

  • Non dare per scontato che gli altoparlanti e i microfoni utilizzati nei dispositivi mobili abbiano in genere buoni acustica. A causa delle loro dimensioni ridotte, l'acustica è generalmente scadente, quindi l'elaborazione del segnale è aggiunti per migliorare la qualità audio. Questa elaborazione dei segnali introduce latenza.
  • Non dare per scontato che i callback di input e di output siano sincronizzati. Per input simultaneo e output, vengono utilizzati gestori di completamento separati della coda del buffer per ogni lato. Non esiste garanzia dell'ordine relativo di questi callback o della sincronizzazione degli orologi audio, anche quando entrambe le parti utilizzano la stessa frequenza di campionamento. L'applicazione deve eseguire il buffering dei dati con una corretta sincronizzazione del buffer.
  • Non presumere che la frequenza di campionamento effettiva corrisponda esattamente alla frequenza di campionamento nominale. Per Ad esempio, se la frequenza di campionamento nominale è di 48.000 Hz, è normale che l'orologio audio avanza con una velocità leggermente diversa rispetto a quella del sistema operativo CLOCK_MONOTONIC. Questo perché l'audio e gli orologi di sistema possono derivare da cristalli diversi.
  • Non dare per scontato che la frequenza di campionamento di riproduzione effettiva corrisponda esattamente all'effettivo campione di acquisizione dei dati, soprattutto se gli endpoint si trovano su percorsi separati. Ad esempio, se stai acquisendo microfono sul dispositivo a una frequenza di campionamento nominale di 48.000 Hz e riproduzione su audio USB con una frequenza di campionamento nominale di 48.000 Hz, è probabile che le frequenze di campionamento effettive siano leggermente diverse l'uno dall'altro.

Una conseguenza di orologi audio potenzialmente indipendenti è la necessità di una frequenza di campionamento asincrona e conversione in blocco. Una tecnica semplice (anche se non ideale per la qualità audio) per la frequenza di campionamento asincrona la conversione consiste nel duplicare o rilasciare campioni secondo necessità in prossimità di un punto di zero crossing. Altro sono possibili conversioni sofisticate.

Riduci al minimo la latenza dell'input

Questa sezione fornisce suggerimenti per aiutarti a ridurre la latenza dell'input audio durante la registrazione con una un microfono incorporato o un microfono esterno per le cuffie.

  • Se la tua app sta monitorando l'input, suggerisci agli utenti di utilizzare le cuffie (ad esempio, mostrando la schermata Migliore con le cuffie alla prima esecuzione). Nota che il semplice utilizzo delle cuffie non garantisce la latenza più bassa possibile. Potresti dover eseguire altri passaggi per rimuovere l'elaborazione del segnale indesiderato dal percorso audio, ad esempio utilizzando VOICE_RECOGNITION preimpostato durante la registrazione.
  • Essere preparati a gestire frequenze di campionamento nominali di 44.100 e 48.000 Hz come riportato da getProperty(String) per . PROPERTY_OUTPUT_sample_RATE Sono possibili altre frequenze di campionamento, ma è raro.
  • Preparati a gestire la dimensione del buffer riportata da getProperty(String) per . PROPERTY_OUTPUT_FRAMES_PER_BUFFER Le dimensioni tipiche dei buffer includono 96, 128, 160, 192, 240, 256, o 512 frame, ma sono possibili altri valori.

Riduci al minimo la latenza di output

Utilizzare la frequenza di campionamento ottimale quando crei il lettore audio

Per ottenere la latenza più bassa, devi fornire dati audio che corrispondano ai valori ottimali del dispositivo frequenza di campionamento e dimensione del buffer. Per ulteriori informazioni, vedi Progetta per una latenza ridotta.

In Java, puoi ottenere la frequenza di campionamento ottimale da AudioManager, come illustrato di seguito esempio di codice:

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

Una volta individuata la frequenza di campionamento ottimale, puoi fornirla durante la creazione del player. In questo esempio viene utilizzato 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;
   ...
}

Nota: samplesPerSec si riferisce alla frequenza di campionamento per canale in millihertz (1 Hz = 1000 mHz).

Utilizza la dimensione del buffer ottimale per accodare i dati audio

È possibile ottenere la dimensione ottimale del buffer in modo simile alla frequenza di campionamento ottimale, utilizzando 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

La La proprietà PROPERTY_OUTPUT_FRAMES_PER_BUFFER indica il numero di frame audio che il buffer HAL (Hardware Abstraction Layer) può contenere. Dovresti creare l'audio buffer in modo che contengano un multiplo esatto di questo numero. Se usi il numero corretto dei frame audio, i callback vengono eseguiti a intervalli regolari, riducendo il tremolio.

È importante utilizzare l'API per determinare la dimensione del buffer anziché utilizzare un valore impostato come hardcoded, perché le dimensioni del buffer HAL variano da un dispositivo all'altro e da una build Android all'altra.

Non aggiungere interfacce di output che prevedono l'elaborazione dei segnali

Il mixer rapido supporta solo queste interfacce:

  • SL_IID_ANDROIDSIMPLEBUFFERQUEUE
  • SL_IID_VOLUME
  • SL_IID_MUTESOLO

Queste interfacce non sono consentite perché comportano l'elaborazione dei segnali e causano la tua richiesta di accesso rapido:

  • SL_IID_BASSBOOST
  • SL_IID_EFFETTIEND
  • REVERB_AMBIENTE_SL_IID
  • SL_IID_EQUALIZER
  • SL_IID_PLAYBACKRATE
  • SL_IID_PRESETREVERB
  • VIRTUALIZZATORE_SL_IID
  • SL_IID_ANDROIDE
  • SL_IID_ANDROID EFFETTIEND

Quando crei il player, assicurati di aggiungere solo interfacce veloci, come mostrato in nell'esempio seguente:

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

Verificare di utilizzare una traccia a bassa latenza

Completa questi passaggi per verificare di aver ottenuto correttamente un percorso a bassa latenza:

  1. Avvia l'app ed esegui questo comando:
  2. adb shell ps | grep your_app_name
    
  3. Prendi nota dell'ID processo dell'app.
  4. Ora riproduci un po' di audio dall'app. Hai circa tre secondi di tempo per eseguire questo comando dal terminale:
  5. adb shell dumpsys media.audio_flinger
    
  6. Cerca l'ID di processo. Se vedi una F nella colonna Nome, significa che si trova in una traccia a bassa latenza (la F sta per fast track).

Riduci al minimo la latenza di riscaldamento

Quando accoda i dati audio per la prima volta, la configurazione richiede una piccola ma il tempo necessario per il riscaldamento del circuito audio del dispositivo. Per evitare la latenza di riscaldamento, puoi accoda buffer di dati audio contenenti silenzio, come mostrato nel seguente esempio di codice:

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

Nel momento in cui deve essere prodotto l'audio, puoi passare ai buffer di accodamento contenenti dati audio reali.

Nota: l'uscita costante di contenuti audio comporta un consumo energetico significativo. Assicurati di interrompere l'output nel onPause(). Inoltre, valuta la possibilità di mettere in pausa l'output silenzioso dopo un certo periodo di inattività dell'utente.

Codice campione aggiuntivo

Per scaricare un'app di esempio che mostri la latenza audio, consulta: Campioni NDK.

Per ulteriori informazioni

  1. Latenza audio per sviluppatori di app
  2. Hanno contribuito alla latenza audio
  3. Misurazione della latenza audio
  4. Riscaldamento audio
  5. Latenza (audio)
  6. Tempo di ritardo andata e ritorno