Audio

AAudio to nowy interfejs API Androida C wprowadzony w wersji Androida O. Zaprojektowano je z myślą o aplikacjach audio o wysokiej wydajności, które wymagają niewielkich opóźnień. Aplikacje komunikują się z AAudio, odczytując i zapisując dane w strumieniach.

Interfejs AAudio API został z założenia ograniczony do minimum i nie obsługuje tych funkcji:

  • Wyliczenie urządzeń audio
  • Automatyczne kierowanie między punktami końcowymi audio
  • Wejście-wyjście pliku
  • Dekodowanie skompresowanego dźwięku
  • Automatyczna prezentacja wszystkich danych wejściowych/strumieni w ramach jednego wywołania zwrotnego.

Wprowadzenie

Możesz wywołać AAudio z kodu C++. Aby dodać zestaw funkcji AAudio do aplikacji, dołącz plik nagłówka AAudio.h:

#include <aaudio/AAudio.h>

Strumienie audio

AAudio przenosi dane audio między aplikacją a wejściami i wyjściami audio na urządzeniu z Androidem. Aplikacja przekazuje dane do i z zewnątrz, odczytując i zapisu w strumieniach audio, które są reprezentowane przez strukturę AAudioStream. Wywołania odczytu/zapisu mogą blokować lub nie blokować.

Strumień definiuje się przez:

  • Urządzenie audio, które jest źródłem lub ujściem danych w strumieniu.
  • Tryb udostępniania, który określa, czy strumień ma wyłączny dostęp do urządzenia audio, które w innym przypadku mogłoby być współużytkowane przez wiele transmisji.
  • Format danych audio w strumieniu.

Urządzenie audio

Każdy strumień jest podłączony do jednego urządzenia audio.

Urządzenie audio to interfejs sprzętowy lub wirtualny punkt końcowy, który działa jako źródło lub ujście dla ciągłego strumienia danych audio cyfrowych. Nie pomyl urządzenia audio (wbudowanego mikrofonu lub zestawu słuchawkowego Bluetooth) z urządzeniem z Androidem (telefonem lub zegarkiem), na którym działa Twoja aplikacja.

Aby wykryć urządzenia audio dostępne na Twoim urządzeniu z Androidem, możesz użyć metody AudioManagergetDevices(). Metoda zwraca informacje o type każdego urządzenia.

Każde urządzenie audio ma niepowtarzalny identyfikator na urządzeniu z Androidem. Możesz użyć identyfikatora, aby powiązać strumień audio z konkretnym urządzeniem audio. Jednak w większości przypadków możesz pozwolić AAudio wybrać domyślne urządzenie główne, zamiast określać je samodzielnie.

Urządzenie audio podłączone do strumienia określa, czy jest to strumień wejściowy czy wyjściowy. Strumień może przenosić dane tylko w jednym kierunku. Definiując strumień, określasz też jego kierunek. Gdy otwierasz strumień, Android sprawdza, czy urządzenie audio i kierunek transmisji się zgadzają.

Tryb udostępniania

Strumień ma tryb udostępniania:

  • AAUDIO_SHARING_MODE_EXCLUSIVE oznacza, że strumień ma wyłączny dostęp do swojego urządzenia audio; urządzenia nie można używać w żadnym innym strumieniu audio. Jeśli urządzenie audio jest już w użyciu, do transmisji na wyłączność może się nie udać. Transmisje na wyłączność mają mniejsze opóźnienie, ale zwiększa też prawdopodobieństwo utraty połączenia. Zamykaj transmisje na wyłączność, gdy nie są Ci już potrzebne, aby inne aplikacje miały dostęp do urządzenia. Transmisje na wyłączność mają najniższy możliwy czas oczekiwania.
  • AAUDIO_SHARING_MODE_SHARED zezwala AAudio na miksowanie dźwięku. AAudio łączy wszystkie udostępnione strumienie przypisane do tego samego urządzenia.

Tryb udostępniania możesz ustawić samodzielnie podczas tworzenia transmisji. Domyślnie ustawiony jest tryb udostępniania SHARED.

Format dźwięku

Dane przekazywane przez strumień mają zwykłe atrybuty dźwięku cyfrowego. Oto one:

  • Przykładowy format danych
  • Liczba kanałów (próbki na klatkę)
  • Współczynnik próbkowania

AAudio dopuszcza te przykładowe formaty:

