Sautiflow

High-fidelity, cross-platform Dart/Flutter audio engine for audiophiles. Powered by miniaudio and native C++ FFI.

Zero Latency

Native C++ binding ensures instant playback and response times.

Gapless Playlist

Seamless transitions between tracks with advanced queue management.

Pro FX Chain

Built-in EQ suite, reverb, delay, low/high/band-pass, notch, peak EQ, and shelf filters.

Cross-Platform

Support for Windows, Linux, Android, iOS, and macOS.

Pro Pipeline State

Query true internal flow from native input formats to hardware negotiated device outputs.

Native Track Inspector

Extract exact stream sample rate, bit depth (24-bit/32-bit float), channels & bitrate directly via C++ FFI.

Audio Formats & Quality

Sautiflow is engineered with audiophile-grade fidelity in mind. It handles different audio formats natively within its C++ engine to provide the best possible acoustic reproduction:

  • Lossy Formats (MP3, AAC, OGG etc.): These formats are inherently compressed. Upon playback, Sautiflow upscales/upsamples the decoded bitstream internally (using 32-bit floating point math) ensuring that DSP operations—like EQ, reverb, and spatial width—have plenty of headroom and do not clip or introduce artificial artifacts typical of low-resolution math.
  • Lossless Formats (FLAC, WAV, ALAC, etc.): These are mapped and processed raw. Sautiflow does not dynamically upscale them to guess lost data (since nothing is lost). It maintains their pristine bit-perfect original depth into the DSP chain. If the file's sample rate doesn't match your device's native hardware rate (e.g., streaming an 88.2kHz FLAC file on a 48kHz output device), Sautiflow employs high-fidelity resampling transparently using libsamplerate to match your hardware, avoiding quality loss while retaining original bit-depth fidelity.

Changes Made So Far

Quick implementation summary of the latest work in this package:

  • Integrated libsamplerate (Secret Rabbit Code) for high-fidelity audio resampling (SRC Sinc Best Quality, Medium, Fastest, ZOH, Linear).
  • Added mixed multiband FX chain API with per-band types via EqBandType and EqBandConfig.
  • Added Audiophile Crossfeed (BS2B Weak, BS2B Strong, Joe0bloggs Realistic) algorithm mappings.
  • Added realtime analyzer frame APIs, including stream and pull-style access.
  • Added crossfade controls: setCrossfadeEnabled(bool) and setCrossfadeDurationMs(int).
  • Added real-time injectible custom filters: LPF1, HPF1, Biquad which tap directly into the audio engine node graph.
  • Added standalone object-oriented filter APIs (LPF1, LPF2, Biquad, Resampler, etc) that work independently from the main player.
  • Added s24 and s32 audio formats, with dithering options.
  • Updated sample-rate behavior so setOutputSampleRate(0) uses native device rate mode.
  • Updated example app to test: mixed EQ mode, analyzer visualization, output format/rate/channels, and crossfade controls.
  • Updated docs with implementation snippets for mixed multiband FX, realtime analyzer, crossfade, and output sample-rate native mode.
  • New: Integrated the legendary ViPER DSP Engine with 25+ real-time algorithms (Headphone Surround, Tube Simulator, Psychoacoustic Bass, Multiband Dynamics).
  • Fixes: Fixed bugs and fixed crossfade in the native sautiflow audio engine.
  • Sautiplay: Added ViPER FX effects, UI improvements, performance boosts, and bug fixes to the Sautiplay app.

Installation

Add sautiflow to your pubspec.yaml:

flutter pub add sautiflow

Or manually:

dependencies:
    sautiflow: ^0.2.0
Note: On Android, network streaming support depends on the build configuration. See Platform Specifics.

Quick Start

Initialize the player, set a playlist, and start playing.

import 'package:sautiflow/sautiflow.dart';

