From 7a9f975ebd27cbe71ed47466a2391716c889f28b Mon Sep 17 00:00:00 2001 From: yaso Date: Thu, 6 Feb 2025 09:27:11 +0200 Subject: [PATCH 1/2] change tts voice on select and fix spacing of settings display --- .../lib/mih_packages/mzansi_ai/ai_chat.dart | 107 ++++++++++++------ 1 file changed, 72 insertions(+), 35 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_ai/ai_chat.dart b/Frontend/lib/mih_packages/mzansi_ai/ai_chat.dart index 190eb7ee..5f964760 100644 --- a/Frontend/lib/mih_packages/mzansi_ai/ai_chat.dart +++ b/Frontend/lib/mih_packages/mzansi_ai/ai_chat.dart @@ -31,9 +31,11 @@ class AiChat extends StatefulWidget { class _AiChatState extends State { final TextEditingController _modelController = TextEditingController(); final TextEditingController _fontSizeController = TextEditingController(); - final TextEditingController _ttsController = TextEditingController(); + final TextEditingController _ttsVoiceController = TextEditingController(); final ValueNotifier _showModelOptions = ValueNotifier(false); FlutterTts _flutterTts = FlutterTts(); + final ValueNotifier _ttsVoiceName = ValueNotifier(""); + // bool _ttsOn = false; String? textStream; List _voices = []; Map? _currentVoice; @@ -138,8 +140,9 @@ class _AiChatState extends State { child: IconButton( color: MzanziInnovationHub.of(context)!.theme.primaryColor(), - onPressed: () { + onPressed: () async { print("Start TTS now"); + _speakText(snapshot.requireData); }, icon: const Icon(Icons.volume_up), @@ -351,7 +354,7 @@ class _AiChatState extends State { alignment: Alignment.centerLeft, child: FittedBox( child: Container( - padding: const EdgeInsets.only(top: 5.0), + padding: const EdgeInsets.all(10.0), decoration: BoxDecoration( color: MzanziInnovationHub.of(context)!.theme.primaryColor(), @@ -385,21 +388,18 @@ class _AiChatState extends State { Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 25), - child: SizedBox( - width: 300, - child: MIHDropdownField( - controller: _modelController, - hintText: "AI Model", - dropdownOptions: const [ - 'deepseek-r1:1.5b', - 'gemma2:2b' - ], - required: true, - editable: true, - enableSearch: false, - ), + SizedBox( + width: 300, + child: MIHDropdownField( + controller: _modelController, + hintText: "AI Model", + dropdownOptions: const [ + 'deepseek-r1:1.5b', + 'gemma2:2b' + ], + required: true, + editable: true, + enableSearch: false, ), ), ], @@ -445,29 +445,53 @@ class _AiChatState extends State { ), ], ), - const SizedBox(height: 10), + const SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ + SizedBox( + width: 230, + child: MIHDropdownField( + controller: _ttsVoiceController, + hintText: "AI Voice", + dropdownOptions: _voices + .map((_voice) => _voice["name"] as String) + .toList(), + required: true, + editable: true, + enableSearch: false, + ), + ), + const SizedBox(width: 10), Padding( - padding: const EdgeInsets.symmetric(horizontal: 25), - child: SizedBox( - width: 300, - child: MIHDropdownField( - controller: _ttsController, - hintText: "AI Voice", - dropdownOptions: _voices - .map((_voice) => _voice["name"] as String) - .toList(), - required: true, - editable: true, - enableSearch: false, + padding: const EdgeInsets.all(5.0), + child: Container( + //color: MzanziInnovationHub.of(context)!.theme.successColor(), + decoration: BoxDecoration( + color: MzanziInnovationHub.of(context)! + .theme + .successColor(), + borderRadius: const BorderRadius.all( + Radius.circular(100), + ), + ), + child: IconButton( + color: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + onPressed: () { + print("Start TTS now"); + + _speakText( + "This is the sample of the Mzansi A.I Voice."); + }, + icon: const Icon(Icons.volume_up), ), ), ), ], ), - const SizedBox(height: 10), + const SizedBox(height: 15), ], ), ), @@ -488,7 +512,7 @@ class _AiChatState extends State { .first["locale"] }, ); - _ttsController.text = _currentVoice!["name"]; + _ttsVoiceController.text = _currentVoice!["name"]; } void _speakText(String text) async { @@ -500,13 +524,25 @@ class _AiChatState extends State { } } + void voiceSelected() { + if (_ttsVoiceController.text.isNotEmpty) { + _ttsVoiceName.value = _ttsVoiceController.text; + print( + "======================================== Voice Set ========================================"); + setTtsVoice(_ttsVoiceController.text); + } else { + _ttsVoiceName.value = ""; + } + } + @override void dispose() { // TODO: implement dispose super.dispose(); _modelController.dispose(); _fontSizeController.dispose(); - _ttsController.dispose(); + _ttsVoiceController.dispose(); + _ttsVoiceController.removeListener(voiceSelected); client.endSession(); _flutterTts.stop(); } @@ -521,7 +557,7 @@ class _AiChatState extends State { print("=================== Voices ===================\n$_voices"); setState(() { _voices = _voices - .where((_voice) => _voice["name"].contains("en")) + .where((_voice) => _voice["name"].contains("en-us")) .toList(); _currentVoice = _voices.first; setTtsVoice(_currentVoice!["name"]); @@ -546,6 +582,7 @@ class _AiChatState extends State { ); _modelController.text = 'gemma2:2b'; _fontSizeController.text = _chatFrontSize.ceil().toString(); + _ttsVoiceController.addListener(voiceSelected); _chatHistory.add( ollama.Message( role: ollama.MessageRole.system, From 4444f9bdaaadba64f1e2bad998574751482ad762 Mon Sep 17 00:00:00 2001 From: yaso Date: Thu, 6 Feb 2025 10:45:58 +0200 Subject: [PATCH 2/2] Fix voice order --- .../lib/mih_packages/mzansi_ai/ai_chat.dart | 138 +++++++++--------- 1 file changed, 72 insertions(+), 66 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_ai/ai_chat.dart b/Frontend/lib/mih_packages/mzansi_ai/ai_chat.dart index 5f964760..72b02e63 100644 --- a/Frontend/lib/mih_packages/mzansi_ai/ai_chat.dart +++ b/Frontend/lib/mih_packages/mzansi_ai/ai_chat.dart @@ -38,7 +38,7 @@ class _AiChatState extends State { // bool _ttsOn = false; String? textStream; List _voices = []; - Map? _currentVoice; + List _voicesString = []; List _messages = []; late types.User _user; late types.User _mihAI; @@ -405,6 +405,50 @@ class _AiChatState extends State { ], ), const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: 230, + child: MIHDropdownField( + controller: _ttsVoiceController, + hintText: "AI Voice", + dropdownOptions: _voicesString, + required: true, + editable: true, + enableSearch: false, + ), + ), + const SizedBox(width: 10), + Padding( + padding: const EdgeInsets.all(5.0), + child: Container( + //color: MzanziInnovationHub.of(context)!.theme.successColor(), + decoration: BoxDecoration( + color: MzanziInnovationHub.of(context)! + .theme + .successColor(), + borderRadius: const BorderRadius.all( + Radius.circular(100), + ), + ), + child: IconButton( + color: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + onPressed: () { + print("Start TTS now"); + + _speakText( + "This is the sample of the Mzansi A.I Voice."); + }, + icon: const Icon(Icons.volume_up), + ), + ), + ), + ], + ), + const SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -446,52 +490,6 @@ class _AiChatState extends State { ], ), const SizedBox(height: 15), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox( - width: 230, - child: MIHDropdownField( - controller: _ttsVoiceController, - hintText: "AI Voice", - dropdownOptions: _voices - .map((_voice) => _voice["name"] as String) - .toList(), - required: true, - editable: true, - enableSearch: false, - ), - ), - const SizedBox(width: 10), - Padding( - padding: const EdgeInsets.all(5.0), - child: Container( - //color: MzanziInnovationHub.of(context)!.theme.successColor(), - decoration: BoxDecoration( - color: MzanziInnovationHub.of(context)! - .theme - .successColor(), - borderRadius: const BorderRadius.all( - Radius.circular(100), - ), - ), - child: IconButton( - color: MzanziInnovationHub.of(context)! - .theme - .primaryColor(), - onPressed: () { - print("Start TTS now"); - - _speakText( - "This is the sample of the Mzansi A.I Voice."); - }, - icon: const Icon(Icons.volume_up), - ), - ), - ), - ], - ), - const SizedBox(height: 15), ], ), ), @@ -503,18 +501,6 @@ class _AiChatState extends State { ); } - void setTtsVoice(String voiceName) { - _flutterTts.setVoice( - { - "name": voiceName, - "locale": _voices - .where((_voice) => _voice["name"].contains(voiceName)) - .first["locale"] - }, - ); - _ttsVoiceController.text = _currentVoice!["name"]; - } - void _speakText(String text) async { try { await _flutterTts.stop(); // Stop any ongoing speech @@ -524,11 +510,23 @@ class _AiChatState extends State { } } + void setTtsVoice(String voiceName) { + _flutterTts.setVoice( + { + "name": voiceName, + "locale": _voices + .where((_voice) => _voice["name"].contains(voiceName)) + .first["locale"] + }, + ); + _ttsVoiceController.text = voiceName; + } + void voiceSelected() { if (_ttsVoiceController.text.isNotEmpty) { _ttsVoiceName.value = _ttsVoiceController.text; - print( - "======================================== Voice Set ========================================"); + // print( + // "======================================== Voice Set ========================================"); setTtsVoice(_ttsVoiceController.text); } else { _ttsVoiceName.value = ""; @@ -549,18 +547,25 @@ class _AiChatState extends State { void initTTS() { _flutterTts.setVolume(0.7); + // _flutterTts.setSpeechRate(0.6); + // _flutterTts.setPitch(1.0); _flutterTts.getVoices.then( (data) { try { _voices = List.from(data); - print("=================== Voices ===================\n$_voices"); setState(() { _voices = _voices - .where((_voice) => _voice["name"].contains("en-us")) + .where( + (_voice) => _voice["name"].toLowerCase().contains("en-us")) .toList(); - _currentVoice = _voices.first; - setTtsVoice(_currentVoice!["name"]); + _voicesString = + _voices.map((_voice) => _voice["name"] as String).toList(); + _voicesString.sort(); + // print( + // "=================== Voices ===================\n$_voicesString"); + + setTtsVoice(_voicesString.first); }); } catch (e) { print(e); @@ -582,7 +587,7 @@ class _AiChatState extends State { ); _modelController.text = 'gemma2:2b'; _fontSizeController.text = _chatFrontSize.ceil().toString(); - _ttsVoiceController.addListener(voiceSelected); + _chatHistory.add( ollama.Message( role: ollama.MessageRole.system, @@ -591,6 +596,7 @@ class _AiChatState extends State { ); _loadMessages(); initTTS(); + _ttsVoiceController.addListener(voiceSelected); } @override