Contribute

Learn how to contribute to flutter_soloud

Contribute

To use native code, bindings from Dart to C/C++ are generated from the header file src/bindings.h using package:ffigen. src/bindings.h is the single source of truth for the native ABI.

To regenerate Dart bindings:

  1. Declare your new native C/C++ functions with FFI_PLUGIN_EXPORT in src/bindings.h and implement them in src/bindings.cpp.

Run in terminal:

bash
dart run ffigen --config ffigen.yaml

(On macOS if system headers are missing: CPATH="$(xcrun --show-sdk-path)/usr/include" dart run ffigen --config ffigen.yaml)

  1. The bindings will be automatically placed into lib/src/bindings/flutter_soloud_ffigen.dart.
  1. Wrap and expose the generated functions in lib/src/bindings/bindings_player_ffi.dart and lib/src/soloud.dart.

Project structure

This plugin uses the following structure:

  • lib: Contains the Dart API code and bindings.
  • src: Contains the native C/C++ source code, filters, audio buffers, and SoLoud engine.
  • src/bindings.h: The single source of truth and ffigen entry point for the native C ABI.
  • src/soloud: Contains the SoLoud sources of the fork.
  • hook: Contains the Dart build hooks (hook/build.dart, hook/sources.dart, hook/xiph.dart) that compile native code automatically for Android, iOS, macOS, Windows, and Linux into Code Assets.
  • web: Contains the scripts and assets for compiling and initializing the WebAssembly build on the Web platform.
  • xiph: Contains the prebuilt libraries and scripts for ogg, opus, vorbis, and flac.

The flutter_soloud plugin utilizes a forked repository of SoLoud, where the miniaudio audio backend is used by default.

Debugging

I have provided the necessary settings in the .vscode directory for debugging native C++ code on Linux, macOS, and Windows. To debug on Android, please use Android Studio and open the project located in the example/android directory. On iOS, please use Xcode.

Logging

When debugging the package using the example/ app, you might want to change the logging level to something more granular. For example, in main():

dart
// Capture even the finest log messages.
Logger.root.level = Level.ALL;

Audio Backends

  • Apple (macOS/iOS): Uses miniaudio with CoreAudio / AVFAudio.
  • Linux: Uses miniaudio with ALSA (MA_NO_PULSEAUDIO is enabled by default to prevent stutters).
  • Windows: Uses miniaudio with WASAPI.
  • Android: Uses miniaudio, which automatically selects AAudio on Android 11.0+ or OpenSL|ES on older versions.

Android

The default audio backend is miniaudio, which will automatically select the appropriate audio backend based on your Android version:

  • AAudio with Android 11.0 and newer.
  • OpenSL|ES for older Android versions.

Web

In the web directory, there is a compile_wasm.sh script that generates the .js and .wasm files for the native C code located in the src dir. Run it after installing emscripten. There is also a compile_worker_and_init_module.sh to compile the web worker needed by native code to communicate with Dart and the init_soloud.dart which initializes the WASM module. The default emscripten Module name is Module_soloud instead of the default Module to prevent some other WASM plugins from conflicting.

The generated files are already provided, but if it is needed to modify C/C++ code or the web/worker.dart code, the scripts must be run to reflect the changes.

The compile_wasm.sh script uses the -O3 code optimization flag. To see a better errors logs, use -O0 -g -s ASSERTIONS=1 in compile_wasm.sh.


The AudioIsolate has been removed and all the logic has been implemented natively. Events like voice ended are sent from C back to Dart. However, since it is not possible to call Dart from a native thread (the audio thread), a new web worker is created using the WASM EM_ASM directive. This allows sending the voice ended event back to Dart via the worker.

Here a sketch to show the step used:
sketch

#1. This function is called while initializing the player with FlutterSoLoudWeb.setDartEventCallbacks(). It creates a Web Worker in the WASM Module using the compiled web/worker.dart. After calling this, the WASM Module will have a new variable called Module_soloud.wasmWorker which will be used in Dart to receive messages. By doing this it will be easy to use the Worker to send messages from within the CPP code.

#2. This function, like #1, uses EM_ASM to inline JS. This JS code uses the Module_soloud.wasmWorker created in #1 to send a message.

#3. This is the JS used and created in #1. Every messages sent by #2 are managed here and sent to #4.

#4. Here when the event message has been received, a new event is added to a Stream. This Stream is listened by the SoLoud API.

#5. Here we listen to the event messages coming from the WorkerController stream. Currently, only the "voice ended" event is supported. The Stream is listened in SoLoud._initializeNativeCallbacks().

On this page