format_audio Typ danych C Uwagi
AAUDIO_FORMAT_PCM_I16 int16_t typowe 16-bitowe próbki, format Q0.15
AAUDIO_FORMAT_PCM_FLOAT liczba zmiennoprzecinkowa Od -1,0 do +1,0
AAUDIO_FORMAT_PCM_I24_PACKED uint8_t w grupach po 3 osoby upakowane próbki 24-bitowe, format Q0.23
AAUDIO_FORMAT_PCM_I32 int32_t typowe 32-bitowe próbki, format Q0.31
AAUDIO_FORMAT_IEC61937 uint8_t skompresowany dźwięk zgodny ze standardem IEC61937 na potrzeby przesyłania HDMI lub S/PDIF

Jeśli poprosisz o konkretny format próbki, strumień użyje go nawet wtedy, gdy nie będzie on optymalny dla danego urządzenia. Jeśli nie określisz formatu próbki, AAudio wybierze taki, który będzie optymalny. Po otwarciu strumienia musisz przesłać zapytanie dotyczące przykładowego formatu danych, a następnie w razie potrzeby przekonwertować dane, jak w tym przykładzie:

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

Tworzę strumień audio

Biblioteka AAudio jest zgodna z wzorcem projektowania w narzędziu i udostępnia komponent AAudioStreamBuilder.

  1. Utwórz obiekt AAudioStreamBuilder:

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

  2. Ustaw konfigurację strumienia audio w konstruktorze, korzystając z funkcji konstruktora, które odpowiadają jego parametrom. Dostępne są te opcjonalne funkcje zbiorów:

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

    Pamiętaj, że te metody nie zgłaszają błędów, takich jak niezdefiniowana stała wartość lub wartość spoza zakresu.

    Jeśli nie określisz parametru deviceId, domyślną wartością będzie podstawowe urządzenie wyjściowe. Jeśli nie określisz kierunku strumienia, domyślną wartością będzie strumień wyjściowy. W przypadku pozostałych parametrów możesz bezpośrednio ustawić wartość. Możesz też pozwolić systemowi na przypisanie wartości optymalnej, nie określając żadnego parametru ani ustawiając go na AAUDIO_UNSPECIFIED.

    Dla bezpieczeństwa sprawdź stan strumienia audio po jego utworzeniu, zgodnie z opisem w kroku 4 poniżej.

  3. Po skonfigurowaniu AAudioStreamBuilder użyj go do utworzenia strumienia:

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

  4. Po utworzeniu strumienia sprawdź jego konfigurację. Jeśli podasz format próbki, częstotliwość próbkowania lub liczbę próbek na klatkę, nie ulegną one zmianie. Jeśli określisz tryb udostępniania lub pojemność bufora, te wartości mogą się zmienić w zależności od możliwości urządzenia audio, z którym jest odtwarzana transmisja, oraz urządzenia z Androidem, na którym ją uruchomiono. Aby odpowiednio programować defensywne, sprawdź konfigurację strumienia, zanim go użyjesz. Istnieją funkcje, które umożliwiają pobieranie ustawień strumienia odpowiadających poszczególnym ustawieniom konstruktora:

    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. Możesz zapisać konstruktora i użyć go w przyszłości, aby utworzyć więcej strumieni. Jeśli jednak nie zamierzasz już z niej korzystać, musisz ją usunąć.

    AAudioStreamBuilder_delete(builder);
    

Korzystanie ze strumienia audio

Przejścia stanowe

Strumień AAudio ma zwykle jeden z pięciu stanów stabilnych (stan błędu „Rozłączono” został opisany na końcu tej sekcji):

  • Otwórz
  • Rozpoczęto
  • Wstrzymano
  • Rumieniec
  • Zatrzymano

Dane przepływają przez strumień tylko wtedy, gdy jest on w stanie Rozpoczęto. Aby przenieść strumień między stanami, użyj jednej z funkcji, które wysyłają żądanie o przejście stanu:

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

Pamiętaj, że możesz zażądać wstrzymania lub usunięcia tylko strumienia danych wyjściowych:

Te funkcje są asynchroniczne, a zmiana stanu nie zachodzi od razu. Gdy poprosisz o zmianę stanu, strumień przenosi jeden z odpowiednich stanów przejściowych:

  • W trakcie uruchamiania
  • Wstrzymuję
  • Rumieniec
  • Zatrzymuję
  • Zamykanie

Poniższy diagram stanu pokazuje stany stabilne jako zaokrąglone prostokąty, a stany przejściowe – kropki. Chociaż ta opcja nie jest widoczna, możesz zadzwonić pod numer close() z dowolnego stanu

Cykl życia audio

AAudio nie udostępnia wywołań zwrotnych, które ostrzegają użytkownika o zmianach. Do oczekiwania na zmianę stanu można użyć funkcji AAudioStream_waitForStateChange(stream, inputState, nextState, timeout).

