Agrega sonido y música a tu juego de Flutter

Agrega sonido y música a tu juego de Flutter

Acerca de este codelab

subjectÚltima actualización: jun 6, 2025
account_circleEscrito por Filip Hracek

1. Antes de comenzar

Los juegos son experiencias audiovisuales. Flutter es una gran herramienta para crear imágenes atractivas y una IU sólida, por lo que te lleva muy lejos en el aspecto visual. El ingrediente que falta es el audio. En este codelab, aprenderás a usar el complemento flutter_soloud para incorporar sonido y música de baja latencia a tu proyecto. Comienzas con un andamiaje básico de modo que puedas pasar directamente a las partes interesantes.

Ilustración a mano de auriculares.

Por supuesto, puedes usar lo que aprendas aquí para agregar audio a tus apps, no solo a los juegos. Sin embargo, si bien casi todos los juegos requieren sonido y música, la mayoría de las apps no, por lo que este codelab se enfoca en los juegos.

Requisitos previos

  • Conocimientos básicos de Flutter
  • Conocimientos para ejecutar y depurar apps de Flutter

Qué aprenderá

  • Cómo reproducir sonidos únicos
  • Cómo reproducir y personalizar bucles de música sin interrupciones
  • Cómo atenuar y aumentar el volumen de los sonidos
  • Cómo aplicar efectos ambientales a los sonidos
  • Cómo lidiar con las excepciones
  • Cómo encapsular todas estas funciones en un solo controlador de audio

Requisitos

  • El SDK de Flutter
  • El editor de código que prefieras

2. Configurar

  1. Descarga los siguientes archivos. Si tienes una conexión lenta, no te preocupes. Necesitarás los archivos reales más adelante, así que puedes permitir que se descarguen mientras trabajas.
  1. Crea un proyecto de Flutter con el nombre que elijas.
  1. Crea un archivo lib/audio/audio_controller.dart en el proyecto.
  2. En el archivo, ingresa el siguiente código:

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

Como puedes ver, este es solo un esqueleto para la funcionalidad futura. Lo implementaremos todo durante este codelab.

  1. A continuación, abre el archivo lib/main.dart y reemplaza su contenido con el siguiente código:

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. Después de descargar los archivos de audio, crea un directorio en la raíz de tu proyecto llamado assets.
  2. En el directorio assets, crea dos subdirectorios, uno llamado music y el otro llamado sounds.
  3. Mueve los archivos descargados a tu proyecto para que el archivo de la canción esté en el archivo assets/music/looped-song.ogg y los sonidos de los bancos estén en los siguientes archivos:
  • assets/sounds/pew1.mp3
  • assets/sounds/pew2.mp3
  • assets/sounds/pew3.mp3

La estructura de tu proyecto debería verse de la siguiente manera:

Una vista de árbol del proyecto, con carpetas como &quot;android&quot;, &quot;ios&quot;, archivos como &quot;README.md&quot; y &quot;analysis_options.yaml&quot;. Entre estos, podemos ver el directorio &quot;assets&quot; con los subdirectorios &quot;music&quot; y &quot;sounds&quot;, el directorio &quot;lib&quot; con &quot;main.dart&quot; y un subdirectorio &quot;audio&quot; con &quot;audio_controller.dart&quot;, y el archivo &quot;pubspec.yaml&quot;.  Las flechas apuntan a los directorios nuevos y a los archivos que tocaste hasta el momento.

Ahora que los archivos están allí, debes informarles a Flutter sobre ellos.

  1. Abre el archivo pubspec.yaml y, luego, reemplaza la sección flutter: que se encuentra en la parte inferior del archivo por lo siguiente:

pubspec.yaml

...

flutter
:
  uses
-material-design: true

  assets
:
   
- assets/music/
   
- assets/sounds/
  1. Agrega una dependencia en el paquete flutter_soloud y en el paquete logging.
flutter pub add flutter_soloud logging

Tu archivo pubspec.yaml ahora debería tener dependencias adicionales en los paquetes flutter_soloud y logging.

pubspec.yaml

...

dependencies
:
  flutter
:
    sdk
: flutter

  flutter_soloud
: ^3.1.10
  logging
: ^1.3.0

...
  1. Ejecutar el proyecto Aún no funciona nada porque agregarás la funcionalidad en las siguientes secciones.

10f0f751c9c47038.png

3. Cómo inicializar y apagar

Para reproducir audio, usa el complemento flutter_soloud. Este complemento se basa en el proyecto SoLoud, un motor de audio C++ para juegos que usa, entre otros, Nintendo SNES Classic.

7ce23849b6d0d09a.png

Para inicializar el motor de audio SoLoud, sigue estos pasos:

  1. En el archivo audio_controller.dart, importa el paquete flutter_soloud y agrega un campo _soloud privado a la clase.

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
 
}

 
...

