From cdd7fe285856bdd7e9a053f211654848c533c73d Mon Sep 17 00:00:00 2001 From: Ian Lavery Date: Tue, 8 Aug 2023 15:47:55 -0700 Subject: [PATCH 1/3] init --- binding/flutter/CHANGELOG.md | 5 +- binding/flutter/lib/rhino_manager.dart | 112 ++++++++++++------------- binding/flutter/pubspec.lock | 2 +- binding/flutter/pubspec.yaml | 4 +- demo/flutter/ios/Podfile.lock | 15 ++-- demo/flutter/lib/main.dart | 12 ++- demo/flutter/pubspec.lock | 10 +-- demo/flutter/pubspec.yaml | 3 +- 8 files changed, 83 insertions(+), 80 deletions(-) diff --git a/binding/flutter/CHANGELOG.md b/binding/flutter/CHANGELOG.md index 4370da00c..a00b3d96b 100644 --- a/binding/flutter/CHANGELOG.md +++ b/binding/flutter/CHANGELOG.md @@ -39,4 +39,7 @@ * Additional language support added (ar, hi, nl, pl, ru, sv, vn, zh) ## [2.2.1] - 2023-06-01 -* Expose Rhino properties through RhinoManager class \ No newline at end of file +* Expose Rhino properties through RhinoManager class + +## [2.2.2] - 2023-08-08 +* Update flutter-voice-processor \ No newline at end of file diff --git a/binding/flutter/lib/rhino_manager.dart b/binding/flutter/lib/rhino_manager.dart index 9df71f055..c3540a8ea 100644 --- a/binding/flutter/lib/rhino_manager.dart +++ b/binding/flutter/lib/rhino_manager.dart @@ -23,14 +23,13 @@ typedef InferenceCallback = Function(RhinoInference inference); typedef ProcessErrorCallback = Function(RhinoException error); class RhinoManager { - final VoiceProcessor? _voiceProcessor; + VoiceProcessor? _voiceProcessor; Rhino? _rhino; - final InferenceCallback _inferenceCallback; - RemoveListener? _removeVoiceProcessorListener; - RemoveListener? _removeErrorListener; + late VoiceProcessorFrameListener _frameListener; + late VoiceProcessorErrorListener _errorListener; - bool _awaitingStop = false; + bool _isListening; /// Rhino version string String? get version => _rhino?.version; @@ -91,88 +90,89 @@ class RhinoManager { } // private constructor - RhinoManager._(this._rhino, this._inferenceCallback, + RhinoManager._(this._rhino, InferenceCallback inferenceCallback, ProcessErrorCallback? processErrorCallback) - : _voiceProcessor = VoiceProcessor.getVoiceProcessor( - _rhino!.frameLength, _rhino.sampleRate) { - if (_voiceProcessor == null) { - throw RhinoRuntimeException("flutter_voice_processor not available."); - } - _removeVoiceProcessorListener = - _voiceProcessor!.addListener((buffer) async { - if (_awaitingStop) { - return; - } - - // cast from dynamic to int array - List rhinoFrame; - try { - rhinoFrame = (buffer as List).cast(); - } on Error { - RhinoException castError = RhinoException( - "flutter_voice_processor sent an unexpected data type."); - processErrorCallback == null - ? print(castError.message) - : processErrorCallback(castError); + : _voiceProcessor = VoiceProcessor.instance, + _isListening = false { + _frameListener = (List frame) async { + if (!_isListening) { return; } - // process frame with Rhino try { - RhinoInference? rhinoResult = await _rhino?.process(rhinoFrame); + RhinoInference? rhinoResult = await _rhino?.process(frame); if ((rhinoResult != null) && (rhinoResult.isFinalized)) { - _awaitingStop = true; - - _inferenceCallback(rhinoResult); - // stop audio processing - await _voiceProcessor?.stop(); + inferenceCallback(rhinoResult); + await _stop(); } } on RhinoException catch (error) { processErrorCallback == null - ? print(error.message) + ? print("RhinoException: ${error.message}") : processErrorCallback(error); - } finally { - _awaitingStop = false; } - }); - - _removeErrorListener = _voiceProcessor!.addErrorListener((errorMsg) { - RhinoException nativeError = RhinoException(errorMsg as String); + }; + _errorListener = (VoiceProcessorException error) { processErrorCallback == null - ? print(nativeError.message) - : processErrorCallback(nativeError); - }); + ? print("RhinoException: ${error.message}") + : processErrorCallback(RhinoException(error.message)); + }; + } + + Future _stop() async { + if (!_isListening) { + return; + } + + _voiceProcessor?.removeErrorListener(_errorListener); + _voiceProcessor?.removeFrameListener(_frameListener); + + if (_voiceProcessor?.numFrameListeners == 0) { + try { + await _voiceProcessor?.stop(); + } on PlatformException catch (e) { + throw RhinoRuntimeException( + "Failed to stop audio recording: ${e.message}"); + } + } + + _isListening = false; } - /// Opens audio input stream and sends audio frames to Rhino until a inference - /// result is sent via inference callback - /// Throws a `RhinoException` if there was a problem starting the audio engine + /// Starts audio recording and processing with the Rhino egine until a + /// inference result is sent via the `inferenceCallback` + /// Throws a `RhinoException` if there was a problem starting audio recording. Future process() async { + if (_isListening) { + return; + } if (_rhino == null || _voiceProcessor == null) { throw RhinoInvalidStateException( "Cannot start Rhino - resources have already been released"); } if (await _voiceProcessor?.hasRecordAudioPermission() ?? false) { + _voiceProcessor?.addFrameListener(_frameListener); + _voiceProcessor?.addErrorListener(_errorListener); try { - await _voiceProcessor!.start(); - } on PlatformException { + await _voiceProcessor?.start(_rhino!.frameLength, _rhino!.sampleRate); + } on PlatformException catch (e) { throw RhinoRuntimeException( - "Audio engine failed to start. Hardware may not be supported."); + "Failed to start audio recording: ${e.message}"); } } else { throw RhinoRuntimeException( "User did not give permission to record audio."); } + + _isListening = true; } - /// Releases Rhino and audio resources + /// Releases Rhino and audio resources. + /// Throws a `RhinoException` if there was a problem stopping audio recording. Future delete() async { - if (_voiceProcessor?.isRecording ?? false) { - await _voiceProcessor!.stop(); - } - _removeVoiceProcessorListener?.call(); - _removeErrorListener?.call(); + await _stop(); + _voiceProcessor = null; + _rhino?.delete(); _rhino = null; } diff --git a/binding/flutter/pubspec.lock b/binding/flutter/pubspec.lock index c90682a1e..5e8d34cc8 100644 --- a/binding/flutter/pubspec.lock +++ b/binding/flutter/pubspec.lock @@ -80,7 +80,7 @@ packages: name: flutter_voice_processor url: "https://pub.dartlang.org" source: hosted - version: "1.0.10" + version: "1.1.0" lints: dependency: "direct dev" description: diff --git a/binding/flutter/pubspec.yaml b/binding/flutter/pubspec.yaml index 7432106ee..570e90b0b 100644 --- a/binding/flutter/pubspec.yaml +++ b/binding/flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: rhino_flutter description: A Flutter plugin for Picovoice's Rhino Speech-to-Intent engine -version: 2.2.1 +version: 2.2.2 homepage: https://picovoice.ai/ repository: https://github.com/Picovoice/rhino/ documentation: https://picovoice.ai/docs/rhino/ @@ -14,7 +14,7 @@ dependencies: flutter: sdk: flutter - flutter_voice_processor: ^1.0.10 + flutter_voice_processor: ^1.1.0 dev_dependencies: flutter_test: diff --git a/demo/flutter/ios/Podfile.lock b/demo/flutter/ios/Podfile.lock index 9374141de..7cc998f52 100644 --- a/demo/flutter/ios/Podfile.lock +++ b/demo/flutter/ios/Podfile.lock @@ -1,15 +1,16 @@ PODS: - Flutter (1.0.0) - - flutter_voice_processor (1.0.6): + - flutter_voice_processor (1.1.0): - Flutter + - ios-voice-processor (~> 1.1.0) - integration_test (0.0.1): - Flutter - - ios-voice-processor (1.0.3) + - ios-voice-processor (1.1.0) - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - Rhino-iOS (2.2.0): - - ios-voice-processor (~> 1.0.2) + - Rhino-iOS (2.2.2): + - ios-voice-processor (~> 1.1.0) - rhino_flutter (2.2.0): - Flutter - Rhino-iOS (~> 2.2.0) @@ -40,11 +41,11 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - flutter_voice_processor: afb59b604d99397a1ccf15c935ac8f2c2327f09f + flutter_voice_processor: 53afbf59ad3feb82f4a379fea9ed8dc98495210f integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5 - ios-voice-processor: 65b25a8db69ea25ffba0eeef37bae71a982f34cc + ios-voice-processor: 8e32d7f980a06d392d128ef1cd19cf6ddcaca3c1 path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 - Rhino-iOS: 5f6c2edc809a913f7559da93fc92c0e66c7bf711 + Rhino-iOS: 0fad86b28d35f67ccb6bd0a2efbbcc0d88b05124 rhino_flutter: 4c3b4bbe00206617c38ea6f7c2f17d23b2879e90 PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d diff --git a/demo/flutter/lib/main.dart b/demo/flutter/lib/main.dart index 20e149cf0..2478faf17 100644 --- a/demo/flutter/lib/main.dart +++ b/demo/flutter/lib/main.dart @@ -154,10 +154,7 @@ class _MyAppState extends State { rhinoText = "Listening..."; }); } on RhinoException catch (ex) { - print("Failed to start audio capture: ${ex.message}"); - setState(() { - isButtonDisabled = false; - }); + errorCallback(ex); } } @@ -223,7 +220,7 @@ class _MyAppState extends State { margin: EdgeInsets.only(right: 10, top: 10), child: ElevatedButton( style: buttonStyle, - onPressed: (isButtonDisabled || isError) + onPressed: (isProcessing || isButtonDisabled || isError) ? null : () { _showContextInfo(context); @@ -249,8 +246,9 @@ class _MyAppState extends State { height: 130, child: ElevatedButton( style: buttonStyle, - onPressed: - (isButtonDisabled || isError) ? null : _startProcessing, + onPressed: (isProcessing || isButtonDisabled || isError) + ? null + : _startProcessing, child: Text(isProcessing ? "..." : "Start", style: TextStyle(fontSize: 30)), ))), diff --git a/demo/flutter/pubspec.lock b/demo/flutter/pubspec.lock index f0e6757e6..e2b7eb4a8 100644 --- a/demo/flutter/pubspec.lock +++ b/demo/flutter/pubspec.lock @@ -99,7 +99,7 @@ packages: name: flutter_voice_processor url: "https://pub.dartlang.org" source: hosted - version: "1.0.10" + version: "1.1.0" fuchsia_remote_debug_protocol: dependency: transitive description: flutter @@ -211,10 +211,10 @@ packages: rhino_flutter: dependency: "direct main" description: - name: rhino_flutter - url: "https://pub.dartlang.org" - source: hosted - version: "2.2.1" + path: "../../binding/flutter" + relative: true + source: path + version: "2.2.2" sky_engine: dependency: transitive description: flutter diff --git a/demo/flutter/pubspec.yaml b/demo/flutter/pubspec.yaml index 9fa05a5eb..c2d70f84c 100644 --- a/demo/flutter/pubspec.yaml +++ b/demo/flutter/pubspec.yaml @@ -13,7 +13,8 @@ dependencies: flutter: sdk: flutter - rhino_flutter: ^2.2.1 + rhino_flutter: + path: ../../binding/flutter dev_dependencies: integration_test: From 52abea1ae20d090b5a96a7fadd2cf9df39ecaf9e Mon Sep 17 00:00:00 2001 From: Ian Lavery Date: Tue, 8 Aug 2023 15:51:06 -0700 Subject: [PATCH 2/3] spelling --- binding/flutter/lib/rhino_manager.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding/flutter/lib/rhino_manager.dart b/binding/flutter/lib/rhino_manager.dart index c3540a8ea..967a59320 100644 --- a/binding/flutter/lib/rhino_manager.dart +++ b/binding/flutter/lib/rhino_manager.dart @@ -138,7 +138,7 @@ class RhinoManager { _isListening = false; } - /// Starts audio recording and processing with the Rhino egine until a + /// Starts audio recording and processing with the Rhino engine until a /// inference result is sent via the `inferenceCallback` /// Throws a `RhinoException` if there was a problem starting audio recording. Future process() async { From 985b6ebf012dcae8dd7a03c84f13affd2c90b885 Mon Sep 17 00:00:00 2001 From: Ian Lavery Date: Wed, 9 Aug 2023 16:57:21 -0700 Subject: [PATCH 3/3] release --- binding/flutter/CHANGELOG.md | 2 +- demo/flutter/pubspec.lock | 6 +++--- demo/flutter/pubspec.yaml | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/binding/flutter/CHANGELOG.md b/binding/flutter/CHANGELOG.md index a00b3d96b..392698f9f 100644 --- a/binding/flutter/CHANGELOG.md +++ b/binding/flutter/CHANGELOG.md @@ -41,5 +41,5 @@ ## [2.2.1] - 2023-06-01 * Expose Rhino properties through RhinoManager class -## [2.2.2] - 2023-08-08 +## [2.2.2] - 2023-08-09 * Update flutter-voice-processor \ No newline at end of file diff --git a/demo/flutter/pubspec.lock b/demo/flutter/pubspec.lock index e2b7eb4a8..c7c61207c 100644 --- a/demo/flutter/pubspec.lock +++ b/demo/flutter/pubspec.lock @@ -211,9 +211,9 @@ packages: rhino_flutter: dependency: "direct main" description: - path: "../../binding/flutter" - relative: true - source: path + name: rhino_flutter + url: "https://pub.dartlang.org" + source: hosted version: "2.2.2" sky_engine: dependency: transitive diff --git a/demo/flutter/pubspec.yaml b/demo/flutter/pubspec.yaml index c2d70f84c..52303ba09 100644 --- a/demo/flutter/pubspec.yaml +++ b/demo/flutter/pubspec.yaml @@ -13,8 +13,7 @@ dependencies: flutter: sdk: flutter - rhino_flutter: - path: ../../binding/flutter + rhino_flutter: ^2.2.2 dev_dependencies: integration_test: