Porcupine Binding for Flutter
Porcupine
Porcupine is a highly accurate and lightweight wake word engine. It enables building always-listening voice-enabled applications using cutting edge voice AI.
Porcupine is:
- private and offline
- accurate
- resource efficient (runs even on microcontrollers)
- data efficient (wake words can be easily generated by simply typing them, without needing thousands of hours of bespoke audio training data and manual effort)
- scalable to many simultaneous wake-words / always-on voice commands
- cross-platform
To learn more about Porcupine, see the product, documentation, and GitHub pages.
Custom wake words
Porcupine includes several built-in keywords, which are stored as .ppn
files. To train custom PPN files, see the Picovoice Console.
Unlike the built-in keywords, custom PPN files generated with the Picovoice Console carry restrictions including (but not limited to): training allowance, time limits, available platforms, and commercial usage.
Compatibility
This binding is for running Porcupine on Flutter 2.8.1+ on the following platforms:
- Android 5.0+ (API 21+)
- iOS 13.0+
Installation
To start, you must have the Flutter SDK installed on your system. Once installed, you can run flutter doctor
to determine any other missing requirements.
To add the Porcupine plugin to your app project, you can reference it in your pub.yaml:
dependencies:
porcupine_flutter: ^<version>
If you prefer to clone the repo and use it locally you can reference the local binding location:
dependencies:
porcupine_flutter:
path: /path/to/porcupine/flutter/binding
NOTE: When archiving for release on iOS, you may have to change the build settings of your project in order to prevent stripping of the Porcupine library. To do this open the Runner project in XCode and change build setting Deployment -> Strip Style to 'Non-Global Symbols'.
AccessKey
Porcupine requires a valid Picovoice AccessKey
at initialization. AccessKey
acts as your credentials when using Porcupine SDKs.
You can get your AccessKey
for free. Make sure to keep your AccessKey
secret.
Signup or Login to Picovoice Console to get your AccessKey
.
Permissions
To enable recording with the hardware's microphone, you must first ensure that you have enabled the proper permission on both iOS and Android.
On iOS, open your Info.plist and add the following line:
<key>NSMicrophoneUsageDescription</key>
<string>[Permission explanation]</string>
On Android, open your AndroidManifest.xml and add the following line:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
Usage
The module provides you with two levels of API to choose from depending on your needs.
High-Level API
PorcupineManager provides a high-level API that takes care of audio recording. This class is the quickest way to get started.
Using the constructor PorcupineManager.fromBuiltInKeywords
will create an instance of the PorcupineManager using one or more of the built-in keywords.
import 'package:porcupine_flutter/porcupine_manager.dart';
import 'package:porcupine_flutter/porcupine_error.dart';
final String accessKey = "{ACCESS_KEY}"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)
void createPorcupineManager() async {
try{
_porcupineManager = await PorcupineManager.fromBuiltInKeywords(
accessKey,
[BuiltInKeyword.PICOVOICE, BuiltInKeyword.PORCUPINE],
_wakeWordCallback);
} on PorcupineException catch (err) {
// handle porcupine init error
}
}
NOTE: the call is asynchronous and therefore should be called in an async block with a try/catch.
The wakeWordCallback
parameter is a function that you want to execute when Porcupine has detected one of the keywords.
The function should accept a single integer, keywordIndex, which specifies which wake word has been detected.
void _wakeWordCallback(int keywordIndex) {
if (keywordIndex == 0) {
// picovoice detected
}
else if (keywordIndex == 1) {
// porcupine detected
}
}
Available built-in keywords are stored in the BuiltInKeyword
enum.
To create an instance of PorcupineManager that detects custom keywords, you can use the PorcupineManager.fromKeywordPaths
static constructor and provide the paths to the .ppn
file(s).
final String accessKey = "{ACCESS_KEY}"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)
_porcupineManager = await PorcupineManager.fromKeywordPaths(
accessKey,
["/path/to/keyword.ppn"],
_wakeWordCallback);
In addition to custom keywords, you can override the default Porcupine model file and/or keyword sensitivities, as well as add an error callback you want to trigger if there's a problem encountered while Porcupine is processing frames.
These optional parameters can be passed in like so:
final String accessKey = "{ACCESS_KEY}"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)
_porcupineManager = await PorcupineManager.fromKeywordPaths(
accessKey,
["/path/to/keyword/file/one.ppn", "/path/to/keyword/file/two.ppn"],
_wakeWordCallback,
modelPath: 'path/to/model/file.pv',
sensitivities: [0.25, 0.6],
errorCallback: _errorCallback);
void _errorCallback(PorcupineException error) {
print(error.message);
}
Once you have instantiated a PorcupineManager, you can start audio capture and wake word detection by calling:
try{
await _porcupineManager.start();
} on PorcupineException catch (ex) {
// deal with either audio exception
}
And then stop it by calling:
await _porcupineManager.stop();
Once the app is done with using an instance of PorcupineManager, be sure you explicitly release the resources allocated to Porcupine:
await _porcupineManager.delete();
NOTE: Avoid calling delete()
from the paused
state unless you have overridden the back button functionality on Android with WillPopScope.
There is no need to deal with audio capture to enable wake word detection with PorcupineManager. This is because it uses our flutter_voice_processor Flutter plugin to capture frames of audio and automatically pass it to the wake word engine.
Low-Level API
Porcupine provides low-level access to the wake word engine for those who want to incorporate wake word detection into an already existing audio processing pipeline.
Porcupine
also has fromBuiltInKeywords
and fromKeywordPaths
static constructors.
import 'package:porcupine_flutter/porcupine_manager.dart';
import 'package:porcupine_flutter/porcupine_error.dart';
final String accessKey = "{ACCESS_KEY}"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)
void createPorcupine() async {
try{
_porcupine = await Porcupine.fromBuiltInKeywords(
accessKey,
[BuiltInKeyword.PICOVOICE]);
} on PorcupineException catch (err) {
// handle porcupine init error
}
}
To search for a keyword in audio, you must pass frames of audio to Porcupine using the process
function. The keywordIndex
returned will either be -1 if no detection was made or an integer specifying which keyword was detected.
List<int> buffer = getAudioFrame();
try {
int keywordIndex = _porcupine.process(buffer);
if (keywordIndex >= 0) {
// detection made!
}
} on PorcupineException catch (error) {
// handle error
}
For process to work correctly, the audio data must be in the audio format required by Picovoice.
The required audio format is found by calling .sampleRate
to get the required sample rate and .frameLength
to
get the required frame size. Audio must be single-channel and 16-bit linearly-encoded.
Finally, once you no longer need the wake word engine, be sure to explicitly release the resources allocated to Porcupine:
_porcupine.delete();
Custom Wake Word Integration
To add a custom wake word to your Flutter application, first add it to an assets
folder in your project directory. Then add them to you your pubspec.yaml:
flutter:
assets:
- assets/keyword.ppn
You can then pass it directly to Porcupine's fromKeywordPaths
constructor:
final String accessKey = "{ACCESS_KEY}"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)
String keywordAsset = "assets/keyword.ppn";
try{
_porcupine = await Porcupine.fromKeywordPaths(
accessKey,
[keywordAsset]);
} on PorcupineException catch (err) {
// handle porcupine init error
}
Alternatively, if the model files are deployed to the device with a different method, the absolute paths to the files on device can be used.
Non-English Wake Words
In order to detect non-English wake words you need to use the corresponding model file. The model files for all supported languages are available here.
Demo App
Check out the Porcupine Flutter demo to see what it looks like to use Porcupine in a cross-platform app!