feat(voice-recognition): add speech recognition and emotion-based video switching
Implement voice recognition functionality that displays transcript in a styled container. Add emotion analysis to switch videos based on detected positive/negative keywords in speech. Includes UI updates for transcript display and microphone interaction.
This commit is contained in:
@@ -38,6 +38,11 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 语音识别结果显示区域 -->
|
||||
<div class="transcript-container">
|
||||
<p id="transcript"></p>
|
||||
</div>
|
||||
|
||||
<!-- 底部麦克风和链接 -->
|
||||
<footer class="bottom-bar">
|
||||
<button class="mic-button" id="mic-button" aria-label="Start Listening">
|
||||
|
||||
@@ -69,33 +69,123 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
activeVideo.addEventListener('ended', switchVideo, { once: true });
|
||||
|
||||
|
||||
// --- 麦克风按钮交互和好感度条模拟 ---
|
||||
// --- 语音识别核心 ---
|
||||
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
||||
let recognition;
|
||||
|
||||
// 检查浏览器是否支持语音识别
|
||||
if (SpeechRecognition) {
|
||||
recognition = new SpeechRecognition();
|
||||
recognition.continuous = true; // 持续识别
|
||||
recognition.lang = 'zh-CN'; // 设置语言为中文
|
||||
recognition.interimResults = true; // 获取临时结果
|
||||
|
||||
recognition.onresult = (event) => {
|
||||
const transcriptContainer = document.getElementById('transcript');
|
||||
let final_transcript = '';
|
||||
let interim_transcript = '';
|
||||
|
||||
for (let i = event.resultIndex; i < event.results.length; ++i) {
|
||||
if (event.results[i].isFinal) {
|
||||
final_transcript += event.results[i][0].transcript;
|
||||
} else {
|
||||
interim_transcript += event.results[i][0].transcript;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示最终识别结果
|
||||
transcriptContainer.textContent = final_transcript || interim_transcript;
|
||||
|
||||
// 基于关键词的情感分析和视频切换
|
||||
if (final_transcript) {
|
||||
analyzeAndReact(final_transcript);
|
||||
}
|
||||
};
|
||||
|
||||
recognition.onerror = (event) => {
|
||||
console.error('语音识别错误:', event.error);
|
||||
};
|
||||
|
||||
} else {
|
||||
console.log('您的浏览器不支持语音识别功能。');
|
||||
// 可以在界面上给用户提示
|
||||
}
|
||||
|
||||
// --- 麦克风按钮交互 ---
|
||||
let isListening = false;
|
||||
let currentFavorability = 65; // 与 CSS 中的初始宽度保持一致
|
||||
|
||||
micButton.addEventListener('click', function() {
|
||||
isListening = !isListening;
|
||||
|
||||
// 切换 "监听中" 的样式 (动画)
|
||||
micButton.classList.toggle('is-listening', isListening);
|
||||
if (!SpeechRecognition) return; // 如果不支持,则不执行任何操作
|
||||
|
||||
isListening = !isListening;
|
||||
micButton.classList.toggle('is-listening', isListening);
|
||||
const transcriptContainer = document.querySelector('.transcript-container');
|
||||
const transcriptText = document.getElementById('transcript');
|
||||
|
||||
// 模拟交互:每次点击麦克风,好感度增加
|
||||
if (isListening) {
|
||||
// 增加好感度,但最高不超过 100
|
||||
currentFavorability += 5;
|
||||
if (currentFavorability > 100) {
|
||||
currentFavorability = 100;
|
||||
}
|
||||
transcriptText.textContent = '聆听中...'; // 立刻显示提示
|
||||
transcriptContainer.classList.add('visible');
|
||||
recognition.start();
|
||||
} else {
|
||||
// 如果停止监听,可以稍微降低一点好感度(可选)
|
||||
currentFavorability -= 2;
|
||||
if (currentFavorability < 0) {
|
||||
currentFavorability = 0;
|
||||
}
|
||||
recognition.stop();
|
||||
transcriptContainer.classList.remove('visible');
|
||||
transcriptText.textContent = ''; // 清空文本
|
||||
}
|
||||
|
||||
// 更新好感度进度条的宽度
|
||||
favorabilityBar.style.width = currentFavorability + '%';
|
||||
});
|
||||
|
||||
|
||||
// --- 情感分析与反应 ---
|
||||
const positiveWords = ['开心', '高兴', '喜欢', '太棒了', '你好', '漂亮'];
|
||||
const negativeWords = ['难过', '生气', '讨厌', '伤心'];
|
||||
|
||||
const positiveVideos = [
|
||||
'视频资源/jimeng-2025-07-16-1043-笑着优雅的左右摇晃,过一会儿手扶着下巴,保持微笑.mp4',
|
||||
'视频资源/jimeng-2025-07-16-4437-比耶,然后微笑着优雅的左右摇晃.mp4',
|
||||
'视频资源/生成加油视频.mp4',
|
||||
'视频资源/生成跳舞视频.mp4'
|
||||
];
|
||||
const negativeVideo = '视频资源/负面/jimeng-2025-07-16-9418-双手叉腰,嘴巴一直在嘟囔,表情微微生气.mp4';
|
||||
|
||||
function analyzeAndReact(text) {
|
||||
let reaction = 'neutral'; // 默认为中性
|
||||
|
||||
if (positiveWords.some(word => text.includes(word))) {
|
||||
reaction = 'positive';
|
||||
} else if (negativeWords.some(word => text.includes(word))) {
|
||||
reaction = 'negative';
|
||||
}
|
||||
|
||||
if (reaction !== 'neutral') {
|
||||
switchVideoByEmotion(reaction);
|
||||
}
|
||||
}
|
||||
|
||||
function switchVideoByEmotion(emotion) {
|
||||
let nextVideoSrc;
|
||||
if (emotion === 'positive') {
|
||||
const randomIndex = Math.floor(Math.random() * positiveVideos.length);
|
||||
nextVideoSrc = positiveVideos[randomIndex];
|
||||
} else { // negative
|
||||
nextVideoSrc = negativeVideo;
|
||||
}
|
||||
|
||||
// 避免重复播放同一个视频
|
||||
const currentVideoSrc = activeVideo.querySelector('source').getAttribute('src');
|
||||
if (nextVideoSrc === currentVideoSrc) return;
|
||||
|
||||
// --- 以下逻辑与 switchVideo 函数类似,用于切换视频 ---
|
||||
inactiveVideo.querySelector('source').setAttribute('src', nextVideoSrc);
|
||||
inactiveVideo.load();
|
||||
|
||||
inactiveVideo.addEventListener('canplaythrough', function onCanPlayThrough() {
|
||||
inactiveVideo.removeEventListener('canplaythrough', onCanPlayThrough);
|
||||
inactiveVideo.play().catch(error => console.error("Video play failed:", error));
|
||||
activeVideo.classList.remove('active');
|
||||
inactiveVideo.classList.add('active');
|
||||
[activeVideo, inactiveVideo] = [inactiveVideo, activeVideo];
|
||||
// 情感触发的视频播放结束后,回归随机播放
|
||||
activeVideo.addEventListener('ended', switchVideo, { once: true });
|
||||
}, { once: true });
|
||||
}
|
||||
|
||||
});
|
||||
@@ -125,6 +125,33 @@ html, body {
|
||||
/* 这个区域现在是空的,可以移除或保留以备将来使用 */
|
||||
}
|
||||
|
||||
/* --- 语音识别结果显示 --- */
|
||||
.transcript-container {
|
||||
position: absolute;
|
||||
bottom: 180px; /* 放置在麦克风按钮上方 */
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 80%;
|
||||
max-width: 600px;
|
||||
padding: 15px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
pointer-events: none; /* 默认不响应鼠标事件 */
|
||||
}
|
||||
|
||||
.transcript-container.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#transcript {
|
||||
font-size: 1.2rem;
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px 2px rgba(0,0,0,0.7);
|
||||
}
|
||||
|
||||
/* --- 底部内容 --- */
|
||||
.bottom-bar {
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user