stk_min
A minimalist, cross-platform Flutter wrapper for the Synthesis ToolKit (STK) library. This plugin provides direct FFI access to STK's physical modeling synthesis algorithms, enabling high-quality musical instrument synthesis on all Flutter platforms.
Features
- 🎵 Cross-Platform: Works on Android, iOS, Linux, macOS, and Windows
- ⚡ High Performance: Direct FFI bindings to native C++ STK library
- 🎹 Physical Modeling: Realistic instrument synthesis using physical models
- 🎛️ Expressive Control: Full access to instrument parameters (vibrato, breath noise, tone color, etc.)
- 🔧 Minimalist Design: Simple, focused API for instrument synthesis
- 🎼 Extensible: Foundation for supporting multiple STK instruments
Currently Supported Instruments
- Flute: Physical model of a flute with breath control, vibrato, and tonal adjustments
Installation
Add this to your package's pubspec.yaml file:
dependencies:
stk_min: ^0.1.0
Then run:
flutter pub get
Usage
Basic Example
import 'package:stk_min/stk_min.dart';
// Create a flute instance
final flute = Flute();
// Initialize with a frequency (A4 = 440 Hz)
flute.init(440.0);
// Trigger a note with frequency and amplitude
flute.noteOn(440.0, 0.8);
// Generate audio samples (44100 samples = 1 second at 44.1kHz)
final samples = flute.render(44100);
// Use the samples with your audio playback library
// (e.g., flutter_soloud, just_audio, audioplayers, etc.)
Advanced Control
final flute = Flute();
// Initialize
flute.init(523.25); // C5
// Add expressive controls
flute.controlChange(1, 30.0); // Vibrato depth
flute.controlChange(11, 60.0); // Vibrato speed
flute.controlChange(4, 20.0); // Breath noise
flute.controlChange(2, 65.0); // Tone color (jet delay)
// Play the note
flute.noteOn(523.25, 0.75);
// Render audio
final samples = flute.render(44100);
Control Change Parameters
The controlChange(int number, double value) method accepts the following parameters (values 0-128):
| Parameter | Control # | Description |
|---|---|---|
| Vibrato Gain | 1 | Depth of pitch vibrato (0 = none, 128 = maximum) |
| Jet Delay | 2 | Tone color/brightness (lower = brighter, higher = darker) |
| Noise Gain | 4 | Breath noise amount (adds realism) |
| Vibrato Frequency | 11 | Speed of vibrato oscillation |
| Breath Pressure | 128 | Overall breath pressure envelope |
Complete Example with Audio Playback
See the example directory for a complete Flutter app demonstrating:
- Real-time parameter adjustment with sliders
- Audio playback using SoLoud
- Playing single notes and melodies
- Converting PCM samples to WAV format
Audio Playback
Important: This plugin generates audio samples but does not include audio playback functionality. You need to use a separate audio library to play the generated samples.
Recommended Audio Libraries
- flutter_soloud: Cross-platform, low-latency audio (recommended)
- just_audio: Popular audio player
- audioplayers: Simple audio playback
Converting Samples to WAV
To play the raw PCM samples, you'll need to convert them to a proper audio format. Here's a helper function to create WAV files:
Uint8List createWavFile(List<double> samples, int sampleRate) {
final numSamples = samples.length;
final dataSize = numSamples * 2; // 16-bit samples
final buffer = ByteData(44 + dataSize);
// RIFF header
buffer.setUint32(0, 0x52494646, Endian.big); // "RIFF"
buffer.setUint32(4, 36 + dataSize, Endian.little);
buffer.setUint32(8, 0x57415645, Endian.big); // "WAVE"
// fmt chunk
buffer.setUint32(12, 0x666D7420, Endian.big); // "fmt "
buffer.setUint32(16, 16, Endian.little); // chunk size
buffer.setUint16(20, 1, Endian.little); // PCM format
buffer.setUint16(22, 1, Endian.little); // mono
buffer.setUint32(24, sampleRate, Endian.little);
buffer.setUint32(28, sampleRate * 2, Endian.little); // byte rate
buffer.setUint16(32, 2, Endian.little); // block align
buffer.setUint16(34, 16, Endian.little); // bits per sample
// data chunk
buffer.setUint32(36, 0x64617461, Endian.big); // "data"
buffer.setUint32(40, dataSize, Endian.little);
// PCM data
var offset = 44;
for (var sample in samples) {
final intSample = (sample * 32767).clamp(-32768, 32767).toInt();
buffer.setInt16(offset, intSample, Endian.little);
offset += 2;
}
return buffer.buffer.asUint8List();
}
Platform Support
| Platform | Status | Notes |
|---|---|---|
| Android | ✅ | API 21+ |
| iOS | ✅ | iOS 12+ |
| Linux | ✅ | ALSA backend |
| macOS | ✅ | macOS 10.14+ |
| Windows | ✅ | DirectSound backend |
How It Works
This plugin uses Flutter's FFI (Foreign Function Interface) to directly call C++ functions from the STK library. The architecture is:
- Native Layer: STK C++ library compiled for each platform
- FFI Bridge: Minimal C wrapper exposing STK functions
- Dart Layer: Type-safe Dart API using
dart:ffi
This approach provides:
- Zero platform channels overhead: Direct native function calls
- Platform independence: Same Dart API on all platforms
- High performance: Native C++ execution speed
Future Instruments
The STK library includes many more instruments that could be added:
- Clarinet, Saxophone, Brass instruments
- Bowed strings (Violin, Cello)
- Plucked strings (Guitar, Mandolin)
- Percussion (Drums, Marimba)
- Modal synthesis (Tubular bells, Bamboo chimes)
Contributions are welcome!
Credits
- STK Library: Perry R. Cook and Gary P. Scavone - The Synthesis ToolKit
- Plugin Development: Built with Flutter FFI
License
This plugin is licensed under the MIT License. See LICENSE for details.
The STK library is licensed under its own terms. Please see the STK License for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. Areas for contribution:
- Adding more STK instruments
- Improving documentation
- Adding more examples
- Performance optimizations
- Bug fixes
Issues
Please file issues on the GitHub repository.