Aggiungi audio e musica al tuo gioco Flutter

1. Prima di iniziare

I giochi sono esperienze audiovisive. Flutter è un ottimo strumento per creare splendide immagini e una UI solida, così puoi sfruttare appieno le potenzialità visive. L'ingrediente mancante è l'audio. In questo codelab, imparerai a utilizzare il plug-in flutter_soloud per introdurre audio e musica a bassa latenza nel tuo progetto. Si inizia con un'impilamento di base in modo da poter passare direttamente alle parti interessanti.

Un'illustrazione disegnata a mano delle cuffie.

Ovviamente puoi usare ciò che impari qui per aggiungere audio alle tue app, non solo ai giochi. Tuttavia, anche se quasi tutti i giochi richiedono audio e musica, la maggior parte delle app non lo richiedono, quindi questo codelab è incentrato sui giochi.

Prerequisiti

  • Familiarità di base con Flutter.
  • Conoscenza di come eseguire ed eseguire il debug delle app Flutter.

Cosa imparerai

  • Come riprodurre i suoni one-shot.
  • Come riprodurre e personalizzare i loop musicali senza interruzioni.
  • Come far sfumare i suoni in entrata e in uscita.
  • Come applicare effetti ambientali ai suoni.
  • Come gestire le eccezioni.
  • Come incapsulare tutte queste funzionalità in un unico controller audio.

Cosa serve

  • SDK Flutter
  • Un editor di codice a tua scelta

2. Configura

  1. Scarica i seguenti file. Se hai una connessione lenta, non preoccuparti. Ti serviranno i file effettivi in un secondo momento, così potrai lasciarli scaricare mentre lavori.
  1. Crea un progetto Flutter con un nome a tua scelta.
  1. Crea un file lib/audio/audio_controller.dart nel progetto.
  2. Nel file, inserisci il codice seguente:

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

Come si può vedere, si tratta solo di uno schema per le funzionalità future. Implementeremo tutto durante questo codelab.

  1. A questo punto, apri il file lib/main.dart e sostituiscine il contenuto con il seguente codice:

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. Dopo aver scaricato i file audio, crea una directory nella directory radice del progetto chiamata assets.
  2. Nella directory assets, crea due sottodirectory, una denominata music e l'altra denominata sounds.
  3. Sposta i file scaricati nel tuo progetto in modo che il file del brano si trovi nel file assets/music/looped-song.ogg e i suoni si trovino nei seguenti file:
  • assets/sounds/pew1.mp3
  • assets/sounds/pew2.mp3
  • assets/sounds/pew3.mp3

La struttura del tuo progetto dovrebbe avere un aspetto simile a questo:

Una visualizzazione ad albero del progetto, con cartelle come &quot;android&quot;, &quot;ios&quot;, file come &quot;README.md&quot; e &quot;analysis_options.yaml&quot;. Tra queste, possiamo vedere la directory &quot;assets&quot; con le sottodirectory &quot;music&quot; e &quot;sounds&quot;, la directory &quot;lib&quot; con &quot;main.ardi&quot; e una sottodirectory &quot;audio&quot; con &quot;audio_controller.arrow&quot; e il file &quot;pubspec.yaml&quot;.  Le frecce puntano alle nuove directory e ai file toccati finora.

Ora che i file sono lì, devi informare Flutter in merito.

  1. Apri il file pubspec.yaml e sostituisci la sezione flutter: in fondo al file con quanto segue:

pubspec.yaml

...

flutter:
  uses-material-design: true

  assets:
    - assets/music/
    - assets/sounds/
  1. Aggiungi una dipendenza al pacchetto flutter_soloud e al pacchetto logging.

pubspec.yaml

...

dependencies:
  flutter:
    sdk: flutter

  flutter_soloud: ^2.0.0
  logging: ^1.2.0

...
  1. Esegui il progetto. Non funziona ancora perché hai aggiunto la funzionalità nelle sezioni seguenti.

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

