Skip to content

Commit 250447e

Browse files
authored
Refactor Dart API to check for nullptr. (k2-fsa#3329)
This pull request refactors the Dart API by integrating robust null pointer checks and improved error handling mechanisms. The changes aim to enhance the stability and reliability of the sherpa-onnx Flutter bindings by preventing operations on uninitialized or invalid native pointers, thereby making the API more resilient and easier to debug for consumers.
1 parent 33dc6b2 commit 250447e

15 files changed

Lines changed: 742 additions & 40 deletions

flutter/sherpa_onnx/lib/src/audio_tagging.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,20 +172,48 @@ class AudioTagging {
172172
}
173173

174174
void free() {
175+
if (SherpaOnnxBindings.sherpaOnnxDestroyAudioTagging == null) {
176+
throw Exception("Please initialize sherpa-onnx first");
177+
}
178+
179+
if (ptr == nullptr) {
180+
return;
181+
}
175182
SherpaOnnxBindings.sherpaOnnxDestroyAudioTagging?.call(ptr);
176183
ptr = nullptr;
177184
}
178185

179186
/// The user has to invoke stream.free() on the returned instance
180187
/// to avoid memory leak
181188
OfflineStream createStream() {
189+
if (SherpaOnnxBindings.sherpaOnnxAudioTaggingCreateOfflineStream == null) {
190+
throw Exception("Please initialize sherpa-onnx first");
191+
}
192+
193+
if (ptr == nullptr) {
194+
throw Exception("Failed to create offline stream");
195+
}
196+
182197
final p = SherpaOnnxBindings.sherpaOnnxAudioTaggingCreateOfflineStream
183198
?.call(ptr) ??
184199
nullptr;
200+
201+
if (p == nullptr) {
202+
throw Exception("Failed to create offline stream");
203+
}
204+
185205
return OfflineStream(ptr: p);
186206
}
187207

188208
List<AudioEvent> compute({required OfflineStream stream, required int topK}) {
209+
if (SherpaOnnxBindings.sherpaOnnxAudioTaggingCompute == null) {
210+
throw Exception("Please initialize sherpa-onnx first");
211+
}
212+
213+
if (ptr == nullptr || stream.ptr == nullptr) {
214+
return <AudioEvent>[];
215+
}
216+
189217
final pp = SherpaOnnxBindings.sherpaOnnxAudioTaggingCompute
190218
?.call(ptr, stream.ptr, topK) ??
191219
nullptr;

flutter/sherpa_onnx/lib/src/keyword_spotter.dart

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,15 +171,39 @@ class KeywordSpotter {
171171
}
172172

173173
void free() {
174+
if (SherpaOnnxBindings.destroyKeywordSpotter == null) {
175+
throw Exception("Please initialize sherpa-onnx first");
176+
}
177+
178+
if (ptr == nullptr) {
179+
return;
180+
}
174181
SherpaOnnxBindings.destroyKeywordSpotter?.call(ptr);
175182
ptr = nullptr;
176183
}
177184

178185
/// The user has to invoke stream.free() on the returned instance
179186
/// to avoid memory leak
180187
OnlineStream createStream({String keywords = ''}) {
188+
if (keywords == '') {
189+
if (SherpaOnnxBindings.createKeywordStream == null) {
190+
throw Exception("Please initialize sherpa-onnx first");
191+
}
192+
} else {
193+
if (SherpaOnnxBindings.createKeywordStreamWithKeywords == null) {
194+
throw Exception("Please initialize sherpa-onnx first");
195+
}
196+
}
197+
198+
if (ptr == nullptr) {
199+
throw Exception("Failed to create online stream");
200+
}
201+
181202
if (keywords == '') {
182203
final p = SherpaOnnxBindings.createKeywordStream?.call(ptr) ?? nullptr;
204+
if (p == nullptr) {
205+
throw Exception("Failed to create online stream");
206+
}
183207
return OnlineStream(ptr: p);
184208
}
185209

@@ -188,17 +212,38 @@ class KeywordSpotter {
188212
SherpaOnnxBindings.createKeywordStreamWithKeywords?.call(ptr, utf8) ??
189213
nullptr;
190214
calloc.free(utf8);
215+
216+
if (p == nullptr) {
217+
throw Exception("Failed to create online stream");
218+
}
219+
191220
return OnlineStream(ptr: p);
192221
}
193222

194223
bool isReady(OnlineStream stream) {
224+
if (SherpaOnnxBindings.isKeywordStreamReady == null) {
225+
throw Exception("Please initialize sherpa-onnx first");
226+
}
227+
228+
if (ptr == nullptr || stream.ptr == nullptr) {
229+
return false;
230+
}
231+
195232
int ready =
196233
SherpaOnnxBindings.isKeywordStreamReady?.call(ptr, stream.ptr) ?? 0;
197234

198235
return ready == 1;
199236
}
200237

201238
KeywordResult getResult(OnlineStream stream) {
239+
if (SherpaOnnxBindings.getKeywordResultAsJson == null) {
240+
throw Exception("Please initialize sherpa-onnx first");
241+
}
242+
243+
if (ptr == nullptr || stream.ptr == nullptr) {
244+
return KeywordResult(keyword: '');
245+
}
246+
202247
final json =
203248
SherpaOnnxBindings.getKeywordResultAsJson?.call(ptr, stream.ptr) ??
204249
nullptr;
@@ -216,10 +261,24 @@ class KeywordSpotter {
216261
}
217262

218263
void decode(OnlineStream stream) {
264+
if (SherpaOnnxBindings.decodeKeywordStream == null) {
265+
throw Exception("Please initialize sherpa-onnx first");
266+
}
267+
268+
if (ptr == nullptr || stream.ptr == nullptr) {
269+
return;
270+
}
219271
SherpaOnnxBindings.decodeKeywordStream?.call(ptr, stream.ptr);
220272
}
221273

222274
void reset(OnlineStream stream) {
275+
if (SherpaOnnxBindings.resetKeywordStream == null) {
276+
throw Exception("Please initialize sherpa-onnx first");
277+
}
278+
279+
if (ptr == nullptr || stream.ptr == nullptr) {
280+
return;
281+
}
223282
SherpaOnnxBindings.resetKeywordStream?.call(ptr, stream.ptr);
224283
}
225284

flutter/sherpa_onnx/lib/src/offline_punctuation.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,26 @@ class OfflinePunctuation {
100100
}
101101

102102
void free() {
103+
if (SherpaOnnxBindings.sherpaOnnxDestroyOfflinePunctuation == null) {
104+
throw Exception("Please initialize sherpa-onnx first");
105+
}
106+
107+
if (ptr == nullptr) {
108+
return;
109+
}
103110
SherpaOnnxBindings.sherpaOnnxDestroyOfflinePunctuation?.call(ptr);
104111
ptr = nullptr;
105112
}
106113

107114
String addPunct(String text) {
115+
if (SherpaOnnxBindings.sherpaOfflinePunctuationAddPunct == null) {
116+
throw Exception("Please initialize sherpa-onnx first");
117+
}
118+
119+
if (ptr == nullptr) {
120+
return '';
121+
}
122+
108123
final textPtr = text.toNativeUtf8();
109124

110125
final p = SherpaOnnxBindings.sherpaOfflinePunctuationAddPunct

flutter/sherpa_onnx/lib/src/offline_recognizer.dart

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,13 @@ class OfflineRecognizer {
785785
OfflineRecognizer._({required this.ptr, required this.config});
786786

787787
void free() {
788+
if (SherpaOnnxBindings.destroyOfflineRecognizer == null) {
789+
throw Exception("Please initialize sherpa-onnx first");
790+
}
791+
792+
if (ptr == nullptr) {
793+
return;
794+
}
788795
SherpaOnnxBindings.destroyOfflineRecognizer?.call(ptr);
789796
ptr = nullptr;
790797
}
@@ -813,6 +820,14 @@ class OfflineRecognizer {
813820
}
814821

815822
void setConfig(OfflineRecognizerConfig config) {
823+
if (SherpaOnnxBindings.offlineRecognizerSetConfig == null) {
824+
throw Exception("Please initialize sherpa-onnx first");
825+
}
826+
827+
if (ptr == nullptr) {
828+
return;
829+
}
830+
816831
final c = convertConfig(config);
817832

818833
SherpaOnnxBindings.offlineRecognizerSetConfig?.call(ptr, c);
@@ -1017,15 +1032,51 @@ class OfflineRecognizer {
10171032
/// The user has to invoke stream.free() on the returned instance
10181033
/// to avoid memory leak
10191034
OfflineStream createStream() {
1035+
if (SherpaOnnxBindings.createOfflineStream == null) {
1036+
throw Exception("Please initialize sherpa-onnx first");
1037+
}
1038+
1039+
if (ptr == nullptr) {
1040+
throw Exception("Failed to create offline stream");
1041+
}
1042+
10201043
final p = SherpaOnnxBindings.createOfflineStream?.call(ptr) ?? nullptr;
1044+
1045+
if (p == nullptr) {
1046+
throw Exception("Failed to create offline stream");
1047+
}
1048+
10211049
return OfflineStream(ptr: p);
10221050
}
10231051

10241052
void decode(OfflineStream stream) {
1053+
if (SherpaOnnxBindings.decodeOfflineStream == null) {
1054+
throw Exception("Please initialize sherpa-onnx first");
1055+
}
1056+
1057+
if (ptr == nullptr || stream.ptr == nullptr) {
1058+
return;
1059+
}
1060+
10251061
SherpaOnnxBindings.decodeOfflineStream?.call(ptr, stream.ptr);
10261062
}
10271063

10281064
OfflineRecognizerResult getResult(OfflineStream stream) {
1065+
if (SherpaOnnxBindings.getOfflineStreamResultAsJson == null) {
1066+
throw Exception("Please initialize sherpa-onnx first");
1067+
}
1068+
1069+
if (ptr == nullptr || stream.ptr == nullptr) {
1070+
return OfflineRecognizerResult(
1071+
text: '',
1072+
tokens: [],
1073+
timestamps: [],
1074+
lang: '',
1075+
emotion: '',
1076+
event: '',
1077+
);
1078+
}
1079+
10291080
final json =
10301081
SherpaOnnxBindings.getOfflineStreamResultAsJson?.call(stream.ptr) ??
10311082
nullptr;

flutter/sherpa_onnx/lib/src/offline_speaker_diarization.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,13 @@ class OfflineSpeakerDiarization {
185185
{required this.ptr, required this.config, required this.sampleRate});
186186

187187
void free() {
188+
if (SherpaOnnxBindings.sherpaOnnxDestroyOfflineSpeakerDiarization == null) {
189+
throw Exception("Please initialize sherpa-onnx first");
190+
}
191+
192+
if (ptr == nullptr) {
193+
return;
194+
}
188195
SherpaOnnxBindings.sherpaOnnxDestroyOfflineSpeakerDiarization?.call(ptr);
189196
ptr = nullptr;
190197
}
@@ -240,6 +247,10 @@ class OfflineSpeakerDiarization {
240247

241248
List<OfflineSpeakerDiarizationSegment> process(
242249
{required Float32List samples}) {
250+
if (SherpaOnnxBindings.sherpaOnnxOfflineSpeakerDiarizationProcess == null) {
251+
throw Exception("Please initialize sherpa-onnx first");
252+
}
253+
243254
if (ptr == nullptr) {
244255
return <OfflineSpeakerDiarizationSegment>[];
245256
}
@@ -266,6 +277,12 @@ class OfflineSpeakerDiarization {
266277
required Float32List samples,
267278
required int Function(int numProcessedChunks, int numTotalChunks) callback,
268279
}) {
280+
if (SherpaOnnxBindings
281+
.sherpaOnnxOfflineSpeakerDiarizationProcessWithCallbackNoArg ==
282+
null) {
283+
throw Exception("Please initialize sherpa-onnx first");
284+
}
285+
269286
if (ptr == nullptr) {
270287
return <OfflineSpeakerDiarizationSegment>[];
271288
}

flutter/sherpa_onnx/lib/src/offline_speech_denoiser.dart

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ class OfflineSpeechDenoiser {
142142
/// The user is responsible to call the OfflineSpeechDenoiser.free()
143143
/// method of the returned instance to avoid memory leak.
144144
factory OfflineSpeechDenoiser(OfflineSpeechDenoiserConfig config) {
145+
if (SherpaOnnxBindings.sherpaOnnxCreateOfflineSpeechDenoiser == null) {
146+
throw Exception("Please initialize sherpa-onnx first");
147+
}
148+
145149
final c = calloc<SherpaOnnxOfflineSpeechDenoiserConfig>();
146150
c.ref.model.gtcrn.model = config.model.gtcrn.model.toNativeUtf8();
147151
c.ref.model.dpdfnet.model = config.model.dpdfnet.model.toNativeUtf8();
@@ -150,10 +154,6 @@ class OfflineSpeechDenoiser {
150154
c.ref.model.debug = config.model.debug ? 1 : 0;
151155
c.ref.model.provider = config.model.provider.toNativeUtf8();
152156

153-
if (SherpaOnnxBindings.sherpaOnnxCreateOfflineSpeechDenoiser == null) {
154-
throw Exception("Please initialize sherpa-onnx first");
155-
}
156-
157157
final ptr =
158158
SherpaOnnxBindings.sherpaOnnxCreateOfflineSpeechDenoiser?.call(c) ??
159159
nullptr;
@@ -172,11 +172,27 @@ class OfflineSpeechDenoiser {
172172
}
173173

174174
void free() {
175+
if (SherpaOnnxBindings.sherpaOnnxDestroyOfflineSpeechDenoiser == null) {
176+
throw Exception("Please initialize sherpa-onnx first");
177+
}
178+
179+
if (ptr == nullptr) {
180+
return;
181+
}
182+
175183
SherpaOnnxBindings.sherpaOnnxDestroyOfflineSpeechDenoiser?.call(ptr);
176184
ptr = nullptr;
177185
}
178186

179187
DenoisedAudio run({required Float32List samples, required int sampleRate}) {
188+
if (SherpaOnnxBindings.sherpaOnnxOfflineSpeechDenoiserRun == null) {
189+
throw Exception("Please initialize sherpa-onnx first");
190+
}
191+
192+
if (ptr == nullptr) {
193+
return DenoisedAudio(samples: Float32List(0), sampleRate: 0);
194+
}
195+
180196
final n = samples.length;
181197
final Pointer<Float> psamples = calloc<Float>(n);
182198

@@ -193,19 +209,32 @@ class OfflineSpeechDenoiser {
193209
return DenoisedAudio(samples: Float32List(0), sampleRate: 0);
194210
}
195211

196-
final denoisedSamples = p.ref.samples.asTypedList(p.ref.n);
197-
final denoisedSampleRate = p.ref.sampleRate;
198-
final newSamples = Float32List.fromList(denoisedSamples);
212+
final sampleRateOut = p.ref.sampleRate;
213+
final nOut = p.ref.n;
214+
Float32List newSamples = Float32List(0);
215+
if (nOut > 0 && p.ref.samples != nullptr) {
216+
newSamples = Float32List.fromList(p.ref.samples.asTypedList(nOut));
217+
}
199218

200219
SherpaOnnxBindings.sherpaOnnxDestroyDenoisedAudio?.call(p);
201220

202-
return DenoisedAudio(samples: newSamples, sampleRate: denoisedSampleRate);
221+
return DenoisedAudio(samples: newSamples, sampleRate: sampleRateOut);
203222
}
204223

205-
int get sampleRate =>
206-
SherpaOnnxBindings.sherpaOnnxOfflineSpeechDenoiserGetSampleRate
207-
?.call(ptr) ??
208-
0;
224+
int get sampleRate {
225+
if (SherpaOnnxBindings.sherpaOnnxOfflineSpeechDenoiserGetSampleRate ==
226+
null) {
227+
throw Exception("Please initialize sherpa-onnx first");
228+
}
229+
230+
if (ptr == nullptr) {
231+
return 0;
232+
}
233+
234+
return SherpaOnnxBindings.sherpaOnnxOfflineSpeechDenoiserGetSampleRate
235+
?.call(ptr) ??
236+
0;
237+
}
209238

210239
Pointer<SherpaOnnxOfflineSpeechDenoiser> ptr;
211240
OfflineSpeechDenoiserConfig config;

0 commit comments

Comments
 (0)