El controlador de audio administra el motor subyacente de SoLoud a través de este campo y reenvía todas las llamadas a él.

  1. En el método initialize(), ingresa el siguiente código:

lib/audio/audio_controller.dart

...

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

...

Esto propaga el campo _soloud y espera la inicialización. Ten en cuenta lo siguiente:

  • SoLoud proporciona un campo instance singleton. No hay forma de crear instancias de varias instancias de SoLoud. El motor de C++ no permite esto, por lo que el complemento de Dart tampoco lo permite.
  • La inicialización del complemento es asíncrona y no finaliza hasta que se muestra el método init().
  • Para abreviar este ejemplo, no capturas errores en un bloque try/catch. En el código de producción, debes hacerlo y, luego, informar cualquier error al usuario.
  1. En el método dispose(), ingresa el siguiente código:

lib/audio/audio_controller.dart

...

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

...

Es recomendable que cierres SoLoud cuando salgas de la app, aunque todo debería funcionar bien incluso si no lo haces.

  1. Observa que ya se llama al método AudioController.initialize() desde la función main(). Esto significa que reiniciar el proyecto en caliente inicializa SoLoud en segundo plano, pero no te servirá de nada antes de que reproduzcas algunos sonidos.

4. Reproducir sonidos únicos

Carga un recurso y reprodúcelo

Ahora que sabes que SoLoud se inicializa al inicio, puedes pedirle que reproduzca sonidos.

SoLoud diferencia entre una fuente de audio, que son los datos y metadatos que se usan para describir un sonido, y sus "instancias de sonido", que son los sonidos que se reproducen. Un ejemplo de una fuente de audio puede ser un archivo mp3 cargado en la memoria, listo para reproducirse y representado por una instancia de la clase AudioSource. Cada vez que reproduces esta fuente de audio, SoLoud crea una "instancia de sonido" que está representada por el tipo SoundHandle.

Para obtener una instancia de AudioSource, debes cargarla. Por ejemplo, si tienes un archivo mp3 en tus recursos, puedes cargarlo para obtener un AudioSource. Luego, le dices a SoLoud que reproduzca este AudioSource. Puedes jugarlo muchas veces, incluso de forma simultánea.

Cuando termines de usar una fuente de audio, elimínala con el método SoLoud.disposeSource().

Para cargar un recurso y reproducirlo, sigue estos pasos:

  1. En el método playSound() de la clase AudioController, ingresa el siguiente código:

lib/audio/audio_controller.dart

  ...

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

  ...
  1. Guarda el archivo, realiza la recarga en caliente y, luego, selecciona Reproducir sonido. Deberías escuchar un sonido de pew. Ten en cuenta lo siguiente:
  • El argumento assetKey proporcionado es algo como assets/sounds/pew1.mp3, la misma cadena que proporcionarías a cualquier otra API de Flutter que cargue recursos, como el widget Image.asset().
  • La instancia de SoLoud proporciona un método loadAsset() que carga de forma asíncrona un archivo de audio de los recursos del proyecto de Flutter y muestra una instancia de la clase AudioSource. Existen métodos equivalentes para cargar un archivo desde el sistema de archivos (el método loadFile()) y para cargarlo a través de la red desde una URL (el método loadUrl()).
  • Luego, la instancia de AudioSource recién adquirida se pasa al método play() de SoLoud. Este método muestra una instancia del tipo SoundHandle que representa el sonido que se acaba de reproducir. A su vez, este identificador se puede pasar a otros métodos de SoLoud para realizar acciones como pausar, detener o modificar el volumen del sonido.
  • Aunque play() es un método asíncrono, la reproducción comienza básicamente de forma instantánea. El paquete flutter_soloud usa la interfaz de función externa (FFI) de Dart para llamar al código C de forma directa y síncrona. No se encuentra en ningún lugar el intercambio de mensajes habitual entre el código de Dart y el código de la plataforma que es característico de la mayoría de los complementos de Flutter. La única razón por la que algunos métodos son asíncronos es que parte del código del complemento se ejecuta en su propio aislamiento, y la comunicación entre los aislamientos de Dart es asíncrona.
  • Declaras que el campo _soloud no es nulo con _soloud!. Esto es, de nuevo, para abreviar. El código de producción debe controlar de forma fluida la situación en la que el desarrollador intenta reproducir un sonido antes de que el controlador de audio haya tenido la oportunidad de inicializarse por completo.

Cómo controlar excepciones

