miniaudio_player 2.1.0
miniaudio_player: ^2.1.0 copied to clipboard
Ultra-low-resource cross-platform audio player and DSP engine using miniaudio. Features equalizer, reverb, limiter, crossfeed, pitch, rate, and real-time visualizer.
Changelog #
2.1.0 #
๐ต Audio Engine Features & Public API #
๐๏ธ Studio-Grade Algorithmic Reverb (Freeverb Topology) & Acoustic Presets
- Algorithmic Reverberation Engine: Implemented a studio-grade stereophonic algorithmic reverb in native C (
src/map_reverb.c,src/map_reverb.h) based on the classic Freeverb/Schroeder architecture:- 8 parallel Lowpass Feedback Comb Filters (LBCF) with tunable feedback and lowpass damping per channel.
- 4 cascaded Allpass Filters (APF) per channel for high-density diffusion without metallic ringing.
- Stereophonic decorrelation with a 23-sample spatial offset between left and right delay lines.
- Strict Real-Time $\mathcal{O}(1)$ Determinism & Zero Allocations:
- Pre-allocated 163,840-sample static buffer (640 KB aligned to 64 bytes) during initialization.
- Guarantees 0 dynamic heap allocations (
malloc/free) in the real-time audio thread, safely handling sample rates up to 192 kHz.
- Hardware Anti-Denormal Guards (Flush-to-Zero):
- Flush-to-zero thresholding (
fabsf(v) < 1e-20f ? 0.0f : v) embedded inside all comb and allpass delay filter loops, completely preventing CPU microcode stalls when reverb tails decay into silence.
- Flush-to-zero thresholding (
- Anti-Zipper Parameter Smoothing:
- Sample-accurate linear interpolation across audio buffers for
roomSize,damping,width,wet, anddryparameters, ensuring smooth, click-free parameter transitions.
- Sample-accurate linear interpolation across audio buffers for
- Studio-Grade Acoustic Presets (
Reverbfactory presets &Reverb.presetsmap):Reverb.off: Transparent bypass with zero processing overhead.Reverb.smallRoom: Intimate, dry room with rapid early reflections (roomSize: 0.35, damping: 0.60, wet: 0.18, dry: 0.82, width: 0.70).Reverb.studio: Controlled recording studio acoustics (roomSize: 0.50, damping: 0.40, wet: 0.22, dry: 0.78, width: 0.85).Reverb.concertHall: Expansive, balanced symphonic hall (roomSize: 0.75, damping: 0.30, wet: 0.32, dry: 0.68, width: 1.00).Reverb.cathedral: Monumental sanctuary with extended airy tail (roomSize: 0.90, damping: 0.15, wet: 0.42, dry: 0.58, width: 1.00).Reverb.plate: Bright, dense electromechanical plate reverberator (roomSize: 0.65, damping: 0.10, wet: 0.28, dry: 0.72, width: 0.90).Reverb.ambient: Atmospheric, enveloping diffuse sonic wash (roomSize: 0.85, damping: 0.50, wet: 0.38, dry: 0.62, width: 1.00).
- Complete Dart API & Reactive Integration:
- Imperative action:
player.action.setReverb(Reverb)and granularplayer.action.configureReverb({roomSize, damping, wet, dry, width, enabled}). - Synchronous state inspection:
player.state.reverb. - Real-time broadcast telemetry stream:
player.stream.reverb. - Immutable model:
ReverbwithcopyWith(),presetName, and backwards-compatible aliastypedef ReverbConfig = Reverb.
- Imperative action:
๐๏ธ Optimized DSP Chain Acoustic Ordering
- Positioned the reverb engine at Step 2.5 in
src/map_dsp.c(Post-Equalizer, Pre-Spatializer/Crossfeed, Pre-Mono/Balance, Pre-Visualizer, Pre-Limiter):- Post-EQ: Prevents uncontrolled low-frequency mud or harsh resonance peaks from exciting the reverberation tank.
- Pre-Spatializer & Crossfeed: Allows the natural diffuse reverberant soundstage to be shaped by binaural headphone crosstalk (
bs2b) or stereophonic widening. - Pre-Visualizer: Ensures the real-time spectrum analyzer and VU meters capture physical acoustic room decay.
- Pre-Limiter: Guarantees all energy accumulation from dense reflections and resonant feedback is safely caught by the True-Peak Soft-Knee Limiter, completely preventing digital clipping.
2.0.0 #
๐ต Audio Engine Features & Public API #
๐ก๏ธ Hardware-Grade Output Limiter (True-Peak Soft-Knee)
- True-Peak brickwall limiter operating directly before DAC handoff.
- Mathematically transparent in the linear zone ($|x| \le 0.95$ FS) with 0.00000% THD and 141.4 dB SNR.
- Smooth second-order parabolic compression across the soft-knee zone ($0.95 < |x| \le 1.0$), capping output safely at $0.996$ FS even under aggressive digital overshoot (+3.5 dBFS) to eliminate harsh digital clipping.
- Controlled via
player.action.setLimiter(bool)with state observed viaplayer.stream.limiterEnabledandplayer.state.limiterEnabled.
๐ง Binaural Headphone Crossfeed (Bauer bs2b)
- Native stereophonic-to-binaural DSP filter modeling acoustic speaker crosstalk to eliminate headphone listening fatigue.
- Standard operational presets (
CrossfeedMode):CrossfeedMode.moffatBauer: Default Moffat-Bauer standard curve (700 Hz cut-off, 4.5 dB feed).CrossfeedMode.chuMoy: Subtle Chu Moy curve (700 Hz cut-off, 6.0 dB feed).CrossfeedMode.off: Bypasses crossfeed processing with zero overhead.
- Controlled via
player.action.setCrossfeed(CrossfeedMode)and monitored viaplayer.stream.crossfeed/player.state.crossfeed.
๐๏ธ Standard ISO 266 10-Band Equalizer & Acoustic Presets
- Aligned native biquad filters to the international standard ISO 266 preferred 1/1 octave center frequencies:
hz31: 31 Hz (Low-Shelf, $Q = 0.7071$)hz63: 63 Hz (Peaking, $Q = 1.15$)hz125: 125 Hz (Peaking, $Q = 1.15$)hz250: 250 Hz (Peaking, $Q = 1.15$)hz500: 500 Hz (Peaking, $Q = 1.15$)hz1k: 1.0 kHz (Peaking, $Q = 1.15$)hz2k: 2.0 kHz (Peaking, $Q = 1.15$)hz4k: 4.0 kHz (Peaking, $Q = 1.15$)hz8k: 8.0 kHz (Peaking, $Q = 1.15$)hz16k: 16.0 kHz (High-Shelf, $Q = 0.7071$)
- Gain range:
-24.0 dBto+24.0 dBper band with analytical Robert Bristow-Johnson (RBJ) audio EQ curves (0.00 dB analytical error). - Harmonized acoustic presets:
Equalizer.rock,Equalizer.pop,Equalizer.jazz,Equalizer.classical,Equalizer.bassBoost, andEqualizer.flat. - Immutable model with
copyWith(),toList(), andEqualizer.fromList().
๐๏ธ Mono Downmixing, Channel Balance & Stereo Spatializer
player.action.setMono(bool): Downmixes stereo audio into mono for single-speaker setups, hardware accessibility, or specialized monitoring.player.action.setBalance(double pan): Smooth stereo balance panning from-1.0(full left) through0.0(center) to+1.0(full right).player.action.setSpatializer({required bool enabled, double width = 1.3}): Stereo field expansion with adjustable width factor (1.0to2.0).
๐ ReplayGain & EBU R128 Loudness Normalization with Clipping Defense
player.action.setReplayGain({required double gainDb, double? peak, double preampDb, bool preventClipping}): Applies track gain normalization with smooth anti-zipper volume ramping.- Anti-clipping defense automatically caps gain factor so
(gainDb + preampDb) * peak <= 1.0, preventing digital overs. player.action.clearReplayGain(): Resets normalization back to unity gain (0.0 dB).player.action.setPreamp(double preampDb): Dedicated standalone manual preamp gain applied prior to equalizer filtering.
๐ Real-Time Reactive Audio Visualizer & Spectrum Analyzer
- Native Direct Form II Transposed biquad bandpass filter bank embedded directly inside the native audio playback node in C (
src/map_dsp.c). - Centered at the exact 10 ISO 266 equalizer frequencies (
31 Hzthrough16 kHz) for 1:1 visual correlation with EQ adjustments. - Stereo VU meters (Left and Right channels), global RMS energy, and instantaneous peak sample tracking.
- Perceptual frequency compensation curve and smooth exponential decay (
0.82f) for fluid 30-60 fps visual rendering. - On-Demand Lifecycle: Subscribing to
player.stream.visualizeractivates DSP filtering on-demand (~30 fps telemetry); unsubscribing turns off processing and resets telemetry, keeping idle/background CPU usage strictly at 0%. - API models and access:
VisualizerDatamodel, synchronous getterplayer.state.visualizer, and explicit manual override viaplayer.action.setVisualizerEnabled(bool).
๐ Native Real-Time Silence Detection & Dynamic Skipping
- In-pipeline streaming silence detector in native C (
src/map_silence.c) positioned upstream of Sonic DSP and the equalizer cascade. - Zero intermediate memory copies, zero buffer starvation, and smooth anti-click crossfading (linear ramp over 5ms) preventing audible pops.
- Modalities (
SilenceSkipMode):SilenceSkipMode.trimEdges: Trims lead-in silence on track start and truncates trailing lead-out silence at track end. Ideal for music playlists and gapless playback.SilenceSkipMode.continuous: Dynamically skips in-between speech pauses down to configurable padding. Ideal for podcasts, audiobooks, lectures, and voice messages.SilenceSkipMode.all: Simultaneously trims lead-in/lead-out track edges and truncates continuous pauses in between.SilenceSkipMode.disabled: Bypasses silence detection completely with zero CPU overhead.
- Customizable parameters via
SilenceSkipConfig:thresholdDb(default:-45.0 dBwith 2 dB hysteresis),minSilenceDuration(default:300 ms), andpadding(default:50 ms). - Real-time tracking of cumulative skipped time:
player.stream.silenceSavedDurationandplayer.state.silenceSavedDuration.
๐ Unified Single-Pass Audio Analysis (Loudness, ReplayGain & Waveform)
- Unified loudness calculation and waveform envelope extraction into a single, highly optimized decoding pass in native C (
src/map_analysis.c). - Eliminates redundant file I/O and frame decoding, reducing CPU usage and analysis duration by ~50% compared to independent analysis runs.
- Streams audio in 32 KB blocks residing in CPU L1/L2 cache (< 100 KB constant RAM footprint).
- Asynchronous API (
MiniaudioPlayer.analyzeAudio): executes on a dedicated native background thread with completion delivered viaNativeCallable.listener(zero Dart Isolates, zero UI frame drops). Synchronous variantMiniaudioPlayer.analyzeAudioSync(...)available for CLI tools. - Rich analysis models:
AudioAnalysisResult,ReplayGainResult(ITU-R BS.1770-4 / EBU R128 LUFS), andWaveformResultwith normalized amplitude points for UI seekbars.
๐ Audio Device Management & Dynamic Routing
- Clean
AudioDevicearchitecture: dedicated model withid,name,resolvedName,isAuto,type, and human-friendlydisplayName. - Global
defaultDeviceDisplayNameResolverfor custom internationalization/localization of system default routing labels. - Strict ID-based equality (
operator ==) preventing UI assertion errors and selection flickers in Flutter dropdown menus. - Instance vs Global separation:
- Per-player:
player.state.audioDevice,player.stream.audioDevice,player.action.setDevice(AudioDevice). - Global:
MiniaudioPlayer.getAudioDevices(),MiniaudioPlayer.getAudioDevicesSync(), andMiniaudioPlayer.getAudioDevicesStream().
- Per-player:
๐ Dual State Paradigm & Safe Lifecycle
- Instant synchronous state getters (
player.state.*) alongside reactive broadcast streams (player.stream.*). MiniaudioPlayerimplementsFinalizablewith a low-level staticNativeFinalizerbound tominiaudio_player_destroy, ensuring leak-free garbage collection if the instance is collected without explicitdispose().
โก Low-Level Platform Engineering & Microarchitecture #
๐ฑ Android 15+ 16 KB ELF Page Alignment & ARM NEON
- Enforced
-Wl,-z,max-page-size=16384link flag inhook/build.dartfor full compatibility with Android 15+ 16 KB memory page kernels. - Enabled ARM NEON vfpv4 vectorization on 32-bit ARM (
armeabi-v7a) and automatic AArch64 SIMD compilation.
๐ค Autonomous Pure C JNI Android Bridge
- Standalone JNI bridge operating without MethodChannels,
package:jni, or Kotlin code inMainActivity.kt. - Multi-tier dynamic
JavaVMdiscovery across Android API 21 through 35+ (viaJNI_OnLoad,libnativehelper.so,libart.so, anddl_iterate_phdrmemory scan). - Reflective application context acquisition compatible with headless background services (
ActivityThread.currentApplication()). - Hardware route classification (earpieces, speakers, wired headphones, USB, Bluetooth A2DP vs SCO).
- Resilient AAudio route disconnection recovery (
AAUDIO_SERVICE_EVENT_DISCONNECTED) without audio stalls.
๐ช Windows WASAPI MMCSS Pro Audio & High-Precision Timers
- Configured
wasapi.usage = ma_wasapi_usage_pro_audio, activating Windows MMCSS (AvSetMmThreadCharacteristicsA("Pro Audio")) for real-time scheduling priority. - Enforced 1 ms Win32 timer resolution (
timeBeginPeriod(1)andtimeEndPeriod(1)), eliminating decoder starvation caused by Windows default 15.6 ms sleep granularity.
๐ฌ Universal Hardware FTZ/DAZ Denormal Mitigation
map_disable_denormals()sets Flush-to-Zero (FTZ) and Denormals-are-Zero (DAZ) bits directly in CPU control registers (x86 MXCSR0x8040, ARM64 FPCR, ARMv7 FPSCR, Windows_controlfp_s). Prevents 100x CPU cycle penalties on decaying IIR filter tails.
โก Microarchitectural Atomic Synchronization & Memory Barriers
- Weak memory model support for ARM64/ARMv7: explicit hardware memory barriers (
DMB ISHfor Release,DMB ISHLDfor Acquire) and pipeline yield instructions (isb; yield). - Total Store Order (TSO) optimization on x86/x86_64: pure compiler barriers (
__atomic_thread_fence/_ReadWriteBarrier) completely eliminating heavy bus-locking instructions (MemoryBarrier()/_mm_mfence()), saving 30โ100 CPU cycles per read. - Seqlock synchronization (
src/map_seqlock.h): pre-check backoff loop (while ((seq & 1) != 0)) and local structure staging (local_vis), reducing the writer critical section to a single atomic 56-byte copy (< 2 CPU cycles). Certified across 35+ million concurrent contention reads without data tearing.
๐๏ธ SIMD Vectorization & Vector Register Hoisting
- Direct Form II Transposed (DF2T) biquad cascade processed in 64-frame contiguous SIMD blocks with hardware FMA instruction chaining (
vmlaq/vmlsq). - Hoisted delay registers ($s_1, s_2$) and bs2b filter states directly into CPU vector registers during block processing without stack spilling.
๐ Lock-Free SPSC Ring Buffer Signaling & Hysteresis
- Atomic
worker_is_sleepingflag inmap_time_stretch_ds: RT audio callback signals the background decoder viama_event_signalonly when the buffer falls below 50% capacity and the worker is asleep. Completely eliminates POSIXpthread_mutexlock contention and prevents priority inversion glitches.
โฑ๏ธ Strict Real-Time Determinism ($\mathcal{O}(1)$) & Zero Heap Allocations
- Zero dynamic heap allocations in RT thread: validated empirically via memory interceptor wrappers (
--wrap=malloc/calloc/realloc/free) during live playback and dynamic parameter mutations. - Cache-line 64-byte alignment (
MAP_ALIGN_CACHE_LINE,ma_aligned_malloc) onminiaudio_player_tand ring buffer cursors to prevent false sharing across CPU cores. - Pre-allocated 64K frame capacity (
SONIC_MIN_STREAM_BUFFER_CAPACITY) in Sonic varispeed and pre-faulting (memset) of working scratch buffers at initialization.
1.0.1 #
๐ Bug Fixes & Playback Lifecycle #
- Stop & Completed State Separation:
- Fixed an issue where calling
stop()after natural completion causedminiaudio_player_get_status()to override theSTOPPEDstate back toCOMPLETEDdue to unconditionedma_sound_at_end()evaluations. - In native C,
ma_sound_at_end()evaluation is now strictly conditioned onMAP_PLAYBACK_STATE_PLAYING. Stopped state is strictly preserved withis_completed = 0andatEndflag reset. - In Dart, modeled
stream.completedas an edge-triggered transition (false -> true) during active playback, preventing redundant completion events. - Decoupled
pollAndEmit(force: true)from re-emitting terminal completion events. - Added synchronous state cleanup (
notifyStopped()) onplayer.action.stop()to ensure position resets to zero andcompletedis immediately reset for subsequent playback.
- Fixed an issue where calling
- OGG/OGA/OPUS Seek-to-End Background Audio Leak:
- Fixed a bug where seeking to or past track duration while playing
.ogg,.oga, or.opusfiles caused audio to continue playing in the background despite the UI reporting stopped/completed. - Resolved root cause in underlying decoders (
stb_vorbisandopusfile), which return seek errors (VORBIS_seek_failed/OP_EINVAL) when attempting to seek at or beyondtotal_frames. - Implemented EOF clamping (to
total_frames - 1) and EOF flag tracking inmap_time_stretch_ds_seekandma_libopus_seek_to_pcm_frame. - Explicitly stopped the native sound (
ma_sound_stop) when seeking to or past track duration to cleanly halt audio output.
- Fixed a bug where seeking to or past track duration while playing
๐งช Tests #
- Added tests verifying edge-triggered completion transitions, post-completion stop state integrity, and immunity to
force: truepolling (test/tier2_boundary_test.dart). - Added tests verifying clean audio termination when seeking to or past track duration across OGG, OGA, and Opus codecs (
test/tier1_codecs_test.dart).
1.0.0 #
๐ Relicensed to MIT #
- Permissive MIT License: The package has transitioned from GPL-v3 to the MIT License, removing all copyleft restrictions and allowing free integration into commercial, closed-source, proprietary, and open-source applications alike.
๐ Direct FFI & Zero-Isolate Architecture #
- Eliminated Secondary Dart Isolates: Removed the internal isolate worker layer (
lib/src/isolate/). All player commands (player.action.*) are now invoked directly through Dart FFI in sub-microseconds with zero message-passing latency and zero isolate memory footprint (~0 MB Dart overhead). - Native OS Audio Threads: Audio decoding, Sonic DSP (pitch & speed), 10-band biquad EQ, and hardware output streaming run concurrently on native OS audio threads (CoreAudio, AAudio, WASAPI, PulseAudio/ALSA) with real-time priority, guaranteeing zero UI frame drops or stutter on the Flutter main thread.
- Ultra-Responsive Reactive Engine:
- Events originating from native audio threads (such as track completion) are dispatched safely to Dart streams using
NativeCallable.listenerwithout polling. - Position and progress reporting is driven by a lightweight internal timer, providing fluid 60/120fps UI updates without port serialization thrashing.
- Synchronous getters (
player.state.*) provide instantaneous access to atomically cached native playback values.
- Events originating from native audio threads (such as track completion) are dispatched safely to Dart streams using
- Seamless Background Isolate Support:
MiniaudioPlayercan still be optionally instantiated directly inside standalone worker isolates or background services (audio_service,flutter_background_service) without any architectural friction.
๐ฑ Native Android Audio Device Discovery (Pure C JNI) #
- Zero-Configuration Device Enumeration:
MiniaudioPlayer.getAudioDevices()on Android now queries Android'sAudioManagerandAudioDeviceInfodirectly through an embedded native C JNI bridge. - No MethodChannels or Kotlin Required: Completely eliminated the need for
package:jni, platform channels, and manual Kotlin implementations in the host app'sMainActivity.kt. - Autonomous Multi-Tier JavaVM Resolution: Automatically acquires the running
JavaVMacross all Android versions (API 21 to API 35+) using a 6-stage fallback strategy (JNI_OnLoad,libnativehelper.so,libart.so, and dynamic linker memory scanning viadl_iterate_phdrto bypass bionic linker namespace restrictions). - Headless & Background Compatible: Acquires application context via reflection on
ActivityThread.currentApplication(), enabling audio device queries in headless background services without an attached Flutter Activity. - Smart Routing & Classification:
- Identifies earpieces, built-in speakers, wired headphones, USB audio, and Bluetooth accessories.
- Distinguishes high-fidelity Bluetooth A2DP audio from SCO telephony headsets.
- Correctly marks active system default outputs using Android 12+
AudioManager.getCommunicationDevice()and fallback routing heuristics (isBluetoothA2dpOn,isWiredHeadsetOn). - Safely ignores internal telephony and modem devices.
- Hot-Plug & AAudio Rerouting Resiliency: Intercepts Android AAudio route-change disconnect events (
AAUDIO_SERVICE_EVENT_DISCONNECTED/ error-899) during Bluetooth connection/disconnection and seamlessly recreates the audio stream on the new hardware output without audio stalls or crashes. - Zero Memory Leaks: Rigorous JNI local reference management (
DeleteLocalRef), UTF string release (ReleaseStringUTFChars), and clean thread detachment. - Cleaned Log Levels: Internal operational traces changed to
ANDROID_LOG_DEBUGto keep standard Android logcat output clean and production-ready.
๐งช Quality & Tests #
- Updated and verified 100% test suite compatibility (131/131 tests passing across native codecs, parametric EQ, pitch/speed, and device routing).
0.1.0 #
- Initial release of
miniaudio_player. - Ultra-low-resource cross-platform audio player powered by miniaudio and Sonic DSP.
- 100% pure Dart FFI with Native Assets (isolate-friendly, zero platform channel overhead in core).
- Built-in 10-band parametric equalizer with presets (Rock, Pop, Jazz, Classical, Bass Boost, Flat).
- Real-time independent pitch scaling and playback rate (time-stretching).
- Dynamic runtime audio device selection with automatic fallback and recovery.
- Multi-codec decoding support (MP3, WAV, FLAC, OGG, Opus, AAC, M4A, ALAC, AIFF, W64).
- Dual state API with synchronous getters and real-time reactive broadcast streams.
