Sound und Musik zum Flutter-Spiel hinzufügen

1. Hinweis

Spiele sind audiovisuelle Erlebnisse. Flutter ist ein hervorragendes Tool zum Erstellen ansprechender visueller Elemente und einer soliden Benutzeroberfläche. Die fehlende Zutat ist Audio. In diesem Codelab erfahren Sie, wie Sie mit dem flutter_soloud-Plug-in Ton und Musik mit niedriger Latenz in Ihr Projekt einbinden. Sie beginnen mit einem einfachen Gerüst, damit Sie direkt zu den interessanten Teilen springen können.

Eine handgezeichnete Illustration von Kopfhörern.

Sie können das Gelernte natürlich nicht nur für Spiele, sondern auch für Apps verwenden. Während fast alle Spiele Ton und Musik erfordern, ist das bei den meisten Apps nicht der Fall. Daher liegt der Schwerpunkt dieses Codelabs auf Spielen.

Vorbereitung

  • Grundkenntnisse in Flutter
  • Kenntnisse zum Ausführen und Entwickeln von Flutter-Apps

Lerninhalte

  • Einmalig wiedergegebene Töne abspielen
  • Musikschleifen ohne Pausen abspielen und anpassen
  • So kannst du Geräusche ein- und ausblenden.
  • So wenden Sie Umgebungseffekte auf Töne an.
  • Umgang mit Ausnahmen
  • Wie Sie alle diese Funktionen in einem einzigen Audiocontroller kapseln.

Voraussetzungen

  • Das Flutter SDK
  • Einen Code-Editor Ihrer Wahl

2. Einrichten

  1. Laden Sie die folgenden Dateien herunter. Keine Sorge, wenn Sie eine langsame Verbindung haben. Sie benötigen die Dateien später, sodass Sie sie während der Arbeit herunterladen können.
  1. Erstellen Sie ein Flutter-Projekt mit einem beliebigen Namen.
  1. Erstellen Sie im Projekt eine lib/audio/audio_controller.dart-Datei.
  2. Geben Sie in die Datei den folgenden Code ein:

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

Wie Sie sehen, ist dies nur ein Skelett für zukünftige Funktionen. Wir werden das alles in diesem Codelab implementieren.

  1. Öffnen Sie als Nächstes die Datei lib/main.dart und ersetzen Sie den Inhalt durch den folgenden Code:

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),
      ),
      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. Nachdem die Audiodateien heruntergeladen wurden, erstellen Sie im Stammverzeichnis Ihres Projekts ein Verzeichnis mit dem Namen assets.
  2. Erstellen Sie im Verzeichnis assets zwei Unterverzeichnisse mit den Namen music und sounds.
  3. Verschiebe die heruntergeladenen Dateien in dein Projekt, sodass sich die Songdatei in der Datei assets/music/looped-song.ogg und die Kirchenbänken-Sounds in den folgenden Dateien befinden:
  • assets/sounds/pew1.mp3
  • assets/sounds/pew2.mp3
  • assets/sounds/pew3.mp3

Die Projektstruktur sollte jetzt in etwa so aussehen:

Eine Baumansicht des Projekts mit Ordnern wie „android“, „ios“ und Dateien wie „README.md“ und „analysis_options.yaml“. Darunter sehen wir den Ordner „assets“ mit den Unterverzeichnissen „music“ und „sounds“, den Ordner „lib“ mit „main.dart“ und dem Unterverzeichnis „audio“ mit „audio_controller.dart“ sowie die Datei „pubspec.yaml“.  Die Pfeile zeigen auf die neuen Verzeichnisse und die Dateien, die Sie bisher bearbeitet haben.

Nachdem Sie die Dateien erstellt haben, müssen Sie Flutter darüber informieren.

  1. Öffnen Sie die Datei pubspec.yaml und ersetzen Sie den Abschnitt flutter: unten in der Datei durch Folgendes:

pubspec.yaml

...

flutter:
  uses-material-design: true

  assets:
    - assets/music/
    - assets/sounds/
  1. Fügen Sie eine Abhängigkeit vom Paket flutter_soloud und vom Paket logging hinzu.
flutter pub add flutter_soloud logging

Ihre pubspec.yaml-Datei sollte jetzt zusätzliche Abhängigkeiten von den Paketen flutter_soloud und logging haben.

pubspec.yaml

...

dependencies:
  flutter:
    sdk: flutter

  flutter_soloud: ^3.1.10
  logging: ^1.3.0

...
  1. Führen Sie das Projekt aus. Noch funktioniert nichts, da Sie die Funktionen in den folgenden Abschnitten hinzufügen.

