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>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<!-- 语音识别结果显示区域 -->
|
||||||
|
<div class="transcript-container">
|
||||||
|
<p id="transcript"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 底部麦克风和链接 -->
|
<!-- 底部麦克风和链接 -->
|
||||||
<footer class="bottom-bar">
|
<footer class="bottom-bar">
|
||||||
<button class="mic-button" id="mic-button" aria-label="Start Listening">
|
<button class="mic-button" id="mic-button" aria-label="Start Listening">
|
||||||
|
|||||||
130
script.js
130
script.js
@@ -69,33 +69,123 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
activeVideo.addEventListener('ended', switchVideo, { once: true });
|
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 isListening = false;
|
||||||
let currentFavorability = 65; // 与 CSS 中的初始宽度保持一致
|
|
||||||
|
|
||||||
micButton.addEventListener('click', function() {
|
micButton.addEventListener('click', function() {
|
||||||
isListening = !isListening;
|
if (!SpeechRecognition) return; // 如果不支持,则不执行任何操作
|
||||||
|
|
||||||
// 切换 "监听中" 的样式 (动画)
|
isListening = !isListening;
|
||||||
micButton.classList.toggle('is-listening', isListening);
|
micButton.classList.toggle('is-listening', isListening);
|
||||||
|
const transcriptContainer = document.querySelector('.transcript-container');
|
||||||
|
const transcriptText = document.getElementById('transcript');
|
||||||
|
|
||||||
// 模拟交互:每次点击麦克风,好感度增加
|
|
||||||
if (isListening) {
|
if (isListening) {
|
||||||
// 增加好感度,但最高不超过 100
|
transcriptText.textContent = '聆听中...'; // 立刻显示提示
|
||||||
currentFavorability += 5;
|
transcriptContainer.classList.add('visible');
|
||||||
if (currentFavorability > 100) {
|
recognition.start();
|
||||||
currentFavorability = 100;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// 如果停止监听,可以稍微降低一点好感度(可选)
|
recognition.stop();
|
||||||
currentFavorability -= 2;
|
transcriptContainer.classList.remove('visible');
|
||||||
if (currentFavorability < 0) {
|
transcriptText.textContent = ''; // 清空文本
|
||||||
currentFavorability = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新好感度进度条的宽度
|
|
||||||
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 });
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
27
style.css
27
style.css
@@ -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 {
|
.bottom-bar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
Reference in New Issue
Block a user