void main() async {
  final player = MiniAudioPlayer();
  
  // 1. Initialize the engine
  player.init();

  // 2. Define playlist
  final playlist = [
    AudioSource.network('https://example.com/track1.mp3'),
    AudioSource.file('/path/to/local/track2.mp3'),
  ];

  // 3. Load and play
  player.setAudioSources(playlist);
  player.play();

  // 4. Control playback
  await Future.delayed(Duration(seconds: 5));
  player.seekToNext();
}

Initialization

The MiniAudioPlayer is the main entry point.

init

bool init({int sampleRate, int channels, bool enableSystemAudio})

Initializes the underlying C++ audio engine. Must be called before any other operation.

Parameter Type Description
sampleRate int Output sample rate (default: 48000)
channels int Output channels (default: 2)
enableSystemAudio bool Integrate with OS media controls (Android/iOS)

dispose

void dispose()

Releases all native resources and stops the audio engine. Always call this when the player is no longer needed.

Playback Controls

play / pause / stop

bool play() / bool pause() / bool stop()

Basic transport controls. Returns true if the operation was successful.

seek

bool seek(Duration position, {int? index})

Seeks to a specific position. If index is provided, it jumps to that track in the playlist first.

seekToNext / seekToPrevious

bool seekToNext() / bool seekToPrevious()

Skips to the next or previous track in the playlist.

setVolume / setGain

void setVolume(double volume)

Sets the master volume gain. 1.0 is unity gain.

Playlist Management

setAudioSources

bool setAudioSources(List<AudioSource> sources, ...)

Replaces the current playlist with a new list of sources.

player.setAudioSources([
  AudioSource.network('https://stream.url/audio.mp3'),
  AudioSource.file('assets/music.mp3')
]);

Playlist Mutation

  • addAudioSource(AudioSource source): Append a track.
  • insertAudioSource(int index, AudioSource source): Insert at position.
  • removeAudioSourceAt(int index): Remove track.
  • moveAudioSource(int from, int to): Reorder playback.
  • clearAudioSources(): Clear playlist.

Modes

  • setLoopMode(LoopMode mode): off, all, one.
  • setShuffleModeEnabled(bool enabled): Toggles shuffle.
  • reshuffle(): Re-randomizes the shuffle order.

Player Status

You can listen to the player status stream for real-time UI updates.

StreamBuilder<PlayerStatus>(
  stream: player.statusStream,
  builder: (context, snapshot) {
    final status = snapshot.data;
    if (status == null) return CircularProgressIndicator();
    
    return Column(
      children: [
        Text("Time: ${status.positionSeconds} / ${status.durationSeconds}"),
        LinearProgressIndicator(
          value: status.positionSeconds / status.durationSeconds
        ),
      ],
    );
  },
);

Native C++ Track Inspector

Sautiflow includes a high-speed native stream inspector (ae_inspect_file) exported directly via C++ FFI. It parses raw binary container headers (including FLAC STREAMINFO blocks and WAV fmt chunks) and queries the miniaudio decoder engine without needing to decode full PCM frames.

inspectFile

TrackNativeInfo? inspectFile(String filePath)

Inspects a local audio file and returns exact stream specifications instantly.

import 'package:sautiflow/sautiflow.dart';

void inspectTrack(MiniAudioPlayer player, String filePath) {
  final info = player.inspectFile(filePath);

  if (info != null) {
    print('Sample Rate: ${info.sampleRate} Hz');    // e.g. 96000 Hz, 192000 Hz
    print('Bit Depth:   ${info.bitDepth} bits');    // e.g. 24-bit, 32-bit
    print('Float PCM:   ${info.isFloat}');         // true for 32-bit Float WAV
    print('Channels:    ${info.channels}');        // 2 (Stereo) or 1 (Mono)
    print('Bitrate:     ${info.bitrateKbps} kbps');// e.g. 1411 kbps, 2304 kbps
    print('Codec:       ${info.formatName}');     // "FLAC", "WAV", "MP3", "AAC"
    print('Duration:    ${info.durationSecs}s');  // Track duration in seconds
    print('File Size:   ${info.fileSizeBytes} B'); // Exact size on disk
  }
}
Field Type Description
sampleRate int True native source sample rate (e.g. 44100, 48000, 88200, 96000, 192000 Hz)
bitDepth int Native PCM sample bit resolution (8, 16, 24, 32)
isFloat bool true if audio data is 32-bit IEEE floating point PCM
channels int Channel count (1 = Mono, 2 = Stereo, 6 = 5.1 Surround)
bitrateKbps int Calculated average bitrate in kilobits per second (kbps)
formatName String Container format or codec string (e.g. "FLAC", "WAV", "MP3")