Funkcja nie wykrywa samodzielnie zmiany stanu ani nie czeka na określony stan. Czeka, aż obecny stan będzie inny niż określony przez Ciebie stan inputState.

Na przykład po wysłaniu prośby o wstrzymanie strumienia powinien natychmiast przejść w stan przejściowy (wstrzymanie) i kiedyś później przejść do stanu wstrzymania (chociaż nie ma gwarancji, że tak się stanie. Nie możesz czekać na stan wstrzymania, więc użyj waitForStateChange(), by poczekać na dowolny stan inny niż wstrzymanie. Aby to zrobić:

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

Jeśli stan strumienia nie jest wstrzymany (czyli inputState, w którym zakładamy, że był to aktualny stan w momencie wywołania), funkcja zwraca wyniki natychmiast. W przeciwnym razie zablokuje się, dopóki stan nie przestanie być wstrzymany lub nie upłynie czas oczekiwania. Po zwróceniu funkcji parametr nextState pokazuje bieżący stan strumienia.

Możesz użyć tej samej metody po wywołaniu żądania start, stop lub flush, używając odpowiedniego stanu przejściowego jako właściwościinputState. Nie wywołuj funkcji waitForStateChange() po wywołaniu metody AAudioStream_close(), ponieważ strumień zostanie usunięty zaraz po zamknięciu. Nie wywołuj funkcji AAudioStream_close(), gdy waitForStateChange() działa w innym wątku.

Czytanie i pisanie w strumieniu audio

Dane w strumieniu można przetworzyć na 2 sposoby:

W przypadku blokowania odczytu lub zapisu, który przenosi określoną liczbę klatek, ustaw limit czasuNanos większy niż 0. W przypadku połączenia nieblokującego ustaw czas oczekiwaniaNanos na 0. W tym przypadku wynik to rzeczywista liczba przeniesionych klatek.

Przy odczytywaniu danych wejściowych sprawdź, czy została odczytana prawidłowa liczba klatek. W przeciwnym razie bufor może zawierać nieznane dane, które mogą powodować zakłócenia w dźwięku. Możesz wypełnić bufor zerami, aby uzyskać dyskretny spadek:

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

Przed uruchomieniem strumienia możesz przygotować bufor strumienia, wpisując w nim dane lub wyciszając. Należy to zrobić w wywołaniu nieblokującym z limitem czasuNanos ustawionym na 0.

Dane w buforze muszą być zgodne z formatem danych zwróconym przez funkcję AAudioStream_getDataFormat().

Zamykanie strumienia audio

Gdy skończysz korzystać ze strumienia, zamknij go:

AAudioStream_close(stream);

Po zamknięciu strumienia nie można używać go z żadną funkcją opartą na strumieniu AAudio.

Odłączony strumień audio

Strumień audio może zostać rozłączony w dowolnym momencie, jeśli wystąpi jedno z tych zdarzeń:

  • Powiązane urządzenie audio nie jest już połączone (np. gdy odłączono słuchawki).
  • Wystąpił błąd wewnętrzny.
  • Urządzenie audio nie jest już głównym urządzeniem audio.

Gdy strumień jest rozłączony, ma stan „Rozłączono”, a każda próba wykonania funkcji AAudioStream_write() lub innej funkcji spowoduje zwrócenie błędu. Musisz zawsze zatrzymać i zamknąć niepołączoną transmisję, niezależnie od kodu błędu.

Jeśli korzystasz z wywołania zwrotnego danych (w przeciwieństwie do jednej z metod bezpośredniego odczytu/zapisu), nie otrzymasz żadnego kodu zwrotnego po odłączeniu strumienia. Aby otrzymać informację o takiej sytuacji, wpisz funkcję AAudioStream_errorCallback i zarejestruj ją za pomocą AAudioStreamBuilder_setErrorCallback().

Jeśli otrzymasz powiadomienie o rozłączeniu w wątku wywołania zwrotnego błędu, zatrzymanie i zamknięcie strumienia musi zostać wykonane w innym wątku. W przeciwnym razie może wystąpić zakleszczenie.

Pamiętaj, że gdy otworzysz nową transmisję, będzie ona mieć inne ustawienia niż pierwotna (np. framePerBurst):

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.
}

Optymalizacja skuteczności

Możesz zoptymalizować wydajność aplikacji audio, dostosowując jej bufory wewnętrzne i używając specjalnych wątków o wysokim priorytecie.

Bufory dostrajania w celu zminimalizowania opóźnienia

Plik audio przekazuje dane do i z utrzymanych przez siebie wewnętrznych buforów, po jednym dla każdego urządzenia audio.

