miniaudio_player 0.1.0 copy "miniaudio_player: ^0.1.0" to clipboard
miniaudio_player: ^0.1.0 copied to clipboard

Ultra-low-resource cross-platform audio player using miniaudio. Equalizer, pitch and rate are supported.

miniaudio_player banner

miniaudio_player #

NOT READY YET: This version is a pre-release alpha. Expect breaking changes, incomplete features, and bugs. Please report issues on GitHub #

pub package License: GPL-3.0 Platform Dart FFI Tests

An ultra-low-resource, high-performance cross-platform audio player for Flutter and Dart. Powered by the industry-proven miniaudio C engine, Sonic DSP, and Dart Native Assets with 100% pure FFI bindings.

Engineered from the ground up for high fidelity, low latency, and zero UI stutter by delegating heavy decoding and audio processing to isolated background pipelines.


Screenshot with arrows pointing to features


✨ Features at a Glance #

  • ⚡ Ultra-Low Memory & CPU Overhead: Direct native C memory management and zero-copy streaming ensure minimal battery and hardware resource drain.
  • 🔀 100% Pure FFI & Isolate-Friendly: No platform channels (MethodChannel) in the core audio pipeline. You can instantiate, run, and control MiniaudioPlayer directly inside background Isolates, worker threads, or headless background audio services.
  • 🎛️ 10-Band Parametric Equalizer: Native biquad filtering with low-shelf, peaking, and high-shelf bands. Includes instant acoustic presets (Rock, Pop, Jazz, Classical, Bass Boost, Flat).
  • 🚀 High-Definition Pitch & Speed (Sonic DSP): Real-time time-stretching and independent pitch shifting with exceptional clarity and no phase distortion.
  • 🎧 Dynamic Audio Device Routing: Real-time switching between audio outputs (speakers, headphones, Bluetooth) with automatic, crash-free recovery fallbacks.
  • 📦 Extensive Audio Codec Support: Built-in native decoding for MP3, WAV, FLAC, OGG, Opus, AAC, M4A (AAC & ALAC), AIFF, W64, and more without external system dependencies.
  • 🔄 Dual State Paradigm: Access synchronous instant state snapshots (player.state.*) or listen to reactive broadcast streams (player.stream.*).

🚀 Performance & Architecture #

Unlike traditional audio plugins that bridge through Java/Kotlin or Objective-C/Swift platform channels with thread hops and serialization overhead, miniaudio_player communicates directly with compiled C native code:

┌────────────────────────────────────────────────────────┐
│                      Flutter UI                        │
│             (Smooth 60/120 FPS Rendering)              │
└───────────────────────────┬────────────────────────────┘
                            │ SendPort / ReceivePort
┌───────────────────────────▼────────────────────────────┐
│              Dedicated Background Isolate              │
│            MiniaudioPlayer Command Processor           │
└───────────────────────────┬────────────────────────────┘
                            │ Direct Dart:FFI Pointer Calls
┌───────────────────────────▼────────────────────────────┐
│               Native C Audio Core (miniaudio)          │
│   ┌──────────────────┐  ┌──────────────────────────┐   │
│   │ Sonic Time/Pitch │  │ 10-Band Biquad Equalizer │   │
│   └──────────────────┘  └──────────────────────────┘   │
│               Direct OS Audio Device Backend           │
│      (WASAPI / CoreAudio / AAudio / ALSA / Pulse)      │
└────────────────────────────────────────────────────────┘
  1. Zero UI Thread Blocking: File reading, decoding, seeking, and parameter adjustments happen asynchronously off the main UI thread.
  2. Deterministic Latency: Buffer sizes can be customized down to device hardware periods (bufferSize) for low-latency feedback.
  3. Resilient Error Recovery: Native audio route disconnections (e.g. unplugging headphones or system device switches) recover automatically without crashing or stalling.

📊 Platform Support Matrix #

Feature Android iOS macOS Windows Linux
Native Audio Backend AAudio / OpenSL ES CoreAudio CoreAudio WASAPI / DirectSound ALSA / PulseAudio
Playback & Streaming ✅ ✅ ✅ ✅ ✅
10-Band Equalizer ✅ ✅ ✅ ✅ ✅
Speed (Time Stretch) ✅ ✅ ✅ ✅ ✅
Pitch Scaling ✅ ✅ ✅ ✅ ✅
Background Isolate Instantiation ✅ ✅ ✅ ✅ ✅
Built-in Device Enumeration ⚠️ See note ✅ ✅ ✅ ✅
Dynamic Device Switching ✅ ✅ ✅ ✅ ✅

