A-Audio

AAudio ist eine neue Android C API, die mit der Android O-Version eingeführt wird. Es wurde für leistungsstarke Audioanwendungen entwickelt, die eine niedrige Latenz erfordern. Apps kommunizieren mit AAudio, indem sie Daten lesen und in Streams schreiben.

Die AAudio API ist minimal aufgebaut und führt folgende Funktionen nicht aus:

  • Liste von Audiogeräten
  • Automatisiertes Routing zwischen Audioendpunkten
  • Datei-E/A
  • Decodierung komprimierter Audioinhalte
  • Automatische Darstellung aller Eingaben/Streams in einem einzigen Callback.

Erste Schritte

Sie können AAudio auch über C++-Code aufrufen. Fügen Sie die AAudio.h-Header-Datei hinzu, um Ihrer App den AAudio-Funktionssatz hinzuzufügen:

#include <aaudio/AAudio.h>

Audiostreams

Über „Audio“ werden Audiodaten zwischen Ihrer App und den Audioein- und -ausgängen Ihres Android-Geräts übertragen. Ihre App übergibt Daten durch das Lesen und Schreiben in Audiostreams, dargestellt durch die Struktur AAudioStream. Die Lese-/Schreibaufrufe können blockieren oder nicht blockieren.

Ein Stream wird so definiert:

  • Das Audiogerät, das die Quelle oder Senke für die Daten im Stream ist.
  • Der Freigabemodus bestimmt, ob ein Stream exklusiven Zugriff auf ein Audiogerät hat, das ansonsten von mehreren Streams genutzt werden könnte.
  • Das Format der Audiodaten im Stream.

Audiogerät

Jeder Stream ist an ein einzelnes Audiogerät angehängt.

Ein Audiogerät ist eine Hardwareschnittstelle oder ein virtueller Endpunkt, die bzw. der als Quelle oder Senke für einen kontinuierlichen Stream digitaler Audiodaten dient. Verwechseln Sie ein Audiogerät (ein integriertes Mikrofon oder Bluetooth-Headset) nicht mit dem Android-Gerät (dem Smartphone oder der Smartwatch), auf dem Ihre App ausgeführt wird.

Mit der AudioManager-Methode getDevices() können Sie die Audiogeräte finden, die auf Ihrem Android-Gerät verfügbar sind. Die Methode gibt Informationen zum type jedes Geräts zurück.

Jedes Audiogerät hat auf dem Android-Gerät eine eindeutige ID. Mithilfe der ID können Sie einen Audiostream mit einem bestimmten Audiogerät verknüpfen. In den meisten Fällen kannst du AAudio aber das primäre Standardgerät auswählen lassen, anstatt eines selbst anzugeben.

Das an einen Stream angeschlossene Audiogerät bestimmt, ob der Stream für die Ein- oder Ausgabe verwendet wird. In einem Stream können Daten nur in eine Richtung verschoben werden. Wenn Sie einen Stream definieren, legen Sie auch seine Richtung fest. Wenn du einen Stream öffnest, prüft Android, ob Audiogerät und Streamrichtung übereinstimmen.

Freigabemodus

Ein Stream hat einen Freigabemodus:

  • AAUDIO_SHARING_MODE_EXCLUSIVE bedeutet, dass der Stream exklusiven Zugriff auf sein Audiogerät hat und nicht von einem anderen Audiostream verwendet werden kann. Wenn das Audiogerät bereits verwendet wird, erhält der Stream möglicherweise keinen exklusiven Zugriff. Exklusive Streams weisen wahrscheinlich eine geringere Latenz auf, aber sie werden auch mit höherer Wahrscheinlichkeit getrennt. Du solltest exklusive Streams schließen, sobald du sie nicht mehr benötigst, damit andere Apps auf das Gerät zugreifen können. Exklusive Streams bieten die geringstmögliche Latenz.
  • AAUDIO_SHARING_MODE_SHARED ermöglicht AAudio, Audioinhalte zu mischen. Mit AAudio werden alle freigegebenen Streams gemischt, die demselben Gerät zugewiesen sind.

Sie können den Freigabemodus beim Erstellen eines Streams explizit festlegen. Der Freigabemodus ist standardmäßig SHARED.

