A comprehensive Flutter package for generating and displaying audio waveforms with isolate-based processing to prevent UI thread blocking. Sonix supports multiple audio formats (MP3, OGG, WAV, FLAC, Opus) using native C libraries through Dart FFI for optimal performance.
π Isolate-Based Processing: All audio processing runs in background isolates to keep UI responsive
β¨ Multi-format Support: MP3, OGG, WAV, FLAC, and Opus audio formats
π High Performance: Native C libraries via Dart FFI with FFmpeg-based decoding
π¨ Extensive Customization: Colors, gradients, styles, and animations
ποΈ Interactive Playback: Real-time position visualization and seeking
π¦ Instance-Based API: Modern API with proper resource management
β
Easy Integration: Simple API with comprehensive error handling
π Multiple Algorithms: RMS, Peak, Average, and Median downsampling
π― Optimized Presets: Ready-to-use configurations for different use cases
Add Sonix to your pubspec.yaml:
dependencies:
sonix: <latest>Then run:
flutter pub getNote: Sonix includes the plugin glue code and native wrapper sources. Desktop platforms require FFmpeg installed on the system. The Flutter toolchain builds and bundles the plugin artifacts automatically; you should not need to compile C code manually for typical usage.
Sonix uses FFmpeg for audio decoding. For desktop platforms, you must install FFmpeg on the system. Mobile platforms do not require a separate FFmpeg install.
Recommended installation methods:
- macOS: Homebrew
- Install:
brew install ffmpeg - Verify:
ffmpeg -version
- Install:
- Linux: Your distroβs package manager
- Debian/Ubuntu:
sudo apt install ffmpeg - Fedora:
sudo dnf install ffmpeg - Arch:
sudo pacman -S ffmpeg
- Debian/Ubuntu:
- Windows: Install FFmpeg and ensure the DLLs are on PATH or co-located next to your appβs executable
- Options: winget, chocolatey, or manual install from ffmpeg.org (DLLs must be discoverable at runtime)
- Verify:
ffmpeg -version
- β Android (API 21+)
- β iOS (11.0+)
- β Windows (Windows 10+) - requires FFmpeg DLLs
- β macOS (10.15+) - requires FFmpeg dylibs
- β Linux (Ubuntu 18.04+) - requires FFmpeg shared objects
Desktop apps can also load FFmpeg when the libraries are placed alongside the app at runtime:
- Windows: place FFmpeg DLLs next to the appβs exe (or ensure they are on PATH)
- Linux: place
.sofiles under the appβslibdirectory - macOS: prefer system-installed FFmpeg; bundling FFmpeg into an app may have licensing implications
Required FFmpeg libraries: avformat, avcodec, avutil, swresample
import 'package:sonix/sonix.dart';
// Create a Sonix instance
final sonix = Sonix();
// Generate waveform in background isolate (recommended for UI apps)
final waveformData = await sonix.generateWaveformInIsolate('path/to/audio.mp3');
// Or generate on main thread (simpler, but blocks the thread)
// final waveformData = await sonix.generateWaveform('path/to/audio.mp3');
// Display the waveform
WaveformWidget(
waveformData: waveformData,
style: WaveformStylePresets.soundCloud,
)
// Clean up when done
sonix.dispose();class AudioPlayer extends StatefulWidget {
@override
_AudioPlayerState createState() => _AudioPlayerState();
}
class _AudioPlayerState extends State<AudioPlayer> {
late Sonix sonix;
WaveformData? waveformData;
double playbackPosition = 0.0;
@override
void initState() {
super.initState();
sonix = Sonix(SonixConfig.mobile()); // Optimized for mobile
}
@override
void dispose() {
sonix.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
if (waveformData != null)
WaveformWidget(
waveformData: waveformData!,
playbackPosition: playbackPosition,
style: WaveformStylePresets.spotify,
onSeek: (position) {
setState(() {
playbackPosition = position;
});
// Update your audio player position
},
),
],
);
}
}// SoundCloud-style waveform
WaveformWidget(
waveformData: waveformData,
style: WaveformStylePresets.soundCloud,
)
// Spotify-style waveform
WaveformWidget(
waveformData: waveformData,
style: WaveformStylePresets.spotify,
)
// Custom style with gradient
WaveformWidget(
waveformData: waveformData,
style: WaveformStylePresets.filledGradient(
startColor: Colors.blue,
endColor: Colors.purple,
height: 100,
),
)
// Professional audio editor style
WaveformWidget(
waveformData: waveformData,
style: WaveformStylePresets.professional,
)// Mobile-optimized configuration (lower memory limit)
final mobileSonix = Sonix(SonixConfig.mobile());
// Desktop-optimized configuration (higher memory limit)
final desktopSonix = Sonix(SonixConfig.desktop());
// Custom configuration
final customSonix = Sonix(SonixConfig(
maxMemoryUsage: 50 * 1024 * 1024, // 50MB
logLevel: 2, // ERROR level
));// Create a config tuned for your UI
const musicConfig = WaveformConfig(
resolution: 2000,
algorithm: DownsamplingAlgorithm.rms,
normalize: true,
scalingCurve: ScalingCurve.logarithmic,
enableSmoothing: true,
smoothingWindowSize: 5,
);
const podcastConfig = WaveformConfig(
resolution: 1500,
algorithm: DownsamplingAlgorithm.rms,
normalize: true,
scalingCurve: ScalingCurve.linear,
enableSmoothing: false,
);
const editorConfig = WaveformConfig(
resolution: 5000, // High detail for editing
algorithm: DownsamplingAlgorithm.peak,
normalize: true,
scalingCurve: ScalingCurve.linear,
enableSmoothing: false,
);
// Use with instance
final sonix = Sonix();
final waveformData = await sonix.generateWaveform(
'music.mp3',
config: musicConfig,
);If you prefer a quick preset helper:
final musicConfig = Sonix.getOptimalConfig(
useCase: WaveformUseCase.musicVisualization,
customResolution: 2000,
);final ffmpegOk = Sonix.isFFmpegAvailable();
if (!ffmpegOk) {
// Show instructions to install/ship FFmpeg for the current platform.
}// Use pre-computed waveform data
final jsonData = await loadWaveformFromCache();
final waveformData = WaveformData.fromJson(jsonData);
// Or from amplitude array
final amplitudes = [0.1, 0.5, 0.8, 0.3, 0.7, ...];
final waveformData = WaveformData.fromAmplitudes(amplitudes);
WaveformWidget(
waveformData: waveformData,
style: WaveformStylePresets.professional,
)final sonix = Sonix();
try {
final waveformData = await sonix.generateWaveform('audio.mp3');
// Use waveformData
} on UnsupportedFormatException catch (e) {
print('Unsupported format: ${e.format}');
print('Supported formats: ${Sonix.getSupportedFormats()}');
} on DecodingException catch (e) {
print('Decoding failed: ${e.message}');
} on FileSystemException catch (e) {
print('File access error: ${e.message}');
} finally {
sonix.dispose();
}The main entry point for generating waveforms. This is an instance-based class that manages background isolates for processing.
Constructor:
Sonix([SonixConfig? config])- Create a new instance with optional configuration
Instance Methods:
generateWaveform(String filePath, {...})βFuture<WaveformData>- Process on main threadgenerateWaveformInIsolate(String filePath, {...})βFuture<WaveformData>- Process in background isolate (recommended for UI apps)dispose()βvoid- Clean up resources
Static Utility Methods:
isFormatSupported(String filePath)βboolgetSupportedFormats()βList<String>getSupportedExtensions()βList<String>isExtensionSupported(String extension)βboolgetOptimalConfig({required WaveformUseCase useCase, ...})βWaveformConfig
Configuration options for Sonix instances.
Factory Constructors:
SonixConfig.defaultConfig()- Default configurationSonixConfig.mobile()- Optimized for mobile devicesSonixConfig.desktop()- Optimized for desktop devices
Properties:
maxMemoryUsage: Maximum memory usage in byteslogLevel: FFmpeg log level (0-6, default 2 for ERROR)
Interactive waveform display with playback position and seeking.
Properties:
waveformData(required): The waveform data to displayplaybackPosition: Current playback position (0.0 to 1.0)style: Customization options (WaveformStyle)onTap: Callback when user taps the widgetonSeek: Callback when user seeks to a positionenableSeek: Whether to enable touch interactionanimationDuration: Duration for position animationsanimationCurve: Animation curve for transitions
Contains processed waveform data and metadata.
Properties:
amplitudes: List of amplitude values (0.0 to 1.0)duration: Duration of the original audiosampleRate: Sample rate of the original audiometadata: Generation metadata
Methods:
toJson()βMap<String, dynamic>fromJson(Map<String, dynamic>)βWaveformDatafromAmplitudes(List<double>)βWaveformData
Customization options for waveform appearance.
Properties:
playedColor: Color for played portionunplayedColor: Color for unplayed portionheight: Height of the waveformtype: Visualization type (bars, line, filled)gradient: Optional gradient overlayborderRadius: Border radius for rounded corners
Pre-configured styles for common use cases:
soundCloud: SoundCloud-inspired orange and grey barsspotify: Spotify-inspired green and grey barsminimalLine: Minimal line-style waveformretro: Vintage style with rounded bars and warm colorscompact: Compact mobile-friendly stylepodcast: Optimized for podcast/speech contentprofessional: Clean style for professional audio applicationsfilledGradient({Color startColor, Color endColor, double height}): Filled waveform with customizable gradientglassEffect({Color accentColor, double height}): Modern glass-like effectneonGlow({Color glowColor, double height}): Glowing neon effect with shadows
Sonix includes performance monitoring tools for production applications.
Profile operations to identify bottlenecks:
final profiler = PerformanceProfiler();
// Profile waveform generation
final result = await profiler.profile('waveform_generation', () async {
final sonix = Sonix();
final waveformData = await sonix.generateWaveform('audio.mp3');
sonix.dispose();
return waveformData;
});
print('Processing took: ${result.duration.inMilliseconds}ms');
print('Memory used: ${result.memoryUsage}MB');
// Generate performance report
final report = profiler.generateReport();
print(report.toString());Validate platform compatibility:
final validator = PlatformValidator();
// Validate current platform
final validation = await validator.validatePlatform();
print('Platform supported: ${validation.isSupported}');
// Check specific format support
final mp3Support = await validator.validateFormatSupport('mp3');
print('MP3 supported: ${mp3Support.isSupported}');
// Get optimization recommendations
final recommendations = validator.getOptimizationRecommendations();
for (final rec in recommendations) {
print('${rec.category}: ${rec.title}');
}- Use appropriate configuration: Choose
SonixConfig.mobile()orSonixConfig.desktop()based on your target platform - Dispose instances: Always call
dispose()on Sonix instances when done - Handle errors: Wrap operations in try-catch blocks for proper error handling
- Use a config: Pass a
WaveformConfigtuned for your UI - Profile performance: Use
PerformanceProfilerto identify bottlenecks in production - Validate formats: Check
Sonix.isFormatSupported()before processing files - Use isolate for UI apps: Prefer
generateWaveformInIsolate()in Flutter apps to keep UI responsive
- Minimum API level 21 (Android 5.0)
- FFMPEG binaries are automatically included in APK during build
- Isolate processing works seamlessly on all Android versions
- Minimum iOS version 11.0
- FFMPEG binaries are statically linked during build
- Background isolates work within iOS app lifecycle constraints
- macOS/Linux: Use system FFmpeg installed via your package manager (no bundling)
- Windows: Provide FFmpeg DLLs via PATH or next to the app executable
- Background isolate processing for UI responsiveness
- Runtime loading of FFmpeg libraries
If you encounter issues with FFmpeg:
# Check installation
ffmpeg -version
# On macOS (Homebrew): reinstall
brew reinstall ffmpeg
# On Linux: use your distro package manager to reinstall
# e.g., Ubuntu/Debian
sudo apt --reinstall install ffmpegCommon issues:
- "FFmpeg libraries not found": Install FFmpeg using your system package manager
- "Unsupported platform": Check supported platforms list above
Sonix supports multiple audio formats through FFMPEG integration:
| Format | Extension | Decoder Backend | Notes |
|---|---|---|---|
| MP3 | .mp3 | FFMPEG | Most common audio format |
| WAV | .wav | FFMPEG | Uncompressed audio |
| FLAC | .flac | FFMPEG | Lossless compression |
| OGG Vorbis | .ogg | FFMPEG | Open source format |
| Opus | .opus | FFMPEG | Modern codec |
| MP4/AAC | .mp4, .m4a | FFMPEG | Container with AAC |
Note: All audio decoding is handled by FFmpeg libraries. Ensure FFmpeg is installed on the system for desktop platforms.
We welcome contributions! Please see our Contributing Guide for details.
- Clone the repository
- Run
flutter pub get - Install system FFmpeg (desktop dev machines)
- macOS:
brew install ffmpeg - Linux:
sudo apt install ffmpeg(or your distro equivalent) - Windows: install FFmpeg and ensure DLLs are available on PATH
- Build native library for testing:
dart run tool/build_native_for_development.dart - Run tests:
flutter test - Run example:
cd example && flutter run
Note for Contributors:
- Use
dart run tool/build_native_for_development.dartfor quick development builds - Use
dart run tool/build_native_for_distribution.dartfor release builds - Desktop users must install FFmpeg via their system package manager
This project is licensed under the MIT License - see the LICENSE file for details.
- π Documentation
- π Issue Tracker
- π¬ Discussions
See CHANGELOG.md for a detailed list of changes.