10f0f751c9c47038.png

3. Initialisieren und Herunterfahren

Zum Abspielen von Audioinhalten verwenden Sie das Plug-in flutter_soloud. Dieses Plug-in basiert auf dem SoLoud-Projekt, einer C++-Audio-Engine für Spiele, die unter anderem von Nintendo SNES Classic verwendet wird.

7ce23849b6d0d09a.png

So initialisieren Sie die SoLoud-Audio-Engine:

  1. Importieren Sie in der Datei audio_controller.dart das Paket flutter_soloud und fügen Sie der Klasse ein privates Feld vom Typ _soloud hinzu.

lib/audio/audio_controller.dart

import 'dart:async';

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
  }

  ...

Der Audiocontroller verwaltet die zugrunde liegende SoLoud-Engine über dieses Feld und leitet alle Aufrufe an sie weiter.

  1. Geben Sie in der initialize()-Methode den folgenden Code ein:

lib/audio/audio_controller.dart

...

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

...

Dadurch wird das Feld _soloud ausgefüllt und wartet auf die Initialisierung. Wichtige Hinweise:

  • SoLoud bietet ein Singleton-Feld instance. Es ist nicht möglich, mehrere SoLoud-Instanzen zu erstellen. Das ist in der C++-Engine nicht zulässig und daher auch nicht im Dart-Plug-in.
  • Die Initialisierung des Plug-ins ist asynchron und wird erst abgeschlossen, wenn die init()-Methode zurückgegeben wird.
  • Der Übersichtlichkeit halber werden in diesem Beispiel keine Fehler in einem try/catch-Block abgefangen. Im Produktionscode sollten Sie dies tun und alle Fehler dem Nutzer melden.
  1. Geben Sie in der dispose()-Methode den folgenden Code ein:

lib/audio/audio_controller.dart

...

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

...

Es ist empfehlenswert, SoLoud beim Schließen der App zu beenden. Alles sollte jedoch auch dann einwandfrei funktionieren, wenn Sie das nicht tun.

  1. Die Methode AudioController.initialize() wird bereits von der Funktion main() aufgerufen. Das bedeutet, dass SoLoud beim Hot-Neustart des Projekts im Hintergrund initialisiert wird, aber erst funktioniert, wenn Sie tatsächlich Töne abspielen.

4. Einzeltöne abspielen

Asset laden und abspielen

Da Sie jetzt wissen, dass SoLoud beim Start initialisiert wird, können Sie es bitten, Töne abzuspielen.

SoLoud unterscheidet zwischen einer Audioquelle, also den Daten und Metadaten, die zum Beschreiben eines Tons verwendet werden, und den „Toninstanzen“, also den tatsächlich wiedergegebenen Tönen. Ein Beispiel für eine Audioquelle ist eine MP3-Datei, die in den Arbeitsspeicher geladen und zur Wiedergabe bereit ist und durch eine Instanz der Klasse AudioSource dargestellt wird. Jedes Mal, wenn du diese Audioquelle abspielst, erstellt SoLoud eine „Toninstanz“, die durch den Typ SoundHandle dargestellt wird.

Sie erhalten eine AudioSource-Instanz, indem Sie sie laden. Wenn Sie beispielsweise eine MP3-Datei in Ihren Assets haben, können Sie sie laden, um eine AudioSource zu erhalten. Dann bitten Sie SoLoud, diese AudioSource abzuspielen. Sie können sie beliebig oft und sogar gleichzeitig abspielen.

Wenn Sie mit einer Audioquelle fertig sind, können Sie sie mit der Methode SoLoud.disposeSource() entsorgen.

So laden Sie ein Asset und spielen es ab:

  1. Geben Sie in der playSound()-Methode der AudioController-Klasse den folgenden Code ein:

