diff options
author | Anuj Solanki <anuj603362@gmail.com> | 2024-09-29 15:57:43 +0530 |
---|---|---|
committer | Anuj Solanki <anuj603362@gmail.com> | 2024-09-29 16:16:41 +0530 |
commit | 053ba8d80e405ca84a0e179c1551c3d440829579 (patch) | |
tree | afcbe6698afcef799d15f0730d6346e859a5e469 | |
parent | 539efac2637415a930b3077ff91abe003ce2fcd4 (diff) |
Implemented auto-mode
- Implemented auto-mode in flutter-speechrecognition-demo
to recognize the wake word and start the voice assistant
automatically.
Bug-AGL: SPEC-5200
Change-Id: Ic946a3f4535a7b16a4e45a5ffdf4a3b4015fdb6f
Signed-off-by: Anuj Solanki <anuj603362@gmail.com>
-rw-r--r-- | lib/models/app_state.dart | 1 | ||||
-rw-r--r-- | lib/screens/home_screen.dart | 77 | ||||
-rw-r--r-- | lib/widgets/wake_word_command_processing.dart | 61 | ||||
-rw-r--r-- | lib/widgets/wake_word_recording.dart | 71 |
4 files changed, 195 insertions, 15 deletions
diff --git a/lib/models/app_state.dart b/lib/models/app_state.dart index 8325b50..4c97654 100644 --- a/lib/models/app_state.dart +++ b/lib/models/app_state.dart @@ -9,4 +9,5 @@ class AppState extends ChangeNotifier { String commandProcessingText = "Processing..."; String sttFramework = "vosk"; bool onlineMode = false; + int recordingTime = 4; } diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 4c2407f..d2d8898 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -16,6 +16,8 @@ import '../grpc/generated/voice_agent.pbgrpc.dart'; import '../grpc/voice_agent_client.dart'; import '../utils/app_config.dart'; import '../widgets/stt_model_choice.dart'; +import '../widgets/wake_word_command_processing.dart'; +import '../widgets/wake_word_recording.dart'; class HomePage extends StatefulWidget { final AppConfig config; @@ -203,24 +205,56 @@ class HomePageState extends State<HomePage> { // Function to start listening for wake word status responses void _startWakeWordDetection(BuildContext context) { final appState = context.read<AppState>(); + // Base condition + if(appState.isWakeWordMode==false){ + return; + } + setState(() {}); voiceAgentClient = VoiceAgentClient(_config.grpcHost, _config.grpcPort); + appState.isWakeWordDetected = false; + appState.isCommandProcessing = false; _wakeWordStatusSubscription = voiceAgentClient.detectWakeWord().listen( - (response) { + (response) async { if (response.status) { - // Wake word detected, you can handle this case here - // Set _isDetectingWakeWord to false to stop the loop + // Wake word detected, handle this case here _stopWakeWordDetection(); appState.isWakeWordDetected = true; - addChatMessage( - 'Wake word detected! Now you can send your command by pressing the record button.'); - setState(() {}); // Trigger a rebuild + addChatMessage('Wake word detected! Starting recording...'); + + // Start recording + appState.isCommandProcessing = false; + String streamId = await startRecording(); + if (streamId.isNotEmpty) { + addChatMessage('Recording started. Please speak your command.'); + + // Wait for 4-5 seconds + await Future.delayed(Duration(seconds: appState.recordingTime)); + + // Stop recording and get the response + appState.isCommandProcessing = true; + RecognizeResult recognizeResult = await stopRecording(streamId, appState.intentEngine,appState.sttFramework,appState.onlineMode); + // Execute the command + await executeCommand(recognizeResult); + + // Wait for 1-2 seconds before resuming wake word detection + await Future.delayed(Duration(seconds: 1)); + + // Resume wake word detection + _startWakeWordDetection(context); + } else { + addChatMessage('Failed to start recording. Please try again.'); + // Resume wake word detection + + _startWakeWordDetection(context); + } } }, onError: (error) { - // Handle any errors that occur during wake word detection print('Error during wake word detection: $error'); // Set _isDetectingWakeWord to false to stop the loop _stopWakeWordDetection(); + // Resume wake word detection + _startWakeWordDetection(context); }, cancelOnError: true, ); @@ -679,11 +713,11 @@ class HomePageState extends State<HomePage> { theme: _config.theme, ), SizedBox(height: 10), - if (!appState.isWakeWordMode || appState.isWakeWordDetected) + if (!appState.isWakeWordMode) TryCommandsSection( onCommandTap: handleCommandTap, theme: _config.theme), SizedBox(height: 30), - if (!appState.isWakeWordMode || appState.isWakeWordDetected) + if (!appState.isWakeWordMode) if (!appState.isCommandProcessing) Center( child: @@ -712,13 +746,26 @@ class HomePageState extends State<HomePage> { ), ]) else - Center( - child: Consumer<AppState>( + if(!appState.isWakeWordDetected) + Center( + child: Consumer<AppState>( builder: (context, appState, _) { - return ListeningForWakeWordSection(); - }, + return ListeningForWakeWordSection(); + }, + ), + ) + else + if (!appState.isCommandProcessing && appState.isWakeWordDetected) + Center( + child: + Consumer<AppState>(builder: (context, appState, _) { + return WakeWordRecording(); + }), + ) + else + Center( + child: ProcessingCommandSection(), ), - ), SizedBox(height: 30), ], ), @@ -727,4 +774,4 @@ class HomePageState extends State<HomePage> { ), ); } -} +}
\ No newline at end of file diff --git a/lib/widgets/wake_word_command_processing.dart b/lib/widgets/wake_word_command_processing.dart new file mode 100644 index 0000000..9872d36 --- /dev/null +++ b/lib/widgets/wake_word_command_processing.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; + +class ProcessingCommandSection extends StatefulWidget { + @override + ProcessingCommandSectionState createState() => ProcessingCommandSectionState(); +} + +class ProcessingCommandSectionState extends State<ProcessingCommandSection> + with SingleTickerProviderStateMixin { + late AnimationController _controller; + + @override + void initState() { + super.initState(); + + // Create an animation controller + _controller = AnimationController( + vsync: this, + duration: Duration(seconds: 2), // Adjust the duration as needed + ); + + // Start the animation + _controller.repeat(); + } + + @override + void dispose() { + _controller.dispose(); // Dispose of the animation controller + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return Transform.rotate( + angle: _controller.value * 2.0 * 3.1415927, // 2 * pi + child: Icon( + Icons.autorenew, // Replace with your processing icon + size: 60, + color: Colors.blueAccent, + ), + ); + }, + ), + SizedBox(height: 8), + Text( + 'Processing...', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ); + } +}
\ No newline at end of file diff --git a/lib/widgets/wake_word_recording.dart b/lib/widgets/wake_word_recording.dart new file mode 100644 index 0000000..38ccc77 --- /dev/null +++ b/lib/widgets/wake_word_recording.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; + +class WakeWordRecording extends StatefulWidget { + @override + WakeWordRecordingState createState() => WakeWordRecordingState(); +} + +class WakeWordRecordingState extends State<WakeWordRecording> + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation<double> _bounceAnimation; + + @override + void initState() { + super.initState(); + + // Create an animation controller + _controller = AnimationController( + vsync: this, + duration: Duration(milliseconds: 500), // Adjust the duration as needed + ); + + // Create a bounce animation + _bounceAnimation = Tween<double>( + begin: 0, + end: 10, + ).animate(CurvedAnimation( + parent: _controller, + curve: Curves.easeInOut, + )); + + // Start the animation + _controller.repeat(reverse: true); + } + + @override + void dispose() { + _controller.dispose(); // Dispose of the animation controller + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return Transform.translate( + offset: Offset(0, _bounceAnimation.value), + child: Icon( + Icons.mic, // Replace with your recording icon + size: 60, + color: Colors.redAccent, + ), + ); + }, + ), + SizedBox(height: 8), + Text( + 'Recording...', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ); + } +}
\ No newline at end of file |