Dodaj dźwięk i muzykę do gry Flutter

1. Zanim zaczniesz

Gry to doświadczenia audiowizualne. Flutter to świetne narzędzie do tworzenia atrakcyjnych wizualizacji i przejrzystego interfejsu, które znacznie rozszerzają walory wizualne. Pozostały brakujący składnik to dźwięk. Z tego ćwiczenia w Codelabs dowiesz się, jak za pomocą wtyczki flutter_soloud wprowadzić do projektu dźwięk i muzykę o małym opóźnieniu. Zaczynasz od podstawowego szablonu, dzięki czemu możesz od razu przejść do interesujących Cię części.

Ręcznie rysowana ilustracja słuchawek.

Korzystając z uzyskanych tu informacji, możesz oczywiście dodać dźwięk do aplikacji, a nie tylko gier. Jednak prawie wszystkie gry wymagają dźwięku i muzyki, a większość aplikacji nie. Dlatego ten samouczek koncentruje się na grach.

Wymagania wstępne

  • Podstawowa znajomość technologii Flutter.
  • umiejętność uruchamiania i debugowania aplikacji Flutter;

Czego się nauczysz

  • Jak odtwarzać dźwięki jednego uderzenia.
  • Jak odtwarzać i dostosowywać pętle muzyczne bez przerw.
  • Jak włączać i wyłączać dźwięk.
  • Jak zastosować efekty środowiskowe do dźwięków.
  • Jak radzić sobie z wyjątkami.
  • jak połączyć wszystkie te funkcje w jednym kontrolerze audio.

Wymagania

  • Pakiet SDK Flutter
  • Wybrany przez Ciebie edytor kodu.

2. Skonfiguruj

  1. Pobierz te pliki. Jeśli masz wolne połączenie, nie martw się. Potrzebujesz tych plików później, więc możesz je pobrać podczas pracy.
  1. Utwórz projekt Flutter o dowolnej nazwie.
  1. Utwórz w projekcie plik lib/audio/audio_controller.dart.
  2. W pliku wpisz ten kod:

lib/audio/audio_controller.dart

import 'dart:async';

import 'package:logging/logging.dart';

class AudioController {
  static final Logger _log = Logger('AudioController');

  Future<void> initialize() async {
    // TODO
  }

  void dispose() {
    // TODO
  }

  Future<void> playSound(String assetKey) async {
    _log.warning('Not implemented yet.');
  }

  Future<void> startMusic() async {
    _log.warning('Not implemented yet.');
  }

  void fadeOutMusic() {
    _log.warning('Not implemented yet.');
  }

  void applyFilter() {
    // TODO
  }

  void removeFilter() {
    // TODO
  }
}

Jak widzisz, to tylko szkielet przyszłej funkcji. W ramach tego Codelab wprowadzimy to wszystko w kodzie.

  1. Następnie otwórz plik lib/main.dart i zastąp jego zawartość tym kodem:

lib/main.dart

import 'dart:developer' as dev;

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';

import 'audio/audio_controller.dart';

