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 di 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 una tantum.
  • 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 la connessione è lenta, non preoccuparti. Poiché avrai bisogno dei file effettivi, puoi 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 puoi vedere, si tratta solo di uno scheletro per le funzionalità future. Implementeremo tutto durante questo codelab.

  1. Successivamente, apri il file lib/main.dart e sostituisci i relativi contenuti 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 progetto in modo che il file del brano sia nel file assets/music/looped-song.ogg e i suoni della panca siano nei seguenti file:
  • assets/sounds/pew1.mp3
  • assets/sounds/pew2.mp3
  • assets/sounds/pew3.mp3

La struttura del tuo progetto ora dovrebbe essere simile alla seguente:

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.dart&quot; e una sottodirectory &quot;audio&quot; con &quot;audio_controller.dart&quot; e il file &quot;pubspec.yaml&quot;.  Le frecce rimandano alle nuove directory e ai file che hai modificato finora.

Ora che i file sono presenti, devi comunicarli a Flutter.

  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 dal pacchetto flutter_soloud e dal 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 ignorati in tutta sicurezza.

3. Inizializza e arresta

Per riprodurre l'audio, usa il plug-in flutter_soloud. Questo plug-in si basa sul progetto SoLoud, un motore audio C++ per 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 tramite questo campo e inoltra tutte le chiamate.

  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 più istanze di 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 ti sarà 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.

Puoi ottenere un'istanza AudioSource caricandola. Ad esempio, se hai un file MP3 tra le risorse, puoi caricarlo per ottenere un AudioSource. Poi chiedi a SoLoud di riprodurre questo AudioSource. Puoi riprodurlo più volte, anche contemporaneamente.

Quando non hai più bisogno di un'origine audio, puoi rimuoverla con il metodo SoLoud.disposeSource().

Per caricare un asset e riprodurlo:

  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 un suono buffo. Tieni presente quanto segue:
  • L'argomento assetKey fornito è simile a assets/sounds/pew1.mp3, la stessa stringa che forniresti a qualsiasi altra API Flutter di caricamento di asset, come il widget Image.asset().
  • L'istanza SoLoud fornisce un metodo loadAsset() che carica in modo asincrono un file audio dalle risorse 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 poi passata al metodo play() di SoLoud. Questo metodo restituisce un'istanza del tipo SoundHandle che rappresenta il suono appena riprodotto. Questo handle può essere passato ad altri metodi SoLoud per eseguire operazioni come mettere in pausa, interrompere o modificare il volume dell'audio.
  • Sebbene play() sia un metodo asincrono, la riproduzione inizia praticamente istantaneamente. Il pacchetto flutter_soloud utilizza l'interfaccia di funzione esterna (FFI) di Dart per chiamare il codice C in modo diretto e sincrono. I soliti messaggi tra il codice Dart e il codice della piattaforma, caratteristici della maggior parte dei plug-in Flutter, non sono presenti. 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.

Gestire 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. La documentazione dell'API di ogni metodo elenca i tipi di eccezioni che potrebbero essere lanciate.

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, quando non vuoi che la sessione di gioco del giocatore abbia un arresto anomalo solo 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, provare a caricare asset non inclusi nel gioco è un problema che devi risolvere, quindi si tratta di un errore.

Riproduci suoni diversi

Potresti aver notato che riproduci solo il file pew1.mp3, ma nella directory delle risorse sono presenti altre due versioni dell'audio. Spesso sembra più naturale quando i giochi hanno più versioni dello stesso suono e le riproducono in modo casuale o a rotazione. In questo modo, ad esempio, i passi e gli spari non risulteranno troppo uniformi e quindi 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

Gestire gli audio più lunghi

Parte dell'audio è pensata per essere riprodotta per lunghi periodi di tempo. 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 durate che possono essere misurate in minuti. È necessario tenerne traccia in modo da poterli mettere in pausa o interrompere quando è necessario. Inoltre, spesso sono supportati da file di grandi dimensioni e possono consumare molta memoria. Un altro motivo per monitorarli è che puoi 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 prima 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ò, prendi il handle audio restituito dal metodo play() per manipolare l'audio durante la riproduzione.

  • Se vuoi, puoi implementare il metodo AudioController.startMusic() autonomamente. Non preoccuparti se non ricordi alcuni dettagli. L'importante è che la musica inizi 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). Ciò significa semplicemente che il file viene caricato solo a blocchi, in base alle esigenze. Per audio più lunghi, è generalmente preferibile caricare i file in modalità disco. Per gli effetti sonori brevi, ha più senso caricarli e decomprimerli in memoria (l'enum LoadMode.memory predefinito).

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. Il problema è facile da risolvere utilizzando il parametro volume del metodo play. Ad esempio, puoi provare a dire _soloud!.play(musicSource, volume: 0.6) per riprodurre il brano con il volume al 60%. 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 attenuato.

