A low-level audio recorder plugin that uses miniaudio as the backend and supports all the platforms. It can detect silence and save to a WAV audio file. Audio wave and FFT data can be obtained in real-time as for the volume level.
Linux | Windows | Android | MacOS (under test) | iOS (under test) | web (WASM compatible) |
---|---|---|---|---|---|
π | π | π | π | π | π |
- π₯οΈ Cross-platform: Supports Linux, Windows, Android, MacOS, iOS, and web.
- β‘ High performance: Built using the fast and efficient miniaudio C library with FFI.
- ποΈ WAV Recording with Pause: Record in WAV format with pause functionality.
- βοΈ Choose Data Type: samplerate, mono or stereo, audio format (u8, s8, s16le, s24le, s32le or f32le).
- ποΈ Device Flexibility: Choose your recording device.
- π’ Stream audio data: Listen to PCM audio data stream.
- π Silence Detection: Automatically detects silence via callback or Stream.
- π Customizable Silence Threshold: Define whatβs considered βsilenceβ for your recordings.
- β±οΈ Adjustable Pause Timing: Set how long silence lasts before pausing, and how soon to resume recording.
- π Real-time Audio Metrics: Access volume, audio wave, and FFT data in real-time.
- ποΈ Auto Gain: Experimental Auto Gain filter.
- π Cross Platform: Supports all platforms with WASM support for the web.
A web example compiled in WASM.
After setting up permission for you Android, MacOS or iOS, in your app, you will need to ask for permission to use the microphone maybe using permission_handler plugin. https://pub.dev/packages/permission_handler
Add the permission in the AndroidManifest.xml
.
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Add the permission in Runner/Info.plist
.
<key>NSMicrophoneUsageDescription</key>
<string>Some message to describe why you need this permission</string>
Add this in web/index.html
under the <head>
tag.
<script src="assets/packages/flutter_recorder/web/libflutter_recorder_plugin.js" defer></script>
<script src="assets/packages/flutter_recorder/web/init_recorder_module.dart.js" defer></script>
The plugin is WASM compatible and your app can be compiled and run locally with something like:
flutter run -d chrome --web-renderer canvaskit --web-browser-flag '--disable-web-security' -t lib/main.dart --release
-
GStreamer
is installed by default on most distributions, but if not, please install it through your distribution's package manager. - Installing Flutter using
snap
could cause compilation problems with native plugins. The only solution is to uninstall it withsudo snap remove flutter
and install it the official way.
import 'package:permission_handler/permission_handler.dart';
[...]
/// If you are running on Android, MacOS or iOS, ask the permission to use the microphone:
if (defaultTargetPlatform == TargetPlatform.android ||
defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS) {
Permission.microphone.request().isGranted.then((value) async {
if (!value) {
await [Permission.microphone].request();
}
});
/// Initialize the capture device and start it:
try {
Recorder.instance.init();
Recorder.instance.start();
} on Exception catch (e) {
debugPrint('init() error: $e\n');
}
/// On Web platform it is better to initialize and wait the user to give
/// mic permission. Then use `start()` when it's needed.
//Start recording:
Recorder.instance.startRecording(completeFilePath: 'audioCompleteFilenameWithPath.wav`);
/// Stop recording:
Recorder.instance.stopRecording();
Tip: Use Recorder.instance.listCaptureDevices()
to see available devices and pass an optional deviceID
to init()
.
Tip2: Use the format
, sampleRate
and channels
with the init()
method to define recorder parameters.
Tip3: When recording with silence detection and want to record a little bit before the threshold db is reached, use the setSecondsOfAudioToWriteBefore()
method.
Want to skip the silence? Hereβs how to configure it:
Recorder.instance.setSilenceDetection(
enable: true,
onSilenceChanged: (isSilent, decibel) {
/// Here you can check if silence is changed.
/// Or you can do the same thing with the Stream
/// [Recorder.instance.silenceChangedEvents]
},
);
/// the silence threshold in dB. A volume under this value is considered to be silence.
Recorder.instance.setSilenceThresholdDb(-27);
/// the value in seconds of silence after which silence is considered as such.
Recorder.instance.setSilenceDuration(0.5);
/// Set seconds of audio to write before starting recording again after silence.
Recorder.instance.setSecondsOfAudioToWriteBefore(0.0);
You can also access raw audio data and volume information like this:
/// Get the current volume in dB in the [-100, 0] range.
double volume = Recorder.instance.getVolumeDb();
/// Return a 256 float array containing wave data in the range [-1.0, 1.0] not clamped.
Float32List waveAudio = Recorder.instance.getWave();
/// Return a 256 float array containing FFT data in the range [-1.0, 1.0] not clamped.
Float32List fftAudio = Recorder.instance.getFft();
NOTE: this is only available when initializing the recorder with PCMFormat.f32le
format.
/// Listen to audio data stream. The data is received in Uint8List.
Recorder.instance.uint8ListStream.listen((data) {
/// the [data] is of type `AudioDataContainer` and, whatever format is passed to
/// the `init()` method, it is available with [data.rawData] which is of `Uint8List`
/// type. This is useful if we want to write into a file.
/// It is possible to convert audio data to the desired format using one of the
/// `data.to[*]List` methods. Be aware that the conversion is compute expensive and
/// should be avoided if possible initializing the recorder with the format
/// desired.
});
/// Start streaming:
Recorder.instance.startStreamingData();
/// Stop streaming:
Recorder.instance.stopStreamingData();
Caution
Audio data must be processed as it is received. To optimize performance, the same memory is used to store data for all incoming streams, meaning the data will be overwritten. Therefore, you must copy the data if you need to populate a buffer while it arrives.
For example, when using RxDart.bufferTime, it will fill a List of AudioDataContainer
objects, but when you attempt to read them, you will find that all the items contain the same data.
Warning
This is an experimental feature, may change in the future.
final Recorder recorder = Recorder.instance;
// Please look at the [Recorder.instance.autoGainFilter] doc to have a parameters overview.
final AutoGain autoGain = recorder.filters.autoGainFilter;
// You can now query or set parameters:
// For example with [autoGain.queryTargetRms] you can query the "human" name, `min`, `max` and `def` values.
// Set a new parameter value:
autoGain.targetRms.value = newValue;
// Get a new parameter value:
final value = autoGain.targetRms.value;
Available parameters: targetRMS
, attackTime
, releaseTime
, gainSmoothing
, maxGain
, minGain
.