Audioformat

Die über einen Stream übergebenen Daten haben die üblichen digitalen Audioattribute. Diese sind folgende:

  • Beispieldatenformat
  • Kanalanzahl (Beispiele pro Frame)
  • Abtastrate

AAudio unterstützt folgende Beispielformate:

Audioformat_t C-Datentyp Hinweise
AAUDIO-FORMAT_PCM_I16 Int16_t Gängige 16-Bit-Beispiele, Q0.15-Format
AAUDIO-FORMAT_PCM_FLOAT schweben -1,0 bis +1,0
AAUDIOFORMAT_PCM_I24_PAKET uint8_t in Dreiergruppen gepackte 24-Bit-Beispiele, Q0.23-Format
AAUDIO-FORMAT_PCM_I32 int32_t Gängige 32-Bit-Beispiele, Q0.31-Format
AAUDIO-FORMAT_IEC61937 Uint8_t Komprimiertes Audio, verpackt in IEC61937 für HDMI- oder S/PDIF-Passthrough

Wenn Sie ein bestimmtes Beispielformat anfordern, verwendet der Stream dieses Format, auch wenn das Format für das Gerät nicht optimal ist. Wenn Sie kein Beispielformat angeben, wählt AAudio das optimale Format aus. Nachdem der Stream geöffnet wurde, müssen Sie das Beispieldatenformat abfragen und dann bei Bedarf Daten konvertieren, wie in diesem Beispiel:

aaudio_format_t dataFormat = AAudioStream_getDataFormat(stream);
//... later
if (dataFormat == AAUDIO_FORMAT_PCM_I16) {
     convertFloatToPcm16(...)
}

Audiostream erstellen

Die AAudio-Bibliothek folgt einem Builder-Designmuster und stellt AAudioStreamBuilder bereit.

  1. Erstellen Sie einen AAudioStreamBuilder:

    AAudioStreamBuilder *builder;
    aaudio_result_t result = AAudio_createStreamBuilder(&builder);
    

  2. Legen Sie die Konfiguration des Audiostreams im Builder mithilfe der Builder-Funktionen fest, die den Streamparametern entsprechen. Diese optionalen Set-Funktionen sind verfügbar:

    AAudioStreamBuilder_setDeviceId(builder, deviceId);
    AAudioStreamBuilder_setDirection(builder, direction);
    AAudioStreamBuilder_setSharingMode(builder, mode);
    AAudioStreamBuilder_setSampleRate(builder, sampleRate);
    AAudioStreamBuilder_setChannelCount(builder, channelCount);
    AAudioStreamBuilder_setFormat(builder, format);
    AAudioStreamBuilder_setBufferCapacityInFrames(builder, frames);
    

    Beachten Sie, dass diese Methoden keine Fehler melden, z. B. eine nicht definierte Konstante oder ein Wert außerhalb des zulässigen Bereichs.

    Wenn Sie keine deviceId angeben, wird standardmäßig das primäre Ausgabegerät verwendet. Wenn Sie keine Streamrichtung angeben, wird standardmäßig ein Ausgabestream verwendet. Für alle anderen Parameter können Sie explizit einen Wert festlegen. Alternativ können Sie den optimalen Wert vom System zuweisen lassen, indem Sie den Parameter überhaupt nicht angeben oder auf AAUDIO_UNSPECIFIED setzen.

    Überprüfen Sie daher den Status des Audiostreams, nachdem Sie ihn erstellt haben, wie unten in Schritt 4 beschrieben.

  3. Wenn AAudioStreamBuilder konfiguriert ist, können Sie damit einen Stream erstellen:

    AAudioStream *stream;
    result = AAudioStreamBuilder_openStream(builder, &stream);
    

  4. Prüfen Sie nach dem Erstellen des Streams seine Konfiguration. Wenn Sie ein Stichprobenformat, eine Abtastrate oder Stichproben pro Frame angegeben haben, ändern sich diese nicht. Wenn Sie einen Freigabemodus oder eine Pufferkapazität angegeben haben, können sich diese je nach den Funktionen des Audiogeräts des Streams und des Android-Geräts, auf dem er ausgeführt wird, ändern. Im Interesse einer guten defensiven Programmierung sollten Sie die Konfiguration des Streams überprüfen, bevor Sie ihn verwenden. Es gibt Funktionen zum Abrufen der Streameinstellung, die der jeweiligen Builder-Einstellung entspricht:

    AAudioStreamBuilder_setDeviceId() AAudioStream_getDeviceId()
    AAudioStreamBuilder_setDirection() AAudioStream_getDirection()
    AAudioStreamBuilder_setSharingMode() AAudioStream_getSharingMode()
    AAudioStreamBuilder_setSampleRate() AAudioStream_getSampleRate()
    AAudioStreamBuilder_setChannelCount() AAudioStream_getChannelCount()
    AAudioStreamBuilder_setFormat() AAudioStream_getFormat()
    AAudioStreamBuilder_setBufferCapacityInFrames() AAudioStream_getBufferCapacityInFrames()

  5. Du kannst den Builder speichern und später wiederverwenden, um weitere Streams zu erstellen. Wenn Sie sie jedoch nicht mehr verwenden möchten, sollten Sie sie löschen.

    AAudioStreamBuilder_delete(builder);
    