Es posible que hayas notado que, una vez más, estás ignorando posibles excepciones. Es hora de corregir eso para este método en particular con fines de aprendizaje. (Por motivos de brevedad, el codelab vuelve a ignorar las excepciones después de esta sección).

  • Para controlar las excepciones en este caso, une las dos líneas del método playSound() en un bloque try/catch y solo captura instancias de 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 arroja varias excepciones, como las excepciones SoLoudNotInitializedException o SoLoudTemporaryFolderFailedException. En la documentación de la API de cada método, se enumeran los tipos de excepciones que se pueden generar.

SoLoud también proporciona una clase superior a todas sus excepciones, la excepción SoLoudException, para que puedas detectar todos los errores relacionados con la funcionalidad del motor de audio. Esto es especialmente útil en los casos en que no es fundamental reproducir audio. Por ejemplo, cuando no quieres que falle la sesión de juego del jugador solo porque no se pudo cargar uno de los sonidos de pew-pew.

Como es de esperar, el método loadAsset() también puede arrojar un error FlutterError si proporcionas una clave de activo que no existe. Por lo general, debes abordar el intento de cargar recursos que no están empaquetados con el juego, por lo que se trata de un error.

Reproducir diferentes sonidos

Es posible que hayas notado que solo reproduces el archivo pew1.mp3, pero hay otras dos versiones del sonido en el directorio de recursos. A menudo, suena más natural cuando los juegos tienen varias versiones del mismo sonido y reproducen las diferentes versiones de forma aleatoria o de forma rotativa. Esto evita, por ejemplo, que los pasos y los disparos suenen demasiado uniformes y, por lo tanto, falsos.

  • Como ejercicio opcional, modifica el código para que se reproduzca un sonido de iglesia diferente cada vez que se presione el botón.

Una ilustración de

5. Reproducir bucles de música

Cómo administrar sonidos de larga duración

Algunos audios están diseñados para reproducirse durante períodos prolongados. La música es el ejemplo más obvio, pero muchos juegos también reproducen ambientes, como el viento que silba por los pasillos, el canto lejano de los monjes, el crujido de metal centenario o las toses distantes de los pacientes.

Son fuentes de audio con tiempos de reproducción que se pueden medir en minutos. Debes hacer un seguimiento de ellos para poder pausarlos o detenerlos cuando sea necesario. A menudo, también se respaldan con archivos grandes y pueden consumir mucha memoria, por lo que otra razón para hacerles un seguimiento es que puedas descartar la instancia de AudioSource cuando ya no sea necesaria.

Por ese motivo, agregarás un nuevo campo privado a AudioController. Es un identificador para la canción que se está reproduciendo, si la hay. Agrega la siguiente línea:

lib/audio/audio_controller.dart

...

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

 
SoLoud? _soloud;

 
SoundHandle? _musicHandle;    // ← Add this.

 
...

Cómo iniciar música

En esencia, reproducir música no es diferente de reproducir un sonido único. Primero, debes cargar el archivo assets/music/looped-song.ogg como una instancia de la clase AudioSource y, luego, usar el método play() de SoLoud para reproducirlo.

Sin embargo, esta vez, tomas el control de sonido que muestra el método play() para manipular el audio mientras se reproduce.

  • Si lo deseas, implementa el método AudioController.startMusic() por tu cuenta. No pasa nada si no recuerdas algunos detalles. Lo importante es que la música comience cuando selecciones Iniciar música.

A continuación, se muestra una implementación de referencia:

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

...

Ten en cuenta que cargas el archivo de música en modo de disco (la enumeración LoadMode.disk). Esto significa que el archivo solo se carga en fragmentos según sea necesario. Para audios de mayor duración, lo mejor es cargarlos en modo de disco. Para los efectos de sonido cortos, tiene más sentido cargarlos y descomprimirlos en la memoria (la enumeración LoadMode.memory predeterminada).

Sin embargo, tienes algunos problemas. En primer lugar, la música es demasiado alta y predomina sobre los sonidos. En la mayoría de los juegos, la música se reproduce en segundo plano la mayor parte del tiempo, lo que permite que el audio más informativo, como la voz y los efectos de sonido, se destaque. Esto se corrige con el parámetro de volumen del método de reproducción. Por ejemplo, puedes probar _soloud!.play(musicSource, volume: 0.6) para reproducir la canción con un volumen del 60%. Como alternativa, puedes establecer el volumen en cualquier momento con algo como _soloud!.setVolume(_musicHandle, 0.6).

El segundo problema es que la canción se detiene abruptamente. Esto se debe a que es una canción que se supone que se debe reproducir en un bucle y el punto de partida del bucle no es el principio del archivo de audio.

88d2c57fffdfe996.png

Esta es una opción popular para la música de juegos, ya que significa que la canción comienza con una introducción natural y, luego, se reproduce todo el tiempo que sea necesario sin un punto de bucle obvio. Cuando el juego necesita hacer la transición de la canción que se está reproduciendo, la atenúa.

