NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #1051 most downloaded on pub.dev
A low-level audio plugin for Flutter, mainly meant for games and immersive apps. Based on the SoLoud (C++) audio engine.
Last release 5 days ago
02 Oct 2026
Ships on a steady schedule
a new release about every 1 weeks
Nearly every release is documented
notes for 59 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
3 years old
119 releases · first in 2023
One column per quarter.
feat Apple: add privacy manifest for iOS and macOS #577
fix: Android device enumeration crash and AAudio route-change race conditions. Thanks to @Colton127 #572
setLinuxAudioBackend() from running on non Linux platforms. Thanks to @Colton127 #572fix: Android libflutter_soloud_plugin.so has no GNU build ID since the build-hook migration #570
added AmplitudeModulatorFilter: a sine ring modulator (bipolar amplitude modulation, output = input * sin(2π·frequency·t) at full wet) with wet and fr
AmplitudeModulatorFilter: a sine ring modulator (bipolar amplitude modulation, output = input * sin(2π·frequency·t) at full wet) with wet and frequency (0.1–20000 Hz) parameters, available globally, per sound and per bus via filters.amplitudeModulatorFilter. Thanks to @Colton127 #566fix: resolve Linux .so library bundling using runtime SONAME #560
.so library bundling using runtime SONAME #560harden asset and URL loader against engine deinit races #558
.xcframeworks from the plugin, which are no longer used since build Hooks was introduced #558implemented the algorithm according to the W3C Web Audio API Specification to compute FFT.
setFftDecibelRange to set the decibel range for FFT magnitude normalization.LinuxAudioBackend enum and SoLoud.instance.setLinuxAudioBackend(backend) to select or dynamically switch audio backends (ALSA, PulseAudio, JACK) at runtime or choose it in the new init() parameter #557.xiph/prebuild/<platform>/, keeping platform folders clean and ensuring packaged apps (Android AAB/APK, iOS IPA, macOS APP, Windows EXE) bundle all codecs out of the box with zero external build dependencies #556.<platform>_use_system_libs (to link against system packages on desktop) and <platform>_force_build_libs (to compile from source via CMake into .dart_tool/) under hooks.user_defines.flutter_soloud in pubspec.yaml. See docs for more info #556.removed ".github" for the possible location to install skills
Rename agent skills to hyphenated names for Zed compatibility. Thanks to @Kunstderfug #550
> - breaking change: web loader rename: Renamed web/init_module.dart. Update web/index.html to:
- ⚠️ IMPORTANT CHANGES in v5 ⚠️:
- breaking change: web loader rename: Renamed
web/init_module.dart. Updateweb/index.htmlto:This script will automatically load single thread or multi thread WASM module, which uses AudioWorklet, depending on the server configuration. See docs.<script src="assets/packages/flutter_soloud/web/init_soloud.js" defer></script>- breaking change:
NO_XIPH_LIBSis now configured via hook user-defines in the app pubspec instead of an environment variable:See docs.hooks: user_defines: flutter_soloud: no_xiph_libs: true- breaking change: audio visualization overhaul: Replaced the legacy
AudioDatapolling class with a reactive stream:SoLoud.instance.audioVisualizationEventsemittingAudioVisualizationData. See docs.
dart run flutter_soloud:skills
# or this to check whether installed skills are up to date
dart run flutter_soloud:skills --check
soundPath and tempFilePath to AudioSource to query the source identifier (path, asset key, or url) and any temporary file created on disk on native platforms.fix: removed unused FLAC++ headers from macOS and iOS include directories that caused compile-time errors in Xcode. Fixes #545.
FLAC++ headers from macOS and iOS include directories that caused compile-time errors in Xcode. Fixes #545.breaking change: build system migration to Dart build hooks (https://dart.dev/tools/hooks):
hook/build.dart (package:hooks + package:native_toolchain_c) instead of per-platform plumbing (Android CMake, iOS/macOS CocoaPods script phases, SwiftPM unity build). No CMake or podspec script phases are needed anymore.NO_XIPH_LIBS is now configured via hook user-defines in the app pubspec instead of an environment variable:hooks:
user_defines:
flutter_soloud:
no_xiph_libs: true
src/bindings.cpp now has a real header, src/bindings.h, which is the single ffigen entry point (the hand-maintained src/ffi_gen_tmp.h is gone).@Native with asset id package:flutter_soloud/src/bindings.cpp; the native library is resolved by the VM, so no DynamicLibrary.open/force-load/STRIP_STYLE workarounds are needed.web/ is still used.breaking change: audio visualization overhaul:
AudioData polling class with a reactive stream: SoLoud.instance.audioVisualizationEvents emitting AudioVisualizationData.pffft with Blackman windowing and temporal smoothing.setVisualizationEnabled() now accepts windowSize (powers of two from 128 to 8192, default 256), kind (wave, fft, waveAndFft), and channel (VisualizationChannel.merged, VisualizationChannel.all, or a specific channel index).> - breaking change: changeDevice() now returns Future instead of void.
TL;DR:
- Render-ahead ring (native): Ultra-low keypress-to-sound latency via retroactive mixing into a lookahead ring buffer (mostly real-time audio for
playScheduledandplayClockedcalls which play in the current audio frame, e.g. 10 ms latency).- Device management & async control: Non-blocking device start/stop, async
changeDevice(), and configurable idle timeouts to save power/wakelocks.- breaking change:
changeDevice()now returnsFuture<void>instead ofvoid.- breaking change: Web AudioWorklet: Dedicated audio thread rendering on Web (immune to UI/GC jank) when cross-origin isolated. Use
<script src="assets/packages/flutter_soloud/web/init_soloud.js" defer></script>inindex.htmlto automatically detect which module can be used on the web server.- Lifecycle fixes: Native engine lifecycle is now tied to
FlutterEngineto fix hot-restart leaks.- Extended playback controls: Added
scale,looping,loopinStart, andloopingEndto allplay*methods.
SoLoud.init() accepts devicePeriodFrames and renderAheadFrames. When enabled, the engine mixes ahead into an internal ring and mixes new play() calls retroactively, reducing keypress-to-sound latency down to the device period even with a large bufferSize. New getters: getPlayheadTime(), getOutputLatency(), and isRenderAheadEnabled.play3dScheduled() and Bus.play3dScheduled() to schedule 3D spatial sounds with sample accuracy at an absolute engine time.scale, looping, loopinStart, and loopingEnd parameters to all the play* methodschangeDevice() now selects the system default device when called without an argument and reports device-change failures instead of silently succeeding. Thanks to @Colton127 #532stopAudioDevice() / startAudioDevice() to control the audio output device without deinitializing the engine. Loaded sounds, active voices and filter state are all preserved, so playback resumes exactly where it left off. Thanks to @Colton127 #508getAudioDeviceState(), returning the actual current device state as an AudioDeviceState enum (uninitialized, stopped, started, starting, stopping). It is a cheap synchronous read and is safe to call before init(). Thanks to @Colton127 #508setAudioDeviceIdleTimeout() to configure how long the output device keeps running while the engine is idle before it is stopped: null keeps it running indefinitely, Duration.zero stops it as soon as possible, a positive duration sets the grace period (default 500 ms). Thanks to @Colton127 #508SoLoud.audioDeviceStartFailures, a stream that reports an automatic output-device start failing. Voice state is untouched, so the usual recovery is await startAudioDevice(). Thanks to @Colton127 #508changeDevice() now returns Future<void> instead of void and runs off the UI thread. Await it to know when the swap finished. Thanks to @Colton127 #508play(), play3d(), setPause(), pauseSwitch(), speechText(), playClocked(), play3dClocked(), playScheduled() and play3dScheduled() no longer blocks the UI thread. These methods therefore no longer throw SoLoudAudioDeviceFailedToStartCppException — listen to audioDeviceStartFailures for those failures. Voice-allocation failures still throw SoLoudFailedToStartPlaybackCppException, and startAudioDevice() and changeDevice() still report device-start failures to their caller. Thanks to @Colton127 #508AudioMix partial wakelock #250. Use setAudioDeviceIdleTimeout(null) to keep it running.paused: true is no longer silently unpaused by the buffer-stream buffering logic.libflutter_soloud_plugin_mt.js/.wasm, compiled with -pthread/SharedArrayBuffer + MA_ENABLE_AUDIO_WORKLETS + -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY=1) renders audio on a real-time AudioWorklet thread instead of the deprecated main-thread ScriptProcessorNode, making mixing immune to Flutter UI/GC jank. init_soloud.js picks it automatically only when the page is cross-origin isolated (COOP/COEP headers); everywhere else the single-threaded ScriptProcessorNode flavor is used, so hosts that cannot send those headers (e.g. game portals) keep working unchanged #523index.html only the row below should be left: <script src="assets/packages/flutter_soloud/web/init_soloud.js" defer></script>fix: a device change that still fails now reports SoLoudAudioDeviceFailedToStartCppException instead of hanging. Thanks to @Colton127 #533
SoLoudAudioDeviceFailedToStartCppException instead of hanging. Thanks to @Colton127 #533changeDevice() now selects the system default device when called without an argument and reports device-change failures instead of silently succeeding. Thanks to @Colton127 #533init() no longer blocks the UI thread while the audio device starts. On Android a slow or busy audio HAL could stall the platform thread long enough for the app to be reported as not responding; engine startup and teardown now run on a short-lived worker isolate. Thanks to @Colton127 #533deinitAsync(), a non-blocking counterpart to deinit(). deinit() is unchanged and still supported, but it can stall the UI thread when it lands while init() is still starting the device — prefer deinitAsync() in new code. Thanks to @Colton127 #533fix: iOS/macOS SPM build fails with error: unknown argument: '-Wl,-undefined,dynamic_lookup' #530
-pthread/SharedArrayBuffer from the WASM build. The requirement for COOP/COEP headers (cross-origin isolation) is gone and the plugin now works on hosts that cannot set them (e.g. game portals like CrazyGames/Poki). Moving the use of threads for a future release #523fix web: crash with --optimization-level=0 due to HEAPU8.buffer declared as JSArrayBuffer #526
--optimization-level=0 due to HEAPU8.buffer declared as JSArrayBuffer #526play (and its variants), pauseSwitch, setPause and stop now report failures instead of returning success with an unusable handle. Note: these methods can now throw where they previously failed silently. Thanks to @Colton127 #527
PlayerErrors.audioDeviceFailedToStart and PlayerErrors.failedToStartPlayback (with matching exceptions), so you can catch device/playback startup failures specifically.loadFile/loadMem/seek. Out-of-memory and not-implemented errors were mapped to unrelated exceptions (e.g. "DLL not found" for low memory); they now throw the correct ones. Thanks to @Colton127 #527await is the only error channel now. Thanks to @Colton127 #527fix: the voice-ended callback is no longer invoked while SoLoud's audio mutex is held. The symptom was a wedged engine: handles and sources still look
deinit() never completed. Ended voices are now queued and dispatched once the mutex is released. Thanks to @Colton127 #518another SPM fix: add wav_stream_decoder.cpp to SPM unity build (crash on Apple platforms)
now when passing a time <= 0 to fadeFilterParameter or to oscillateFilterParameter the value is set. Before was a no-op #519
time <= 0 to fadeFilterParameter or to oscillateFilterParameter the value is set. Before was a no-op #519ios SPM fix: include mixer_output with relative path #514
get rid of stb_vorbis c file in favor of the Xiph OGG libraries by @alnitak in #491
Stream<Uint8List> by @alnitak in #495getEngineTime, playScheduled, stopScheduled and fadeScheduled (extends #505) by @alnitak in #509Full Changelog: v4.0.13...v4.1.0
setPullBufferStream, addPullBufferDataStream, getPullBufferTimeRange) with support for MP3, WAV, FLAC, Ogg Opus, Ogg Vorbis, and Ogg FLAC.autoDispose parameter to setBufferStream to automatically dispose the sound when it is finished. This eliminates the need to manually call disposeSource.Stream<Uint8List> in PCM (F32LE, S8, S16LE, S32LE) or compressed formats (Opus, Vorbis, FLAC, WAV). See SoLoud.startMixerOutputStream / stopMixerOutputStream / isMixerOutputStreamRunning.mixer_capture example (example/lib/mixer_capture/mixer_capture.dart) that shows how to capture the master mix and save it to a file.--wasm (or any server that sends Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp headers) because the WASM module uses SharedArrayBuffer for the audio thread. Running with flutter run -d chrome without --wasm is not supported by the default dev server. Please see the doc here.SoLoudIsolate, an isolate-safe singleton for running mixer output capture (and other safe operations, for now only readSamplesFrom*) from a non-main isolate without touching the main isolate's loader, filters, or event callbacks.example/lib/mixer_capture/isolate_capture_test.dart to demostrate mixer output capture from a separate isolate.loopingEndAt and the live getLoopEndPoint / setLoopEndPoint APIs, allowing half-open [start, end) loop regions #499. Thanks to @KunstderfugplayClocked and play3dClocked (plus Bus.playClocked / Bus.play3dClocked) for sample-accurate scheduled playback, along with the related setDelaySamples, getStreamTime and resetStreamTime (re-anchor the clocked-play clock) APIs. The sounds are spaced with sub-millisecond accuracy regardless of the engine buffer size instead of clumping at buffer boundaries.play and playClocked.getEngineTime, playScheduled, stopScheduled and fadeScheduled (plus Bus.playScheduled) for score/manifest-style scheduling pinned to the engine's own clock: read getEngineTime once and schedule a batch of sounds at absolute engine times, with sample accuracy and no ~2 s window limit like playClocked. playScheduled accepts an optional duration to stop the sound automatically, and fadeScheduled a thenStop flag to stop the sound when the fade ends. Scheduled stops are sample-accurate (not quantized to output buffer boundaries like scheduleStop).deinit() now stops the engine (joining the audio thread) before closing the Dart NativeCallable trampolines, so voices ending from the mixing thread can no longer call into deleted callbacks while tearing down.fix: Waveform audio sources do not match engine sample rate #501. Thanks to @Colton127
add lowLatency init option to allow recordable Android output #492. Thanks to @MjnMixael
lowLatency init option to allow recordable Android output #492. Thanks to @MjnMixaelfix web: don't spawn the deferred-pause std::thread on web (Aborted() in initEngine) #488. Thanks to @felixmin
wait some ms to pause device when there are no more sounds playing #486
Windows: prevent the compiler from complaining about min and max macros. Fixes #483
min and max macros. Fixes #483fix released buffer stream size reporting #480. Thanks to @Kunstderfug
add look-ahead brickwall limiter and fix planar DSP indexing #468. Thanks to @Kunstderfug
BufferingType.released on some Android devices #476wet parameter of parametric eq was not evaluated #477Fix iOS SPM miniaudio duplicate symbols #465. Thanks to @coolswood
fix sample count calculations in BufferStreamInstance and BufferStream #462
-flto compile arg in Package.swift #463flutter_zxing which uses the common.h source file name too #464fix Apple: build forcing to add c++ std lib #456
fix: rebind Dart callbacks after hot restart #444. Thanks to @skylartaylor
apple: fix building issue with XCode
fixed get eq params other than bands values
soloud.filters.parametricEqFilter.bandFrequency(index))removed deprecated equalizerFilter in favor of parametricEqFilter
SoundEventType.handleIsNoMoreValidcreateVoiceGroup return was interpreted as a signed int instead of an unsigned because it always has the sign bit flagequalizerFilter in favor of parametricEqFilterfix: FFI symbol stripping causing "symbol not found" errors in iOS/macOS when uploading to App Store #431
macOS/iOS fix: check for cmake in the path while building
- macOS fix: build error
added Mixing Bus feature and example https://docs.page/alnitak/flutter_soloud_docs/advanced/mixing_bus
getApproximateVolume to get the approximate volume of a channel of the playerautoDispose parameter to load* methods to automatically dispose the sound when it is finished. This eliminates the need to manually call disposeSourceplaySource to play a source from assets, URLs, or a file and automatically dispose of its AudioSource when it is finishedfilters/parametric_eq.dart examplejs and wasm only on web (no more included on other platforms)TRY_SYSTEM_LIBS_FIRST=1 environment variable #421. Useful for Raspberry Pi because the precompiled libs are available only for x86_64.breaking changes
NO_OPUS_OGG_LIBS environment variable to NO_XIPH_LIBSSoLoudOpusOggVorbisLibsNotAvailableException to SoLoudXiphLibsNotAvailableExceptionareOpusOggLibsAvailable to areXiphLibsAvailableremove wasm/js assets for non-web builds#425. Thanks to @adil192
fix: compilation error on Windows #423
fix: wire miniaudio backend pause/resume to stop AudioUnit on iOS #406. Thanks to @sbauly
audio_session and audio_servicegetStreamTimeConsumed returns the wrong time for s16le and s8 #419
getStreamTimeConsumed returns the wrong time for s16le and s8 #419desktop_drop and maybe others #401Harden loader temp directory logic #404. Thanks to @filiph
Nothing published for this version
Nothing published for this version
fix loadMem issue after deinit #399
fix crash when seeking with a negative value #386
fix MP3 stream decoding missing last few seconds of audio #381
fix: null check before accessing sound in seek() #384. Thanks to @9AZX
win fix: loadMem/loadAsset futures never finish when run in parallel with the same file #376
fixed Bad state: Future already completed error during integration tests. Thanks to @Taormina #373
Bad state: Future already completed error during integration tests. Thanks to @Taormina #373fix: Crash during hot-restart using Dart 3.10 #369
fix Android: crashes after opus stream playback #365
fix Android: NO_OPUS_OGG_LIBS for Android build used in gradle.properties not always worked #358. And Thanks to @mingjunsiek #361 #354
gradle.properties not always worked #358. And Thanks to @mingjunsiek #361 #354crash when adding data as PCM data on v3.4.0 #348
added support for OGG FLAC and its metadata to BufferStream #294
Player::findByHandle crash in some circumstancies #342removed experimental tag for allInstancesFinished
allInstancesFinishedfix: audio stream with released mode failed to consume BufferStream #335 #318
Your coding agent can read these notes before it upgrades. Set up the MCP server →