Important

📱 Note on Android Audio Device Enumeration #

On desktop platforms and iOS/macOS, miniaudio directly queries the native audio system APIs to enumerate available output devices. However, on Android, native C/AAudio does not expose hardware output enumeration. Device discovery on Android strictly requires accessing Java/Kotlin Android SDK APIs (android.media.AudioManager and AudioDeviceInfo).

To preserve miniaudio_player as a 100% pure Dart FFI package that can run inside raw Dart Isolates, background services, and CLI tools without depending on a Flutter Activity or engine attachment, Android device enumeration is intentionally left to the host application.

Switching audio devices via player.action.setDevice(...) is fully supported on Android once you pass the target device's integer ID string. See Listing Devices on Android below for a ready-to-use implementation.


📦 Supported Audio Formats #

Format Extensions Decoding Backend
MPEG Audio .mp3, .mp2, .mp1 Native miniaudio (dr_mp3)
Waveform Audio .wav, .bwf, .rifx, .w64, .rf64 Native miniaudio (dr_wav)
FLAC .flac Native miniaudio (dr_flac)
Ogg Vorbis .ogg, .oga Native miniaudio (stb_vorbis)
Opus .opus Native Opus decoder
Apple Lossless / AAC .m4a, .aac Native ALAC / AAC decoder
Audio Interchange .aif, .aiff, .aifc Native miniaudio

🛠️ Getting Started #

Add miniaudio_player to your pubspec.yaml:

flutter pub add miniaudio_player

Or for pure Dart projects:

dart pub add miniaudio_player

💻 Usage Examples #

1. Basic Playback #

import 'package:miniaudio_player/miniaudio_player.dart';

void main() async {
  // Optional: configure global defaults before instantiating players
  MiniaudioPlayer.config(
    defaultBufferSize: 0, // 0 = native device default
    logLevel: MiniaudioLogLevel.info,
  );

  final player = MiniaudioPlayer();

  // Open an audio file and start playback immediately
  await player.action.open('/path/to/song.flac', autoplay: true);

  // Playback controls
  await player.action.pause();
  await player.action.play();
  await player.action.seek(const Duration(seconds: 45));
  await player.action.setVolume(0.85);

  // Clean up resources when finished
  await player.action.dispose();
}

2. Reactive Streams & Synchronous State #

miniaudio_player offers both real-time broadcast streams and synchronous getters for effortless UI binding:

// Synchronous snapshot:
print('Is Playing: ${player.state.playing}');
print('Current Position: ${player.state.position}');
print('Track Duration: ${player.state.duration}');
print('Bitrate: ${player.state.audioBitrate} kbps');

// Reactive streams:
player.stream.position.listen((position) {
  print('Position: $position');
});

player.stream.playing.listen((isPlaying) {
  print('State changed: ${isPlaying ? "Playing" : "Paused"}');
});

player.stream.completed.listen((isCompleted) {
  if (isCompleted) {
    print('Track finished playing!');
  }
});

Using with Flutter StreamBuilder:

StreamBuilder<Duration>(
  stream: player.stream.position,
  builder: (context, snapshot) {
    final position = snapshot.data ?? Duration.zero;
    final total = player.state.duration;
    return Text('${position.inMinutes}:${(position.inSeconds % 60).toString().padLeft(2, '0')} / '
                '${total.inMinutes}:${(total.inSeconds % 60).toString().padLeft(2, '0')}');
  },
)

3. Running Inside a Background Isolate #

Because miniaudio_player has no platform channel dependencies, you can run an audio player entirely inside a standalone background Dart Isolate or headless background service (e.g. audio_service, flutter_background_service, or Isolate.spawn):

import 'dart:isolate';
import 'package:miniaudio_player/miniaudio_player.dart';

