Sound und Musik zum Flutter-Spiel hinzufügen

1. Hinweis

Spiele sind audiovisuelle Erlebnisse. Flutter ist ein hervorragendes Tool, um visuell ansprechende Bilder und eine solide Benutzeroberfläche zu erstellen. Die fehlende Zutat ist Audio. In diesem Codelab erfährst du, wie du mit dem flutter_soloud-Plug-in Sound und Musik mit niedriger Latenz in dein Projekt einführst. Sie beginnen mit einem einfachen Gerüst, sodass Sie direkt zu den interessanten Stellen 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
  • Unterbrechungsfreie Musikschleifen abspielen und anpassen
  • Ton ein- und ausblenden
  • So wendest du Umgebungseffekte auf Geräusche an.
  • So gehen Sie mit Ausnahmen um.
  • 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 Ihre Verbindung langsam ist. 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 Gerüst 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 ihren 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),
        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. 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, das eine music und das andere 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“ sowie Dateien wie „README.md“ und „analysis_options.yaml“. Darunter befinden sich das Verzeichnis „assets“ mit den Unterverzeichnissen „music“ und „sounds“, das Verzeichnis „lib“ mit „main.dart“ und das Unterverzeichnis „audio“ mit „audio_controller.dart“ sowie die Datei „pubspec.yaml“.  Die Pfeile zeigen auf die neuen Verzeichnisse und auf 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: am Ende 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 dem Paket logging hinzu.

pubspec.yaml

...

dependencies:
  flutter:
    sdk: flutter

  flutter_soloud: ^2.0.0
  logging: ^1.2.0

...
  1. Führen Sie das Projekt aus. Noch nichts funktioniert, da Sie die Funktionalität in den folgenden Abschnitten hinzugefügt haben.

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

Diese stammen aus der zugrunde liegenden SoLoud-C++-Bibliothek. Sie haben keine Auswirkungen auf die Funktionalität und können ignoriert werden.

3. Initialisieren und Herunterfahren

Zum Abspielen von Audioinhalten verwenden Sie das flutter_soloud-Plug-in. 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: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
  }

  ...

Der Audio-Controller verwaltet die zugrunde liegende SoLoud-Engine über dieses Feld und leitet alle Aufrufe an diese weiter.

  1. Geben Sie in der Methode initialize() 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. Die C++-Engine lässt dies nicht zu und wird vom Dart-Plug-in ebenfalls nicht zugelassen.
  • Die Initialisierung des Plug-ins ist asynchron und wird erst abgeschlossen, wenn die init()-Methode zurückgegeben wird.
  • Der Einfachheit halber werden in diesem Beispiel keine Fehler in einem try/catch-Block erfasst. Im Produktionscode sollten Sie dies tun und dem Nutzer eventuelle Fehler melden.
  1. Geben Sie in der Methode dispose() 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. Beachten Sie, dass die Methode AudioController.initialize() bereits über die Funktion main() aufgerufen wird. Das bedeutet, dass SoLoud beim Hot-Neustart des Projekts im Hintergrund initialisiert wird, aber erst funktioniert, wenn Sie tatsächlich Töne abspielen.

4. Töne aus einer Aufnahme 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, d. h. den Daten und Metadaten, die zur Beschreibung eines Tons verwendet werden, und ihren „Toninstanzen“, d. h. den tatsächlich abgespielten 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“ der durch den Typ SoundHandle dargestellt wird.