lib/audio/audio_controller.dart

  ...

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

  ...
  1. Speichern Sie die Datei, führen Sie einen Hot-Reload durch und wählen Sie dann Ton wiedergeben aus. Sie sollten einen alberne Knall hören. Wichtige Hinweise:
  • Das angegebene assetKey-Argument ist beispielsweise assets/sounds/pew1.mp3 – derselbe String, den Sie für jede andere Flutter API zum Laden von Assets angeben würden, z. B. für das Image.asset()-Widget.
  • Die SoLoud-Instanz bietet eine loadAsset()-Methode, die eine Audiodatei asynchron aus den Assets des Flutter-Projekts lädt und eine Instanz der Klasse AudioSource zurückgibt. Es gibt gleichwertige Methoden, eine Datei aus dem Dateisystem (loadFile()-Methode) und über das Netzwerk von einer URL (loadUrl()-Methode) zu laden.
  • Die neu gewonnene AudioSource-Instanz wird dann an die play()-Methode von SoLoud übergeben. Diese Methode gibt eine Instanz des Typs SoundHandle zurück, die den gerade wiedergegebenen Ton darstellt. Dieser Handle kann wiederum an andere SoLoud-Methoden übergeben werden, um den Ton beispielsweise zu pausieren, anzuhalten oder die Lautstärke zu ändern.
  • Obwohl play() eine asynchrone Methode ist, beginnt die Wiedergabe praktisch sofort. Das flutter_soloud-Paket verwendet die Foreign Function Interface (FFI) von Dart, um C-Code direkt und synchron aufzurufen. Die üblichen Nachrichten zwischen Dart-Code und Plattformcode, die für die meisten Flutter-Plug-ins charakteristisch sind, sind nirgends zu finden. Der einzige Grund, warum einige Methoden asynchron sind, ist, dass ein Teil des Codes des Plug-ins in einem eigenen Isolate ausgeführt wird und die Kommunikation zwischen Dart-Isolaten asynchron ist.
  • Sie stellen mit _soloud! fest, dass das Feld _soloud nicht null ist. Dies dient der Kürze. Der Produktionscode sollte die Situation fehlerfrei bewältigen, wenn der Entwickler versucht, einen Ton abzuspielen, bevor der Audiocontroller vollständig initialisiert wurde.

Mit Ausnahmen umgehen

Sie haben vielleicht bemerkt, dass Sie wieder einmal mögliche Ausnahmen ignorieren. Es ist an der Zeit, das für diese Methode zu ändern. Der Kürze halber wird im Codelab nach diesem Abschnitt wieder darauf zurückgekommen, Ausnahmen zu ignorieren.

  • Um in diesem Fall mit Ausnahmen umzugehen, können Sie die beiden Zeilen der playSound()-Methode in einen try/catch-Block einschließen und nur Instanzen von SoLoudException abfangen.

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 löst verschiedene Ausnahmen aus, z. B. die SoLoudNotInitializedException- oder SoLoudTemporaryFolderFailedException-Ausnahme. In den API-Dokumenten für jede Methode werden die Arten von Ausnahmen aufgeführt, die auftreten können.

SoLoud bietet auch eine übergeordnete Klasse für alle Ausnahmen, die SoLoudException-Ausnahme, damit Sie alle Fehler im Zusammenhang mit der Funktionalität der Audio-Engine abfangen können. Das ist besonders hilfreich, wenn das Abspielen von Audio nicht kritisch ist. Beispiel: Sie möchten nicht, dass die Spielsitzung des Spielers nur deshalb abstürzt, weil einer der „Pew-Pew“-Sounds nicht geladen werden konnte.

Wie du dir wahrscheinlich vorstellen kannst, kann auch die loadAsset()-Methode einen FlutterError-Fehler auslösen, wenn du einen nicht vorhandenen Asset-Schlüssel angibst. Der Versuch, Assets zu laden, die nicht im Lieferumfang des Spiels enthalten sind, sollte in der Regel behoben werden. Daher ist dies ein Fehler.

Unterschiedliche Töne abspielen

Möglicherweise haben Sie bemerkt, dass nur die Datei pew1.mp3 wiedergegeben wird, obwohl sich im Assets-Verzeichnis zwei weitere Versionen des Tons befinden. Es klingt oft natürlicher, wenn Spiele mehrere Versionen desselben Tons haben und die verschiedenen Versionen nach dem Zufallsprinzip oder im Wechsel abspielen. So klingen beispielsweise Schritte und Schüsse nicht zu einheitlich und damit unecht.

  • Optional können Sie den Code so ändern, dass jedes Mal, wenn auf die Schaltfläche getippt wird, ein anderer Glockenton abgespielt wird.

Eine Abbildung von

5. Musikschleifen abspielen

Länger laufende Töne verwalten

Einige Audioinhalte sind für eine längere Wiedergabe vorgesehen. Musik ist das offensichtliche Beispiel, aber in vielen Spielen wird auch Umgebungssound verwendet, z. B. der Wind, der durch die Gänge heult, das entfernte Singen von Mönchen, das Knarren von jahrhundertealtem Metall oder das entfernte Husten von Patienten.

Das sind Audioquellen mit einer Wiedergabezeit, die in Minuten gemessen werden kann. Sie müssen sie im Auge behalten, damit Sie sie bei Bedarf pausieren oder beenden können. Außerdem werden sie häufig von großen Dateien unterstützt und können viel Arbeitsspeicher verbrauchen. Ein weiterer Grund, sie zu überwachen, ist, dass Sie die AudioSource-Instanz entsorgen können, wenn sie nicht mehr benötigt wird.