Pojemność bufora to łączna ilość danych, które może zawierać. Aby ustawić limit, możesz wywołać metodę AAudioStreamBuilder_setBufferCapacityInFrames(). Metoda ogranicza pojemność, którą możesz przydzielić do maksymalnej wartości, na jaką pozwala urządzenie. Aby sprawdzić rzeczywistą pojemność bufora, użyj danych AAudioStream_getBufferCapacityInFrames().

Aplikacja nie musi wykorzystywać całego bufora. AAudio wypełnia bufor do określonego rozmiaru. Bufor nie może być większy niż jego pojemność i często jest mniejszy. Kontrolując rozmiar bufora, określasz liczbę serii wymaganych do jego wypełnienia i kontrolujesz opóźnienie. Aby pracować z rozmiarem bufora, użyj metod AAudioStreamBuilder_setBufferSizeInFrames() i AAudioStreamBuilder_getBufferSizeInFrames().

Gdy aplikacja odtwarza dźwięk, zapisuje dane w buforze i blokuje je do czasu zakończenia zapisu. Dźwięk jest odczytywany z bufora w oddzielnych seriach. Każda seria zawiera wiele klatek audio i zwykle jest mniejsza niż rozmiar czytanego bufora. System steruje rozmiarem i szybkością serii. Te właściwości są zwykle zależne od obwodu urządzenia audio. Nie możesz zmienić rozmiaru serii ani szybkości serii, ale możesz ustawić rozmiar wewnętrznego bufora odpowiednio do liczby serii. Ogólnie najmniejsze opóźnienie występuje wtedy, gdy rozmiar bufora AAudioStream jest wielokrotnością rozmiaru zarejestrowanej serii.

      Buforowanie dźwięku

Jednym ze sposobów na zoptymalizowanie rozmiaru bufora jest rozpoczęcie od dużego bufora i stopniowe zmniejszanie go, aż zacznie spadać, a następnie ponawianie go. Możesz też zacząć od małego rozmiaru bufora, a jeśli spowoduje to zawinięcie, zwiększ rozmiar bufora, aż dane wyjściowe znowu będą płynnie przesyłane.

Ten proces może nastąpić bardzo szybko, prawdopodobnie przed odtworzeniem pierwszego dźwięku. Możesz najpierw ustawić początkowy rozmiar bufora, korzystając z wyciszenia, aby użytkownik nie usłyszał zacięć w dźwięku. Z czasem wydajność systemu może się zmienić (na przykład użytkownik może wyłączyć tryb samolotowy). Ponieważ dostrajanie buforowania wiąże się z bardzo niewielkim nakładem pracy, aplikacja może robić to w trybie ciągłym, podczas gdy aplikacja odczytuje lub zapisuje dane w strumieniu.

Oto przykład pętli optymalizacji bufora:

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

Stosowanie tej metody w celu optymalizacji rozmiaru bufora strumienia wejściowego nie ma żadnych dodatkowych korzyści. Strumienie wejściowe działają tak szybko, jak to możliwe, starając się ograniczyć ilość zbuforowanych danych do jak najmniejszego minimum, a następnie zapełniają się, gdy aplikacja jest wywłaszczana.

Korzystanie z połączenia zwrotnego o wysokim priorytecie

Jeśli aplikacja odczytuje lub zapisuje dane dźwiękowe ze zwykłego wątku, może ono zostać wywłaszczone lub powodować zakłócenia w czasie. Może to powodować zakłócenia w dźwięku. Użycie większych buforów może eliminować takie zakłócenia, ale duży bufor również powoduje dłuższe opóźnienie dźwięku. W przypadku aplikacji wymagających małego opóźnienia strumień audio może używać asynchronicznej funkcji wywołania zwrotnego do przesyłania danych do i z aplikacji. AAudio wykonuje wywołanie zwrotne w wątku o wyższym priorytecie, który ma większą wydajność.

Funkcja wywołania zwrotnego ma taki prototyp:

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

Aby zarejestrować wywołanie zwrotne, użyj narzędzia strumienia:

AAudioStreamBuilder_setDataCallback(builder, myCallback, myUserData);

W najprostszym przypadku strumień okresowo wykonuje funkcję wywołania zwrotnego, aby pobrać dane do następnej serii.

Funkcja wywołania zwrotnego nie powinna wykonywać operacji odczytu ani zapisu w strumieniu, który ją wywołał. Jeśli wywołanie zwrotne należy do strumienia wejściowego, kod powinien przetwarzać dane dostarczone w buforze audioData (określonym jako trzeci argument). Jeśli wywołanie zwrotne należy do strumienia wyjściowego, kod powinien umieścić dane w buforze.