Por suerte, SoLoud ofrece formas de reproducir audio en bucle. El método play() toma un valor booleano para el parámetro looping y también el valor del punto inicial del bucle como el parámetro loopingStartAt. El código resultante se ve así:

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

...

Si no estableces el parámetro loopingStartAt, el valor predeterminado es Duration.zero (en otras palabras, el inicio del archivo de audio). Si tienes una pista de música que es un bucle perfecto sin ninguna introducción, esta es la opción que quieres.

  • Para verificar que la fuente de audio se elimine correctamente una vez que termine de reproducirse, escucha la transmisión allInstancesFinished que proporciona cada fuente de audio. Con las llamadas de registro agregadas, el método startMusic() se ve de la siguiente manera:

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

...

Atenuar sonido

El siguiente problema es que la música nunca termina. Es hora de implementar un desvanecimiento.

Una forma de implementar la atenuación sería tener algún tipo de función a la que se llame varias veces por segundo, como Ticker o Timer.periodic, y bajar el volumen de la música en pequeños decrementos. Esto funcionaría, pero requiere mucho trabajo.

Por suerte, SoLoud proporciona métodos convenientes de "activar y olvidar" que hacen esto por ti. A continuación, te mostramos cómo puedes atenuar la música durante cinco segundos y, luego, detener la instancia de sonido para que no consuma recursos de la CPU de forma innecesaria. Reemplaza el método fadeOutMusic() por este código:

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. Cómo aplicar efectos

Una gran ventaja de tener un motor de audio adecuado a tu disposición es que puedes realizar el procesamiento de audio, como enrutar algunos sonidos a través de una reverberación, un ecualizador o un filtro de paso bajo.

En los juegos, se puede usar para la diferenciación auditiva de las ubicaciones. Por ejemplo, un aplauso suena diferente en un bosque que en un búnker de hormigón. Mientras que un bosque ayuda a disipar y absorber el sonido, las paredes desnudas de un búnker reflejan las ondas sonoras, lo que genera reverberación. Del mismo modo, las voces de las personas suenan diferentes cuando se escuchan a través de una pared. Las frecuencias más altas de esos sonidos se atenúan más a medida que viajan a través del medio sólido, lo que genera un efecto de filtro pasa-bajo.

Ilustración de dos personas hablando en una habitación. Las ondas de sonido no solo van de una persona a la otra directamente, sino que también rebotan en las paredes y el techo.

SoLoud proporciona varios efectos de audio diferentes que puedes aplicar al audio.

  • Para que suene como si el reproductor estuviera en una sala grande, como una catedral o una cueva, usa el 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();
 
}

...

El campo SoLoud.filters te brinda acceso a todos los tipos de filtros y sus parámetros. Cada parámetro también tiene funciones integradas, como la atenuación gradual y la oscilación.

Nota: _soloud!.filters expone filtros globales. Si deseas aplicar filtros a una sola fuente, usa el equivalente AudioSource.filters, que actúa de la misma manera.

Con el código anterior, haz lo siguiente:

  • Habilita el filtro freeverb de forma global.
  • Establece el parámetro Wet en 0.2, lo que significa que el audio resultante será un 80% original y un 20% del resultado del efecto de reverberación. Si configuras este parámetro en 1.0, sería como escuchar solo las ondas de sonido que te llegan desde las paredes distantes de la habitación y nada del audio original.
  • Establece el parámetro Room Size en 0.9. Puedes ajustar este parámetro a tu gusto o incluso cambiarlo de forma dinámica. 1.0 es una caverna enorme, mientras que 0.0 es un baño.
  • Si quieres, cambia el código y aplica uno de los siguientes filtros o una combinación de ellos:
  • biquadFilter (se puede usar como filtro de paso bajo)
  • pitchShiftFilter
  • equalizerFilter
  • echoFilter
  • lofiFilter
  • flangerFilter
  • bassboostFilter
  • waveShaperFilter
  • robotizeFilter

7. Felicitaciones

Implementaste un controlador de audio que reproduce sonidos, repite música y aplica efectos.

Más información

  • Intenta aprovechar al máximo el controlador de audio con funciones como la carga previa de sonidos al inicio, la reproducción de canciones en una secuencia o la aplicación de un filtro gradualmente con el tiempo.
  • Lee la documentación del paquete de flutter_soloud.
  • Lee la página principal de la biblioteca C++ subyacente.
  • Obtén más información sobre la FFI de Dart, la tecnología que se usa para interactuar con la biblioteca de C++.
  • Mira la conferencia de Guy Somberg sobre la programación de audio de juegos para inspirarte. (También hay una versión más larga). Cuando Guy habla de "middleware", se refiere a bibliotecas como SoLoud y FMOD. El resto del código suele ser específico de cada juego.
  • Compila y lanza tu juego.

Ilustración de auriculares