audio_io 0.5.0 copy "audio_io: ^0.5.0" to clipboard
audio_io: ^0.5.0 copied to clipboard

Very simple interface to provide a stream of audio data for processing/visualising in Flutter

audio_io #

A Flutter plugin for cross-platform real-time audio streaming. Provides low-latency audio input/output with simple Stream-based API for audio processing, recording, and visualization.

Live demo #

Try the PCM16 streaming + Gemini Live example in your browser: wamf.github.io/audio_io — paste your own Gemini API key, allow the microphone, and talk.

Features #

  • Real-time audio streaming from microphone to Flutter
  • Audio output/playback through speakers
  • Cross-platform support (iOS, macOS, Android, Web, Linux, Windows)
  • Simple Stream-based API
  • PCM16 byte streams at 16/24/48 kHz for realtime voice APIs (e.g. Gemini Live)
  • Configurable audio latency modes
  • Optional dedicated audio isolate on FFI platforms
  • Consistent data format across all platforms (Float64, 48kHz, mono)
  • Low-latency audio processing
  • Volume level monitoring

Platform Support #

Platform Status Implementation
iOS ✅ Supported Native (AVAudioEngine)
macOS ✅ Supported Native (AVAudioEngine)
Android ✅ Supported FFI (miniaudio)
Web ✅ Supported Web Audio API
Linux ✅ Supported FFI (miniaudio)
Windows ✅ Supported FFI (miniaudio)

Getting Started #

Installation #

Add audio_io to your pubspec.yaml:

dependencies:
  audio_io: ^0.5.0

iOS Setup #

Add microphone usage description to your Info.plist:

<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to the microphone for audio processing.</string>

macOS Setup #

  1. Add microphone usage description to your macos/Runner/Info.plist:
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to the microphone for audio processing.</string>
  1. Enable audio input in your entitlements files:
    • macos/Runner/DebugProfile.entitlements
    • macos/Runner/Release.entitlements
<key>com.apple.security.device.audio-input</key>
<true/>

Android Setup #

Add microphone permission to your android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.RECORD_AUDIO" />

Request the microphone permission at runtime (for example with the permission_handler package) before calling start(). If permission has not been granted, start() throws an AudioIoException whose isPermissionDenied is true.

Usage #

import 'package:audio_io/audio_io.dart';

// Get the audio instance
final audioIo = AudioIo.instance;

// Configure latency (optional)
await audioIo.requestLatency(AudioIoLatency.Balanced);

// Start audio processing
await audioIo.start();

// Listen to input audio stream
audioIo.input.listen((audioData) {
  // Process audio data (List<double>)
  print('Received ${audioData.length} samples');
  
  // Calculate volume level (RMS)
  final sum = audioData.fold<double>(
    0.0, (sum, sample) => sum + sample * sample);
  final rms = sqrt(sum / audioData.length);
});

// Send audio to output (echo example)
audioIo.input.listen((data) {
  audioIo.output.add(data);
});

// Stop audio processing
await audioIo.stop();

Latency Configuration #

The plugin supports three latency modes:

enum AudioIoLatency {
  Realtime,  // Lowest latency (~1.5ms buffer)
  Balanced,  // Balanced latency/CPU (~3ms buffer)
  Powersave, // Lower CPU usage (~6ms buffer)
}

// Set before starting audio
await audioIo.requestLatency(AudioIoLatency.Realtime);

PCM16 byte streams (realtime voice APIs) #

Realtime voice APIs typically speak little-endian PCM16 at specific sample rates (Gemini Live expects 16 kHz in / 24 kHz out). startWith configures byte streams in the rate and format the API expects, while the engine keeps its internal 48 kHz contract — resampling and conversion are handled for you, on the audio rendering thread where the platform supports it:

final audioIo = AudioIo.instance;

await audioIo.startWith(const AudioIoConfig(
  format: AudioIoFormat.pcm16,
  sampleRate: AudioIoSampleRate.rate16000,
));

// Microphone as PCM16 bytes at 16 kHz
audioIo.inputBytes.listen(api.sendAudio);

// Play PCM16 bytes (decode + resample handled internally)
api.audioResponses.listen(audioIo.outputBytes.add);

// Barge-in: discard audio queued for playback but not yet rendered
await audioIo.clearOutput();

See example_gemini_live for a complete voice conversation app, or try it in the browser at wamf.github.io/audio_io.

Threading (optional dedicated audio isolate) #

By default the audio transport runs on the main isolate, which suits most apps and every platform. On the FFI back ends (Android, Windows, Linux) you can opt into a dedicated audio isolate so device polling and native buffer copies are unaffected by main-isolate jank (heavy widget builds, GC):

await audioIo.startWith(const AudioIoConfig(
  threading: AudioIoThreading.audioIsolate,
));

Platforms without dedicated-isolate support (iOS, macOS, web) silently fall back to main-isolate operation. The input / output streams still surface on the main isolate in both modes, so listener callbacks run there; move heavy DSP out of the listener if it competes with UI work.

Audio Format #

All platforms use a consistent audio format:

  • Sample Rate: 48kHz (may adapt to device capabilities)
  • Channels: Mono (1 channel)
  • Data Type: Double precision floats (Float64/double)
  • Stream Format: Chunks of audio samples as List<double>
  • Internal Processing: Platform-specific (Float32 on native platforms)

Requirements #

  • Dart SDK: >=3.0.0 <4.0.0
  • Flutter: >=3.10.0
  • iOS: 13.0 or higher
  • macOS: 10.15 or higher

License #

See LICENSE file for details.

4
likes
140
points
575
downloads

Documentation

API reference

Publisher

verified publisherwearemobilefirst.com

Weekly Downloads

Very simple interface to provide a stream of audio data for processing/visualising in Flutter

Homepage

License

MIT (license)

Dependencies

ffi, flutter, flutter_web_plugins, web

More

Packages that depend on audio_io

Packages that implement audio_io