stk_min 0.3.1
stk_min: ^0.3.1 copied to clipboard
A high-performance Flutter wrapper for the Synthesis ToolKit (STK). Features physically modeled musical instruments: Flute, Saxophone, Shakers, and Percussion (Drums, Marimba, Agogo, WoodBlocks). Uses [...]
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
- Saxophone: Physical model of a saxophone with reed stiffness control, vibrato, and breath dynamics
- Shakers: Physically informed stochastic event modeling (PhISEM) of various percussion instruments (Maracas, Tambourine, Sleighbells, etc.)
- Drummer: General MIDI-compatible drum kit using audio samples
- ModalBar: Resonant bar instruments with presets for Marimba, Vibraphone, Agogo (African percussion), and more.
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
stk_min: ^0.2.0
Then run:
flutter pub get
Usage #
Usage 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.)
Saxophone Example #
import 'package:stk_min/saxophone.dart';
// Create a saxophone instance
final saxophone = Saxophone();
// Initialize with a frequency (D4 = 293.66 Hz)
saxophone.init(293.66);
// Play a note
saxophone.noteOn(293.66, 0.9);
// Generate audio samples
final samples = saxophone.render(44100);
// Add expressive controls
saxophone.controlChange(1, 40.0); // Vibrato depth
saxophone.controlChange(11, 50.0); // Vibrato speed
saxophone.controlChange(2, 80.0); // Reed stiffness
saxophone.controlChange(4, 15.0); // Breath noise
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);
Shakers Example #
import 'package:stk_min/shakers.dart';
// Create a Shakers instance (default to Maraca)
final shakers = Shakers(Shakers.maraca);
// Shake it! (instrument type, amplitude)
shakers.noteOn(Shakers.maraca.toDouble(), 0.8);
// Trigger a different sound (e.g., Tambourine)
shakers.noteOn(Shakers.tambourine.toDouble(), 0.9);
// Add expressive controls
shakers.controlChange(2, 80.0); // Shake energy
shakers.controlChange(4, 50.0); // System decay
shakers.controlChange(11, 20.0); // Number of objects
// Render audio
final samples = shakers.render(44100);
Drummer Example #
import 'package:stk_min/stk_min.dart';
import 'package:stk_min/drummer.dart';
// Set path to STK rawwave files (required for Drummer)
setRawwavePath('/path/to/stk/rawwaves/');
final drummer = Drummer();
// Play a Bass Drum sound (MIDI 36)
drummer.noteOn(36.0, 0.9);
// Play a Snare Drum sound (MIDI 38)
drummer.noteOn(38.0, 0.82);
// Render audio
final samples = drummer.render(22050);
ModalBar Example (African Percussion) #
import 'package:stk_min/modal_bar.dart';
final modalBar = ModalBar();
// Initialize with Agogo preset (African drum sound)
modalBar.init(ModalBar.agogo);
// Play a high "slap" sound (880 Hz)
modalBar.noteOn(880.0, 0.8);
// Gain control (Stick hardness)
modalBar.controlChange(2, 80.0);
final samples = modalBar.render(44100);
Control Change Parameters #
The controlChange(int number, double value) method accepts the following parameters (values 0-128):
Flute Control Changes
| 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 |
Saxophone Control Changes
| Parameter | Control # | Description |
|---|---|---|
| Vibrato Gain | 1 | Depth of pitch vibrato (0 = none, 128 = maximum) |
| Reed Stiffness | 2 | Reed flexibility (lower = softer, higher = stiffer) |
| Noise Gain | 4 | Breath noise amount (adds realism) |
| Vibrato Frequency | 11 | Speed of vibrato oscillation |
| Breath Pressure | 128 | Overall breath pressure envelope |
Shakers Control Changes
| Parameter | Control # | Description |
|---|---|---|
| Shake Energy | 2 | Intensity of the shake |
| System Decay | 4 | How fast the sound fades out |
| Number Of Objects | 11 | Number of shaking objects (e.g., beads in maraca) |
| Resonance Frequency | 1 | Main resonance of the instrument |
| Shake Energy | 128 | Overall volume/energy |
ModalBar Control Changes
| Parameter | Control # | Description |
|---|---|---|
| Stick Hardness | 2 | Hardness of the strike (0 = soft, 128 = hard) |
| Strike Position | 4 | Where the bar is struck (0 = edge, 128 = center) |
| Vibrato Gain | 1 | Depth of pitch vibrato |
| Vibrato Frequency | 11 | Speed of vibrato oscillation |
| Preset | 16 | Switch between instruments (Marimba, Vibraphone, Agogo, etc.) |
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 use wget to download the STK library from the following link: https://github.com/thestk/stk and add the necessary files to the src directory. For example Flute.h and Flute.cpp for flute support. If the instrument causes compilation errors then download additional files. A simpler way is to use wget -q https://raw.githubusercontent.com/thestk/stk/master/include/Flute.h to download the header files and wget -q https://raw.githubusercontent.com/thestk/stk/master/src/Flute.cpp to download the source files.
-
Improving documentation
-
Adding more examples
-
Performance optimizations
-
Bug fixes
Issues #
Please file issues on the GitHub repository.