Dodawanie dźwięku przestrzennego do aplikacji XR

Funkcje dźwięku przestrzennego w Jetpack SceneCore umożliwiają tworzenie wrażeń dźwiękowych w aplikacji na Androida XR.

Dźwięk przestrzenny symuluje sposób, w jaki użytkownicy odbierają dźwięk w środowisku 3D. Tworzy on wrażenie dźwięku dobiegającego ze wszystkich kierunków, w tym z góry i z dołu. System robi to, symulując co najmniej 1 „wirtualnego głośnika” w określonych miejscach w przestrzeni 3D.

Dotychczasowe aplikacje, które nie zostały zaprojektowane ani zmodyfikowane pod kątem Androida XR, mają dźwięk automatycznie osadzony w przestrzeni w Androidzie XR. Gdy użytkownik porusza się po pokoju, cały dźwięk aplikacji będzie emitowany z panelu, na którym renderowane jest interfejs użytkownika. Jeśli na przykład minutnik w aplikacji zegar włączy się, dźwięk będzie wydawał się dobiegający z pozycji panelu aplikacji. Android XR automatycznie zmienia dźwięk, aby zapewnić realizm dźwięku zależnego od położenia. Na przykład odległość między użytkownikiem a panelem aplikacji będzie subtelnie wpływać na głośność dźwięku, aby zwiększyć realizm.

Więcej informacji o tym, jak istniejące aplikacje renderują dźwięk przestrzenny, znajdziesz na tej stronie w artykule Dodawanie dźwięku stereo i przestrzennego do aplikacji.

Jeśli optymalizujesz aplikację pod kątem XR, Jetpack SceneCore udostępnia narzędzia do zaawansowanej personalizacji dźwięku przestrzennego. Możesz precyzyjnie umieszczać dźwięki w środowisku 3D, używać dźwięku ambisonicznego do realistycznych pól dźwiękowych i korzystać z wbudowanej integracji z dźwiękiem przestrzennym.

Typy dźwięku przestrzennego dostępne w Androidzie XR

Android XR obsługuje dźwięk przestrzenny, stereofoniczny, ambisoniowy i z uwzględnieniem położenia.

Dźwięk przestrzenny

Dźwięk pozycyjny może być odtwarzany z określonego punktu w przestrzeni 3D. Możesz na przykład umieścić model 3D psa szczekającego w rogu swojego wirtualnego środowiska. Możesz mieć wiele obiektów emitujących dźwięk z poszczególnych pozycji. Aby renderować dźwięk przestrzenny, pliki muszą być mono lub stereo.

Stereo przestrzenne i dźwięk przestrzenny

Obsługiwane są wszystkie formaty multimediów na Androidzie w przypadku dźwięku przestrzennego, stereo i dźwięku przestrzennego.

Dźwięk stereo to formaty dźwięku z 2 kanałami, a dźwięk przestrzenny to formaty dźwięku z więcej niż 2 kanałami, takie jak konfiguracje dźwięku przestrzennego 5.1 lub dźwięku przestrzennego 7.1. Dane dźwiękowe każdego kanału są powiązane z jednym głośnikiem. Podczas odtwarzania muzyki w stereo lewy kanał głośnika może emitować inne ścieżki instrumentów niż prawy.

Dźwięk przestrzenny jest często używany w filmach i programach telewizyjnych, aby zwiększyć realizm i wciągnąć widza w film za pomocą wielu kanałów głośników. Na przykład dialog często odtwarzany jest z kanału głośnika środkowego, podczas gdy dźwięk helikoptera może używać różnych kanałów kolejno, aby stworzyć wrażenie, że helikopter lata wokół przestrzeni 3D.

Dźwięk ambisonikowy

Ambisonic (lub ambisonics) to coś w rodzaju skyboxa dla dźwięku, który zapewnia użytkownikom wciągający dźwięk. Używaj ambisonicznego dźwięku w przypadku dźwięków otoczenia w tle lub w innych sytuacjach, w których chcesz odtworzyć pełne dźwiękowe pole wokół słuchacza. Android XR obsługuje format dźwięku ambisonics AmbiX w przypadku ambisonics pierwszego, drugiego i trzeciego rzędu. Zalecamy typy plików Opus (.ogg) i PCM/Wave (.wav).

Korzystanie z dźwięku przestrzennego w Jetpacku SceneCore

Wdrożenie dźwięku przestrzennego za pomocą Jetpacka SceneCore wymaga sprawdzenia możliwości przestrzennych i wybrania interfejsu API do wczytywania dźwięku przestrzennego.

Sprawdzanie możliwości dźwięku przestrzennego