Dynamic Bass (ViperFX)

Sautiflow integrates a powerful Dynamic Bass algorithm (based on ViperFX) that combines Mid/Side poles, lowpass filtering, and side-gain adjustments specifically tailored for various audio equipment.

Applying Dynamic Bass

Configure the effect by selecting a preset and bass gain amount (0.0 to 100.0).

player.setDynamicBass(
  enabled: true,
  preset: 18,     // Presets range from 0 to 18 (e.g. Extreme Headphone, Common Earphone)
  gain: 100.0,    // Sub-bass gain boost amount in percentage %
);

Available Presets:

  • 0: Extreme Headphone (v2)
  • 1: High-end Headphone (v2)
  • 2: Common Headphone (v2)
  • 3: Low-end Headphone (v2)
  • ... up to 18 presets tailored for various outputs.

Equalizer (EQ)

Sautiflow includes a 3-band parametric EQ and a multiband equalizer.

3-Band EQ

player.setEqEnabled(true);
player.setEq(low: 1.2, mid: 0.8, high: 1.1);

Multiband EQ

Initialize with custom frequencies.

player.initMultibandEq([32, 64, 125, 250, 500, 1000, 2000, 4000, 8000, 16000]);
player.setMultibandEqEnabled(true);
player.setMultibandEqBandGain(0, 2.0); // Boost 32Hz by 2dB

Mixed Multiband FX (Peak + Shelf + Bandpass + Notch)

Build a single, ordered chain where each band can use a different filter type.

final bands = <EqBandConfig>[
  EqBandConfig(type: EqBandType.lowshelf, frequencyHz: 120, gainDb: 4.0, slope: 1.0),
  EqBandConfig(type: EqBandType.peak, frequencyHz: 1000, gainDb: 2.0, q: 1.2),
  EqBandConfig(type: EqBandType.notch, frequencyHz: 60, q: 10.0),
  EqBandConfig(type: EqBandType.bandpass, frequencyHz: 2500, q: 0.9),
  EqBandConfig(type: EqBandType.highshelf, frequencyHz: 9000, gainDb: 2.5, slope: 1.0),
];

// Initialize and enable the mixed chain.
player.initMultibandFx(bands, enabled: true);

// Update all bands later (for dynamic user-added bands in UI).
player.setMultibandFxBands(bands);

// Toggle or clear.
player.setMultibandFxEnabled(false);
player.clearMultibandFx();

Realtime Analyzer Frames + fl_chart

Expose frame values as a stream and plug into any pub chart library (example below uses fl_chart).

// pubspec.yaml (app)
// fl_chart: ^0.69.0

player.configureAnalyzer(frameSize: 1024);
player.setAnalyzerEnabled(true);

StreamSubscription? sub;
List<double> bins = List.filled(96, 0.0);

sub = player.analyzerStream.listen((frame) {
    // Convert raw frame samples to chart bins.
    const targetBins = 96;
    final out = List<double>.filled(targetBins, 0.0);
    for (var i = 0; i < targetBins; i++) {
        final from = (i * frame.length / targetBins).floor();
        final to = ((i + 1) * frame.length / targetBins).ceil();
        var sum = 0.0;
        var count = 0;
        for (var j = from; j < to && j < frame.length; j++) {
            sum += frame[j].abs();
            count++;
        }
        out[i] = count > 0 ? sum / count : 0.0;
    }
    bins = out;
    // setState(() {});
});

// Pull API also exists:
final latest = player.getLatestAnalyzerFrame();

Reverb

Add spatial depth to your audio.