Audiostream verwenden

Statusübergänge

Ein AAudio-Stream befindet sich normalerweise in einem von fünf stabilen Status (der Fehlerstatus "Getrennt" wird am Ende dieses Abschnitts beschrieben):

  • Öffnen
  • Gestartet
  • Pausiert
  • Errötet
  • Beendet

Daten fließen nur dann durch einen Stream, wenn er den Status „Gestartet“ hat. Verwenden Sie eine der Funktionen, die einen Statusübergang anfordern, um einen Stream zwischen Zuständen zu verschieben:

aaudio_result_t result;
result = AAudioStream_requestStart(stream);
result = AAudioStream_requestStop(stream);
result = AAudioStream_requestPause(stream);
result = AAudioStream_requestFlush(stream);

Sie können das Pausieren oder Leeren nur für einen Ausgabestream anfordern:

Diese Funktionen sind asynchron und die Statusänderung erfolgt nicht sofort. Wenn Sie eine Statusänderung anfordern, verschiebt der Stream einen der entsprechenden vorübergehenden Zustände:

  • Wird gestartet
  • Wird pausiert
  • Spülen
  • Wird beendet
  • Closing

Das folgende Zustandsdiagramm zeigt die stabilen Status als abgerundete Rechtecke und die Vorübergehenden Zustände als gepunktete Rechtecke. Auch wenn es nicht angezeigt wird, kannst du close() von jedem Status aus aufrufen

AAudio-Lebenszyklus

AAudio bietet keine Callbacks, mit denen Sie auf Statusänderungen hingewiesen werden. Eine spezielle Funktion, AAudioStream_waitForStateChange(stream, inputState, nextState, timeout), kann verwendet werden, um auf eine Statusänderung zu warten.

Die Funktion erkennt keine Statusänderung selbst und wartet nicht auf einen bestimmten Zustand. Es wird gewartet, bis der aktuelle Status von dem von Ihnen angegebenen inputState-Wert unterscheidet.

Nach der Anfrage zum Pausieren sollte ein Stream beispielsweise sofort in den vorübergehenden Status „Pausiert“ wechseln und später in den Status „Pausiert“ eintreten. Es gibt jedoch keine Garantie, dass dies der Fall ist. Da Sie nicht auf den Status „Pausiert“ warten können, verwenden Sie waitForStateChange(), um auf einen anderen Status als „Pausieren“ zu warten. Und so funktioniert es:

aaudio_stream_state_t inputState = AAUDIO_STREAM_STATE_PAUSING;
aaudio_stream_state_t nextState = AAUDIO_STREAM_STATE_UNINITIALIZED;
int64_t timeoutNanos = 100 * AAUDIO_NANOS_PER_MILLISECOND;
result = AAudioStream_requestPause(stream);
result = AAudioStream_waitForStateChange(stream, inputState, &nextState, timeoutNanos);