Zanim zaczniesz korzystać z funkcji dźwięku przestrzennego, sprawdź, czy Session obsługuje dźwięk przestrzenny. W wszystkich fragmentach kodu w następnych sekcjach sprawdzane są możliwości przed odtworzeniem dźwięku przestrzennego.

Wczytywanie dźwięku przestrzennego

Do wczytania dźwięku przestrzennego na potrzeby Jetpacka SceneCore możesz użyć dowolnego z tych interfejsów API.

  • SoundPool: idealne rozwiązanie na potrzeby krótkich efektów dźwiękowych o rozmiary poniżej 1 MB. Są one wczytywane z wyprzedzeniem i można ich używać wielokrotnie. To świetny sposób na wczytywanie dźwięku dla dźwięku pozycyjnego.
  • ExoPlayer: idealny do wczytywania treści w formacie stereo i dźwięku przestrzennego, takich jak muzyka i filmy. Umożliwia też odtwarzanie multimediów w tle.
  • MediaPlayer: najprostszy sposób wczytywania dźwięku ambisonicznego.
  • AudioTrack: zapewnia największą kontrolę nad sposobem wczytywania danych audio. Umożliwia bezpośrednie zapisywanie buforów dźwięku lub zsyntetyzowanych lub dekodowanych własnych plików audio.

Dodawanie dźwięku zależnego od położenia do aplikacji

Pozycyjne źródła dźwięku są definiowane przez PointSourceAttributes i powiązane z nim Entity. Pozycja i orientacja Entityokreśla, gdzie PointSourceAttribute jest renderowany w przestrzeni 3D.

Przykład dźwięku pozycjonowanego

W tym przykładzie wczytujemy plik audio z efektem dźwiękowym do puli dźwięków i odtwarzamy go w miejscu, w którym znajduje się Entity.

// Check spatial capabilities before using spatial audio
if (xrSession.getSpatialCapabilities().hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO)) {
    // The session has spatial audio capabilities

    val maxVolume = 1F
    val lowPriority = 0
    val infiniteLoop = -1
    val normalSpeed = 1F

    val soundPool = SoundPool.Builder()
        .setAudioAttributes(
            AudioAttributes.Builder()
                .setContentType(CONTENT_TYPE_SONIFICATION)
                .setUsage(USAGE_ASSISTANCE_SONIFICATION)
                .build()
        )
        .build()

    val pointSource = PointSourceAttributes(entity)

    val soundEffect = appContext.assets.openFd("sounds/tiger_16db.mp3")
    val pointSoundId = soundPool.load(soundEffect, lowPriority)

    soundPool.setOnLoadCompleteListener{ soundPool, sampleId, status ->
        //wait for the sound file to be loaded into the soundPool
        if (status == 0){

            SpatialSoundPool.play(
                session = xrSession,
                soundPool = soundPool,
                soundID = pointSoundId,
                attributes = pointSource,
                volume = maxVolume,
                priority = lowPriority,
                loop = infiniteLoop,
                rate = normalSpeed
            )
        }
    }
} else {
    // The session does not have spatial audio capabilities
}

Najważniejsze informacje o kodzie

  • Najpierw sprawdź, czy funkcje dźwięku przestrzennego są obecnie dostępne, korzystając z getSpatialCapabilities().
  • Ustawienie wartości contentType na CONTENT_TYPE_SONIFICATION i ustawienie wartości usage na USAGE_ASSISTANCE_SONIFICATION powoduje, że system będzie traktować ten plik audio jako efekt dźwiękowy.
  • W powyższym przykładzie plik audio jest wczytywany do puli bezpośrednio przed jego użyciem, aby utrzymać kod w jednym miejscu ze względów uproszczenia. Najlepiej jest wczytywać wszystkie efekty dźwiękowe asynchronicznie podczas wczytywania aplikacji, aby wszystkie pliki audio były dostępne w zbiorze, gdy ich potrzebujesz.

Dodawanie dźwięku stereo i przestrzennego do aplikacji

Zalecane jest dodawanie dźwięku stereo i dźwięku przestrzennego do aplikacji za pomocą Exoplayer. Więcej informacji o korzystaniu z dźwięku przestrzennego w przypadku Exoplayer znajdziesz w przewodniku po dźwięku przestrzennym.

Rozmieszczenie głośników stereo i głośników dźwięku przestrzennego

W przypadku głośników z dźwiękiem przestrzennym wirtualne głośniki z dźwiękiem przestrzennym są ustawione i zorientowane względem głośnika środkowego, wokół użytkownika w standardowej konfiguracji ITU.