Sie erhalten eine AudioSource-Instanz, indem Sie sie laden. Wenn deine Assets beispielsweise eine MP3-Datei enthalten, kannst du 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 Methode playSound() der Klasse AudioController 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, aktualisieren Sie sie bei Bedarf und wählen Sie dann Ton abspielen aus. Sie sollten ein albernes Pew-Geräusch 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, um 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 abgespielten Ton darstellt. Dieser Handle kann wiederum an andere SoLoud-Methoden übergeben werden, um beispielsweise den Ton 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 übliche Kommunikation zwischen Dart-Code und Plattformcode, die für die meisten Flutter-Plug-ins charakteristisch ist, ist nirgendwo zu finden. Einige Methoden sind nur asynchron, da ein Teil des Codes des Plug-ins in einem eigenen Isola ausgeführt wird und die Kommunikation zwischen Dart-Isolierungen asynchron ist.
  • Sie bestätigen einfach mit _soloud!, dass das Feld _soloud nicht null ist. Dies dient der Kürze. Der Produktionscode sollte mit Situationen umgehen können, in denen der Entwickler versucht, einen Ton abzuspielen, bevor der Audio-Controller vollständig initialisiert werden konnte.

Ausnahmen für den Deal

Sie haben vielleicht bemerkt, dass Sie wieder einmal mögliche Ausnahmen ignorieren. Korrigieren wir das für diese Methode zu Lernzwecken. Der Einfachheit halber werden im Codelab Ausnahmen nach diesem Abschnitt ignoriert.

  • 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 Ausnahmen SoLoudNotInitializedException oder SoLoudTemporaryFolderFailedException. In der API-Dokumentation der einzelnen Methoden sind die Arten von Ausnahmen aufgeführt, die möglicherweise ausgelöst werden.

SoLoud bietet auch eine übergeordnete Klasse für alle Ausnahmen, die SoLoudException-Ausnahme, damit du alle Fehler im Zusammenhang mit der Funktionalität der Audio-Engine abfangen kannst. Das ist besonders hilfreich, wenn das Abspielen von Audio nicht kritisch ist. Das ist beispielsweise der Fall, wenn Sie die Spielsitzung des Spielers nicht zum Absturz bringen möchten, nur weil eines der Töne von Peng-Pew nicht geladen werden konnte.

Wie zu erwarten wäre, kann die Methode loadAsset() auch den Fehler FlutterError ausgeben, wenn Sie einen nicht vorhandenen Assetschlüssel angeben. Der Versuch, Assets zu laden, die nicht im Lieferumfang des Spiels enthalten sind, sollte in der Regel behoben werden. Daher ist dies ein Fehler.

Verschiedene Töne abspielen

Vielleicht hast du bemerkt, dass du nur die Datei pew1.mp3 wiedergibst, aber im Asset-Verzeichnis befinden sich zwei weitere Versionen des Tons. 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.

  • Als optionale Übung können Sie den Code so ändern, dass bei jedem Antippen der Schaltfläche ein anderer Pew-Ton abgespielt wird.

Illustration von

5. Musikschleifen abspielen

Länger laufende Töne verwalten

Einige Audioinhalte sind für einen längeren Zeitraum gedacht. Musik ist das offensichtliche Beispiel, aber viele Spiele spielen auch eine Atmosphäre, beispielsweise der Wind, der durch die Gänge heult, das Gesang der Mönche aus der Ferne, das Knarren jahrhundertealter Metalle oder das Husten von Patienten in der Ferne.

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 stoppen 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ühren Sie ein neues privates Feld für AudioController ein. Ein Handle für den aktuell abgespielten Song, falls vorhanden. 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 Wesentlichen unterscheidet sich das Abspielen von Musik nicht vom Abspielen eines Sounds mit einer Aufnahme. Du musst trotzdem zuerst die Datei assets/music/looped-song.ogg als Instanz der AudioSource-Klasse laden und dann die play()-Methode von SoLoud verwenden, um sie abzuspielen.

Dieses Mal greifen Sie jedoch auf den Audio-Handle zu, der von der play()-Methode zurückgegeben wird, um das Audio 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);
    _musicHandle = await _soloud!.play(musicSource);
  }

...