/// Entrypoint for a dedicated worker isolate
void audioWorkerIsolate(SendPort sendPort) async {
  final commandPort = ReceivePort();
  sendPort.send(commandPort.sendPort);

  // MiniaudioPlayer initializes seamlessly in any background isolate!
  final player = MiniaudioPlayer();

  player.stream.position.listen((pos) {
    sendPort.send({'event': 'position', 'value': pos.inMilliseconds});
  });

  player.stream.completed.listen((done) {
    if (done) sendPort.send({'event': 'completed'});
  });

  await for (final message in commandPort) {
    if (message is Map) {
      switch (message['command']) {
        case 'open':
          await player.action.open(message['path'] as String, autoplay: true);
          break;
        case 'pause':
          await player.action.pause();
          break;
        case 'resume':
          await player.action.play();
          break;
        case 'stop':
          await player.action.stop();
          break;
        case 'dispose':
          await player.action.dispose();
          commandPort.close();
          return;
      }
    }
  }
}

void startIsolatedPlayer() async {
  final receivePort = ReceivePort();
  await Isolate.spawn(audioWorkerIsolate, receivePort.sendPort);

  final sendPort = await receivePort.first as SendPort;

  // Send commands to your isolated player
  sendPort.send({'command': 'open', 'path': '/music/ambient.opus'});
}

4. 10-Band Parametric Equalizer #

Shape the audio frequency response in real time across 10 precision bands (60Hz, 170Hz, 310Hz, 600Hz, 1kHz, 3kHz, 6kHz, 12kHz, 14kHz, 16kHz) from -24.0 dB to +24.0 dB:

// Apply built-in acoustic presets
await player.action.setEqualizer(Equalizer.rock);
await player.action.setEqualizer(Equalizer.bassBoost);
await player.action.setEqualizer(Equalizer.jazz);
await player.action.setEqualizer(Equalizer.flat);

// Or create a custom equalizer configuration
final customEq = Equalizer(
  hz60: 4.0,   // Boost sub-bass
  hz170: 2.5,  // Warmth
  hz1k: -1.0,  // Tame mid harshness
  hz14k: 3.0,  // Treble air
  hz16k: 4.0,  // High-shelf sparkle
);
await player.action.setEqualizer(customEq);

5. High-Fidelity Speed & Pitch Control #

Adjust playback rate and pitch independently with Sonic DSP:

// Playback rate: 0.25x (slow) to 4.0x (fast) without changing pitch
await player.action.setRate(1.5);

// Pitch scaling: 0.5x to 2.0x without changing playback speed
await player.action.setPitch(1.2);

6. Audio Output Device Selection #

List and switch output devices at runtime:

// Enumerate system playback devices (macOS, Windows, Linux, iOS)
final devices = await MiniaudioPlayer.getAudioDevices();

for (final device in devices) {
  print('Device: ${device.name} (ID: ${device.id}, Default: ${device.isDefault})');
}

// Switch to a specific device
await player.action.setDevice(devices[1]);

// Return to automatic system default routing
await player.action.setDevice(AudioDevice.auto);

📱 Listing Devices on Android #

Because Android device discovery requires the Android AudioManager Java/Kotlin SDK, you can quickly expose it to Flutter using a simple MethodChannel in your host application:

Step 1: In your Kotlin MainActivity.kt #

package com.example.yourapp

import android.content.Context
import android.media.AudioDeviceInfo
import android.media.AudioManager
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
    private val channelName = "com.example.yourapp/devices"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channelName)
            .setMethodCallHandler { call, result ->
                if (call.method == "getAudioDevices") {
                    val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
                    val devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
                    val resultList = mutableListOf<Map<String, Any>>()

                    for (device in devices) {
                        // Filter out non-media outputs (earpiece, telephony, screencast submix)
                        if (device.type == AudioDeviceInfo.TYPE_TELEPHONY ||
                            device.type == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE ||
                            device.type == AudioDeviceInfo.TYPE_REMOTE_SUBMIX) {
                            continue
                        }

                        resultList.add(mapOf(
                            "id" to device.id.toString(),
                            "name" to (device.productName?.toString() ?: "Audio Device"),
                            "type" to device.type
                        ))
                    }
                    result.success(resultList)
                } else {
                    result.notImplemented()
                }
            }
    }
}

Step 2: In your Flutter code #

