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
libsamplerateto 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
EqBandTypeandEqBandConfig. - 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)andsetCrossfadeDurationMs(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
s24ands32audio 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
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.
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 stateresetAll()- Flush buffers and parameter statessetMasterLimiter(threshold, outputVolume, channelPan)- Absolute output protectionsetPlaybackGain(enable, strength, maxGain, outputThreshold)- Auto-volume levelingsetLufs(enable, target, maxGainDb, speed)- Broadcast standard loudness targeting
Spatial & Surround
setHeadphoneSurround(enable, quality)- Eliminates "in-head" localization fatiguesetDiffSurround(enable, delay, reverse, wetDryMix, lpCutoffHz)- Psychoacoustic surround widthsetFieldSurround(enable, widening, midImage, depth)- 3D Soundfield expansionsetStereoImager(enable, lowWidth, midWidth, highWidth, crossovers...)- Multiband stereo wideningsetConvolver(enable, crossChannel)- Impulse Response convolutionplayer.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-gainsetMultibandCompressor(enable, bands, crossovers)- Surgical compression using up to 5 discrete frequency bands (List ofViperMultibandCompressorBand)setDynamicEq(enable, bands)- EQ that reacts to audio levels in real-time (List ofViperDynamicEqBand)
Bass & Clarity
setBass(enable, mode, frequencyHz, gain, antiPop)- Pure/Natural/Sub-woofer harmonic generationsetBassMono(...)- Centers bass frequencies to prevent phase issues on speakerssetPsychoacousticBass(enable, cutoffHz, intensity, harmonicOrder...)- Tricks the brain into hearing sub-bass on small phone speakerssetClarity(enable, mode, gain)- Natural/Ozone/XHiFi vocal and treble extractionsetSpectrumExtension(enable, strength, exciter)- Rebuilds high frequencies lost in MP3 compression
Analog & Emulation
setTubeSimulator(enable)- Odd-harmonic warmth modeling 6N1J tubessetAnalogX(enable, mode)- Console coloration (slight, moderate, extreme)setCure(enable, crossfeedPreset)- Advanced Crossfeed system for headphonessetDynamicSystem(enable, xCoeff, yCoeff, sideGain, strength)- High-end car audio / Hi-Fi equipment emulationsetDdc(enable)/loadDdcCoefficients(...)- Digital Device Correction for calibrating specific headphones