Beachte, dass du die Musikdatei im Laufwerkmodus (LoadMode.disk-Enum) lädst. Das bedeutet einfach, dass die Datei nur bei Bedarf in Teilen geladen wird. Bei Audioinhalten mit einer längeren Laufzeit empfiehlt es sich im Allgemeinen, den Datenträger im Datenträgermodus 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. Das lässt sich ganz einfach mit dem Lautstärkeparameter der Wiedergabemethode beheben. Sie können beispielsweise _soloud!.play(musicSource, volume: 0.6) sagen, um den Titel mit 60 % der Lautstärke abzuspielen. Alternativ kannst du die Lautstärke später mit einem Wert wie _soloud!.setVolume(_musicHandle, 0.6) einstellen.

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

88d2c57fffdfe996.png

Dies ist eine beliebte Wahl für Spielemusik, da der Song mit einem natürlichen Intro beginnt und dann so lange wie nötig abgespielt wird – ohne einen offensichtlichen Schleifenpunkt. Wenn im Spiel ein Übergang vom aktuell abgespielten Song erforderlich ist, wird der Song einfach ausgeblendet.

Zum Glück bietet SoLoud die Möglichkeit, Audioschleifen abzuspielen. Die Methode play() verwendet einen booleschen Wert für den Parameter looping sowie den Wert für den Startpunkt der Schleife als Parameter loopingStartAt. 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.

  • Damit die Audioquelle nach der Wiedergabe ordnungsgemäß entsorgt wird, solltest du den allInstancesFinished-Stream der jeweiligen Audioquelle abspielen. Mit hinzugefügten Logaufrufen sieht die Methode startMusic() dann 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

Das nächste Problem ist, dass die Musik nie zu Ende ist. Lassen Sie uns eine Blende implementieren.

Eine Möglichkeit, die Ausblendung zu implementieren, wäre eine Funktion, die mehrmals pro Sekunde aufgerufen wird, z. B. Ticker oder Timer.periodic, und die Lautstärke der Musik dabei geringfügig zu verringern. Das würde funktionieren, ist aber aufwendig.

Glücklicherweise bietet SoLoud praktische Fire-and-Forget-Methoden, die dies für dich 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. Zum Beispiel klingt ein Klatschen im 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 leichter gedämpft, was zu einem Tiefpassfiltereffekt führt.

Eine Abbildung von zwei Personen, die in einem Raum miteinander sprechen. Die Schallwellen dringen nicht nur direkt von einer Person zur anderen aus, sondern prallen auch von Wänden und Decke ab.

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

  • Wenn es sich so anfühlen soll, als befinde sich der Player in einem großen Raum, z. B. einer Kathedrale oder einer Höhle, verwenden Sie 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 ein allmähliches Ausblenden und eine Oszillation.

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

Mit dem vorherigen Code geschieht Folgendes:

  • Aktivieren Sie global den Freeverb-Filter.
  • 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 und 0.0 ein Badezimmer.
  • 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 Audio-Controller implementiert, der Klänge abspielt, Musik als Schleife wiedergeben und Effekte anwendet.

Weitere Informationen

  • Mit Funktionen wie dem Vorladen von Tönen beim Starten, dem Abspielen von Titeln in einer bestimmten Reihenfolge oder dem schrittweisen Anwenden eines Filters können Sie den Audiocontroller noch weiter optimieren.
  • Lesen Sie die Paketdokumentation für flutter_soloud.
  • Lesen Sie die Startseite der zugrunde liegenden C++-Bibliothek.
  • Weitere Informationen zu Dart FFI, der Technologie für die Schnittstelle zur C++-Bibliothek
  • Sehen Sie sich Guy Sombergs Vortrag zum Thema Audioprogrammierung für Spiele an, um sich inspirieren zu lassen. (Es gibt auch eine längere.) Wenn Guy von „Middleware“ spricht, meint er Bibliotheken wie SoLoud und FMOD. Der Rest des Codes ist tendenziell für jedes Spiel spezifisch.
  • Entwickeln Sie Ihr Spiel und veröffentlichen Sie es.

Eine Abbildung von Kopfhörern