Możesz np. użyć wywołania zwrotnego, aby stale generować falę sinusoidalną w taki sposób:

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

Przy użyciu AAudio można przetworzyć więcej niż jeden strumień. Możesz używać jednego strumienia jako głównego i przekazywać wskaźniki do innych strumieni w danych użytkownika. Zarejestruj wywołanie zwrotne strumienia głównego. Następnie w innych strumieniach użyj nieblokowania wejścia-wyjścia. Oto przykład wywołania zwrotnego w obie strony, które przekazuje strumień wejściowy do strumienia wyjściowego. Strumień wywołujący główny jest strumieniem wyjściowym. Strumień wejściowy jest uwzględniony w danych użytkownika.

Wywołanie zwrotne wykonuje nieblokujący odczyt ze strumienia wejściowego, umieszczając dane w buforze strumienia wyjściowego:

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

Zwróć uwagę, że w tym przykładzie zakładamy, że strumienie wejściowe i wyjściowe mają taką samą liczbę kanałów, format i częstotliwość próbkowania. Format strumieni może być niezgodny – jeśli kod poprawnie obsługuje tłumaczenia.

Ustawiam tryb wydajności

Każdy AAudioStream ma tryb wydajności, który ma duży wpływ na działanie aplikacji. Dostępne są 3 tryby:

  • Trybem domyślnym jest AAUDIO_PERFORMANCE_MODE_NONE. Wykorzystuje podstawowy strumień, który równoważy opóźnienie i oszczędność energii.
  • AAUDIO_PERFORMANCE_MODE_LOW_LATENCY używa mniejszych buforów i zoptymalizowanej ścieżki danych, by zmniejszyć opóźnienie.
  • AAUDIO_PERFORMANCE_MODE_POWER_SAVING używa większych buforów wewnętrznych i ścieżki danych, która kompensuje opóźnienie i zmniejsza zużycie energii.

Aby wybrać tryb wydajności, wywołaj funkcję setPerformanceMode(), i odkryć bieżący tryb, wywołując funkcję getPerformanceMode().

Jeśli małe opóźnienie jest ważniejsze niż oszczędność energii w aplikacji, użyj AAUDIO_PERFORMANCE_MODE_LOW_LATENCY. Przydaje się to w przypadku aplikacji bardzo interaktywnych, takich jak gry czy syntezatory klawiatury.

Jeśli oszczędzanie energii jest ważniejsze niż małe opóźnienie w aplikacji, użyj AAUDIO_PERFORMANCE_MODE_POWER_SAVING. Jest to typowe dla aplikacji, które odtwarzają wcześniej wygenerowaną muzykę, na przykład przez strumieniowe przesyłanie dźwięku lub odtwarzacze MIDI.

W bieżącej wersji AAudio należy użyć trybu wydajności AAUDIO_PERFORMANCE_MODE_LOW_LATENCY z wywołaniem zwrotnym o wysokim priorytecie, aby uzyskać możliwie najkrótszy czas oczekiwania. Skorzystaj z tego przykładu:

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

Bezpieczeństwo wątków

Interfejs AAudio API nie jest całkowicie bezpieczny dla wątków. Nie można wywoływać niektórych funkcji AAudio jednocześnie z więcej niż jednego wątku. Dzieje się tak, ponieważ AAudio nie używa wyciszeń, które mogą powodować wywłaszczanie wątków i zakłócenia działania.

Dla bezpieczeństwa nie wywołuj funkcji AAudioStream_waitForStateChange() ani nie czytaj ani nie zapisuj w tym samym strumieniu z 2 różnych wątków. Podobnie nie zamykaj strumienia w jednym wątku podczas czytania lub pisania w innym wątku.

Połączenia, które zwracają ustawienia strumienia, takie jak AAudioStream_getSampleRate() i AAudioStream_getChannelCount(), są bezpieczne dla wątków.

Te połączenia są również bezpieczne w wątku:

  • AAudio_convert*ToText()
  • AAudio_createStreamBuilder()
  • AAudioStream_get*() oprócz AAudioStream_getTimestamp()

Znane problemy

  • Opóźnienie dźwięku jest wysokie w przypadku blokowania zapisu(), ponieważ wersja Android O DP2 nie korzysta ze ścieżki FAST. Aby zmniejszyć opóźnienie, użyj wywołania zwrotnego.

Dodatkowe materiały

Aby dowiedzieć się więcej, skorzystaj z tych materiałów:

Jak korzystać z interfejsu API

Ćwiczenia z programowania

Filmy