aboutsummaryrefslogtreecommitdiffstats
path: root/lib/screens
diff options
context:
space:
mode:
authorAnuj-S62 <anuj603362@gmail.com>2024-07-11 15:18:31 +0530
committerAnuj-S62 <anuj603362@gmail.com>2024-07-21 19:52:11 +0530
commit0b59086cdddd40689e57969aa7914ba38f1ec2dd (patch)
tree975c38663f57d497eeab0e2649e51308aef9a755 /lib/screens
parentecd34435c1a74b39bf41d59ad479fdc85d0afb7b (diff)
Update Voice Agent Flutter App
- update voice-agent flutter app to use whisper AI for speech-to-text functionality. - Integrated SharedPreferences to store the application state. Bug-AGL: SPEC-5200 Change-Id: I9a05b1d135c1fa07949333391ff828f166b7fe8e Signed-off-by: Anuj-S62 <anuj603362@gmail.com>
Diffstat (limited to 'lib/screens')
-rw-r--r--lib/screens/home_screen.dart380
-rw-r--r--lib/screens/loading_screen.dart81
2 files changed, 363 insertions, 98 deletions
diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart
index a9d567b..4c2407f 100644
--- a/lib/screens/home_screen.dart
+++ b/lib/screens/home_screen.dart
@@ -1,8 +1,10 @@
import 'dart:ui';
import 'package:flutter/material.dart';
+import 'package:flutter_voiceassistant/widgets/online_mode_choice.dart';
import 'package:flutter_voiceassistant/widgets/try_commands.dart';
import 'package:provider/provider.dart';
+import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async';
import '../models/app_state.dart';
import '../widgets/nlu_engine_choice.dart';
@@ -13,6 +15,7 @@ import '../widgets/chat_section.dart';
import '../grpc/generated/voice_agent.pbgrpc.dart';
import '../grpc/voice_agent_client.dart';
import '../utils/app_config.dart';
+import '../widgets/stt_model_choice.dart';
class HomePage extends StatefulWidget {
final AppConfig config;
@@ -36,18 +39,30 @@ class HomePageState extends State<HomePage> {
super.initState();
_config = widget.config; // Initialize _config in the initState
_wakeWord = widget.wakeWord; // Initialize _wakeWord in the initState
- addChatMessage(
- "Assistant in Manual mode. You can send commands directly by pressing the record button.");
+ final appState = context.read<AppState>();
+ if(appState.isWakeWordMode){
+ addChatMessage(
+ 'Switched to Wake Word mode. I\'ll listen for the wake word "$_wakeWord" before responding.');
+ _startWakeWordDetection(context);
+ }
+ else{
+ addChatMessage(
+ "Assistant in Manual mode. You can send commands directly by pressing the record button.");
+ }
+
}
- void changeAssistantMode(BuildContext context, AssistantMode newMode) {
+ Future<void> changeAssistantMode(BuildContext context, AssistantMode newMode) async {
final appState = context.read<AppState>();
clearChatMessages();
appState.streamId = "";
appState.isWakeWordDetected = false;
appState.isCommandProcessing = false;
+ SharedPreferences prefs = await SharedPreferences.getInstance();
+
if (newMode == AssistantMode.wakeWord) {
+ await prefs.setBool('isWakeWordMode', true);
addChatMessage(
'Switched to Wake Word mode. I\'ll listen for the wake word "$_wakeWord" before responding.');
@@ -62,6 +77,7 @@ class HomePageState extends State<HomePage> {
toggleWakeWordDetection(context, true);
}
} else if (newMode == AssistantMode.manual) {
+ prefs.setBool('isWakeWordMode', false);
addChatMessage(
'Switched to Manual mode. You can send commands directly by pressing record button.');
@@ -74,15 +90,17 @@ class HomePageState extends State<HomePage> {
setState(() {}); // Trigger a rebuild
}
- void changeIntentEngine(BuildContext context, NLUEngine newEngine) {
+ Future<void> changeIntentEngine(BuildContext context, NLUEngine newEngine) async {
final appState = context.read<AppState>();
-
+ SharedPreferences prefs = await SharedPreferences.getInstance();
if (newEngine == NLUEngine.snips) {
appState.intentEngine = "snips";
+ await prefs.setString("intentEngine","snips");
addChatMessage(
'Switched to 🚀 Snips engine. Lets be precise and accurate.');
} else if (newEngine == NLUEngine.rasa) {
appState.intentEngine = "rasa";
+ await prefs.setString("intentEngine","rasa");
addChatMessage(
'Switched to 🤖 RASA engine. Conversations just got smarter!');
}
@@ -90,6 +108,42 @@ class HomePageState extends State<HomePage> {
setState(() {}); // Trigger a rebuild
}
+ Future<void> changeSTTFramework(BuildContext context, STTModel newModel) async {
+ final appState = context.read<AppState>();
+ SharedPreferences prefs = await SharedPreferences.getInstance();
+ if (newModel == STTModel.vosk) {
+ appState.sttFramework = "vosk";
+ await prefs.setString("sttFramework", "vosk");
+ // vosk is fast and efficient
+ addChatMessage(
+ 'Switched to 🚀 Vosk framework. Lets be quick and efficient.');
+ } else if (newModel == STTModel.whisper) {
+ appState.sttFramework = "whisper";
+ await prefs.setString("sttFramework", "whisper");
+ addChatMessage(
+ 'Switched to 🤖 Whisper framework. Conversations just got smarter!');
+ }
+ print(appState.sttFramework);
+ setState(() {}); // Trigger a rebuild
+ }
+
+ Future<void> toggleOnlineMode(BuildContext context, OnlineModeEnum mode) async {
+ final appState = context.read<AppState>();
+ SharedPreferences prefs = await SharedPreferences.getInstance();
+ if (mode == OnlineModeEnum.enabled) {
+ appState.onlineMode = true;
+ await prefs.setBool('onlineMode', true);
+ addChatMessage(
+ 'Switched to Online mode. I\'ll be connected to the internet for better results.');
+ } else {
+ appState.onlineMode = false;
+ await prefs.setBool('onlineMode', false);
+ addChatMessage(
+ 'Switched to Offline mode. I\'ll be disconnected from the internet.');
+ }
+ setState(() {}); // Trigger a rebuild
+ }
+
void addChatMessage(String text, {bool isUserMessage = false}) {
final newMessage = ChatMessage(text: text, isUserMessage: isUserMessage);
setState(() {
@@ -123,7 +177,7 @@ class HomePageState extends State<HomePage> {
setState(
() {}); // Trigger a rebuild to ensure the loading indicator is shown, tis a bad practice though but deosn't heavily affect the performance
final response =
- await stopRecording(appState.streamId, appState.intentEngine);
+ await stopRecording(appState.streamId, appState.intentEngine,appState.sttFramework,appState.onlineMode);
// Process and store the result
if (response.status == RecognizeStatusType.REC_SUCCESS) {
appState.commandProcessingText = "Executing command...";
@@ -204,19 +258,31 @@ class HomePageState extends State<HomePage> {
}
Future<RecognizeResult> stopRecording(
- String streamId, String nluModel) async {
+ String streamId, String nluModel, String stt,bool isOnlineMode) async {
+
try {
NLUModel model = NLUModel.RASA;
if (nluModel == "snips") {
model = NLUModel.SNIPS;
}
+ STTFramework sttFramework = STTFramework.VOSK;
+ if (stt == "whisper") {
+ sttFramework = STTFramework.WHISPER;
+ }
+ OnlineMode onlineMode = OnlineMode.OFFLINE;
+ if (isOnlineMode) {
+ onlineMode = OnlineMode.ONLINE;
+ }
// Create a RecognizeControl message to stop recording
final controlMessage = RecognizeVoiceControl()
..action = RecordAction.STOP
..nluModel = model
..streamId =
streamId // Use the same stream ID as when starting recording
- ..recordMode = RecordMode.MANUAL;
+ ..recordMode = RecordMode.MANUAL
+ ..sttFramework = sttFramework
+ ..onlineMode = onlineMode;
+
// Create a Stream with the control message
final controlStream = Stream.fromIterable([controlMessage]);
@@ -375,116 +441,234 @@ class HomePageState extends State<HomePage> {
style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold),
),
SizedBox(height: 15),
- Row(
- mainAxisAlignment: MainAxisAlignment.center,
+ Column(
children: [
- Flexible(
- flex: 1,
- child: ClipRect(
- child: BackdropFilter(
- filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
- child: Card(
- color: _config.theme == "textured-dark" ||
- _config.theme == "textured-light"
- ? Colors.transparent
- : null,
- elevation: 4, // Add elevation for shadow
- shadowColor: _config.theme == "textured-dark" ||
- _config.theme == "textured-light"
- ? Colors.transparent
- : null,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(12),
- ),
- child: Padding(
- padding: EdgeInsets.all(16),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- 'Assistant Mode',
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.bold,
- ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Flexible(
+ flex: 1,
+ child: ClipRect(
+ child: BackdropFilter(
+ filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
+ child: Card(
+ color: _config.theme == "textured-dark" ||
+ _config.theme == "textured-light"
+ ? Colors.transparent
+ : null,
+ elevation: 4, // Add elevation for shadow
+ shadowColor: _config.theme == "textured-dark" ||
+ _config.theme == "textured-light"
+ ? Colors.transparent
+ : null,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'Assistant Mode',
+ style: TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ SizedBox(height: 16), // Add spacing if needed
+ Center(
+ child: Consumer<AppState>(
+ builder: (context, appState, _) {
+ return AssistantModeChoice(
+ onModeChanged: (newMode) {
+ changeAssistantMode(
+ context, newMode);
+ print(newMode);
+ },
+ theme: _config.theme,
+ );
+ },
+ ),
+ ),
+ ],
),
- SizedBox(height: 16), // Add spacing if needed
- Center(
- child: Consumer<AppState>(
- builder: (context, appState, _) {
- return AssistantModeChoice(
- onModeChanged: (newMode) {
- changeAssistantMode(
- context, newMode);
- print(newMode);
+ ),
+ ),
+ ),
+ ),
+ ),
+
+ SizedBox(width: 20), // Add spacing between buttons
+
+ Flexible(
+ flex: 1,
+ child: ClipRect(
+ child: BackdropFilter(
+ filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
+ child: Card(
+ color: _config.theme == "textured-dark" ||
+ _config.theme == "textured-light"
+ ? Colors.transparent
+ : null,
+ elevation: 4, // Add elevation for shadow
+ shadowColor: _config.theme == "textured-dark" ||
+ _config.theme == "textured-light"
+ ? Colors.transparent
+ : null,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'Intent Engine',
+ style: TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ SizedBox(height: 16), // Add spacing if needed
+ Center(
+ child: Consumer<AppState>(
+ builder: (context, appState, _) {
+ return NLUEngineChoice(
+ onEngineChanged: (newEngine) {
+ changeIntentEngine(
+ context, newEngine);
+ print(newEngine);
+ },
+ theme: _config.theme,
+ );
},
- theme: _config.theme,
- );
- },
- ),
+ ),
+ ),
+ ],
),
- ],
+ ),
),
),
),
),
- ),
+ ],
),
- SizedBox(width: 20), // Add spacing between buttons
-
- Flexible(
- flex: 1,
- child: ClipRect(
- child: BackdropFilter(
- filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
- child: Card(
- color: _config.theme == "textured-dark" ||
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Flexible(
+ flex: 1,
+ child: ClipRect(
+ child: BackdropFilter(
+ filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
+ child: Card(
+ color: _config.theme == "textured-dark" ||
_config.theme == "textured-light"
- ? Colors.transparent
- : null,
- elevation: 4, // Add elevation for shadow
- shadowColor: _config.theme == "textured-dark" ||
+ ? Colors.transparent
+ : null,
+ elevation: 4, // Add elevation for shadow
+ shadowColor: _config.theme == "textured-dark" ||
_config.theme == "textured-light"
- ? Colors.transparent
- : null,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(12),
- ),
- child: Padding(
- padding: EdgeInsets.all(16),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- 'Intent Engine',
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.bold,
- ),
+ ? Colors.transparent
+ : null,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'Speech-to-Text Model',
+ style: TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ SizedBox(height: 16), // Add spacing if needed
+ Center(
+ child: Consumer<AppState>(
+ builder: (context, appState, _) {
+ return STTModelChoice(
+ onModelChanged: (newModel) {
+ changeSTTFramework(
+ context, newModel);
+ print(newModel);
+ },
+ theme: _config.theme,
+ );
+ },
+ ),
+ ),
+ ],
),
- SizedBox(height: 16), // Add spacing if needed
- Center(
- child: Consumer<AppState>(
- builder: (context, appState, _) {
- return NLUEngineChoice(
- onEngineChanged: (newEngine) {
- changeIntentEngine(
- context, newEngine);
- print(newEngine);
+ ),
+ ),
+ ),
+ ),
+ ),
+
+ SizedBox(width: 20), // Add spacing between buttons
+
+ Flexible(
+ flex: 1,
+ child: ClipRect(
+ child: BackdropFilter(
+ filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
+ child: Card(
+ color: _config.theme == "textured-dark" ||
+ _config.theme == "textured-light"
+ ? Colors.transparent
+ : null,
+ elevation: 4, // Add elevation for shadow
+ shadowColor: _config.theme == "textured-dark" ||
+ _config.theme == "textured-light"
+ ? Colors.transparent
+ : null,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'Online Mode',
+ style: TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ SizedBox(height: 16), // Add spacing if needed
+ Center(
+ child: Consumer<AppState>(
+ builder: (context, appState, _) {
+ return OnlineModeChoice(
+ onModeChanged: (mode) {
+ toggleOnlineMode(context, mode);
+ print(mode);
+ },
+ theme: _config.theme,
+ );
},
- theme: _config.theme,
- );
- },
- ),
+ ),
+ ),
+ ],
),
- ],
+ ),
),
),
),
),
- ),
+ ],
),
+
+
],
),
SizedBox(height: 15),
diff --git a/lib/screens/loading_screen.dart b/lib/screens/loading_screen.dart
new file mode 100644
index 0000000..f4d6c6d
--- /dev/null
+++ b/lib/screens/loading_screen.dart
@@ -0,0 +1,81 @@
+import 'dart:async';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_voiceassistant/screens/home_screen.dart';
+import 'package:provider/provider.dart';
+import 'package:shared_preferences/shared_preferences.dart';
+
+import '../models/app_state.dart';
+import '../utils/app_config.dart';
+
+class Loading extends StatelessWidget {
+ final AppConfig config;
+ final String wakeWord;
+ Loading({required this.config, required this.wakeWord});
+
+ Future<void> initialize(BuildContext context) async {
+ // Initialize the app
+ final appState = context.read<AppState>();
+ bool isWakeWordMode = false;
+ String intentEngine = "snips";
+ String streamId = "";
+ bool isCommandProcessing = false;
+ String commandProcessingText = "Processing...";
+ String sttFramework = "vosk";
+ bool onlineMode = false;
+
+ SharedPreferences prefs = await SharedPreferences.getInstance();
+ prefs.reload();
+ if(prefs.containsKey('isWakeWordMode')) {
+ isWakeWordMode = await prefs.getBool('isWakeWordMode')!;
+ }
+ if(prefs.containsKey('intentEngine')) {
+ intentEngine = await prefs.getString('intentEngine')!;
+ }
+ if(prefs.containsKey('streamId')) {
+ streamId = await prefs.getString('streamId')!;
+ }
+ if(prefs.containsKey('isCommandProcessing')) {
+ isCommandProcessing = await prefs.getBool('isCommandProcessing')!;
+ }
+ if(prefs.containsKey('commandProcessingText')) {
+ commandProcessingText = await prefs.getString('commandProcessingText')!;
+ }
+ if(prefs.containsKey('sttFramework')) {
+ sttFramework = await prefs.getString('sttFramework')!;
+ }
+ if(prefs.containsKey('onlineMode')) {
+ onlineMode = await prefs.getBool('onlineMode')!;
+ }
+
+ appState.isWakeWordMode = isWakeWordMode;
+ appState.intentEngine = intentEngine;
+ appState.streamId = streamId;
+ appState.isCommandProcessing = isCommandProcessing;
+ appState.commandProcessingText = commandProcessingText;
+ appState.sttFramework = sttFramework;
+ appState.onlineMode = onlineMode;
+
+ print('isWakeWordMode: $isWakeWordMode');
+ print('intentEngine: $intentEngine');
+ print('streamId: $streamId');
+ print('isCommandProcessing: $isCommandProcessing');
+ print('commandProcessingText: $commandProcessingText');
+ print('sttFramework: $sttFramework');
+ print('onlineMode: $onlineMode');
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder(
+ future: initialize(context),
+ builder: (context, snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ return HomePage(config: config, wakeWord: wakeWord);
+ } else {
+ return CupertinoActivityIndicator();
+ }
+ },
+ );
+ }
+}