Skip to content

Commit 7ff32f8

Browse files
committed
Implemented null safety
1 parent 57893d0 commit 7ff32f8

File tree

6 files changed

+42
-33
lines changed

6 files changed

+42
-33
lines changed

binding/flutter/lib/rhino.dart

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import 'package:rhino/rhino_error.dart';
2222

2323
class Rhino {
2424
static bool _resourcesExtracted = false;
25-
static String _defaultModelPath;
25+
static String? _defaultModelPath;
2626

27-
int _handle;
27+
int? _handle;
2828
final String _contextInfo;
2929
final Pointer<Int16> _cFrame;
3030

@@ -56,7 +56,7 @@ class Rhino {
5656
///
5757
/// returns an instance of the speech-to-intent engine
5858
static Future<Rhino> create(String contextPath,
59-
{String modelPath, double sensitivity = 0.5}) async {
59+
{String? modelPath, double sensitivity = 0.5}) async {
6060
if (!_resourcesExtracted) {
6161
await _extractRhinoResources();
6262
_resourcesExtracted = true;
@@ -66,6 +66,9 @@ class Rhino {
6666
throw new PvArgumentError("No context file provided.");
6767
}
6868

69+
if (modelPath == null && _defaultModelPath == null) {
70+
throw new PvError("No model file provided and default model file not available.");
71+
}
6972
modelPath ??= _defaultModelPath;
7073

7174
if (sensitivity < 0 || sensitivity > 1 || sensitivity.isNaN) {
@@ -74,7 +77,7 @@ class Rhino {
7477
}
7578

7679
// generate arguments for ffi
77-
Pointer<Utf8> cModelPath = modelPath.toNativeUtf8();
80+
Pointer<Utf8> cModelPath = modelPath!.toNativeUtf8();
7881
Pointer<Utf8> cContextPath = contextPath.toNativeUtf8();
7982
Pointer<IntPtr> handlePtr = malloc<IntPtr>(1);
8083

@@ -112,7 +115,7 @@ class Rhino {
112115
/// - isUnderstood: if isFinalized, whether Rhino understood what it heard based on the context
113116
/// - intent: if isUnderstood, name of intent that were inferred
114117
/// - slots: if isUnderstood, dictionary of slot keys and values that were inferred
115-
Map<String, dynamic> process(List<int> frame) {
118+
Map<String, dynamic> process(List<int>? frame) {
116119
if (_handle == null) {
117120
throw new PvStateError(
118121
"Attempted to process an audio frame after Rhino was been deleted.");
@@ -132,7 +135,7 @@ class Rhino {
132135
_cFrame.asTypedList(frame.length).setAll(0, frame);
133136
Pointer<Uint8> isFinalized = malloc(1);
134137

135-
int status = _rhinoProcess(_handle, _cFrame, isFinalized);
138+
int status = _rhinoProcess(_handle!, _cFrame, isFinalized);
136139
PvStatus pvStatus = PvStatus.values[status];
137140
if (pvStatus != PvStatus.SUCCESS) {
138141
pvStatusToException(pvStatus, "Rhino failed to process an audio frame.");
@@ -147,7 +150,7 @@ class Rhino {
147150

148151
// get isUnderstood
149152
Pointer<Uint8> isUnderstood = malloc(1);
150-
status = _rhinoIsUnderstood(_handle, isUnderstood);
153+
status = _rhinoIsUnderstood(_handle!, isUnderstood);
151154
pvStatus = PvStatus.values[status];
152155
if (pvStatus != PvStatus.SUCCESS) {
153156
pvStatusToException(pvStatus, "Rhino failed to get IsUnderstood value.");
@@ -161,7 +164,7 @@ class Rhino {
161164
Pointer<Int32> cNumSlots = malloc(1);
162165
Pointer<Pointer<Pointer<Utf8>>> cSlots = malloc(1);
163166
Pointer<Pointer<Pointer<Utf8>>> cValues = malloc(1);
164-
status = _rhinoGetIntent(_handle, cIntent, cNumSlots, cSlots, cValues);
167+
status = _rhinoGetIntent(_handle!, cIntent, cNumSlots, cSlots, cValues);
165168
pvStatus = PvStatus.values[status];
166169
if (pvStatus != PvStatus.SUCCESS) {
167170
pvStatusToException(pvStatus, "Rhino failed to get intent.");
@@ -178,7 +181,7 @@ class Rhino {
178181
inference['slots'] = slots;
179182

180183
// free slots
181-
status = _rhinoFreeSlotsAndValues(_handle, cSlots[0], cValues[0]);
184+
status = _rhinoFreeSlotsAndValues(_handle!, cSlots[0], cValues[0]);
182185
pvStatus = PvStatus.values[status];
183186
if (pvStatus != PvStatus.SUCCESS) {
184187
pvStatusToException(pvStatus, "Rhino failed to free slots.");
@@ -188,7 +191,7 @@ class Rhino {
188191
}
189192

190193
// reset Rhino
191-
status = _rhinoReset(_handle);
194+
status = _rhinoReset(_handle!);
192195
pvStatus = PvStatus.values[status];
193196
if (pvStatus != PvStatus.SUCCESS) {
194197
pvStatusToException(pvStatus, "Rhino failed to reset.");
@@ -200,7 +203,7 @@ class Rhino {
200203
/// Frees memory that was allocated for Rhino
201204
void delete() {
202205
if (_handle != null) {
203-
_rhinoDelete(_handle);
206+
_rhinoDelete(_handle!);
204207
_handle = null;
205208
}
206209
}

binding/flutter/lib/rhino_error.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
//
1111

1212
class PvAudioException implements Exception {
13-
final String message;
13+
final String? message;
1414
PvAudioException([this.message]);
1515
}
1616

1717
class PvError extends Error {
18-
final String message;
18+
final String? message;
1919
PvError([this.message]);
2020
}
2121

binding/flutter/lib/rhino_manager.dart

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ typedef InferenceCallback(Map<String, dynamic> inference);
2323
typedef ErrorCallback(PvError error);
2424

2525
class RhinoManager {
26-
VoiceProcessor _voiceProcessor;
27-
Rhino _rhino;
26+
VoiceProcessor? _voiceProcessor;
27+
Rhino? _rhino;
2828

2929
final InferenceCallback _inferenceCallback;
30-
RemoveListener _removeVoiceProcessorListener;
30+
RemoveListener? _removeVoiceProcessorListener;
3131

3232
bool _awaitingStop = false;
3333

@@ -49,20 +49,23 @@ class RhinoManager {
4949
/// returns an instance of the speech-to-intent engine
5050
static Future<RhinoManager> create(
5151
String contextPath, InferenceCallback inferenceCallback,
52-
{String modelPath,
52+
{String? modelPath,
5353
double sensitivity = 0.5,
54-
ErrorCallback errorCallback}) async {
54+
ErrorCallback? errorCallback}) async {
5555
Rhino rhino = await Rhino.create(contextPath,
5656
modelPath: modelPath, sensitivity: sensitivity);
5757
return new RhinoManager._(rhino, inferenceCallback, errorCallback);
5858
}
5959

6060
// private constructor
6161
RhinoManager._(
62-
this._rhino, this._inferenceCallback, ErrorCallback errorCallback)
62+
this._rhino, this._inferenceCallback, ErrorCallback? errorCallback)
6363
: _voiceProcessor = VoiceProcessor.getVoiceProcessor(
6464
Rhino.frameLength, Rhino.sampleRate) {
65-
_removeVoiceProcessorListener = _voiceProcessor.addListener((buffer) async {
65+
if (_voiceProcessor == null) {
66+
throw new PvError("flutter_voice_processor not available.");
67+
}
68+
_removeVoiceProcessorListener = _voiceProcessor!.addListener((buffer) async {
6669
if (_awaitingStop) {
6770
return;
6871
}
@@ -82,15 +85,15 @@ class RhinoManager {
8285

8386
// process frame with Rhino
8487
try {
85-
Map<String, dynamic> rhinoResult = _rhino.process(rhinoFrame);
86-
if (rhinoResult['isFinalized']) {
88+
Map<String, dynamic>? rhinoResult = _rhino?.process(rhinoFrame);
89+
if (rhinoResult?['isFinalized']) {
8790
_awaitingStop = true;
8891

8992
// send inference minus isFinalized
90-
rhinoResult.remove('isFinalized');
93+
rhinoResult!.remove('isFinalized');
9194
_inferenceCallback(rhinoResult);
9295
// stop audio processing
93-
await _voiceProcessor.stop();
96+
await _voiceProcessor?.stop();
9497
}
9598
} on PvError catch (error) {
9699
errorCallback == null ? print(error.message) : errorCallback(error);
@@ -109,9 +112,9 @@ class RhinoManager {
109112
"Cannot start RhinoManager - resources have already been released");
110113
}
111114

112-
if (await _voiceProcessor.hasRecordAudioPermission()) {
115+
if (await _voiceProcessor?.hasRecordAudioPermission() == true) {
113116
try {
114-
await _voiceProcessor.start();
117+
await _voiceProcessor!.start();
115118
} on PlatformException {
116119
throw new PvAudioException(
117120
"Audio engine failed to start. Hardware may not be supported.");
@@ -125,15 +128,15 @@ class RhinoManager {
125128
/// Releases Rhino and audio resouces
126129
void delete() async {
127130
if (_voiceProcessor != null) {
128-
if (_voiceProcessor.isRecording) {
129-
await _voiceProcessor.stop();
131+
if (_voiceProcessor!.isRecording) {
132+
await _voiceProcessor!.stop();
130133
}
131134
_removeVoiceProcessorListener?.call();
132135
_voiceProcessor = null;
133136
}
134137

135138
if (_rhino != null) {
136-
_rhino.delete();
139+
_rhino!.delete();
137140
_rhino = null;
138141
}
139142
}

binding/flutter/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version: 1.6.6
44
homepage: https://picovoice.ai/
55

66
environment:
7-
sdk: ">=2.7.0 <3.0.0"
7+
sdk: ">=2.12.0 <3.0.0"
88
flutter: ">=1.20.0"
99

1010
dependencies:

demo/flutter/lib/main.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class _MyAppState extends State<MyApp> {
3434
bool isButtonDisabled = false;
3535
bool isProcessing = false;
3636
String rhinoText = "";
37-
RhinoManager _rhinoManager;
37+
RhinoManager? _rhinoManager;
3838

3939
@override
4040
void initState() {
@@ -121,7 +121,10 @@ class _MyAppState extends State<MyApp> {
121121
});
122122

123123
try {
124-
await _rhinoManager.process();
124+
if (_rhinoManager == null) {
125+
throw PvAudioException("_rhinoManager not initialized.");
126+
}
127+
await _rhinoManager!.process();
125128
this.setState(() {
126129
isProcessing = true;
127130
rhinoText = "Listening...";

demo/flutter/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: Demonstrates how to use the rhino plugin.
44
publish_to: 'none'
55

66
environment:
7-
sdk: ">=2.7.0 <3.0.0"
7+
sdk: ">=2.12.0 <3.0.0"
88

99
dependencies:
1010
flutter:

0 commit comments

Comments
 (0)