1
0
mirror of https://github.com/Picovoice/porcupine.git synced 2022-01-28 03:27:53 +03:00
Files
porcupine-pertev-wakeword/binding/flutter/lib/porcupine_manager.dart
Alireza Kenarsari 556630dcf4 v2.0
2021-11-25 10:08:21 -08:00

173 lines
6.6 KiB
Dart

//
// Copyright 2020-2021 Picovoice Inc.
//
// You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE"
// file accompanying this source.
//
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_voice_processor/flutter_voice_processor.dart';
import 'package:porcupine_flutter/porcupine.dart';
import 'package:porcupine_flutter/porcupine_error.dart';
typedef WakeWordCallback = Function(int keywordIndex);
typedef ErrorCallback = Function(PorcupineException error);
class PorcupineManager {
VoiceProcessor? _voiceProcessor;
Porcupine? _porcupine;
final WakeWordCallback _wakeWordCallback;
RemoveListener? _removeVoiceProcessorListener;
RemoveListener? _removeErrorListener;
/// Static creator for initializing PorcupineManager from a selection of built-in keywords
///
/// [accessKey] AccessKey obtained from Picovoice Console (https://console.picovoice.ai/).
///
/// [keywords] is a List of (phrases) for detection. The list of available
/// keywords can be retrieved using [BuiltInKeyword]
///
/// [wakeWordCallback] A callback that is triggered when one of the given keywords
/// has been detected by Porcupine
///
/// [modelPath] is a path to the file containing model parameters. If not set
/// it will be set to the default location.
///
/// [sensitivities] sensitivities for each keywords model. A higher sensitivity
/// reduces miss rate at the cost of potentially higher false alarm rate.
/// Sensitivity should be a floating-point number within 0 and 1.
///
/// [errorCallback] is an optional callback that triggers if Porcupine
/// experiences a problem while processing audio
///
/// returns an instance of PorcupineManager
static Future<PorcupineManager> fromBuiltInKeywords(String accessKey,
List<BuiltInKeyword> keywords, WakeWordCallback wakeWordCallback,
{String? modelPath,
List<double>? sensitivities,
ErrorCallback? errorCallback}) async {
Porcupine porcupine = await Porcupine.fromBuiltInKeywords(
accessKey, keywords,
modelPath: modelPath, sensitivities: sensitivities);
return PorcupineManager._(porcupine, wakeWordCallback, errorCallback);
}
/// Static creator for initializing PorcupineManager from a list of paths to custom keyword files
///
/// [accessKey] AccessKey obtained from Picovoice Console (https://console.picovoice.ai/).
///
/// [keywordPaths] A List of absolute paths to keyword model files.
///
/// [wakeWordCallback] A callback that is triggered when one of the given keywords
/// has been detected by Porcupine
///
/// [modelPath] is a path to the file containing model parameters. If not set
/// it will be set to the default location.
///
/// [sensitivities] sensitivities for each keywords model. A higher sensitivity
/// reduces miss rate at the cost of potentially higher false alarm rate.
/// Sensitivity should be a floating-point number within 0 and 1.
///
/// [errorCallback] is an optional callback that triggers if Porcupine
/// experiences a problem while processing audio
///
/// Thows a `PvError` if not initialized correctly
///
/// returns an instance of PorcupineManager
static Future<PorcupineManager> fromKeywordPaths(String accessKey,
List<String> keywordPaths, WakeWordCallback wakeWordCallback,
{String? modelPath,
List<double>? sensitivities,
ErrorCallback? errorCallback}) async {
Porcupine porcupine = await Porcupine.fromKeywordPaths(
accessKey, keywordPaths,
modelPath: modelPath, sensitivities: sensitivities);
return PorcupineManager._(porcupine, wakeWordCallback, errorCallback);
}
// private constructor
PorcupineManager._(
this._porcupine, this._wakeWordCallback, ErrorCallback? errorCallback)
: _voiceProcessor = VoiceProcessor.getVoiceProcessor(
_porcupine!.frameLength, _porcupine.sampleRate) {
_removeVoiceProcessorListener =
_voiceProcessor?.addListener((buffer) async {
// cast from dynamic to int array
List<int> porcupineFrame;
try {
porcupineFrame = (buffer as List<dynamic>).cast<int>();
} on Error {
PorcupineException castError = PorcupineException(
"flutter_voice_processor sent an unexpected data type.");
errorCallback == null
? print(castError.message)
: errorCallback(castError);
return;
}
// process frame with Porcupine
try {
int? keywordIndex = await _porcupine?.process(porcupineFrame);
if (keywordIndex != null && keywordIndex >= 0) {
_wakeWordCallback(keywordIndex);
}
} on PorcupineException catch (error) {
errorCallback == null ? print(error.message) : errorCallback(error);
}
});
_removeErrorListener = _voiceProcessor?.addErrorListener((errorMsg) {
PorcupineException nativeRecorderError =
PorcupineException(errorMsg as String);
errorCallback == null
? print(nativeRecorderError.message)
: errorCallback(nativeRecorderError);
});
}
/// Opens audio input stream and sends audio frames to Porcupine
/// Throws a `PvAudioException` if there was a problem starting the audio engine
Future<void> start() async {
if (_porcupine == null || _voiceProcessor == null) {
throw PorcupineInvalidStateException(
"Cannot start Porcupine - resources have already been released");
}
var ret = await _voiceProcessor?.hasRecordAudioPermission();
if (ret ?? false) {
try {
await _voiceProcessor?.start();
} on PlatformException {
throw PorcupineRuntimeException(
"Audio engine failed to start. Hardware may not be supported.");
}
} else {
throw PorcupineRuntimeException(
"User did not give permission to record audio.");
}
}
/// Closes audio stream
Future<void> stop() async => await _voiceProcessor?.stop();
/// Releases Porcupine and audio resources
Future<void> delete() async {
if (_voiceProcessor?.isRecording ?? false) {
await _voiceProcessor?.stop();
}
_removeVoiceProcessorListener?.call();
_removeErrorListener?.call();
_voiceProcessor = null;
_porcupine?.delete();
_porcupine = null;
}
}