Wenn der Status des Streams nicht pausiert ist (der inputState, der zum Zeitpunkt des Aufrufs der aktuelle Status war), wird die Funktion sofort zurückgegeben. Andernfalls wird sie blockiert, bis der Status nicht mehr pausiert wird oder das Zeitlimit abläuft. Wenn die Funktion zurückgegeben wird, zeigt der Parameter nextState den aktuellen Status des Streams an.

Sie können dieselbe Technik verwenden, nachdem Sie das Starten, Beenden oder Leeren von Anfragen aufgerufen haben und dabei den entsprechenden vorübergehenden Status als „inputState“ verwenden. Rufen Sie waitForStateChange() nach dem Aufruf von AAudioStream_close() nicht auf, da der Stream gelöscht wird, sobald er geschlossen wird. Rufen Sie AAudioStream_close() nicht auf, während waitForStateChange() in einem anderen Thread ausgeführt wird.

Lesen und Schreiben in einem Audiostream

Es gibt zwei Möglichkeiten, die Daten in einem Stream nach dem Start zu verarbeiten:

Legen Sie für einen blockierenden Lese- oder Schreibvorgang, der die angegebene Anzahl von Frames überträgt, einen Wert größer als null fest. Für einen nicht blockierenden Anruf setzen Sie timeNanos auf null. In diesem Fall ist das Ergebnis die tatsächliche Anzahl der übertragenen Frames.

Wenn Sie eine Eingabe lesen, sollten Sie prüfen, ob die richtige Anzahl von Frames gelesen wurde. Andernfalls enthält der Zwischenspeicher möglicherweise unbekannte Daten, die einen Audiofehler verursachen können. Sie können den Puffer mit Nullen auffüllen, um einen stummen Dropout zu erzeugen:

aaudio_result_t result =
    AAudioStream_read(stream, audioData, numFrames, timeout);
if (result < 0) {
  // Error!
}
if (result != numFrames) {
  // pad the buffer with zeros
  memset(static_cast<sample_type*>(audioData) + result * samplesPerFrame, 0,
      sizeof(sample_type) * (numFrames - result) * samplesPerFrame);
}

Du kannst den Puffer des Streams aufrüsten, bevor du ihn startest, indem du Daten in ihn schreibst oder stummschaltest. Das muss in einem nicht blockierenden Anruf erfolgen, bei dem „timeNanos“ auf null gesetzt ist.

Die Daten im Zwischenspeicher müssen dem von AAudioStream_getDataFormat() zurückgegebenen Datenformat entsprechen.

Audiostream schließen

Wenn Sie einen Stream nicht mehr benötigen, schließen Sie ihn:

AAudioStream_close(stream);

Nachdem Sie einen Stream geschlossen haben, können Sie ihn nicht mehr mit einer AAudio-Stream-basierten Funktion verwenden.

Nicht verbundener Audiostream

Die Verbindung eines Audiostreams kann jederzeit getrennt werden, wenn eines der folgenden Ereignisse eintritt:

  • Das zugehörige Audiogerät ist nicht mehr verbunden, z. B. wenn Kopfhörer getrennt sind.
  • Intern ist ein Fehler aufgetreten.
  • Ein Audiogerät ist nicht mehr das primäre Audiogerät.

Wenn die Verbindung zu einem Stream getrennt wird, erhält er den Status "Getrennt" und jeder Versuch, AAudioStream_write() oder andere Funktionen auszuführen, gibt einen Fehler zurück. Ein getrennter Stream muss unabhängig vom Fehlercode immer beendet und geschlossen werden.

Wenn Sie einen Daten-Callback verwenden (im Gegensatz zu einer der direkten Lese-/Schreibmethoden), erhalten Sie keinen Rückgabecode, wenn der Stream getrennt wird. Wenn Sie darüber informiert werden möchten, schreiben Sie eine AAudioStream_errorCallback-Funktion und registrieren Sie sie mit AAudioStreamBuilder_setErrorCallback().

Wenn Sie in einem Fehler-Callback-Thread über das Trennen der Verbindung informiert werden, muss das Beenden und Schließen des Streams über einen anderen Thread erfolgen. Andernfalls könnte es zu einem Deadlock kommen.

Wenn du einen neuen Stream öffnest, kann dieser möglicherweise andere Einstellungen als der ursprüngliche Stream haben (z. B. FramesPerBurst):

