diff --git a/.github/workflows/flutter.yaml b/.github/workflows/flutter.yaml index 86ca8260..01660d82 100644 --- a/.github/workflows/flutter.yaml +++ b/.github/workflows/flutter.yaml @@ -123,6 +123,7 @@ jobs: pushd example/assets curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx + curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx git clone https://github.com/csukuangfj/sr-data rm -rf sr-data/.git diff --git a/sherpa-onnx/flutter/.gitignore b/sherpa-onnx/flutter/.gitignore index e54732c8..cf8f95e1 100644 --- a/sherpa-onnx/flutter/.gitignore +++ b/sherpa-onnx/flutter/.gitignore @@ -143,3 +143,5 @@ xcuserdata/ ## Xcode 8 and earlier *.xcscmblueprint *.xccheckout + +flutter_jank_metrics*.json diff --git a/sherpa-onnx/flutter/example/assets/README.md b/sherpa-onnx/flutter/example/assets/README.md index cffaf536..e3135e91 100644 --- a/sherpa-onnx/flutter/example/assets/README.md +++ b/sherpa-onnx/flutter/example/assets/README.md @@ -6,6 +6,7 @@ # switch to this directory and run wget https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx git clone https://github.com/csukuangfj/sr-data rm -rf sr-data/.git diff --git a/sherpa-onnx/flutter/example/lib/main.dart b/sherpa-onnx/flutter/example/lib/main.dart index 317b91f9..874eedd6 100644 --- a/sherpa-onnx/flutter/example/lib/main.dart +++ b/sherpa-onnx/flutter/example/lib/main.dart @@ -1,7 +1,9 @@ // Copyright (c) 2024 Xiaomi Corporation import 'package:sherpa_onnx/sherpa_onnx.dart' as sherpa_onnx; import 'package:flutter/material.dart'; + import "./speaker_identification_test.dart"; +import "./vad_test.dart"; void main() { runApp(const MyApp()); @@ -51,6 +53,7 @@ class _MyHomePageState extends State { if (_counter <= 10) { sherpa_onnx.initBindings(); await testSpeakerID(); + // await testVad(); } setState(() { diff --git a/sherpa-onnx/flutter/example/lib/vad_test.dart b/sherpa-onnx/flutter/example/lib/vad_test.dart new file mode 100644 index 00000000..d6edbb30 --- /dev/null +++ b/sherpa-onnx/flutter/example/lib/vad_test.dart @@ -0,0 +1,62 @@ +// Copyright (c) 2024 Xiaomi Corporation +import 'dart:typed_data'; +import 'package:sherpa_onnx/sherpa_onnx.dart' as sherpa_onnx; +import './utils.dart'; + +Future testVad() async { + final src = 'assets/silero_vad.onnx'; + final modelPath = await copyAssetFile(src: src, dst: 'silero_vad.onnx'); + + final sileroVadConfig = sherpa_onnx.SileroVadModelConfig(model: modelPath); + final config = sherpa_onnx.VadModelConfig( + sileroVad: sileroVadConfig, + numThreads: 1, + debug: true, + ); + + final vad = sherpa_onnx.VoiceActivityDetector( + config: config, bufferSizeInSeconds: 10); + print('before vad.free(): ${vad.ptr}'); + vad.free(); + print('after vad.free(): ${vad.ptr}'); + + final buffer = sherpa_onnx.CircularBuffer(capacity: 16000 * 2); + + final d = Float32List.fromList([0, 10, 20, 30]); + buffer.push(d); + assert(d.length == buffer.size, '${d.length} vs ${buffer.size}'); + + final f = Float32List.fromList([-5, 100.25, 599]); + buffer.push(f); + + assert(buffer.size == d.length + f.length); + final g = buffer.get(startIndex: 0, n: 5); + + assert(g.length == 5); + assert(g[0] == 0); + assert(g[1] == 10); + assert(g[2] == 20); + assert(g[3] == 30); + assert(g[4] == -5); + + assert(buffer.size == d.length + f.length); + + buffer.pop(3); + assert(buffer.size == d.length + f.length - 3); + + final h = buffer.get(startIndex: buffer.head, n: 4); + assert(h.length == 4); + assert(h[0] == 30); + assert(h[1] == -5); + assert(h[2] == 100.25); + assert(h[3] == 599); + + buffer.reset(); + + assert(buffer.size == 0); + assert(buffer.head == 0); + + print('before free: ${buffer.ptr}'); + buffer.free(); + print('after free: ${buffer.ptr}'); +} diff --git a/sherpa-onnx/flutter/lib/sherpa_onnx.dart b/sherpa-onnx/flutter/lib/sherpa_onnx.dart index 2985fb41..160e5455 100644 --- a/sherpa-onnx/flutter/lib/sherpa_onnx.dart +++ b/sherpa-onnx/flutter/lib/sherpa_onnx.dart @@ -2,10 +2,11 @@ import 'dart:io'; import 'dart:ffi'; -import 'src/sherpa_onnx_bindings.dart'; -export 'src/speaker_identification.dart'; export 'src/online_stream.dart'; +export 'src/speaker_identification.dart'; +export 'src/vad.dart'; export 'src/wave_reader.dart'; +import 'src/sherpa_onnx_bindings.dart'; final DynamicLibrary _dylib = () { if (Platform.isIOS) { diff --git a/sherpa-onnx/flutter/lib/src/online_stream.dart b/sherpa-onnx/flutter/lib/src/online_stream.dart index c585ae9a..ad487507 100644 --- a/sherpa-onnx/flutter/lib/src/online_stream.dart +++ b/sherpa-onnx/flutter/lib/src/online_stream.dart @@ -1,8 +1,9 @@ // Copyright (c) 2024 Xiaomi Corporation -import 'dart:typed_data'; import 'dart:ffi'; +import 'dart:typed_data'; import 'package:ffi/ffi.dart'; -import "./sherpa_onnx_bindings.dart"; + +import './sherpa_onnx_bindings.dart'; class OnlineStream { /// The user has to call OnlineStream.free() to avoid memory leak. diff --git a/sherpa-onnx/flutter/lib/src/sherpa_onnx_bindings.dart b/sherpa-onnx/flutter/lib/src/sherpa_onnx_bindings.dart index 95869542..920cfbb1 100644 --- a/sherpa-onnx/flutter/lib/src/sherpa_onnx_bindings.dart +++ b/sherpa-onnx/flutter/lib/src/sherpa_onnx_bindings.dart @@ -2,6 +2,47 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +final class SherpaOnnxSileroVadModelConfig extends Struct { + external Pointer model; + + @Float() + external double threshold; + + @Float() + external double minSilenceDuration; + + @Float() + external double minSpeechDuration; + + @Int32() + external int windowSize; +} + +final class SherpaOnnxVadModelConfig extends Struct { + external SherpaOnnxSileroVadModelConfig sileroVad; + + @Int32() + external int sampleRate; + + @Int32() + external int numThreads; + + external Pointer provider; + + @Int32() + external int debug; +} + +final class SherpaOnnxSpeechSegment extends Struct { + @Int32() + external int start; + + external Pointer samples; + + @Int32() + external int n; +} + final class SherpaOnnxWave extends Struct { external Pointer samples; @@ -24,17 +65,136 @@ final class SherpaOnnxSpeakerEmbeddingExtractorConfig extends Struct { external Pointer provider; } +final class SherpaOnnxCircularBuffer extends Opaque {} + +final class SherpaOnnxVoiceActivityDetector extends Opaque {} + final class SherpaOnnxOnlineStream extends Opaque {} final class SherpaOnnxSpeakerEmbeddingExtractor extends Opaque {} final class SherpaOnnxSpeakerEmbeddingManager extends Opaque {} +typedef SherpaOnnxCreateVoiceActivityDetectorNative + = Pointer Function( + Pointer, Float); + +typedef SherpaOnnxCreateVoiceActivityDetector + = Pointer Function( + Pointer, double); + +typedef SherpaOnnxDestroyVoiceActivityDetectorNative = Void Function( + Pointer); + +typedef SherpaOnnxDestroyVoiceActivityDetector = void Function( + Pointer); + +typedef SherpaOnnxVoiceActivityDetectorAcceptWaveformNative = Void Function( + Pointer, Pointer, Int32); + +typedef SherpaOnnxVoiceActivityDetectorAcceptWaveform = void Function( + Pointer, Pointer, int); + +typedef SherpaOnnxVoiceActivityDetectorEmptyNative = Int32 Function( + Pointer); + +typedef SherpaOnnxVoiceActivityDetectorEmpty = int Function( + Pointer); + +typedef SherpaOnnxVoiceActivityDetectorDetectedNative = Int32 Function( + Pointer); + +typedef SherpaOnnxVoiceActivityDetectorDetected = int Function( + Pointer); + +typedef SherpaOnnxVoiceActivityDetectorPopNative = Void Function( + Pointer); + +typedef SherpaOnnxVoiceActivityDetectorPop = void Function( + Pointer); + +typedef SherpaOnnxVoiceActivityDetectorClearNative = Void Function( + Pointer); + +typedef SherpaOnnxVoiceActivityDetectorClear = void Function( + Pointer); + +typedef SherpaOnnxVoiceActivityDetectorResetNative = Void Function( + Pointer); + +typedef SherpaOnnxVoiceActivityDetectorReset = void Function( + Pointer); + +typedef SherpaOnnxVoiceActivityDetectorFrontNative + = Pointer Function( + Pointer); + +typedef SherpaOnnxVoiceActivityDetectorFront + = SherpaOnnxVoiceActivityDetectorFrontNative; + +typedef SherpaOnnxDestroySpeechSegmentNative = Void Function( + Pointer); + +typedef SherpaOnnxDestroySpeechSegment = void Function( + Pointer); + +typedef SherpaOnnxCreateCircularBufferNative = Pointer + Function(Int32); + +typedef SherpaOnnxCreateCircularBuffer = Pointer + Function(int); + +typedef SherpaOnnxDestroyCircularBufferNative = Void Function( + Pointer); + +typedef SherpaOnnxDestroyCircularBuffer = void Function( + Pointer); + +typedef SherpaOnnxCircularBufferPushNative = Void Function( + Pointer, Pointer, Int32); + +typedef SherpaOnnxCircularBufferPush = void Function( + Pointer, Pointer, int); + +typedef SherpaOnnxCircularBufferGetNative = Pointer Function( + Pointer, Int32, Int32); + +typedef SherpaOnnxCircularBufferGet = Pointer Function( + Pointer, int, int); + +typedef SherpaOnnxCircularBufferFreeNative = Void Function(Pointer); + +typedef SherpaOnnxCircularBufferFree = void Function(Pointer); + +typedef SherpaOnnxCircularBufferPopNative = Void Function( + Pointer, Int32); + +typedef SherpaOnnxCircularBufferPop = void Function( + Pointer, int); + +typedef SherpaOnnxCircularBufferSizeNative = Int32 Function( + Pointer); + +typedef SherpaOnnxCircularBufferSize = int Function( + Pointer); + +typedef SherpaOnnxCircularBufferHeadNative = Int32 Function( + Pointer); + +typedef SherpaOnnxCircularBufferHead = int Function( + Pointer); + +typedef SherpaOnnxCircularBufferResetNative = Void Function( + Pointer); + +typedef SherpaOnnxCircularBufferReset = void Function( + Pointer); + typedef SherpaOnnxCreateSpeakerEmbeddingManagerNative - = Pointer Function(Int32 dim); + = Pointer Function(Int32); typedef SherpaOnnxCreateSpeakerEmbeddingManager - = Pointer Function(int dim); + = Pointer Function(int); typedef SherpaOnnxDestroySpeakerEmbeddingManagerNative = Void Function( Pointer); @@ -190,6 +350,45 @@ typedef SherpaOnnxFreeWaveNative = Void Function(Pointer); typedef SherpaOnnxFreeWave = void Function(Pointer); class SherpaOnnxBindings { + static SherpaOnnxCreateVoiceActivityDetector? createVoiceActivityDetector; + + static SherpaOnnxDestroyVoiceActivityDetector? destroyVoiceActivityDetector; + + static SherpaOnnxVoiceActivityDetectorAcceptWaveform? + voiceActivityDetectorAcceptWaveform; + + static SherpaOnnxVoiceActivityDetectorEmpty? voiceActivityDetectorEmpty; + + static SherpaOnnxVoiceActivityDetectorDetected? voiceActivityDetectorDetected; + + static SherpaOnnxVoiceActivityDetectorPop? voiceActivityDetectorPop; + + static SherpaOnnxVoiceActivityDetectorClear? voiceActivityDetectorClear; + + static SherpaOnnxVoiceActivityDetectorFront? voiceActivityDetectorFront; + + static SherpaOnnxDestroySpeechSegment? destroySpeechSegment; + + static SherpaOnnxVoiceActivityDetectorReset? voiceActivityDetectorReset; + + static SherpaOnnxCreateCircularBuffer? createCircularBuffer; + + static SherpaOnnxDestroyCircularBuffer? destroyCircularBuffer; + + static SherpaOnnxCircularBufferPush? circularBufferPush; + + static SherpaOnnxCircularBufferGet? circularBufferGet; + + static SherpaOnnxCircularBufferFree? circularBufferFree; + + static SherpaOnnxCircularBufferPop? circularBufferPop; + + static SherpaOnnxCircularBufferSize? circularBufferSize; + + static SherpaOnnxCircularBufferHead? circularBufferHead; + + static SherpaOnnxCircularBufferReset? circularBufferReset; + static SherpaOnnxCreateSpeakerEmbeddingExtractor? createSpeakerEmbeddingExtractor; @@ -252,8 +451,107 @@ class SherpaOnnxBindings { static SherpaOnnxFreeWave? freeWave; static void init(DynamicLibrary dynamicLibrary) { + createVoiceActivityDetector ??= dynamicLibrary + .lookup>( + 'SherpaOnnxCreateVoiceActivityDetector') + .asFunction(); + + destroyVoiceActivityDetector ??= dynamicLibrary + .lookup>( + 'SherpaOnnxDestroyVoiceActivityDetector') + .asFunction(); + + voiceActivityDetectorAcceptWaveform ??= dynamicLibrary + .lookup< + NativeFunction< + SherpaOnnxVoiceActivityDetectorAcceptWaveformNative>>( + 'SherpaOnnxVoiceActivityDetectorAcceptWaveform') + .asFunction(); + + voiceActivityDetectorEmpty ??= dynamicLibrary + .lookup>( + 'SherpaOnnxVoiceActivityDetectorEmpty') + .asFunction(); + + voiceActivityDetectorDetected ??= dynamicLibrary + .lookup>( + 'SherpaOnnxVoiceActivityDetectorDetected') + .asFunction(); + + voiceActivityDetectorPop ??= dynamicLibrary + .lookup>( + 'SherpaOnnxVoiceActivityDetectorPop') + .asFunction(); + + voiceActivityDetectorClear ??= dynamicLibrary + .lookup>( + 'SherpaOnnxVoiceActivityDetectorClear') + .asFunction(); + + voiceActivityDetectorFront ??= dynamicLibrary + .lookup>( + 'SherpaOnnxVoiceActivityDetectorFront') + .asFunction(); + + destroySpeechSegment ??= dynamicLibrary + .lookup>( + 'SherpaOnnxDestroySpeechSegment') + .asFunction(); + + voiceActivityDetectorReset ??= dynamicLibrary + .lookup>( + 'SherpaOnnxVoiceActivityDetectorReset') + .asFunction(); + + createCircularBuffer ??= dynamicLibrary + .lookup>( + 'SherpaOnnxCreateCircularBuffer') + .asFunction(); + + destroyCircularBuffer ??= dynamicLibrary + .lookup>( + 'SherpaOnnxDestroyCircularBuffer') + .asFunction(); + + circularBufferPush ??= dynamicLibrary + .lookup>( + 'SherpaOnnxCircularBufferPush') + .asFunction(); + + circularBufferGet ??= dynamicLibrary + .lookup>( + 'SherpaOnnxCircularBufferGet') + .asFunction(); + + circularBufferFree ??= dynamicLibrary + .lookup>( + 'SherpaOnnxCircularBufferFree') + .asFunction(); + + circularBufferPop ??= dynamicLibrary + .lookup>( + 'SherpaOnnxCircularBufferPop') + .asFunction(); + + circularBufferSize ??= dynamicLibrary + .lookup>( + 'SherpaOnnxCircularBufferSize') + .asFunction(); + + circularBufferHead ??= dynamicLibrary + .lookup>( + 'SherpaOnnxCircularBufferHead') + .asFunction(); + + circularBufferReset ??= dynamicLibrary + .lookup>( + 'SherpaOnnxCircularBufferReset') + .asFunction(); + createSpeakerEmbeddingExtractor ??= dynamicLibrary - .lookup>( + .lookup< + NativeFunction< + SherpaOnnxCreateSpeakerEmbeddingExtractorNative>>( 'SherpaOnnxCreateSpeakerEmbeddingExtractor') .asFunction(); diff --git a/sherpa-onnx/flutter/lib/src/speaker_identification.dart b/sherpa-onnx/flutter/lib/src/speaker_identification.dart index 693ae483..0e01044f 100644 --- a/sherpa-onnx/flutter/lib/src/speaker_identification.dart +++ b/sherpa-onnx/flutter/lib/src/speaker_identification.dart @@ -2,19 +2,20 @@ import 'dart:ffi'; import 'dart:typed_data'; import 'package:ffi/ffi.dart'; -import "./sherpa_onnx_bindings.dart"; -import "./online_stream.dart"; + +import './online_stream.dart'; +import './sherpa_onnx_bindings.dart'; class SpeakerEmbeddingExtractorConfig { const SpeakerEmbeddingExtractorConfig( {required this.model, this.numThreads = 1, this.debug = true, - this.provider = "cpu"}); + this.provider = 'cpu'}); @override String toString() { - return "SpeakerEmbeddingExtractorConfig(model: $model, numThreads: $numThreads, debug: $debug, provider: $provider)"; + return 'SpeakerEmbeddingExtractorConfig(model: $model, numThreads: $numThreads, debug: $debug, provider: $provider)'; } final String model; @@ -116,7 +117,7 @@ class SpeakerEmbeddingManager { /// Return true if added successfully; return false otherwise bool add({required String name, required Float32List embedding}) { - assert(embedding.length == this.dim, "${embedding.length} vs ${this.dim}"); + assert(embedding.length == this.dim, '${embedding.length} vs ${this.dim}'); final Pointer namePtr = name.toNativeUtf8(); final int n = embedding.length; @@ -145,7 +146,7 @@ class SpeakerEmbeddingManager { int offset = 0; for (final e in embeddingList) { - assert(e.length == this.dim, "${e.length} vs ${this.dim}"); + assert(e.length == this.dim, '${e.length} vs ${this.dim}'); pList.setAll(offset, e); offset += this.dim; diff --git a/sherpa-onnx/flutter/lib/src/vad.dart b/sherpa-onnx/flutter/lib/src/vad.dart new file mode 100644 index 00000000..ad424f90 --- /dev/null +++ b/sherpa-onnx/flutter/lib/src/vad.dart @@ -0,0 +1,213 @@ +// Copyright (c) 2024 Xiaomi Corporation +import 'dart:ffi'; +import 'dart:typed_data'; +import 'package:ffi/ffi.dart'; + +import './sherpa_onnx_bindings.dart'; + +class SileroVadModelConfig { + const SileroVadModelConfig( + {this.model = '', + this.threshold = 0.5, + this.minSilenceDuration = 0.5, + this.minSpeechDuration = 0.25, + this.windowSize = 512}); + + @override + String toString() { + return 'SileroVadModelConfig(model: $model, threshold: $threshold, minSilenceDuration: $minSilenceDuration, minSpeechDuration: $minSpeechDuration, windowSize: $windowSize)'; + } + + final String model; + final double threshold; + final double minSilenceDuration; + final double minSpeechDuration; + final int windowSize; +} + +class VadModelConfig { + VadModelConfig( + {this.sileroVad = const SileroVadModelConfig(), + this.sampleRate = 16000, + this.numThreads = 1, + this.provider = 'cpu', + this.debug = true}); + + @override + String toString() { + return 'VadModelConfig(sileroVad: $sileroVad, sampleRate: $sampleRate, numThreads: $numThreads, provider: $provider, debug: $debug)'; + } + + final SileroVadModelConfig sileroVad; + final int sampleRate; + final int numThreads; + final String provider; + final bool debug; +} + +class SpeechSegment { + SpeechSegment({required this.samples, required this.start}); + final Float32List samples; + final int start; +} + +class CircularBuffer { + CircularBuffer._({required this.ptr}); + + /// The user has to invoke CircularBuffer.free() on the returned instance + /// to avoid memory leak. + factory CircularBuffer({required int capacity}) { + assert(capacity > 0, 'capacity is $capacity'); + final p = + SherpaOnnxBindings.createCircularBuffer?.call(capacity) ?? nullptr; + + return CircularBuffer._(ptr: p); + } + + void free() { + SherpaOnnxBindings.destroyCircularBuffer?.call(ptr); + ptr = nullptr; + } + + void push(Float32List data) { + final n = data.length; + final Pointer p = calloc(n); + + final pList = p.asTypedList(n); + pList.setAll(0, data); + + SherpaOnnxBindings.circularBufferPush?.call(this.ptr, p, n); + + calloc.free(p); + } + + Float32List get({required int startIndex, required int n}) { + final Pointer p = + SherpaOnnxBindings.circularBufferGet?.call(this.ptr, startIndex, n) ?? + nullptr; + + if (p == nullptr) { + return Float32List(0); + } + + final pList = p.asTypedList(n); + final Float32List ans = Float32List.fromList(pList); + + SherpaOnnxBindings.circularBufferFree?.call(p); + + return ans; + } + + void pop(int n) { + SherpaOnnxBindings.circularBufferPop?.call(this.ptr, n); + } + + void reset() { + SherpaOnnxBindings.circularBufferReset?.call(this.ptr); + } + + int get size => SherpaOnnxBindings.circularBufferSize?.call(this.ptr) ?? 0; + int get head => SherpaOnnxBindings.circularBufferHead?.call(this.ptr) ?? 0; + + Pointer ptr; +} + +class VoiceActivityDetector { + VoiceActivityDetector._({required this.ptr}); + + // The user has to invoke VoiceActivityDetector.free() to avoid memory leak. + factory VoiceActivityDetector( + {required VadModelConfig config, required double bufferSizeInSeconds}) { + final c = calloc(); + + final modelPtr = config.sileroVad.model.toNativeUtf8(); + c.ref.sileroVad.model = modelPtr; + + c.ref.sileroVad.threshold = config.sileroVad.threshold; + c.ref.sileroVad.minSilenceDuration = config.sileroVad.minSilenceDuration; + c.ref.sileroVad.minSpeechDuration = config.sileroVad.minSpeechDuration; + c.ref.sileroVad.windowSize = config.sileroVad.windowSize; + + c.ref.sampleRate = config.sampleRate; + c.ref.numThreads = config.numThreads; + + final providerPtr = config.provider.toNativeUtf8(); + c.ref.provider = providerPtr; + + c.ref.debug = config.debug ? 1 : 0; + + final ptr = SherpaOnnxBindings.createVoiceActivityDetector + ?.call(c, bufferSizeInSeconds) ?? + nullptr; + + calloc.free(providerPtr); + calloc.free(modelPtr); + calloc.free(c); + + return VoiceActivityDetector._(ptr: ptr); + } + + void free() { + SherpaOnnxBindings.destroyVoiceActivityDetector?.call(ptr); + ptr = nullptr; + } + + void acceptWaveform(Float32List samples) { + final n = samples.length; + final Pointer p = calloc(n); + + final pList = p.asTypedList(n); + pList.setAll(0, samples); + + SherpaOnnxBindings.voiceActivityDetectorAcceptWaveform + ?.call(this.ptr, p, n); + + calloc.free(p); + } + + bool isEmpty() { + final int empty = + SherpaOnnxBindings.voiceActivityDetectorEmpty?.call(this.ptr) ?? 0; + + return empty == 1; + } + + bool isDetected() { + final int detected = + SherpaOnnxBindings.voiceActivityDetectorDetected?.call(this.ptr) ?? 0; + + return detected == 1; + } + + void pop() { + SherpaOnnxBindings.voiceActivityDetectorPop?.call(this.ptr); + } + + void clear() { + SherpaOnnxBindings.voiceActivityDetectorClear?.call(this.ptr); + } + + SpeechSegment front() { + final Pointer segment = + SherpaOnnxBindings.voiceActivityDetectorFront?.call(this.ptr) ?? + nullptr; + if (segment == nullptr) { + return SpeechSegment(samples: Float32List(0), start: 0); + } + + final sampleList = segment.ref.samples.asTypedList(segment.ref.n); + final start = segment.ref.start; + + final samples = Float32List.fromList(sampleList); + + SherpaOnnxBindings.destroySpeechSegment?.call(segment); + + return SpeechSegment(samples: samples, start: start); + } + + void reset() { + SherpaOnnxBindings.voiceActivityDetectorReset?.call(this.ptr); + } + + Pointer ptr; +} diff --git a/sherpa-onnx/flutter/lib/src/wave_reader.dart b/sherpa-onnx/flutter/lib/src/wave_reader.dart index da844108..200da405 100644 --- a/sherpa-onnx/flutter/lib/src/wave_reader.dart +++ b/sherpa-onnx/flutter/lib/src/wave_reader.dart @@ -2,7 +2,8 @@ import 'dart:ffi'; import 'dart:typed_data'; import 'package:ffi/ffi.dart'; -import "./sherpa_onnx_bindings.dart"; + +import './sherpa_onnx_bindings.dart'; class WaveData { WaveData({required this.samples, required this.sampleRate});