Synthesis: waveforms and speech without assets
flutter_soloud can synthesize audio with zero files. SoLoud.instance.loadWaveform() creates an AudioSource from an oscillator (not a stream to "set" — there is no setSource/setUrl like audioplayers; you create a source, then play() it). The returned AudioSource is reused and disposed exactly like one loaded from an asset. Runtime tweaks (setWaveform*) act on the source, so they affect every playing instance of it.
Minimal example
import 'package:flutter_soloud/flutter_soloud.dart';
Future<void> beep() async {
if (!SoLoud.instance.isInitialized) {
await SoLoud.instance.init();
}
final tone = await SoLoud.instance.loadWaveform(
WaveForm.sin, // oscillator type
false, // superWave
1.0, // scale
0.0, // detune
);
SoLoud.instance.setWaveformFreq(tone, 440); // Hz
final handle = SoLoud.instance.play(tone); // sync, returns SoundHandle
// ... later
await SoLoud.instance.stop(handle);
await SoLoud.instance.disposeSource(tone); // required: it's an AudioSource
}
The API shape
Loading
Future<AudioSource> loadWaveform(
WaveForm waveform,
bool superWave,
double scale,
double detune,
)
Positional args, all required. superWave combines several detuned oscillators for a fatter sound; scale and detune only matter when superWave is true. Throws SoLoudNotInitializedException before init().
WaveForm enum (exact names, lib/src/enums.dart)
square, saw, sin, triangle, bounce (abs(sin)), jaws (quarter sine, rest quiet), humps (half sine, rest quiet), fSquare ("Fourier" square, less noisy), fSaw ("Fourier" saw, less noisy).
Note the casing: fSquare / fSaw — not fsquare/fsaw (the upstream docs page lists them lowercase; the code is authoritative).
Runtime tweaks (all void, all take the AudioSource, not the handle)
| Method | Effect |
|---|---|
setWaveform(AudioSource sound, WaveForm newWaveform) |
switch oscillator type |
setWaveformFreq(AudioSource sound, double newFrequency) |
frequency in Hz |
setWaveformSuperWave(AudioSource sound, bool superwave) |
toggle super wave |
setWaveformScale(AudioSource sound, double newScale) |
super-wave scale |
setWaveformDetune(AudioSource sound, double newDetune) |
super-wave detune |
These change live playback — call them on slider changes; no need to reload or stop.
Speech
AudioSource speechText(String textToSpeech)
Synchronous, and it creates AND immediately plays the source — don't call play() on it. The voice is SoLoud's own built-in formant synth: deliberately retro/robotic, not the platform's system TTS (no flutter_tts-style voice/rate/pitch controls, no per-platform quality differences).
Note bank helper
static Future<List<AudioSource>> SoLoudTools.createNotes({
int octave = 3, // asserted 0..4
WaveForm waveForm = WaveForm.sin,
bool superwave = true,
})
Returns 12 AudioSources (the chromatic notes starting at 55 Hz * 2^octave) ready to play(). Each is a separate source — dispose all 12 when done. Typical keyboard pattern:
final notes = await SoLoudTools.createNotes(octave: 2);
SoLoud.instance.play(notes[4]); // key pressed
await SoLoud.instance.stop(notes[4].handles.first); // key released
Recipe: tone generator / instrument
Model on example/lib/waveform/waveform.dart:
await SoLoud.instance.init()once;SoLoud.instance.deinit()in the owning widget'sdispose().- Create the source with
loadWaveform, thenplay()it and keep bothAudioSourceandSoundHandlein state. - Wire UI controls straight to
setWaveform*on the source while it plays. - On stop:
await SoLoud.instance.stop(handle), and dispose the source when it's no longer needed (await SoLoud.instance.disposeSource(source)).
Traps
play()is synchronous and returnsSoundHandle— noawait, and unlike just_audio there is nosetUrl/AudioPlayerobject; theSoLoudsingleton is the whole player.- Every synthesis call requires
init()first and throwsSoLoudNotInitializedExceptionotherwise.init()is async;isInitializedlets you check synchronously. - Waveform/speech sources are
AudioSources like file-based ones. They are NOT auto-freed when playback ends: calldisposeSource(source)(ordisposeAllSources()), or letdeinit()clean everything up. Creating a newloadWaveformper beep without disposing leaks. speechText()plays immediately. Callingplay()on its returned source is wrong. Dispose it like any other source when finished (seeexample/tests/tests/speech_text.dart).setWaveformScale/setWaveformDetuneare no-ops in practice unless super wave is on (loadWaveform(..., superWave: true, ...)orsetWaveformSuperWave(sound, true)). The docs' own demo disables those sliders when super wave is off.setWaveform*targets the source, not a handle — it retunes all currently playing instances of that source. For per-note pitch, create one source per note (SoLoudTools.createNotes) instead of retuning one shared source.createNotesassertsoctavein 0..4 and each call creates 12 sources — dispose them all, and don't call it repeatedly per key press; build the bank once.- No envelopes/ADSR built in. Raw oscillators drone until stopped. Fade or stop handles yourself (
stop(handle), orfadeVolume(handle, 0, duration)then stop) to avoid clicks and endless tones. - Square/saw are loud and harsh — consider lowering
play(..., volume: ...)or using thefSquare/fSawfiltered variants for smoother output.
More depth
- Demo:
example/lib/waveform/waveform.dart— full tone generator UI (superWave/scale/detune sliders, all 9WaveFormbuttons). - Tests with idiomatic usage:
example/tests/tests/create_notes.dart,example/tests/tests/speech_text.dart,example/tests/tests/waveform_controls.dart. - Real signatures:
loadWaveform/setWaveform*/speechTextatlib/src/soloud.dart:2076-2195;WaveFormatlib/src/enums.dart:290-317;SoLoudTools.createNotesinlib/src/tools/soloud_tools.dart.
Keeping this skill current
This skill ships inside the flutter_soloud package, so upgrading flutter_soloud can carry a newer revision of it than the copy installed in the project. To check, run:
dart run flutter_soloud:skills --check
It reports the installed and bundled skill versions and exits non-zero when an update is available. Offer to update with dart run flutter_soloud:skills (which touches only the skills, never pubspec or build files).