Aus diesem Grund fügen Sie AudioController ein neues privates Feld hinzu. Ein Handle für den abgespielten Titel, falls zutreffend. Fügen Sie folgende Zeile hinzu:

lib/audio/audio_controller.dart

...

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

  SoLoud? _soloud;

  SoundHandle? _musicHandle;    // ← Add this.

  ...

Musik starten

Im Grunde unterscheidet sich das Abspielen von Musik nicht vom Abspielen eines einzelnen Tons. Du musst die assets/music/looped-song.ogg-Datei zuerst als Instanz der AudioSource-Klasse laden und dann mit der play()-Methode von SoLoud wiedergeben.

Dieses Mal greifen Sie jedoch auf den Audio-Handle zu, den die play()-Methode zurückgibt, um den Ton während der Wiedergabe zu manipulieren.

  • Sie können die AudioController.startMusic()-Methode auch selbst implementieren. Es ist in Ordnung, wenn Sie einige Details nicht richtig wiedergeben. Wichtig ist, dass die Musik startet, wenn Sie Musik starten auswählen.

Hier eine Referenzimplementierung:

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

...

Beachten Sie, dass Sie die Musikdatei im Laufwerkmodus (LoadMode.disk-Enum) laden. Das bedeutet, dass die Datei nur bei Bedarf in Teilen geladen wird. Bei längeren Audioinhalten ist es im Allgemeinen am besten, sie im Laufwerkmodus zu laden. Bei kurzen Soundeffekten ist es sinnvoller, sie in den Arbeitsspeicher zu laden und dort zu dekomprimieren (das Standard-LoadMode.memory-Enum).

Es gibt jedoch ein paar Probleme. Erstens: Die Musik ist zu laut und übertönt die Geräusche. In den meisten Spielen läuft die Musik meistens im Hintergrund, sodass die aussagekräftigeren Audioinhalte wie Sprache und Soundeffekte im Vordergrund stehen. Dies dient der Behebung von Problemen bei der Verwendung des Lautstärkeparameters der Wiedergabemethode. Sie können beispielsweise _soloud!.play(musicSource, volume: 0.6) sagen, um den Titel mit 60% der Lautstärke abzuspielen. Alternativ können Sie die Lautstärke auch später mit einer Taste wie _soloud!.setVolume(_musicHandle, 0.6) einstellen.

Das zweite Problem ist, dass der Song abrupt stoppt. Das liegt daran, dass der Song in einer Schleife abgespielt werden soll und der Startpunkt der Schleife nicht der Anfang der Audiodatei ist.

88d2c57fffdfe996.png

Dies ist eine beliebte Option für die Musik in Spielen, da der Song mit einem natürlichen Intro beginnt und dann so lange abgespielt wird, wie es erforderlich ist, ohne einen offensichtlichen Loop-Punkt. Wenn im Spiel ein Übergang zum nächsten Song erforderlich ist, wird der aktuelle Song ausgeblendet.

Glücklicherweise bietet SoLoud Möglichkeiten, Audioinhalte im Loop abzuspielen. Die Methode play() nimmt einen booleschen Wert für den Parameter looping und den Wert für den Startpunkt der Schleife als Parameter loopingStartAt an. Der Code sieht dann so aus:

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

...

Wenn Sie den Parameter loopingStartAt nicht festlegen, wird standardmäßig Duration.zero verwendet, also der Anfang der Audiodatei. Wenn du einen Musiktitel hast, der sich perfekt ohne Einleitung in einem Loop abspielen lässt, ist das die richtige Option.

  • Um zu prüfen, ob die Audioquelle nach der Wiedergabe ordnungsgemäß entsorgt wird, kannst du dir den allInstancesFinished-Stream anhören, den jede Audioquelle bereitstellt. Mit hinzugefügten Protokollaufrufen sieht die startMusic()-Methode so aus:

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

...

Ton ausblenden

Ihr nächstes Problem ist, dass die Musik nie endet. Jetzt ist es an der Zeit, einen Übergang zu implementieren.

Eine Möglichkeit, das Ausblenden zu implementieren, wäre eine Funktion, die mehrmals pro Sekunde aufgerufen wird, z. B. Ticker oder Timer.periodic, und die Lautstärke der Musik in kleinen Schritten senkt. Das würde funktionieren, ist aber mit viel Arbeit verbunden.