Domyślnie głośnik kanału środkowego jest umieszczony na mainPanelEntity aplikacji. Dotyczy to też aplikacji mobilnych, w których dźwięk jest automatycznie przestrzenny dzięki Androidowi XR.

W przypadku dźwięku stereo rozmieszczenie głośników jest podobne do dźwięku przestrzennego, z tym że kanały lewy i prawy są umieszczone odpowiednio po lewej i prawej stronie panelu.

Jeśli masz kilka paneli i chcesz wybrać, który z nich ma emitować dźwięk, lub chcesz, aby dźwięk stereo lub dźwięk przestrzenny był renderowany w stosunku do innego Entity, możesz użyć PointSourceAttributes, aby określić położenie kanału środkowego. Pozostałe kanały zostaną umieszczone zgodnie z wcześniejszym opisem. W takich sytuacjach musisz też użyć MediaPlayer.

Gdy użytkownik porusza się po pomieszczeniu, wirtualne głośniki stereo i dźwięku przestrzennego będą się przemieszczać i dostosowywać, aby zawsze znajdowały się w optymalnej pozycji.

Jeśli skonfigurujesz MediaPlayer lub ExoPlayer tak, aby nadal odtwarzać dźwięk stereo lub dźwięk przestrzenny w tle, po umieszczeniu aplikacji w tle zmieni się pozycjonowanie głośników wirtualnych. Ponieważ nie ma panelu ani innego punktu w przestrzeni, do którego można zakotwiczyć dźwięk, dźwięk przestrzenny porusza się wraz z użytkownikiem (czyli jest „zablokowany w głowie”).

Przykład dźwięku przestrzennego

W tym przykładzie wczytujemy plik audio 5.1 za pomocą funkcji MediaPlayer i ustawiamy kanał środkowy pliku jako Entity.

// Check spatial capabilities before using spatial audio
if (xrSession.getSpatialCapabilities().hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO)) {
    // The session has spatial audio capabilities

    val pointSourceAttributes = PointSourceAttributes(xrSession.mainPanelEntity)

    val mediaPlayer = MediaPlayer()

    val fivePointOneAudio = appContext.assets.openFd("sounds/aac_51.ogg")
    mediaPlayer.reset()
    mediaPlayer.setDataSource(fivePointOneAudio)

    val audioAttributes =
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()

    SpatialMediaPlayer.setPointSourceAttributes(
        xrSession,
        mediaPlayer,
        pointSourceAttributes)

    mediaPlayer.setAudioAttributes(audioAttributes)
    mediaPlayer.prepare()
    mediaPlayer.start()

} else {
    // The session does not have spatial audio capabilities
}

Najważniejsze informacje o kodzie

Dodawanie pól dźwięku ambisonic do aplikacji

Najprostszym sposobem odtwarzania ambisonicznego pola dźwiękowego jest załadowanie pliku za pomocą MediaPlayer. Ponieważ dźwięk ambisonic dotyczy całego krajobrazu dźwiękowego, nie musisz podawać wartości Entity, aby określić pozycję. Zamiast tego tworzysz instancję SoundFieldAttributes z odpowiednim porządkiem ambisonicznym, określając liczbę kanałów.

Przykład Ambionics

W tym przykładzie pole MediaPlayer odtwarza ambisoniczną ścieżkę dźwiękową.

// Check spatial capabilities before using spatial audio
if (xrSession.getSpatialCapabilities().hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO)) {
    // The session has spatial audio capabilities

    val soundFieldAttributes =
        SoundFieldAttributes(SpatializerConstants.AMBISONICS_ORDER_FIRST_ORDER)

    val mediaPlayer = MediaPlayer()

    val soundFieldAudio = appContext.assets.openFd("sounds/foa_basketball_16bit.wav")

    mediaPlayer.reset()
    mediaPlayer.setDataSource(soundFieldAudio)

    val audioAttributes =
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()

    SpatialMediaPlayer.setSoundFieldAttributes(
        xrSession,
        mediaPlayer,
        soundFieldAttributes)

    mediaPlayer.setAudioAttributes(audioAttributes)
    mediaPlayer.prepare()
    mediaPlayer.start()

} else {
    // The session does not have spatial audio capabilities
}

Najważniejsze informacje o kodzie

  • Podobnie jak w przypadku poprzednich fragmentów kodu, pierwszym krokiem jest sprawdzenie, czy funkcje dźwięku przestrzennego są obecnie dostępne, za pomocą funkcji getSpatialCapabilities().
  • Typ treści i sposób użycia są podawane wyłącznie informacyjnie.
  • Wartość AMBISONICS_ORDER_FIRST_ORDER sygnalizuje SceneCore, że plik pola dźwiękowego definiuje 4 kanały.