void main() async {
  // The `flutter_soloud` package logs everything
  // (from severe warnings to fine debug messages)
  // using the standard `package:logging`.
  // You can listen to the logs as shown below.
  Logger.root.level = kDebugMode ? Level.FINE : Level.INFO;
  Logger.root.onRecord.listen((record) {
    dev.log(
      record.message,
      time: record.time,
      level: record.level.value,
      name: record.loggerName,
      zone: record.zone,
      error: record.error,
      stackTrace: record.stackTrace,
    );
  });

  WidgetsFlutterBinding.ensureInitialized();

  final audioController = AudioController();
  await audioController.initialize();

  runApp(
    MyApp(audioController: audioController),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({required this.audioController, super.key});

  final AudioController audioController;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter SoLoud Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.brown),
        useMaterial3: true,
      ),
      home: MyHomePage(audioController: audioController),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.audioController});

  final AudioController audioController;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const _gap = SizedBox(height: 16);

  bool filterApplied = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flutter SoLoud Demo')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            OutlinedButton(
              onPressed: () {
                widget.audioController.playSound('assets/sounds/pew1.mp3');
              },
              child: const Text('Play Sound'),
            ),
            _gap,
            OutlinedButton(
              onPressed: () {
                widget.audioController.startMusic();
              },
              child: const Text('Start Music'),
            ),
            _gap,
            OutlinedButton(
              onPressed: () {
                widget.audioController.fadeOutMusic();
              },
              child: const Text('Fade Out Music'),
            ),
            _gap,
            Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                const Text('Apply Filter'),
                Checkbox(
                  value: filterApplied,
                  onChanged: (value) {
                    setState(() {
                      filterApplied = value!;
                    });
                    if (filterApplied) {
                      widget.audioController.applyFilter();
                    } else {
                      widget.audioController.removeFilter();
                    }
                  },
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
  1. Po pobraniu plików audio utwórz w katalogu głównym projektu katalog o nazwie assets.
  2. W katalogu assets utwórz 2 podkatalogi: jeden o nazwie music, a drugi o nazwie sounds.
  3. Przenieś pobrane pliki do projektu, tak aby plik utworu znajdował się w pliku assets/music/looped-song.ogg, a dźwięki ławki w tych plikach:
  • assets/sounds/pew1.mp3
  • assets/sounds/pew2.mp3
  • assets/sounds/pew3.mp3

Struktura Twojego projektu powinna teraz wyglądać mniej więcej tak:

Widok drzewa projektu z folderami, takimi jak „android”, „ios”, plikami takimi jak „README.md” i „analysis_options.yaml”. Jest wśród nich katalog „assets” z podkatalogami „music” i „sounds”, katalog „lib” z „main.dart” i podkatalogiem „audio” z plikiem „audio_controller.dart” oraz plik „pubspec.yaml”.  Strzałki wskazują nowe katalogi i pliki, których do tej pory nie dotykasz.

Teraz, gdy pliki są już w tym miejscu, musisz poinformować o nich Fluttera.

  1. Otwórz plik pubspec.yaml, a potem zastąp sekcję flutter: na dole pliku tym tekstem:

pubspec.yaml

...

flutter:
  uses-material-design: true

  assets:
    - assets/music/
    - assets/sounds/
  1. Dodaj zależność od pakietu flutter_soloud i pakietu logging.

pubspec.yaml

...

dependencies:
  flutter:
    sdk: flutter

  flutter_soloud: ^2.0.0
  logging: ^1.2.0

...
  1. Uruchom projekt. Nic jeszcze nie działa, ponieważ te funkcje zostały dodane w sekcjach poniżej.

10f0f751c9c47038.png

/flutter_soloud/src/filters/filters.cpp:21:24: warning: implicit conversion loses integer precision: 'decltype(__x.base() - __y.base())' (aka 'long') to 'int' [-Wshorten-64-to-32];

Pochodzą one z biblioteki C++ SoLoud. Nie mają one żadnego wpływu na działanie aplikacji i można je zignorować.

3. Inicjowanie i wyłączanie

Aby odtworzyć dźwięk, musisz użyć wtyczki flutter_soloud. Ta wtyczka jest oparta na projekcie SoLoud, czyli silniku dźwięku w języku C++ używanym między innymi w grach firmy Nintendo SNES Classic.

7ce23849b6d0d09a.png

Aby zainicjować silnik audio SoLoud:

  1. W pliku audio_controller.dart zaimportuj pakiet flutter_soloud i dodaj do klasy prywatne pole _soloud.

lib/audio/audio_controller.dart

import 'dart:ui';

import 'package:flutter_soloud/flutter_soloud.dart';  //  Add this...
import 'package:logging/logging.dart';

class AudioController {
  static final Logger _log = Logger('AudioController');

  SoLoud? _soloud;                                    //  ... and this.

  Future<void> initialize() async {
    // TODO
  }

  ...

Kontroler audio zarządza podstawowym mechanizmem SoLoud za pomocą tego pola i przekierowuje do niego wszystkie wywołania.

  1. W metodzie initialize() wpisz ten kod:

lib/audio/audio_controller.dart

...

  Future<void> initialize() async {
    _soloud = SoLoud.instance;
    await _soloud!.init();
  }

...

Zapełnia pole _soloud i oczekuje na zainicjowanie. Pamiętaj:

  • SoLoud udostępnia pojedyncze pole instance. Nie można utworzyć kilku instancji SoLoud. Silnik C++ nie zezwala na to, więc wtyczka Dart też nie może tego zrobić.
  • Inicjowanie wtyczki jest asynchroniczne i nie kończy się, dopóki nie zostanie zwrócona metoda init().
  • W tym przykładzie w celu zachowania zwięzłości nie przechwytujesz błędów w bloku try/catch. W kodzie produkcyjnym chcesz to zrobić i zgłosić użytkownikowi wszelkie błędy.
  1. W metodzie dispose() wpisz ten kod:

lib/audio/audio_controller.dart

...

  void dispose() {
    _soloud?.deinit();
  }

...

Wyłączanie funkcji SoLoud przy wychodzeniu z aplikacji to dobra praktyka, chociaż wszystko powinno działać jak należy, nawet jeśli tego nie zrobisz.

  1. Zwróć uwagę, że metoda AudioController.initialize() jest już wywoływana z funkcji main(). Oznacza to, że ponowne uruchomienie projektu z pamięcią inicjuje funkcję SoLoud w tle, ale nie przyniesie to pożytku, dopóki nie odtworzy się niektórych dźwięków.

4. Odtwórz dźwięki jednego uderzenia

Ładowanie i odtwarzanie komponentu

Teraz, gdy już wiesz, że SoLoud jest inicjowany podczas uruchamiania, możesz poprosić o odtworzenie dźwięku.

SoLoud odróżnia źródło dźwięku, którym jest dane i metadane używane do opisania dźwięku, od jego „wystąpień dźwięku”, czyli rzeczywiście odtwarzanych dźwięków. Przykładem źródła dźwięku może być plik mp3 wczytany do pamięci, gotowy do odtwarzania i reprezentowany przez instancję klasy AudioSource. Za każdym razem, gdy odtwarzasz to źródło dźwięku, SoLoud tworzy „występ dźwięku”, który jest reprezentowany przez typ SoundHandle.

Po jej wczytaniu otrzymasz instancję AudioSource. Jeśli na przykład masz w zasobach plik MP3, możesz go załadować, aby otrzymać AudioSource. Następnie mówisz SoLoud, aby odtworzyć AudioSource. Możesz w nie grać wiele razy, a nawet jednocześnie.

Gdy nie używasz już danego źródła dźwięku, możesz je usunąć za pomocą metody SoLoud.disposeSource().

Aby załadować zasób i odtworzyć go:

  1. W metodzie playSound() klasy AudioController wpisz ten kod:

lib/audio/audio_controller.dart

  ...

  Future<void> playSound(String assetKey) async {
    final source = await _soloud!.loadAsset(assetKey);
    await _soloud!.play(source);
  }

  ...
  1. Zapisz plik, wykonaj szybkie przeładowanie i wybierz Odtwórz dźwięk. Usłyszysz głupią minę. Pamiętaj:
  • Podany argument assetKey ma postać assets/sounds/pew1.mp3 – ten sam ciąg znaków, który trzeba podać w przypadku dowolnego innego interfejsu Flutter API, który wczytuje zasoby, np. widżetu Image.asset().
  • Instancja SoLoud udostępnia metodę loadAsset(), która asynchronicznie wczytuje plik audio z zasobów projektu Flutter i zwraca instancję klasy AudioSource. Istnieją równoważne metody wczytywania pliku z systemu plików (metoda loadFile()) oraz wczytywania przez sieć z adresu URL (metoda loadUrl()).
  • Nowo pozyskana instancja AudioSource jest następnie przekazywana do metody play() SoLoud. Ta metoda zwraca instancję typu SoundHandle, która reprezentuje odtwarzany obecnie dźwięk. Ten obiekt można następnie przekazać do innych metod SoLoud, aby wykonać takie czynności jak wstrzymanie, zatrzymanie lub zmiana głośności dźwięku.
  • Chociaż play() jest metodą asynchroniczną, odtwarzanie zaczyna się zasadniczo od razu. Pakiet flutter_soloud wykorzystuje interfejs funkcji obcych (FFI) Dart do bezpośredniego i synchronicznego wywoływania kodu C. Normalny komunikat pomiędzy kodem Dart a kodem platformy, charakterystyczny dla większości wtyczek Flutter, nie jest nigdzie dostępny. Jedynym powodem, dla którego niektóre metody są asynchroniczne, jest to, że część kodu wtyczki jest uruchamiana samodzielnie, a komunikacja między izolowanymi elementami Dart jest asynchroniczna.
  • Wystarczy, że pole _soloud nie jest puste w przypadku _soloud!. Powtarzam to dla zwięzłości. Kod produkcyjny powinien bezproblemowo obsługiwać sytuację, w której deweloper próbuje odtworzyć dźwięk, zanim kontroler audio zdąży się w pełni zainicjować.

Wyjątki

Być może zauważyliście, że ponownie ignorujecie potencjalne wyjątki. Poprawmy to w przypadku tej konkretnej metody w celach edukacyjnych. (dla zwięzłości ćwiczenia w Codelabs po tej sekcji wracają do ignorowania wyjątków).

  • Aby rozwiązać w tym przypadku wyjątki, spakuj 2 wiersze metody playSound() w blok try/catch i przechwytuj tylko wystąpienia SoLoudException.

lib/audio/audio_controller.dart

  ...

  Future<void> playSound(String assetKey) async {
    try {
      final source = await _soloud!.loadAsset(assetKey);
      await _soloud!.play(source);
    } on SoLoudException catch (e) {
      _log.severe("Cannot play sound '$assetKey'. Ignoring.", e);
    }
  }

  ...

SoLoud zgłasza różne wyjątki, takie jak SoLoudNotInitializedException lub SoLoudTemporaryFolderFailedException. Dokumentacja interfejsu API każdej metody zawiera listę wyjątków, które mogą wystąpić.

SoLoud udostępnia też klasę nadrzędną dla wszystkich wyjątków, czyli wyjątek SoLoudException, dzięki czemu możesz przechwytywać wszystkie błędy związane z funkcjonalnością silnika audio. Jest to szczególnie przydatne w przypadkach, gdy odtwarzanie dźwięku nie ma kluczowego znaczenia. Jest tak na przykład wtedy, gdy nie chcesz przerywać sesji gry gracza tylko dlatego, że nie udało się wczytać jednego z dźwięków.

Jak można się spodziewać, metoda loadAsset() może też zwrócić błąd FlutterError, jeśli podasz klucz zasobu, który nie istnieje. Próba załadowania zasobów, które nie są zawarte w grze, jest zwykle czymś, co należy naprawić. Dlatego jest to błąd.

Odtwarzanie różnych dźwięków

Zauważysz, że odtwarzasz tylko plik pew1.mp3, ale w katalogu zasobów są 2 inne wersje dźwięku. Często brzmi to bardziej naturalnie, gdy gry mają kilka wersji tego samego dźwięku i grają różne wersje w losowy lub naprzemienny sposób. Zapobiega to na przykład temu, że odgłosy kroków lub strzałów wydają się zbyt jednolite, a tym samym fałszywe.

  • Opcjonalnie zmodyfikuj kod tak, aby przy każdym dotknięciu przycisku odtwarzał inny dźwięk.

Ilustracja

5. Odtwarzanie pętli muzycznych

Zarządzaj dźwiękami, które trwają dłużej

Niektóre dźwięki są przeznaczone do odtwarzania przez dłuższy czas. Oczywistym przykładem jest muzyka, ale w wielu grach panuje klimat, taki jak szum wiatru biegnący przez korytarze, śpiew mnichów w oddali, odgłosy wielowiekowego metalu czy kaszle w oddali pacjentów.

Są to źródła dźwięku, których czas odtwarzania może być mierzony w minutach. Musisz je śledzić, aby w razie potrzeby móc je wstrzymać lub zatrzymać. Często mają też duże pliki i mogą zajmować dużo pamięci, dlatego warto je śledzić, aby pozbyć się instancji AudioSource, gdy nie jest już potrzebna.

W związku z tym wprowadzisz nowe pole prywatne do AudioController. Jest to identyfikator bieżąco odtwarzanego utworu (jeśli taki istnieje). Dodaj ten wiersz:

lib/audio/audio_controller.dart

...

class AudioController {
  static final Logger _log = Logger('AudioController');

  SoLoud? _soloud;

  SoundHandle? _musicHandle;    // ← Add this.

  ...

Rozpocznij odtwarzanie muzyki

W zasadzie odtwarzanie muzyki nie różni się od odtwarzania dźwięku jednorazowego. Najpierw musisz załadować plik assets/music/looped-song.ogg jako instancję klasy AudioSource, a potem odtworzyć go za pomocą metody play() w SoLoud.

Tym razem jednak przechwytujesz uchwyt dźwięku zwracany przez metodę play(), aby manipulować dźwiękiem podczas jego odtwarzania.

  • Jeśli chcesz, samodzielnie zaimplementuj metodę AudioController.startMusic(). Nie musisz podawać wszystkich szczegółów. Ważne jest to, że muzyka zaczyna się od razu po wybraniu opcji Rozpocznij odtwarzanie muzyki.

Oto przykładowa implementacja:

lib/audio/audio_controller.dart

...

  Future<void> startMusic() async {
    if (_musicHandle != null) {
      if (_soloud!.getIsValidVoiceHandle(_musicHandle!)) {
        _log.info('Music is already playing. Stopping first.');
        await _soloud!.stop(_musicHandle!);
      }
    }
    final musicSource = await _soloud!
        .loadAsset('assets/music/looped-song.ogg', mode: LoadMode.disk);
    _musicHandle = await _soloud!.play(musicSource);
  }

...

Pamiętaj, że plik muzyczny jest wczytywany w trybie dyskowym (enumeracja LoadMode.disk). Oznacza to po prostu, że plik jest wczytywany tylko w porcjach, w miarę potrzeby. Jeśli dźwięk działa dłużej, lepiej ładować go w trybie dysku. W przypadku krótkich efektów dźwiękowych lepiej jest je wczytać i dekompresować do pamięci (domyślna wartość enum LoadMode.memory).

Masz jednak kilka problemów. Po pierwsze, muzyka jest zbyt głośna, co przytłacza dźwięki. W większości gier muzyka jest zwykle odtwarzana w tle, aby stworzyć przestrzeń dla bardziej informacyjnych elementów dźwiękowych, takich jak mowa i efekty dźwiękowe. Można to łatwo naprawić, używając parametru głośności metody odtwarzania. Możesz na przykład użyć polecenia _soloud!.play(musicSource, volume: 0.6), by odtworzyć utwór z głośnością 60%. Możesz też ustawić głośność w dowolnym momencie później, na przykład za pomocą polecenia _soloud!.setVolume(_musicHandle, 0.6).

Drugi problem polega na tym, że utwór nagle się zatrzymuje. Dzieje się tak, ponieważ jest to utwór, który ma być odtwarzany w pętli, a punkt początkowy pętli nie znajduje się na początku pliku audio.

88d2c57fffdfe996.png

Jest to popularny wybór w przypadku muzyki z gier, ponieważ oznacza, że utwór zaczyna się od naturalnego wprowadzenia, a potem odtwarza się tak długo, jak to konieczne, bez wyraźnej pętli pętli. Gdy gra musi przejść do następnego utworu, po prostu ściemnia ten, który jest aktualnie odtwarzany.

Na szczęście SoLoud zapewnia sposoby odtwarzania dźwięku w pętli. Metoda play() przyjmuje wartość logiczną parametru looping oraz wartość punktu początkowego pętli jako parametr loopingStartAt. Wynikowy kod wygląda tak:

lib/audio/audio_controller.dart

...

_musicHandle = await _soloud!.play(
  musicSource,
  volume: 0.6,
  looping: true,
  //  The exact timestamp of the start of the loop.
  loopingStartAt: const Duration(seconds: 25, milliseconds: 43),
);

...

Jeśli nie ustawisz parametru loopingStartAt, zostanie on domyślnie ustawiony na Duration.zero (czyli na początek pliku audio). Jeśli masz utwór muzyczny, który idealnie nadaje się do pętli, bez wprowadzenia, to właśnie jest dla Ciebie.

  • Aby mieć pewność, że po zakończeniu odtwarzania źródło dźwięku zostało prawidłowo usunięte, posłuchaj strumienia allInstancesFinished z każdego z nich. Po dodaniu wywołań dziennika metoda startMusic() wygląda tak:

lib/audio/audio_controller.dart

...

  Future<void> startMusic() async {
    if (_musicHandle != null) {
      if (_soloud!.getIsValidVoiceHandle(_musicHandle!)) {
        _log.info('Music is already playing. Stopping first.');
        await _soloud!.stop(_musicHandle!);
      }
    }
    _log.info('Loading music');
    final musicSource = await _soloud!
        .loadAsset('assets/music/looped-song.ogg', mode: LoadMode.disk);
    musicSource.allInstancesFinished.first.then((_) {
      _soloud!.disposeSource(musicSource);
      _log.info('Music source disposed');
      _musicHandle = null;
    });

    _log.info('Playing music');
    _musicHandle = await _soloud!.play(
      musicSource,
      volume: 0.6,
      looping: true,
      loopingStartAt: const Duration(seconds: 25, milliseconds: 43),
    );
  }

...

Zanikanie dźwięku

Kolejny problem polega na tym, że muzyka nigdy się nie kończy. Wprowadzimy efekt znikania.

Jednym ze sposobów wdrożenia zanikania jest użycie jakiejś funkcji, która jest wywoływana kilka razy na sekundę, np. Ticker lub Timer.periodic, i zmniejszanie głośności muzyki o niewielkie wartości. To zadziała, ale jest pracochłonne.

Na szczęście SoLoud udostępnia wygodne metody, które pozwalają to zrobić. Oto jak możesz płynnie zmniejszyć głośność muzyki w ciągu 5 sekund, a potem zatrzymać odtwarzanie, aby nie zużywało niepotrzebnie zasobów procesora. Zastąp metodę fadeOutMusic() tym kodem:

lib/audio/audio_controller.dart

...

  void fadeOutMusic() {
    if (_musicHandle == null) {
      _log.info('Nothing to fade out');
      return;
    }
    const length = Duration(seconds: 5);
    _soloud!.fadeVolume(_musicHandle!, 0, length);
    _soloud!.scheduleStop(_musicHandle!, length);
  }

...

6. Zastosuj efekty

Jedną z wielkich zalet posiadania odpowiedniego silnika audio jest możliwość przetwarzania dźwięku, np. kierowania niektórych dźwięków przez pogłos, korektor lub filtr dolnoprzepustowy.

W grach może służyć do różnicowania dźwięków w zależności od lokalizacji. Na przykład oklaski brzmią inaczej w lesie niż w betonowym bunkrze. Las pomaga rozpraszać i absorbować dźwięk, a gołe ściany bunkra odbijają fale dźwiękowe, co powoduje pogłos. Podobnie głosy ludzi brzmią inaczej, gdy słyszy się je przez ścianę. Wyższe częstotliwości tych dźwięków są łatwiej stłumione, gdy przechodzą przez światło stałe, co skutkuje efektem filtra dolnoprzepustowego.

Ilustracja przedstawiająca 2 osoby rozmawiające w pomieszczeniu. Fale dźwiękowe nie tylko przenoszą się bezpośrednio od jednej osoby do drugiej, ale też odbijają się od ścian i sufitu.

SoLoud udostępnia kilka różnych efektów dźwiękowych, które możesz zastosować do dźwięku.

  • Aby uzyskać efekt dużej sali, na przykład katedry lub jaskini, użyj pola SoLoud.filters:

lib/audio/audio_controller.dart

...

  void applyFilter() {
    _soloud!.filters.freeverbFilter.activate();
    _soloud!.filters.freeverbFilter.wet.value = 0.2;
    _soloud!.filters.freeverbFilter.roomSize.value = 0.9;
  }

  void removeFilter() {
    _soloud!.filters.freeverbFilter.deactivate();
  }

...

Pole SoLoud.filters zapewnia dostęp do wszystkich typów filtrów i ich parametrów. Każdy parametr ma też wbudowane funkcje, takie jak stopniowe zanikanie i oscylacja.

Uwaga: _soloud!.filters udostępnia filtry globalne. Jeśli chcesz zastosować filtry do jednego źródła, użyj odpowiednika AudioSource.filters, który działa tak samo.

Za pomocą poprzedniego kodu:

  • Włącz globalnie filtr Freeverb.
  • Ustaw parametr Wet na 0.2, co oznacza, że wygenerowany dźwięk będzie w 80% oryginalny, a 20% generacji z efektu pogłosu. Ustawienie tego parametru na 1.0 jest jakby słychać tylko fale dźwiękowe, które do Ciebie dochodzi z odległych ścian pomieszczenia, bez żadnego oryginalnego dźwięku.
  • Ustaw wartość parametru Rozmiar sali na 0.9. Możesz dostosować ten parametr do swoich potrzeb lub nawet zmieniać go dynamicznie. 1.0 to ogromna jaskinia, a 0.0 to łazienka.
  • W razie potrzeby zmień kod i zastosuj jeden z tych filtrów lub kombinację tych filtrów:
  • biquadFilter (może być używany jako filtr dolnoprzepustowy)
  • pitchShiftFilter
  • equalizerFilter
  • echoFilter
  • lofiFilter
  • flangerFilter
  • bassboostFilter
  • waveShaperFilter
  • robotizeFilter

7. Gratulacje

Wdrożyłeś/wdróżyłaś kontroler dźwięku, który odtwarza dźwięki, odtwarza muzykę w pętli i zachowuje efekty.

Więcej informacji

  • Możesz też skorzystać z zaawansowanych funkcji sterownika dźwięku, takich jak wstępne wczytywanie dźwięków podczas uruchamiania, odtwarzanie utworów w kolejności lub stopniowe stosowanie filtra z upływem czasu.
  • Przeczytaj dokumentację pakietu flutter_soloud.
  • Przeczytaj stronę główną biblioteki C++.
  • Dowiedz się więcej o Dart FFI, technologii używanej do łączenia się z biblioteką C++.
  • Aby znaleźć inspirację, obejrzyj film Guya Somberga o programowaniu audio z gier. (dostępny jest też dłuższy film). Gdy Guy mówi o oprogramowaniu pośredniczącym, ma na myśli takie biblioteki jak SoLoud i FMOD. Pozostała część kodu jest zwykle specyficzna dla każdej gry.
  • Utwórz grę i opublikuj ją.

Ilustracja przedstawiająca słuchawki