Fortunatamente, SoLoud offre alcuni modi per riprodurre audio in loop. Il metodo play() accetta un valore booleano per il parametro looping e il valore per il punto di partenza del ciclo 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 è Duration.zero (in altre parole, l'inizio del file audio). Se hai una traccia musicale che è un loop perfetto senza introduzione, è quello che ti serve.

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

...

Dissolvenza audio

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

Un modo per implementare l'attenuazione è avere una sorta di funzione chiamata più 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 attenuare la musica nel corso di cinque secondi e poi interrompere l'istanza audio 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 a disposizione un motore audio adeguato è che puoi eseguire l'elaborazione audio, ad esempio instradare alcuni suoni tramite un riverbero, un equalizzatore o un filtro passa basso.

Nei giochi, può essere utilizzato per la differenziazione uditiva delle posizioni. Ad esempio, un applauso suona in modo diverso in una foresta rispetto a un bunker di cemento. Mentre una foresta aiuta a dissipare e assorbire il suono, le pareti spoglie di un bunker riflettono le onde sonore, causando riverbero. Analogamente, le voci delle persone suonano in modo diverso quando vengono ascoltate attraverso una parete. Le frequenze più alte di questi suoni vengono attenuate più facilmente mentre si propagano attraverso il mezzo solido, con un effetto di filtro passa basso.

Un&#39;illustrazione di due persone che parlano in una stanza. Le onde sonore non passano solo da una persona all&#39;altra, ma rimbalzano anche sulle pareti e sul soffitto.

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

  • Per far sembrare che il tuo giocatore si trovi in una grande stanza, come una cattedrale o una grotta, utilizza il campo 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();
  }

...

Il campo SoLoud.filters consente di accedere a tutti i tipi di filtro e ai relativi parametri. Ogni parametro ha anche funzionalità integrate come dissolvenza graduale e oscillazione.

Nota: _soloud!.filters espone i filtri globali. Se vuoi applicare filtri a una singola origine, utilizza l'opzione AudioSource.filters, che ha lo stesso comportamento.

Con il codice precedente, esegui le seguenti operazioni:

  • Attiva il filtro freeverb a livello globale.
  • Imposta il parametro Wet su 0.2, il che significa che l'audio risultante sarà per l'80% originale e per il 20% l'output dell'effetto di riverbero. Se imposti questo parametro su 1.0, è come sentire solo le onde sonore che ti ritornano dalle pareti lontane della stanza e nessun audio originale.
  • Imposta il parametro Dimensioni della stanza su 0.9. Puoi modificare questo parametro in base alle tue esigenze o addirittura cambiarlo in modo dinamico. 1.0 è un'enorme caverna, mentre 0.0 è un bagno.
  • Se vuoi, modifica il codice e applica uno dei seguenti filtri o una combinazione di questi:
  • biquadFilter (può essere utilizzato come filtro passa basso)
  • pitchShiftFilter
  • equalizerFilter
  • echoFilter
  • lofiFilter
  • flangerFilter
  • bassboostFilter
  • waveShaperFilter
  • robotizeFilter

7. Complimenti

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

Scopri di più

  • Prova a migliorare il controller audio con funzionalità come il precaricamento dei suoni all'avvio, la riproduzione dei brani in sequenza o l'applicazione graduale di un filtro 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 l'interfaccia con la libreria C++.
  • Guarda il discorso di Guy Somberg sulla programmazione audio dei giochi per trovare l'ispirazione. Esiste anche una versione più lunga. Quando Guy parla di "middleware", si riferisce a librerie come SoLoud e FMOD. Il resto del codice tende ad essere specifico per ogni gioco.
  • Crea il tuo gioco e rilascialo.

Illustrazione di cuffie