player.setReverbEnabled(true);
player.setReverb(
  mix: 0.3,       // Wet/Dry mix (0.0 - 1.0)
  feedback: 0.5,  // Decay tail
  delayMs: 50.0   // Pre-delay
);

Delay (Echo)

player.setDelay(
  enabled: true,
  mix: 0.4,
  feedback: 0.6,
  delayMs: 250.0 // Quarter second echo
);

Stereo Stage Widening

Enhance the spatial image of your audio without losing punch or mono-compatibility. Uses a hybrid Mid/Side (M/S) Matrix and Haas effect delay strategy.

player.setStereoWiden(
  enabled: true,
  width: 2.0,      // > 1.0 expands the side channels (wide)
  delayMs: 15.0    // ~15-30ms creates psychoacoustic width via the Haas effect
);

Filters & Advanced EQ

Sautiflow provides a suite of professional filters for sound design and mixing.

// High-pass (Remove rumble)
player.setHighpass(enabled: true, cutoffHz: 120.0);

// Low-pass (Lo-fi effect)
player.setLowpass(enabled: true, cutoffHz: 3000.0);

// Band-pass (Telephone effect / Focus)
player.setBandpass(
  enabled: true,
  cutoffHz: 1000.0,
  q: 1.0 // Q-factor (width)
);

// Notch Filter (Remove specific frequency)
player.setNotch(
  enabled: true,
  frequencyHz: 60.0, // Remove 60Hz hum
  q: 10.0
);

// Peak EQ (Parametric Boost/Cut)
player.setPeakEq(
  enabled: true,
  frequencyHz: 2500.0,
  gainDb: 3.0,
  q: 1.0
);

// Low Shelf (Bass Boost)
player.setLowshelf(
  enabled: true,
  frequencyHz: 100.0,
  gainDb: 4.0,
  slope: 1.0
);

// High Shelf (Treble Boost)
player.setHighshelf(
  enabled: true,
  frequencyHz: 10000.0,
  gainDb: 2.0,
  slope: 1.0
);

// Custom Real-Time Filter Injection 
player.setCustomLpf1(enabled: true, cutoffHz: 500.0);
player.setCustomHpf1(enabled: true, cutoffHz: 120.0);
player.setCustomBiquad(
  enabled: true,
  b0: 1.0, b1: 0.0, b2: 0.0, // Feedforward coefficients
  a0: 1.0, a1: 0.0, a2: 0.0, // Feedback coefficients
);

Standalone Filters & Resampling

Advanced audio manipulation independent of the main player instance.

Filter Classes (LPF1, HPF2, Biquad, etc)

Apply standalone filters to arbitrary audio buffers, bypassing the internal player completely.

final ffi = MiniaudioFiltersFFI();
// Initialize a 1-pole Low-pass filter
final lpf = MiniaudioLpf1(ffi, AudioFormat.f32, 2, 48000, 1000.0);

// Process custom frames from pointer
// bool success = lpf.process(outFrames, inFrames, 1024);

// Always dispose the native pointers when done
lpf.dispose();

Resampler

Sample rate conversion with controllable algorithms and dithering.

final ffi = MiniaudioFiltersFFI();
final resampler = MiniaudioResampler(
  ffi,
  AudioFormat.f32,  // including new formats: s24, s32
  2,                // channels
  44100,            // input rate
  48000,            // output rate
  algorithm: ResampleAlgorithm.linear,
  ditherMode: DitherMode.none,
);

// process(inFrames, inCount, outFrames, outCount)
// resampler.setRate(48000, 96000);

resampler.dispose();

Crossfade

Enable transition fades between tracks and control duration in milliseconds.

// Enable track transition fade.
player.setCrossfadeEnabled(true);

// Duration is clamped in native layer to 0..10000 ms.
player.setCrossfadeDurationMs(300);

// Disable if you want hard cuts.
player.setCrossfadeEnabled(false);

Output Format

Configure the audio engine output format.


// Set to Floating Point 32-bit (highest quality)
player.setOutputFormat(AudioFormat.f32);

// Set Sample Rate to 44.1kHz
player.setOutputSampleRate(44100);