Glücklicherweise bietet SoLoud praktische „Fire-and-Forget“-Methoden, die das für Sie erledigen. So können Sie die Musik über einen Zeitraum von fünf Sekunden ausblenden und dann die Audioinstanz beenden, damit sie nicht unnötig CPU-Ressourcen verbraucht: Ersetzen Sie die fadeOutMusic()-Methode durch diesen Code:

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. Effekte anwenden

Ein großer Vorteil einer richtigen Audio-Engine ist, dass du Audiosignale verarbeiten kannst, z. B. indem du einige Töne durch einen Hall, einen Equalizer oder einen Tiefpassfilter leitest.

In Spielen kann dies zur akustischen Unterscheidung von Orten verwendet werden. Ein Klatschen klingt beispielsweise in einem Wald anders als in einem Betonbunker. Während ein Wald dazu beiträgt, den Schall zu verteilen und zu absorbieren, reflektieren die kahlen Wände eines Bunkers die Schallwellen zurück, was zu einem Nachhall führt. Ähnlich klingen die Stimmen von Menschen anders, wenn sie durch eine Wand gehört werden. Die höheren Frequenzen dieser Töne werden beim Durchlaufen des festen Mediums stärker gedämpft, was zu einem Tiefpassfiltereffekt führt.

Eine Abbildung von zwei Personen, die in einem Raum miteinander sprechen. Die Schallwellen gehen nicht nur direkt von einer Person zur anderen, sondern werden auch von den Wänden und der Decke reflektiert.

SoLoud bietet verschiedene Audioeffekte, die Sie auf Audio anwenden können.

  • Wenn es so klingen soll, als befände sich der Spieler in einem großen Raum wie einer Kathedrale oder einer Höhle, verwende das Feld 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();
  }

...

Über das Feld SoLoud.filters haben Sie Zugriff auf alle Filtertypen und ihre Parameter. Jeder Parameter hat außerdem integrierte Funktionen wie allmähliches Ausblenden und Oszillation.

Hinweis : _soloud!.filters gibt globale Filter an. Wenn Sie Filter auf eine einzelne Quelle anwenden möchten, verwenden Sie das Pendant AudioSource.filters, das genauso funktioniert.

Mit dem vorherigen Code geschieht Folgendes:

  • Aktivieren Sie den Freeverb-Filter global.
  • Lege den Parameter Wet auf 0.2 fest. Das bedeutet, dass das resultierende Audiosignal zu 80% aus dem Original und zu 20% aus der Ausgabe des Halleffekts besteht. Wenn Sie diesen Parameter auf 1.0 festlegen, hören Sie nur die Schallwellen, die von den entfernten Wänden des Raums zu Ihnen zurückkommen, und nichts vom ursprünglichen Audiosignal.
  • Legen Sie für den Parameter Raumgröße den Wert 0.9 fest. Sie können diesen Parameter nach Belieben anpassen oder sogar dynamisch ändern. 1.0 ist eine riesige Höhle, während 0.0 ein Badezimmer ist.
  • Wenn Sie möchten, können Sie den Code ändern und einen oder mehrere der folgenden Filter anwenden:
  • biquadFilter (kann als Tiefpassfilter verwendet werden)
  • pitchShiftFilter
  • equalizerFilter
  • echoFilter
  • lofiFilter
  • flangerFilter
  • bassboostFilter
  • waveShaperFilter
  • robotizeFilter

7. Glückwunsch

Sie haben einen Audiocontroller implementiert, der Töne abspielt, Musik in einem Loop wiedergibt und Effekte anwendet.

Weitere Informationen

  • Mit Funktionen wie dem Vorladen von Tönen beim Starten, dem Abspielen von Songs in einer Sequenz oder dem schrittweisen Anwenden eines Filters können Sie den Audiocontroller noch weiter optimieren.
  • Lesen Sie die Paketdokumentation von flutter_soloud.
  • Weitere Informationen finden Sie auf der Startseite der zugrunde liegenden C++-Bibliothek.
  • Weitere Informationen zu Dart FFI, der Technologie, die für die Verknüpfung mit der C++-Bibliothek verwendet wird.
  • Sehen Sie sich den Vortrag von Guy Somberg zum Programmieren von Game-Audio an, um sich inspirieren zu lassen. (Es gibt auch eine längere Version.) Wenn Guy von „Middleware“ spricht, meint er Bibliotheken wie SoLoud und FMOD. Der Rest des Codes ist in der Regel für jedes Spiel spezifisch.
  • Erstellen Sie Ihr Spiel und veröffentlichen Sie es.

Eine Abbildung von Kopfhörern