void errorCallback(AAudioStream *stream,
                   void *userData,
                   aaudio_result_t error) {
    // Launch a new thread to handle the disconnect.
    std::thread myThread(my_error_thread_proc, stream, userData);
    myThread.detach(); // Don't wait for the thread to finish.
}

Leistungsoptimierung

Sie können die Leistung einer Audioanwendung optimieren, indem Sie ihre internen Puffer anpassen und spezielle Threads mit hoher Priorität verwenden.

Puffer zur Minimierung der Latenz abstimmen

AAudio übergibt Daten an und aus den internen Puffern, die es verwaltet, einem für jedes Audiogerät.

Die Kapazität des Puffers ist die Gesamtmenge der Daten, die ein Puffer enthalten kann. Rufen Sie AAudioStreamBuilder_setBufferCapacityInFrames() auf, um die Kapazität festzulegen. Bei dieser Methode wird die Kapazität, die Sie zuweisen können, auf den vom Gerät maximal zulässigen Wert begrenzt. Mit AAudioStream_getBufferCapacityInFrames() können Sie die tatsächliche Kapazität des Zwischenspeichers prüfen.

Eine Anwendung muss nicht die gesamte Kapazität eines Puffers nutzen. AAudio füllt einen Zwischenspeicher bis zu einer von Ihnen festgelegten Größe. Die Größe eines Puffers darf nicht größer als seine Kapazität sein und ist häufig kleiner. Indem Sie die Puffergröße steuern, bestimmen Sie die Anzahl der Bursts, die zum Füllen erforderlich sind, und steuern damit die Latenz. Verwenden Sie die Methoden AAudioStreamBuilder_setBufferSizeInFrames() und AAudioStreamBuilder_getBufferSizeInFrames(), um mit der Puffergröße zu arbeiten.

Wenn eine Anwendung Audioinhalte wiedergibt, schreibt sie in einen Puffer und blockiert, bis der Schreibvorgang abgeschlossen ist. AAudio liest aus dem Zwischenspeicher in diskreten Bursts. Jeder Burst enthält mehrere Audioframes und ist in der Regel kleiner als die Größe des zu lesenden Zwischenspeichers. Das System steuert die Burst-Größe und -Rate. Diese Eigenschaften werden in der Regel durch den Schaltkreis des Audiogeräts bestimmt. Sie können die Größe eines Bursts oder die Burst-Rate nicht ändern. Sie können aber die Größe des internen Puffers entsprechend der Anzahl der enthaltenen Bilder festlegen. Im Allgemeinen erzielen Sie die niedrigste Latenz, wenn die Puffergröße von AAudioStream ein Vielfaches der gemeldeten Burst-Größe ist.

      AAudio-Zwischenspeicherung

Eine Möglichkeit zur Optimierung der Puffergröße besteht darin, mit einem großen Puffer zu beginnen und diesen nach und nach zu verringern, bis Unterlauf eintritt. Anschließend bewegen Sie ihn wieder nach oben. Alternativ können Sie mit einer kleinen Puffergröße beginnen und, falls dies zu Unterläufen führt, die Puffergröße erhöhen, bis die Ausgabe wieder sauber fließt.

Dieser Vorgang kann sehr schnell ablaufen, möglicherweise bevor der Nutzer den ersten Ton wiedergibt. Es kann sinnvoll sein, zuerst die anfängliche Puffergröße mit Stummschaltung festzulegen, damit der Nutzer keine Audiostörungen hören kann. Die Systemleistung kann sich im Laufe der Zeit ändern, z. B. wenn der Nutzer den Flugmodus deaktiviert. Da die Zwischenspeicherabstimmung nur sehr wenig Aufwand verursacht, kann Ihre Anwendung dies kontinuierlich tun, während die Anwendung Daten liest oder in einen Stream schreibt.

Hier ist ein Beispiel für eine Pufferoptimierungsschleife:

int32_t previousUnderrunCount = 0;
int32_t framesPerBurst = AAudioStream_getFramesPerBurst(stream);
int32_t bufferSize = AAudioStream_getBufferSizeInFrames(stream);

int32_t bufferCapacity = AAudioStream_getBufferCapacityInFrames(stream);