import 'dart:io';
import 'package:flutter/services.dart';
import 'package:miniaudio_player/miniaudio_player.dart';

const _deviceChannel = MethodChannel('com.example.yourapp/devices');

Future<List<AudioDevice>> fetchDevices() async {
  if (Platform.isAndroid) {
    final rawList = await _deviceChannel.invokeListMethod<Map>('getAudioDevices');
    final devices = <AudioDevice>[AudioDevice.auto];

    if (rawList != null) {
      for (final map in rawList) {
        devices.add(AudioDevice(
          id: map['id'].toString(),
          name: map['name'].toString(),
          isDefault: false,
          isAuto: false,
          type: map['type'] as int?,
        ));
      }
    }
    return devices;
  }

  // On desktop / iOS, use native built-in discovery:
  return MiniaudioPlayer.getAudioDevices();
}

// Switching works identically across all platforms!
void switchAudioDevice(AudioDevice device) async {
  await player.action.setDevice(device);
}

🤝 Contributing #

Contributions, bug reports, and feature requests are very welcome! Feel free to open an issue or submit a pull request on GitHub.


📄 License & Third-Party Acknowledgements #

This project is licensed under the GNU General Public License v3.0 (GPL-v3) - see the LICENSE file for complete details.

Third-Party Native Libraries & Decoders #

miniaudio_player bundles and links specialized native C libraries to provide out-of-the-box decoding and DSP without external system dependencies. Each component remains governed by its respective author's license:

Component Author / Organization License Source Location Description
miniaudio David Reid Choice of Public Domain or MIT-0 src/miniaudio.h Audio engine, OS device routing, mixing, and built-in decoders (dr_mp3, dr_wav, dr_flac).
Sonic DSP Bill Cox Apache License 2.0 src/sonic/ Real-time high-fidelity time stretching and pitch scaling.
minimp4 lieff, aspt CC0 1.0 Universal (Public Domain) src/decoders/minimp4/ Lightweight MP4 / M4A ISO container demuxer.
ALAC Decoder David Hammerton MIT License src/decoders/alac/ Apple Lossless Audio Codec (ALAC) frame decoder.
stb_vorbis Sean Barrett Public Domain / MIT src/decoders/stb_vorbis.c Ogg Vorbis audio decoding backend.
libogg Xiph.Org Foundation BSD 3-Clause src/decoders/libogg/ Ogg container bitstream and framing parsing.
libopus (Opus & SILK) Xiph.Org Foundation, Skype Limited, Arm Limited BSD 3-Clause src/decoders/libopus/ High-definition Opus audio decoding pipeline.
Helix AAC Decoder RealNetworks, Inc. RealNetworks Public Source License (RPSL v1.0) src/decoders/helix-aac/ Fixed-point HE-AAC audio decoder.

⚖️ GPL-v3 Licensing Notes & Considerations #

  • Inbound Compatibility:
    • The permissive licenses (MIT, MIT-0, Public Domain / CC0, and BSD 3-Clause) are 100% compatible with GPL-v3 and can be combined into a GPL-v3 covered work.
    • Apache 2.0 (Sonic DSP) is explicitly compatible with GPL-v3 according to the Free Software Foundation (FSF).
    • Helix AAC (RPSL): In accordance with Section 7 of the GNU GPL-v3 (Additional Permissions), this project includes a linking permission allowing the program to be compiled and linked with the Helix AAC decoder under the RPSL.
  • Copyleft (Outbound Usage in Flutter Apps):
    • Because miniaudio_player is distributed under GPL-v3, applications that bundle and distribute this package must also make their complete source code available under GPL-v3.
    • If your Flutter application is free and open-source (GPL-compatible), you can use and distribute miniaudio_player with no restrictions.
    • Proprietary or closed-source commercial applications that cannot open-source their codebase should take this copyleft requirement into consideration.
3
likes
150
points
546
downloads

Documentation

API reference

Publisher

verified publisherchomusuke.dev

Weekly Downloads

Ultra-low-resource cross-platform audio player using miniaudio. Equalizer, pitch and rate are supported.

Repository (GitHub)
View/report issues

Topics

#audio #miniaudio #sound #player

License

unknown (license)

Dependencies

code_assets, ffi, hooks, logging, native_toolchain_c

More

Packages that depend on miniaudio_player