Questi provengono dalla libreria C++ SoLoud sottostante. Non hanno alcun effetto sulla funzionalità e possono essere tranquillamente ignorati.

3. Inizializza e arresta

Per riprodurre l'audio, usa il plug-in flutter_soloud. Questo plug-in è basato sul progetto SoLoud, un motore audio C++ per i giochi utilizzato, tra gli altri, da Nintendo SNES Classic.

7ce23849b6d0d09a.png

Per inizializzare il motore audio SoLoud, segui questi passaggi:

  1. Nel file audio_controller.dart, importa il pacchetto flutter_soloud e aggiungi un campo _soloud privato alla classe.

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
  }

  ...

Il controller audio gestisce il motore SoLoud sottostante attraverso questo campo e inoltra tutte le chiamate al controller.

  1. Nel metodo initialize(), inserisci il seguente codice:

lib/audio/audio_controller.dart

...

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

...

Questa operazione compila il campo _soloud e attende l'inizializzazione. Tieni presente quanto segue:

  • SoLoud fornisce un campo instance singleton. Non è possibile creare un'istanza per diverse istanze SoLoud. Questo non è consentito dal motore C++, quindi non è consentito nemmeno dal plug-in Dart.
  • L'inizializzazione del plug-in è asincrona e non termina finché non viene restituito il metodo init().
  • Per brevità, in questo esempio non stai individuando errori in un blocco try/catch. Nel codice di produzione, vuoi farlo e segnalare eventuali errori all'utente.
  1. Nel metodo dispose(), inserisci il seguente codice:

lib/audio/audio_controller.dart

...

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

...

È buona norma spegnere SoLoud all'uscita dall'app, anche se non lo fai.

  1. Nota che il metodo AudioController.initialize() è già chiamato dalla funzione main(). Ciò significa che il riavvio a caldo del progetto inizializza SoLoud in background, ma non è utile prima di riprodurre effettivamente alcuni suoni.

4. Riproduci suoni one-shot

Carica un asset e riproducilo

Ora che sai che SoLoud è inizializzato all'avvio, puoi chiedergli di riprodurre i suoni.

SoLoud fa distinzione tra una sorgente audio, ovvero i dati e i metadati utilizzati per descrivere un suono, e le sue "istanze di suono", ovvero i suoni effettivamente riprodotti. Un esempio di sorgente audio può essere un file mp3 caricato in memoria, pronto per essere riprodotto e rappresentato da un'istanza della classe AudioSource. Ogni volta che riproduci questa sorgente audio, SoLoud crea un'"istanza sonora" rappresentato dal tipo SoundHandle.

Caricala per ottenere un'istanza AudioSource. Ad esempio, se gli asset contengono un file mp3, puoi caricarlo per ottenere un AudioSource. Quindi, di' a SoLoud di suonare questo AudioSource. Puoi riprodurlo più volte, anche contemporaneamente.

Quando hai finito di usare una sorgente audio, puoi smaltirla usando il metodo SoLoud.disposeSource().

Per caricare un asset e riprodurlo, segui questi passaggi:

  1. Nel metodo playSound() della classe AudioController, inserisci il seguente codice:

lib/audio/audio_controller.dart

  ...

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

  ...
  1. Salva il file, ricarica a caldo e seleziona Riproduci suono. Dovresti sentire il suono di un panino. Tieni presente quanto segue:
  • L'argomento assetKey fornito è simile a assets/sounds/pew1.mp3, la stessa stringa che forniresti a qualsiasi altra API Flutter per il caricamento degli asset, ad esempio il widget Image.asset().
  • L'istanza di SoLoud fornisce un metodo loadAsset() che carica in modo asincrono un file audio dagli asset del progetto Flutter e restituisce un'istanza della classe AudioSource. Esistono metodi equivalenti per caricare un file dal file system (il metodo loadFile()) e per caricarlo sulla rete da un URL (il metodo loadUrl()).
  • L'istanza AudioSource appena acquisita viene quindi passata al metodo play() di SoLoud. Questo metodo restituisce un'istanza del tipo SoundHandle che rappresenta il suono appena riprodotto. Questo handle può, a sua volta, essere passato ad altri metodi SoLoud per, ad esempio, mettere in pausa, interrompere o modificare il volume dell'audio.
  • Anche se play() è un metodo asincrono, la riproduzione inizia praticamente all'istante. Il pacchetto flutter_soloud utilizza l'interfaccia di funzione esterna (FFI) di Dart per chiamare il codice C direttamente e in modo sincrono. I normali messaggi scambiati tra il codice Dart e il codice della piattaforma, caratteristici della maggior parte dei plug-in Flutter, non si trovano da nessuna parte. L'unico motivo per cui alcuni metodi sono asincroni è che una parte del codice del plug-in viene eseguita nel proprio isolato e la comunicazione tra gli isolati Dart è asincrona.
  • Devi semplicemente dichiarare che il campo _soloud non è nullo in _soloud!. Anche in questo caso, per brevità. Il codice di produzione dovrebbe gestire agevolmente la situazione in cui lo sviluppatore tenta di riprodurre un suono prima che il controller audio abbia avuto la possibilità di inizializzarlo completamente.

Affrontare le eccezioni

Avrai notato che stai ancora ignorando le possibili eccezioni. Risolviamo il problema per questo particolare metodo per scopi di apprendimento. Per brevità, il codelab torna a ignorare le eccezioni dopo questa sezione.

  • Per gestire le eccezioni in questo caso, aggrega le due righe del metodo playSound() in un blocco try/catch e rileva solo le istanze di 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 genera varie eccezioni, ad esempio le eccezioni SoLoudNotInitializedException o SoLoudTemporaryFolderFailedException. Nella documentazione dell'API di ogni metodo sono elencati i tipi di eccezioni che potrebbero essere generate.

SoLoud fornisce anche una classe padre per tutte le eccezioni, l'eccezione SoLoudException, in modo che tu possa individuare tutti gli errori relativi alla funzionalità del motore audio. Ciò è particolarmente utile nei casi in cui la riproduzione dell'audio non è fondamentale. Ad esempio, se non vuoi causare l'arresto anomalo della sessione di gioco del giocatore soltanto perché non è stato possibile caricare uno dei suoni pew-pew.

Come probabilmente previsto, il metodo loadAsset() può anche generare un errore FlutterError se fornisci una chiave asset che non esiste. In genere si tratta di un problema che dovresti risolvere, quindi si tratta di un errore e provare a caricare asset non integrati nel gioco.

Riproduci suoni diversi

Potresti aver notato che riproduci solo il file pew1.mp3, ma sono presenti altre due versioni dell'audio nella directory assets. Spesso risulta più naturale quando i giochi hanno più versioni dello stesso suono e le diverse versioni vengono riprodotte in modo casuale o a rotazione. In questo modo, ad esempio, le impronte e i colpi di arma da fuoco non sembrano troppo uniformi e pertanto falsi.

  • Come esercizio facoltativo, modifica il codice in modo da riprodurre un suono diverso ogni volta che tocchi il pulsante.

Un&#39;illustrazione di

5. Riproduci loop musicali

Gestisci suoni più lunghi

Parte dell'audio è pensata per essere riprodotta per periodi di tempo prolungati. La musica è l'esempio ovvio, ma molti giochi fanno anche atmosfera, come il vento che ulula attraverso i corridoi, i canti lontani dei monaci, i scricchiolii di metalli secolari o i lontani tosse dei pazienti.

Si tratta di sorgenti audio con tempi di riproduzione che possono essere misurati in minuti. È necessario tenerne traccia in modo da poterli mettere in pausa o interrompere quando è necessario. Inoltre, poiché sono spesso supportati da file di grandi dimensioni, possono consumare molta memoria, quindi un altro motivo per monitorarli è poter eliminare l'istanza AudioSource quando non è più necessaria.

Per questo motivo, introdurrerai un nuovo campo privato in AudioController. È un handle per l'eventuale brano in riproduzione. Aggiungi la seguente riga:

lib/audio/audio_controller.dart

...

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

  SoLoud? _soloud;

  SoundHandle? _musicHandle;    // ← Add this.

  ...

Avvia la musica

In sostanza, riprodurre musica non è diverso da un suono one-shot. Devi comunque caricare il file assets/music/looped-song.ogg come istanza della classe AudioSource, quindi utilizzare il metodo play() di SoLoud per riprodurlo.

Questa volta, però, tieni premuto l'handle dell'audio che il metodo play() restituisce per manipolare l'audio durante la riproduzione.

  • Se vuoi, puoi implementare il metodo AudioController.startMusic() autonomamente. Non è un problema se alcuni dettagli non sono corretti. La cosa importante è che la musica si avvia quando selezioni Avvia musica.

Ecco un'implementazione di riferimento:

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

...

Tieni presente che carichi il file musicale in modalità disco (l'enum LoadMode.disk). Questo significa semplicemente che il file viene caricato solo a blocchi in base alle necessità. Per audio più lunghi, è generalmente preferibile caricare i file in modalità disco. Per gli effetti sonori brevi, è più sensato caricarli e decomprimerli in memoria (l'enumerazione LoadMode.memory predefinita).

Tuttavia, hai un paio di problemi. Innanzitutto, la musica è troppo forte e sovraccarica i suoni. Nella maggior parte dei giochi, la musica è in sottofondo la maggior parte delle volte, dando la priorità all'audio più informativo, come il parlato e gli effetti sonori. Questo problema può essere risolto facilmente utilizzando il parametro del volume del metodo di riproduzione. Ad esempio, puoi provare _soloud!.play(musicSource, volume: 0.6) per riprodurre il brano al 60% del volume. In alternativa, puoi impostare il volume in un secondo momento con un comando simile a _soloud!.setVolume(_musicHandle, 0.6).

Il secondo problema è che la canzone si interrompe bruscamente. Il motivo è che si tratta di un brano che dovrebbe essere riprodotto in loop e il punto di partenza del loop non è l'inizio del file audio.

88d2c57fffdfe996.png

Si tratta di una scelta molto diffusa per la musica dei giochi, perché significa che il brano inizia con un'introduzione naturale e poi viene riprodotto per tutto il tempo necessario senza un punto loop evidente. Quando il gioco deve uscire dal brano in riproduzione, il brano viene semplicemente sbiadito.

Fortunatamente, SoLoud offre alcuni modi per riprodurre audio in loop. Il metodo play() accetta un valore booleano per il parametro looping e anche il valore del punto di partenza del loop come parametro loopingStartAt. Il codice risultante ha il seguente aspetto:

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

...