// Use native device sample rate mode
player.setOutputSampleRate(0);

// Set to Mono
player.setOutputChannels(1);

// Configure Global Native Engine Resampler
// 0 = Linear, 1 = Custom
player.setEngineResampleAlgorithm(0);

// Configure Output Dithering Mode (e.g for 16-bit PCM output)
// 0 = None, 1 = Rectangle, 2 = Triangle
player.setEngineDitherMode(2);
          

Stream Pushing

For custom streaming implementations or raw data handling.

pushStream

Future<void> pushStream({required String url})

Manually fetches chunks from a URL and pushes them to the engine. Useful for environments where native networking is restricted.

Android Full Setup

For a production-ready app with background playback, lockscreen controls, and notifications, follow this checklist.

1. Dependencies

Ensure your pubspec.yaml includes:

dependencies:
    sautiflow: ^0.2.0
  audio_session: ^0.1.21
  audio_service: ^0.18.18

2. AndroidManifest.xml

Add the following permissions and components to android/app/src/main/AndroidManifest.xml.

Critical: You must replace the default FlutterActivity with AudioServiceActivity or Ensure your MainActivity extends it.
<manifest ...>
    <!-- Required Permissions -->
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
    <!-- Android 13+ Notifications -->
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

    <application ...>
        
        <!-- Main Activity (Must use AudioServiceActivity) -->
        <activity
            android:name="com.ryanheise.audioservice.AudioServiceActivity"
            android:exported="true"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <!-- Audio Service -->
        <service
            android:name="com.ryanheise.audioservice.AudioService"
            android:foregroundServiceType="mediaPlayback"
            android:exported="true">
            <intent-filter>
                <action android:name="android.media.browse.MediaBrowserService" />
            </intent-filter>
        </service>

        <!-- Media Button Receiver -->
        <receiver
            android:name="com.ryanheise.audioservice.MediaButtonReceiver"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </receiver>

    </application>
</manifest>

3. Dart Initialization

Initialize the audio session and system integration in your main() function.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 1. Configure Audio Session
  final session = await AudioSession.instance;
  await session.configure(const AudioSessionConfiguration.music());

  // 2. Initialize Player with System Audio
  final player = MiniAudioPlayer();
  await player.init(enableSystemAudio: true);

  runApp(const MyApp());
}

4. Update Metadata & State

For the notification to show correct info, update the now playing info whenever the track changes.

await player.updateNowPlaying(
  title: "Song Title",
  artist: "Artist Name",
  album: "Album Name",
  duration: Duration(minutes: 3, seconds: 45),
  // id, artwork can also be added if supported
);

Platform Specifics

Android Network Streaming

Native properties can be overridden in your `android/gradle.properties`:

MINIAUDIODART_ENABLE_CURL=ON
MINIAUDIODART_CURL_INCLUDE_DIR=/path/to/curl/include
MINIAUDIODART_CURL_LIBRARY=/path/to/libcurl.so

iOS / macOS

Linked statically. Ensure `ffiPlugin` is enabled in your `pubspec.yaml`.

ViPER DSP Engine

A completely typed, high-performance, FFI-bridged wrapper mapping advanced C++ audio algorithms directly into Dart. ViPER is fully integrated with Sautiflow's internal playback pipeline.

Audiophile Profile Example

void setupAudiophileProfile(MiniAudioPlayer player) {
  // 1. Wake up the beast
  player.viper.setEnabled(true);
  player.viper.setSamplingRate(player.getOutputSampleRate());

  // 2. Dynamic Tube Saturation (Warmth)
  player.viper.setTubeSimulator(true);
  player.viper.setAnalogX(enable: true, mode: ViperAnalogXMode.moderate);

  // 3. Bass Reconstruction
  player.viper.setBass(
    enable: true,
    mode: ViperBassMode.pure,
    frequencyHz: 60,
    gain: 0.45,
  );

  // 4. High-frequency Air / Clarity
  player.viper.setClarity(
    enable: true,
    mode: ViperClarityMode.natural,
    gain: 0.2,
  );

  // 5. Protect the master out with LUFS normalization
  player.viper.setLufs(
    enable: true, 
    target: -14.0, 
    maxGainDb: 6.0,
  );
}