while (go) {
    result = writeSomeData();
    if (result < 0) break;

    // Are we getting underruns?
    if (bufferSize < bufferCapacity) {
        int32_t underrunCount = AAudioStream_getXRunCount(stream);
        if (underrunCount > previousUnderrunCount) {
            previousUnderrunCount = underrunCount;
            // Try increasing the buffer size by one burst
            bufferSize += framesPerBurst;
            bufferSize = AAudioStream_setBufferSize(stream, bufferSize);
        }
    }
}

Es hat keinen Vorteil, mit diesem Verfahren die Puffergröße für einen Eingabestream zu optimieren. Eingabestreams werden so schnell wie möglich ausgeführt, versuchen, die Menge der zwischengespeicherten Daten auf ein Minimum zu beschränken, und füllen sich, wenn die Anwendung vorzeitig beendet wird.

Callback mit hoher Priorität verwenden

Wenn Ihre App Audiodaten aus einem gewöhnlichen Thread liest oder schreibt, werden sie möglicherweise vorzeitig beendet oder es kommt zu Zeitjitter. Dies kann zu Audiofehlern führen. Größere Puffer können vor solchen Störungen schützen. Ein großer Zwischenspeicher führt aber auch zu einer längeren Audiolatenz. Bei Anwendungen, die eine niedrige Latenz erfordern, kann ein Audiostream eine asynchrone Callback-Funktion verwenden, um Daten zu und von Ihrer App zu übertragen. AAudio führt den Callback in einem Thread mit höherer Priorität aus, der eine bessere Leistung bietet.

Die Callback-Funktion hat folgenden Prototyp:

typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames);

Verwende die Stream-Erstellung, um den Callback zu registrieren:

AAudioStreamBuilder_setDataCallback(builder, myCallback, myUserData);

Im einfachsten Fall führt der Stream regelmäßig die Callback-Funktion aus, um die Daten für den nächsten Burst zu erfassen.

Die Callback-Funktion sollte keinen Lese- oder Schreibvorgang für den Stream ausführen, der sie aufgerufen hat. Wenn der Callback zu einem Eingabestream gehört, sollte der Code die Daten verarbeiten, die im Zwischenspeicher „audioData“ (als drittes Argument angegeben) bereitgestellt werden. Wenn der Callback zu einem Ausgabestream gehört, sollten die Daten im Code im Zwischenspeicher platziert werden.

Sie könnten beispielsweise einen Callback verwenden, um kontinuierlich eine Sinuswellenausgabe wie diese zu generieren:

aaudio_data_callback_result_t myCallback(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames) {
    int64_t timeout = 0;

    // Write samples directly into the audioData array.
    generateSineWave(static_cast<float *>(audioData), numFrames);
    return AAUDIO_CALLABCK_RESULT_CONTINUE;
}

Es ist möglich, mehrere Streams mit AAudio zu verarbeiten. Sie können einen Stream als Master verwenden und Verweise auf andere Streams in den Nutzerdaten übergeben. Registrieren Sie einen Callback für den Master-Stream. Verwenden Sie dann nicht blockierende E/A für die anderen Streams. Hier ist ein Beispiel für einen Round-Trip-Callback, der einen Eingabestream an einen Ausgabestream übergibt. Der aufrufende Master ist der Ausgabestream. Der Eingabestream ist in den Nutzerdaten enthalten.

Der Callback führt einen nicht blockierenden Lesevorgang aus dem Eingabestream durch und platziert die Daten in den Zwischenspeicher des Ausgabestreams:

aaudio_data_callback_result_t myCallback(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames) {
    AAudioStream *inputStream = (AAudioStream *) userData;
    int64_t timeout = 0;
    aaudio_result_t result =
        AAudioStream_read(inputStream, audioData, numFrames, timeout);

  if (result == numFrames)
      return AAUDIO_CALLABCK_RESULT_CONTINUE;
  if (result >= 0) {
      memset(static_cast<sample_type*>(audioData) + result * samplesPerFrame, 0,
          sizeof(sample_type) * (numFrames - result) * samplesPerFrame);
      return AAUDIO_CALLBACK_RESULT_CONTINUE;
  }
  return AAUDIO_CALLBACK_RESULT_STOP;
}