Se non imposti il parametro loopingStartAt, il valore predefinito sarà Duration.zero (in altre parole, l'inizio del file audio). Se hai un brano musicale che funziona in modo perfetto e senza presentazioni, ti consigliamo di farlo.

  • Per assicurarti che la sorgente audio venga smaltita correttamente al termine della riproduzione, ascolta lo stream allInstancesFinished fornito da ogni sorgente audio. Con le chiamate di log aggiunte, il metodo startMusic() avrà quindi il seguente aspetto:

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

...

Suono di dissolvenza

Il prossimo problema è che la musica non finisce mai. Implementiamo una dissolvenza.

Un modo per implementare la dissolvenza è avere una sorta di funzione chiamata diverse volte al secondo, ad esempio Ticker o Timer.periodic, e abbassare il volume della musica con piccoli decrementi. Questa soluzione andrebbe bene, ma è molto impegnativo.

Fortunatamente, SoLoud offre pratici metodi da usare in modo automatico che permettono di farlo per te. Ecco come puoi eliminare la musica nel corso di cinque secondi e arrestare l'istanza del suono in modo che non consumi risorse della CPU inutilmente. Sostituisci il metodo fadeOutMusic() con questo codice:

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. Applica effetti

Un enorme vantaggio di avere un motore audio adeguato è la possibilità di elaborare l'audio, ad esempio instradare alcuni suoni attraverso un riverbero, un equalizzatore o un filtro passa basso.

Nei giochi, può essere usato per differenziare uditivamente i luoghi. Ad esempio, il suono di un applauso in una foresta è diverso da quello in un bunker di cemento. Mentre la foresta aiuta a dissipare e assorbire il suono, le pareti spoglie di un bunker riflettono le onde sonore che tornano, generando un riverbero. Analogamente, le voci delle persone suonano in modo diverso se percepite attraverso un muro. Le frequenze più alte di questi suoni si attenuano più facilmente man mano che passano attraverso il mezzo solido, generando un effetto filtro passa-basso.

Un&#39;illustrazione di due persone che parlano in una stanza. Le onde sonore non solo vanno da una persona all&#39;altra in modo diretto, ma rimbalzano anche contro le pareti e il soffitto.

SoLoud offre diversi effetti audio che puoi applicare all'audio.

  • Per far sembrare che il tuo player si trovi in una stanza grande, come una cattedrale o una grotta, usa l'enum FilterType.freeverbFilter:

lib/audio/audio_controller.dart

...

  void applyFilter() {
    _soloud!.addGlobalFilter(FilterType.freeverbFilter);
    _soloud!.setFilterParameter(FilterType.freeverbFilter, 0, 0.2);
    _soloud!.setFilterParameter(FilterType.freeverbFilter, 2, 0.9);
  }

  void removeFilter() {
    _soloud!.removeGlobalFilter(FilterType.freeverbFilter);
  }

...

Come puoi vedere, i filtri ti consentono di esplorare un territorio più basso. L'impostazione di un parametro di filtro viene eseguita con l'indice del parametro. Ad esempio, il parametro Wet del freeverb ha l'indice 0, mentre il parametro Room Size ha l'indice 2.

Con il codice precedente, svolgi le seguenti operazioni:

  • Attivare il filtro freeverb a livello globale o per l'intero mix audio, non solo per un singolo suono.
  • Imposta il parametro Wet su 0.2, il che significa che l'audio risultante sarà per l'80% originale e per il 20% sull'output dell'effetto riverbero. Se imposti questo parametro su 1.0, sarebbe come sentire solo le onde sonore che arrivano dalle pareti distanti della stanza e nessuno dell'audio originale.
  • Imposta il parametro Room Size su 0.9. Puoi modificare questo parametro a tuo piacimento o persino cambiarlo in modo dinamico. 1.0 è un'enorme caverna, mentre 0.0 è un bagno.
  • Se sei in grado di farlo, modifica il codice e applica uno dei seguenti filtri o una combinazione dei seguenti filtri:
  • FilterType.biquadResonantFilter (può essere utilizzato come filtro passa basso)
  • FilterType.eqFilter
  • FilterType.echoFilter
  • FilterType.lofiFilter
  • FilterType.flangerFilter
  • FilterType.bassboostFilter
  • FilterType.waveShaperFilter
  • FilterType.robotizeFilter
  • FilterType.freeverbFilter

7. Complimenti

Hai implementato un controller audio che riproduce suoni, riproduce in loop la musica e applica effetti.

Scopri di più

  • Prova a estendere il controller audio con funzionalità come il precaricamento dei suoni all'avvio, la riproduzione dei brani in sequenza o l'applicazione di un filtro gradualmente nel tempo.
  • Leggi la documentazione del pacchetto di flutter_soloud.
  • Leggi la home page della libreria C++ sottostante.
  • Scopri di più su Dart FFI, la tecnologia utilizzata per interfacciarsi con la libreria C++.
  • Guarda il discorso di Guy Somberg sulla programmazione audio dei giochi per trovare l'ispirazione. Ce n'è anche uno più lungo. Quando Guy parla di " middleware ", si riferisce a librerie come SoLoud e FMOD. Il resto del codice tende a essere specifico per ogni gioco.
  • Crea il tuo gioco e rilascialo.

Illustrazione di cuffie