AI Hunter
Member
Hôm nay chúng ta sẽ hoàn thiện vòng lặp giao tiếp: Nghe (Voice Input) -> Xử lý (AI) -> Nói (TTS).
Mục tiêu: Jarvis tự động đọc to câu trả lời ngay khi nhận được phản hồi từ Server.
Expo cung cấp sẵn thư viện
Tại thư mục
Chúng ta cần làm 3 việc:
Mở file
Thêm Import:
Thêm State và Hàm xử lý:
Trong
Tích hợp vào quá trình nhận tin nhắn:
Tìm đến hàm
Thêm lệnh
Ví dụ trong
Cập nhật Giao diện (Thêm nút Mute):
Sửa lại phần
Để tránh nhầm lẫn, đây là cấu trúc file
*Mẹo:* Nếu điện thoại đọc tiếng Anh thay vì tiếng Việt, hãy vào phần Cài đặt của điện thoại -> Text-to-Speech -> Chọn Engine là Google (trên Android) và tải gói ngôn ngữ Tiếng Việt về.
Bây giờ bạn đã có một Jarvis hoàn chỉnh trên điện thoại:
Nhưng Jarvis vẫn đang "mù". Nó chưa nhìn thấy thế giới thực thông qua Camera điện thoại.
Sẽ ra sao nếu bạn giơ điện thoại lên trước một món đồ và hỏi: "Jarvis, cái này là cái gì?" hoặc "Dịch bảng hiệu này sang tiếng Việt"?
Mục tiêu: Jarvis tự động đọc to câu trả lời ngay khi nhận được phản hồi từ Server.
1. Cài đặt thư viện
Expo cung cấp sẵn thư viện
expo-speech cực kỳ xịn xò.Tại thư mục
jarvis-mobile, chạy lệnh:
Mã:
npx expo install expo-speech
2. Cập nhật Code (App.js)
Chúng ta cần làm 3 việc:
- Import thư viện Speech.
- Viết hàm
speak()để đọc văn bản. - Gọi hàm này ngay sau khi AI trả lời.
Mở file
App.js, cập nhật các đoạn code sau:Thêm Import:
JavaScript:
// ... Các import cũ ...
import * as Speech from 'expo-speech'; // <--- Thêm dòng này
import { Mic, Send, Volume2, VolumeX } from 'lucide-react-native'; // Thêm icon loa
Thêm State và Hàm xử lý:
Trong
export default function App():
JavaScript:
// ... Các state cũ ...
const [isMuted, setIsMuted] = useState(false); // Trạng thái tắt/bật tiếng
// --- HÀM ĐỌC VĂN BẢN (TTS) ---
const speak = (text) => {
if (isMuted) return; // Nếu đang Mute thì thôi
// Nếu đang nói dở câu trước thì dừng lại để nói câu mới
Speech.stop();
Speech.speak(text, {
language: 'vi-VN', // Tiếng Việt
pitch: 1.0, // Cao độ (0.5 - 2.0)
rate: 0.9, // Tốc độ (1.0 là bình thường, 0.9 cho chậm rãi rõ ràng hơn)
});
};
// --- HÀM TẮT/BẬT TIẾNG ---
const toggleMute = () => {
if (!isMuted) {
Speech.stop(); // Tắt ngay lập tức nếu đang nói
}
setIsMuted(!isMuted);
};
Tích hợp vào quá trình nhận tin nhắn:
Tìm đến hàm
sendMessage (Gửi Text) và uploadAudio (Gửi Voice).Thêm lệnh
speak(res.data.answer) vào ngay sau khi setMessages.Ví dụ trong
uploadAudio:
JavaScript:
// ...
// Hiển thị kết quả
const userMsg = { id: Date.now().toString(), role: 'user', text: "🎤 " + res.data.user_text };
const botMsg = { id: (Date.now() + 1).toString(), role: 'bot', text: res.data.answer };
setMessages(prev => [...prev, userMsg, botMsg]);
// JARVIS NÓI NGAY TẠI ĐÂY
speak(res.data.answer);
// ...
Cập nhật Giao diện (Thêm nút Mute):
Sửa lại phần
header để thêm nút bật tắt loa.
JavaScript:
{/* HEADER */}
<View style={styles.header}>
<View>
<Text style={styles.headerTitle}>J.A.R.V.I.S</Text>
<Text style={styles.status}>● ONLINE</Text>
</View>
{/* Nút Mute/Unmute */}
<TouchableOpacity onPress={toggleMute} style={styles.muteBtn}>
{isMuted ?
<VolumeX color="#555" size={24} /> :
<Volume2 color="#00f0ff" size={24} />
}
</TouchableOpacity>
</View>
3. Code Hoàn chỉnh (Tham khảo)
Để tránh nhầm lẫn, đây là cấu trúc file
App.js sau khi đã lắp ghép:
JavaScript:
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View, TextInput, TouchableOpacity, FlatList, SafeAreaView, ActivityIndicator, KeyboardAvoidingView, Platform, StatusBar, Alert } from 'react-native';
import axios from 'axios';
import { Audio } from 'expo-av';
import * as Speech from 'expo-speech'; // TTS
import { Mic, Send, Volume2, VolumeX } from 'lucide-react-native';
const API_URL = "http://192.168.1.X:8000"; // Nhớ thay IP
const API_TOKEN = "sieumatkhau123456";
export default function App() {
const [messages, setMessages] = useState([{ id: '1', role: 'bot', text: 'Jarvis Mobile sẵn sàng.' }]);
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);
const [recording, setRecording] = useState(null);
const [isRecording, setIsRecording] = useState(false);
const [isMuted, setIsMuted] = useState(false); // State loa
useEffect(() => {
(async () => {
await Audio.requestPermissionsAsync();
})();
}, []);
// --- TTS FUNCTION ---
const speak = (text) => {
if (isMuted) return;
Speech.stop();
Speech.speak(text, { language: 'vi-VN', pitch: 1.0, rate: 0.9 });
};
const toggleMute = () => {
if (!isMuted) Speech.stop();
setIsMuted(!isMuted);
};
// --- SEND TEXT ---
const sendMessage = async () => {
if (!input.trim()) return;
const userMsg = { id: Date.now().toString(), role: 'user', text: input };
setMessages(prev => [...prev, userMsg]);
setInput('');
setLoading(true);
try {
const res = await axios.post(`${API_URL}/chat`,
{ message: userMsg.text },
{ headers: { Authorization: `Bearer ${API_TOKEN}` } }
);
const botMsg = { id: (Date.now() + 1).toString(), role: 'bot', text: res.data.answer };
setMessages(prev => [...prev, botMsg]);
speak(res.data.answer); // <--- Đọc
} catch (error) {
// ... Error handling
} finally {
setLoading(false);
}
};
// --- SEND VOICE ---
// ... (Giữ nguyên logic start/stop recording cũ) ...
const uploadAudio = async (uri) => {
setLoading(true);
try {
const formData = new FormData();
formData.append('file', { uri: uri, type: 'audio/m4a', name: 'voice.m4a' });
const res = await axios.post(`${API_URL}/voice-chat`, formData, {
headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${API_TOKEN}` },
});
const userMsg = { id: Date.now().toString(), role: 'user', text: "🎤 " + res.data.user_text };
const botMsg = { id: (Date.now() + 1).toString(), role: 'bot', text: res.data.answer };
setMessages(prev => [...prev, userMsg, botMsg]);
speak(res.data.answer); // <--- Đọc
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
// ... (Phần render UI giữ nguyên, chỉ thêm nút Mute ở Header như hướng dẫn trên) ...
return (
<SafeAreaView style={styles.container}>
{/* ... UI Code ... */}
{/* Bạn hãy copy phần UI của bài trước và thêm nút Mute vào Header nhé */}
</SafeAreaView>
)
}
const styles = StyleSheet.create({
// ... Styles cũ ...
muteBtn: {
padding: 10,
}
});
4. Test hệ thống
- Reload lại App (
rtrong terminal). - Nhấn giữ nút Mic và hỏi: "Jarvis, hát một bài đi."
- Kết quả:
- Màn hình hiện chữ lời bài hát.
- Điện thoại tự động đọc to nội dung đó lên.
- Nếu thấy ồn, bấm vào icon cái Loa ở góc trên để Mute.
- Màn hình hiện chữ lời bài hát.
*Mẹo:* Nếu điện thoại đọc tiếng Anh thay vì tiếng Việt, hãy vào phần Cài đặt của điện thoại -> Text-to-Speech -> Chọn Engine là Google (trên Android) và tải gói ngôn ngữ Tiếng Việt về.
Tổng kết
Bây giờ bạn đã có một Jarvis hoàn chỉnh trên điện thoại:
- Mắt: Nhìn giao diện Iron Man.
- Tai: Nghe lệnh giọng nói (Whisper).
- Miệng: Trả lời bằng âm thanh (Native TTS).
- Não: Xử lý thông minh (Llama/GPT).
Nhưng Jarvis vẫn đang "mù". Nó chưa nhìn thấy thế giới thực thông qua Camera điện thoại.
Sẽ ra sao nếu bạn giơ điện thoại lên trước một món đồ và hỏi: "Jarvis, cái này là cái gì?" hoặc "Dịch bảng hiệu này sang tiếng Việt"?
Bài viết liên quan