Beachten Sie, dass in diesem Beispiel davon ausgegangen wird, dass die Eingabe- und Ausgabestreams dieselbe Anzahl von Kanälen, Format und Abtastrate haben. Das Format der Streams kann nicht übereinstimmen – vorausgesetzt, der Code verarbeitet die Übersetzungen korrekt.

Leistungsmodus festlegen

Jeder AAudioStream hat einen Leistungsmodus, der großen Einfluss auf das Verhalten deiner App hat. Es gibt drei Modi:

  • AAUDIO_PERFORMANCE_MODE_NONE ist der Standardmodus. Dabei kommt ein einfacher Stream zum Einsatz, der Latenz und Energieeinsparungen ausgleicht.
  • AAUDIO_PERFORMANCE_MODE_LOW_LATENCY verwendet kleinere Zwischenspeicher und einen optimierten Datenpfad, um die Latenz zu reduzieren.
  • AAUDIO_PERFORMANCE_MODE_POWER_SAVING verwendet größere interne Puffer und einen Datenpfad, bei dem die Latenz durch einen geringeren Energieverbrauch abgewogen wird.

Sie können den Leistungsmodus auswählen, indem Sie setPerformanceMode() aufrufen. Um den aktuellen Modus zu ermitteln, rufen Sie getPerformanceMode() auf.

Ist eine niedrige Latenz in Ihrer Anwendung wichtiger als Energieeinsparungen, verwenden Sie AAUDIO_PERFORMANCE_MODE_LOW_LATENCY. Das ist nützlich für Apps, die sehr interaktiv sind, wie Spiele oder Keyboard-Synthesizer.

Wenn in Ihrer Anwendung Energiesparen wichtiger ist als eine niedrige Latenz, verwenden Sie AAUDIO_PERFORMANCE_MODE_POWER_SAVING. Das ist typisch für Apps, die zuvor generierte Musik wiedergeben, z. B. Audiostreaming oder MIDI-Dateiplayer.

In der aktuellen Version von AAudio müssen Sie zum Erreichen der geringstmöglichen Latenz den Leistungsmodus AAUDIO_PERFORMANCE_MODE_LOW_LATENCY zusammen mit einem Callback mit hoher Priorität verwenden. Beispiel:

// Create a stream builder
AAudioStreamBuilder *streamBuilder;
AAudio_createStreamBuilder(&streamBuilder);
AAudioStreamBuilder_setDataCallback(streamBuilder, dataCallback, nullptr);
AAudioStreamBuilder_setPerformanceMode(streamBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);

// Use it to create the stream
AAudioStream *stream;
AAudioStreamBuilder_openStream(streamBuilder, &stream);

Threadsicherheit

Die AAudio API ist nicht vollständig threadsicher. Einige AAudio-Funktionen können nicht gleichzeitig von mehr als einem Thread aufgerufen werden. Das liegt daran, dass AAudio keine Mutexe verwendet, die zu einem vorzeitigen Beenden des Threads und Störungen führen können.

Sicherheitshalber sollten Sie nicht AAudioStream_waitForStateChange() aufrufen und nicht aus zwei verschiedenen Threads in denselben Stream lesen oder schreiben. Schließen Sie auch keinen Stream in einem Thread, während Sie in einem anderen Thread lesen oder darin schreiben.

Aufrufe, bei denen Streameinstellungen wie AAudioStream_getSampleRate() und AAudioStream_getChannelCount() zurückgegeben werden, sind Thread-sicher.

Diese Aufrufe sind außerdem Thread-sicher:

  • AAudio_convert*ToText()
  • AAudio_createStreamBuilder()
  • AAudioStream_get*() mit Ausnahme von AAudioStream_getTimestamp()

Bekannte Probleme

  • Die Audiolatenz ist hoch, um „write()“ zu blockieren, da die DP2-Version von Android O keinen FAST-Track verwendet. Verwenden Sie einen Callback, um die Latenz zu verringern.

Weitere Informationen

Weitere Informationen finden Sie in den folgenden Ressourcen:

API-Referenz

Codelabs

Videos