Immersive Surround Example

void setupImmersiveSurround(MiniAudioPlayer player) {
  player.viper.setEnabled(true);
  
  // 1. Headphone Surround (ViPER Headphone Engine)
  // Re-creates the soundstage of speakers while wearing headphones, eliminating listening fatigue.
  player.viper.setHeadphoneSurround(
    enable: true,
    quality: 1, // 0 = low, 1 = moderate, 2 = extreme
  );

  // 2. Field Surround
  // Widens the 3D soundfield. Great for movies or expansive atmospheric music.
  player.viper.setFieldSurround(
    enable: true,
    widening: 0.8,
    midImage: 0.5,
    depth: 1,
  );

  // 3. Differential Surround (Haas Effect)
  // Delays one channel slightly to create a psychoacoustic widening effect.
  player.viper.setDiffSurround(
    enable: true,
    delay: 5.5, // 5.5ms delay
    reverse: false,
    wetDryMix: 0.5, // 50% wet
    lpCutoffHz: 12000,
  );
}

Available API Features

All ViPER DSP features are accessible through the player.viper getter. Here is the full list of supported algorithms:

Core & Limits

  • setEnabled(bool) / setSamplingRate(int) - Engine state
  • resetAll() - Flush buffers and parameter states
  • setMasterLimiter(threshold, outputVolume, channelPan) - Absolute output protection
  • setPlaybackGain(enable, strength, maxGain, outputThreshold) - Auto-volume leveling
  • setLufs(enable, target, maxGainDb, speed) - Broadcast standard loudness targeting

Spatial & Surround

  • setHeadphoneSurround(enable, quality) - Eliminates "in-head" localization fatigue
  • setDiffSurround(enable, delay, reverse, wetDryMix, lpCutoffHz) - Psychoacoustic surround width
  • setFieldSurround(enable, widening, midImage, depth) - 3D Soundfield expansion
  • setStereoImager(enable, lowWidth, midWidth, highWidth, crossovers...) - Multiband stereo widening
  • setConvolver(enable, crossChannel) - Impulse Response convolution
    player.viper.loadConvolverKernel(float32Samples, channels, kernelId: 1);
  • setReverb(enable, roomSize, width, damp, wet, dry) - Natural room acoustics

Dynamics & Compression

  • setFetCompressor(...) - Studio-grade FET (Field Effect Transistor) emulation with knee, crest, and auto-gain
  • setMultibandCompressor(enable, bands, crossovers) - Surgical compression using up to 5 discrete frequency bands (List of ViperMultibandCompressorBand)
  • setDynamicEq(enable, bands) - EQ that reacts to audio levels in real-time (List of ViperDynamicEqBand)

Bass & Clarity

  • setBass(enable, mode, frequencyHz, gain, antiPop) - Pure/Natural/Sub-woofer harmonic generation
  • setBassMono(...) - Centers bass frequencies to prevent phase issues on speakers
  • setPsychoacousticBass(enable, cutoffHz, intensity, harmonicOrder...) - Tricks the brain into hearing sub-bass on small phone speakers
  • setClarity(enable, mode, gain) - Natural/Ozone/XHiFi vocal and treble extraction
  • setSpectrumExtension(enable, strength, exciter) - Rebuilds high frequencies lost in MP3 compression

Analog & Emulation

  • setTubeSimulator(enable) - Odd-harmonic warmth modeling 6N1J tubes
  • setAnalogX(enable, mode) - Console coloration (slight, moderate, extreme)
  • setCure(enable, crossfeedPreset) - Advanced Crossfeed system for headphones
  • setDynamicSystem(enable, xCoeff, yCoeff, sideGain, strength) - High-end car audio / Hi-Fi equipment emulation
  • setDdc(enable) / loadDdcCoefficients(...) - Digital Device Correction for calibrating specific headphones