'commit'
This commit is contained in:
@@ -6,6 +6,7 @@ import json
|
|||||||
import ssl
|
import ssl
|
||||||
import time
|
import time
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
import base64
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from time import mktime
|
from time import mktime
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
@@ -163,10 +164,11 @@ class XunFeiAudioEvaluator:
|
|||||||
# 查找read_chapter节点
|
# 查找read_chapter节点
|
||||||
read_chapter = root.find('.//read_chapter')
|
read_chapter = root.find('.//read_chapter')
|
||||||
if read_chapter is not None:
|
if read_chapter is not None:
|
||||||
|
# 保持字段名一致,使用completeness_score
|
||||||
self.evaluation_results = {
|
self.evaluation_results = {
|
||||||
'accuracy_score': float(read_chapter.get('accuracy_score', 0)),
|
'accuracy_score': float(read_chapter.get('accuracy_score', 0)),
|
||||||
'fluency_score': float(read_chapter.get('fluency_score', 0)),
|
'fluency_score': float(read_chapter.get('fluency_score', 0)),
|
||||||
'completeness_score': float(read_chapter.get('integrity_score', 0)), # 修复字段名
|
'completeness_score': float(read_chapter.get('integrity_score', 0)),
|
||||||
'standard_score': float(read_chapter.get('standard_score', 0)),
|
'standard_score': float(read_chapter.get('standard_score', 0)),
|
||||||
'total_score': float(read_chapter.get('total_score', 0)),
|
'total_score': float(read_chapter.get('total_score', 0)),
|
||||||
'word_count': int(read_chapter.get('word_count', 0)),
|
'word_count': int(read_chapter.get('word_count', 0)),
|
||||||
@@ -206,7 +208,8 @@ class XunFeiAudioEvaluator:
|
|||||||
summary += f"总得分: {self.evaluation_results.get('total_score', 0):.4f}\n"
|
summary += f"总得分: {self.evaluation_results.get('total_score', 0):.4f}\n"
|
||||||
summary += f"准确度得分: {self.evaluation_results.get('accuracy_score', 0):.4f}\n"
|
summary += f"准确度得分: {self.evaluation_results.get('accuracy_score', 0):.4f}\n"
|
||||||
summary += f"流畅度得分: {self.evaluation_results.get('fluency_score', 0):.4f}\n"
|
summary += f"流畅度得分: {self.evaluation_results.get('fluency_score', 0):.4f}\n"
|
||||||
summary += f"完整度得分: {self.evaluation_results.get('integrity_score', 0):.4f}\n"
|
# 修复这里,使用completeness_score
|
||||||
|
summary += f"完整度得分: {self.evaluation_results.get('completeness_score', 0):.4f}\n"
|
||||||
summary += f"标准度得分: {self.evaluation_results.get('standard_score', 0):.4f}\n"
|
summary += f"标准度得分: {self.evaluation_results.get('standard_score', 0):.4f}\n"
|
||||||
summary += f"单词数量: {self.evaluation_results.get('word_count', 0)}\n"
|
summary += f"单词数量: {self.evaluation_results.get('word_count', 0)}\n"
|
||||||
summary += f"是否被拒绝: {'是' if self.evaluation_results.get('is_rejected', False) else '否'}\n"
|
summary += f"是否被拒绝: {'是' if self.evaluation_results.get('is_rejected', False) else '否'}\n"
|
||||||
@@ -269,8 +272,8 @@ if __name__ == '__main__':
|
|||||||
appid = XF_APPID
|
appid = XF_APPID
|
||||||
api_secret = XF_APISECRET
|
api_secret = XF_APISECRET
|
||||||
api_key = XF_APIKEY
|
api_key = XF_APIKEY
|
||||||
audio_file = "./1.mp3"
|
#audio_file = "./1.mp3"
|
||||||
|
audio_file=r'D:\dsWork\dsProject\dsLightRag\static\audio\audio_1f0a8c47db2d4f9ba7674055a352cab8.wav'
|
||||||
# 创建评测器实例
|
# 创建评测器实例
|
||||||
evaluator = XunFeiAudioEvaluator(appid, api_key, api_secret, audio_file, "english")
|
evaluator = XunFeiAudioEvaluator(appid, api_key, api_secret, audio_file, "english")
|
||||||
|
|
||||||
|
Binary file not shown.
@@ -4,6 +4,7 @@ import uuid
|
|||||||
import tempfile
|
import tempfile
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
import logging
|
||||||
from fastapi import APIRouter, UploadFile, File
|
from fastapi import APIRouter, UploadFile, File
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
@@ -30,7 +31,13 @@ async def save_audio(audio: UploadFile = File(...)):
|
|||||||
content = await audio.read()
|
content = await audio.read()
|
||||||
with open(temp_file, "wb") as f:
|
with open(temp_file, "wb") as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
|
|
||||||
|
# 3. 保存到正式目录
|
||||||
|
file_name = f"audio_{uuid.uuid4().hex}.wav"
|
||||||
|
file_path = os.path.join(UPLOAD_DIR, file_name)
|
||||||
|
shutil.copy2(temp_file, file_path)
|
||||||
|
logger.info(f"已保存文件到: {file_path}")
|
||||||
|
|
||||||
# 2. 讯飞评分
|
# 2. 讯飞评分
|
||||||
from KeDaXunFei.XunFeiAudioEvaluator import XunFeiAudioEvaluator
|
from KeDaXunFei.XunFeiAudioEvaluator import XunFeiAudioEvaluator
|
||||||
evaluator = XunFeiAudioEvaluator(
|
evaluator = XunFeiAudioEvaluator(
|
||||||
@@ -41,11 +48,8 @@ async def save_audio(audio: UploadFile = File(...)):
|
|||||||
language="english"
|
language="english"
|
||||||
)
|
)
|
||||||
results, eval_time = evaluator.run_evaluation()
|
results, eval_time = evaluator.run_evaluation()
|
||||||
|
print(evaluator.get_evaluation_summary())
|
||||||
# 3. 保存到正式目录
|
|
||||||
file_name = f"audio_{uuid.uuid4().hex}.wav"
|
|
||||||
file_path = os.path.join(UPLOAD_DIR, file_name)
|
|
||||||
shutil.copy2(temp_file, file_path)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
|
Binary file not shown.
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>在线录音机</title>
|
<title>英语朗读评测</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -183,6 +183,78 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.evaluation-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
background: rgba(30, 41, 59, 0.5);
|
||||||
|
border-radius: 10px;
|
||||||
|
display: none;
|
||||||
|
border: 1px solid rgba(94, 234, 212, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.evaluation-container.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-card {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||||
|
gap: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-item {
|
||||||
|
text-align: center;
|
||||||
|
padding: 15px;
|
||||||
|
background: rgba(15, 23, 42, 0.7);
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid rgba(94, 234, 212, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-value {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #5eead4;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #cbd5e1;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.words-list {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: rgba(15, 23, 42, 0.7);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 0;
|
||||||
|
border-bottom: 1px solid rgba(94, 234, 212, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-content {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-score {
|
||||||
|
color: #5eead4;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
audio {
|
audio {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
@@ -416,6 +488,33 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="progress-text" id="progressText">上传进度: 0%</div>
|
<div class="progress-text" id="progressText">上传进度: 0%</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 评分结果显示区域 -->
|
||||||
|
<div class="evaluation-container" id="evaluationContainer">
|
||||||
|
<h3 style="color: #5eead4; margin-bottom: 15px;">📊 评测结果</h3>
|
||||||
|
<div class="score-card">
|
||||||
|
<div class="score-item">
|
||||||
|
<span class="score-value" id="totalScore">--</span>
|
||||||
|
<span class="score-label">总分</span>
|
||||||
|
</div>
|
||||||
|
<div class="score-item">
|
||||||
|
<span class="score-value" id="accuracyScore">--</span>
|
||||||
|
<span class="score-label">准确度</span>
|
||||||
|
</div>
|
||||||
|
<div class="score-item">
|
||||||
|
<span class="score-value" id="fluencyScore">--</span>
|
||||||
|
<span class="score-label">流利度</span>
|
||||||
|
</div>
|
||||||
|
<div class="score-item">
|
||||||
|
<span class="score-value" id="completenessScore">--</span>
|
||||||
|
<span class="score-label">完整度</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="words-list" id="wordsList">
|
||||||
|
<h4 style="color: #5eead4; margin-bottom: 10px;">单词评分</h4>
|
||||||
|
<div id="wordsContent">等待评测结果...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -456,6 +555,13 @@
|
|||||||
this.uploadProgress = document.getElementById('uploadProgress');
|
this.uploadProgress = document.getElementById('uploadProgress');
|
||||||
this.progressFill = document.getElementById('progressFill');
|
this.progressFill = document.getElementById('progressFill');
|
||||||
this.progressText = document.getElementById('progressText');
|
this.progressText = document.getElementById('progressText');
|
||||||
|
this.evaluationContainer = document.getElementById('evaluationContainer');
|
||||||
|
this.totalScore = document.getElementById('totalScore');
|
||||||
|
this.accuracyScore = document.getElementById('accuracyScore');
|
||||||
|
this.fluencyScore = document.getElementById('fluencyScore');
|
||||||
|
this.completenessScore = document.getElementById('completenessScore');
|
||||||
|
this.wordsList = document.getElementById('wordsList');
|
||||||
|
this.wordsContent = document.getElementById('wordsContent');
|
||||||
}
|
}
|
||||||
|
|
||||||
setupCanvas() {
|
setupCanvas() {
|
||||||
@@ -493,7 +599,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 设置音频上下文用于可视化
|
|
||||||
this.setupAudioContext(stream);
|
this.setupAudioContext(stream);
|
||||||
|
|
||||||
const mimeType = this.getMimeType();
|
const mimeType = this.getMimeType();
|
||||||
@@ -523,19 +628,12 @@
|
|||||||
this.updateButtons();
|
this.updateButtons();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.mediaRecorder.start(1000); // 每秒触发一次dataavailable事件
|
this.mediaRecorder.start(1000);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('录音错误:', error);
|
console.error('录音错误:', error);
|
||||||
this.updateStatus('无法访问麦克风,请检查权限设置', 'error');
|
this.updateStatus('无法访问麦克风,请检查权限设置', 'error');
|
||||||
|
alert('录音初始化失败: ' + error.message);
|
||||||
if (error.name === 'NotAllowedError') {
|
|
||||||
alert('请允许访问麦克风权限才能录音');
|
|
||||||
} else if (error.name === 'NotFoundError') {
|
|
||||||
alert('未找到麦克风设备');
|
|
||||||
} else {
|
|
||||||
alert('录音初始化失败: ' + error.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,7 +658,6 @@
|
|||||||
if (!this.isRecording) return;
|
if (!this.isRecording) return;
|
||||||
|
|
||||||
this.animationId = requestAnimationFrame(draw);
|
this.animationId = requestAnimationFrame(draw);
|
||||||
|
|
||||||
this.analyser.getByteFrequencyData(this.dataArray);
|
this.analyser.getByteFrequencyData(this.dataArray);
|
||||||
|
|
||||||
this.canvasCtx.fillStyle = 'rgba(15, 23, 42, 0.7)';
|
this.canvasCtx.fillStyle = 'rgba(15, 23, 42, 0.7)';
|
||||||
@@ -573,7 +670,6 @@
|
|||||||
for (let i = 0; i < this.dataArray.length; i++) {
|
for (let i = 0; i < this.dataArray.length; i++) {
|
||||||
barHeight = (this.dataArray[i] / 255) * this.canvas.height * 0.8;
|
barHeight = (this.dataArray[i] / 255) * this.canvas.height * 0.8;
|
||||||
|
|
||||||
// 创建渐变
|
|
||||||
const gradient = this.canvasCtx.createLinearGradient(0, this.canvas.height - barHeight, 0, this.canvas.height);
|
const gradient = this.canvasCtx.createLinearGradient(0, this.canvas.height - barHeight, 0, this.canvas.height);
|
||||||
gradient.addColorStop(0, '#5eead4');
|
gradient.addColorStop(0, '#5eead4');
|
||||||
gradient.addColorStop(1, '#0ea5e9');
|
gradient.addColorStop(1, '#0ea5e9');
|
||||||
@@ -594,7 +690,6 @@
|
|||||||
this.animationId = null;
|
this.animationId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空画布
|
|
||||||
this.canvasCtx.fillStyle = 'rgba(15, 23, 42, 0.7)';
|
this.canvasCtx.fillStyle = 'rgba(15, 23, 42, 0.7)';
|
||||||
this.canvasCtx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
this.canvasCtx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
}
|
}
|
||||||
@@ -609,7 +704,6 @@
|
|||||||
|
|
||||||
const mimeType = supportedTypes[format];
|
const mimeType = supportedTypes[format];
|
||||||
|
|
||||||
// 检查浏览器是否支持选定的格式
|
|
||||||
if (!MediaRecorder.isTypeSupported(mimeType)) {
|
if (!MediaRecorder.isTypeSupported(mimeType)) {
|
||||||
console.warn(`${format} 格式不被支持,回退到 WebM`);
|
console.warn(`${format} 格式不被支持,回退到 WebM`);
|
||||||
return 'audio/webm;codecs=opus';
|
return 'audio/webm;codecs=opus';
|
||||||
@@ -636,7 +730,6 @@
|
|||||||
const url = URL.createObjectURL(this.recordedBlob);
|
const url = URL.createObjectURL(this.recordedBlob);
|
||||||
this.audioPlayer.src = url;
|
this.audioPlayer.src = url;
|
||||||
|
|
||||||
// 显示录音信息
|
|
||||||
const duration = Math.round((Date.now() - this.startTime) / 1000);
|
const duration = Math.round((Date.now() - this.startTime) / 1000);
|
||||||
const sizeInKB = Math.round(this.recordedBlob.size / 1024);
|
const sizeInKB = Math.round(this.recordedBlob.size / 1024);
|
||||||
|
|
||||||
@@ -645,7 +738,7 @@
|
|||||||
this.audioContainer.classList.add('show');
|
this.audioContainer.classList.add('show');
|
||||||
|
|
||||||
this.updateStatus(`录音完成!时长: ${duration}秒, 大小: ${sizeInKB}KB`, 'success');
|
this.updateStatus(`录音完成!时长: ${duration}秒, 大小: ${sizeInKB}KB`, 'success');
|
||||||
this.updateButtons(); // 添加这一行来更新按钮状态
|
this.updateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
playRecording() {
|
playRecording() {
|
||||||
@@ -662,31 +755,19 @@
|
|||||||
this.uploadProgress.classList.add('show');
|
this.uploadProgress.classList.add('show');
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
// 根据选择的格式确定文件扩展名
|
|
||||||
const format = this.audioFormat.value;
|
const format = this.audioFormat.value;
|
||||||
const extensions = {
|
const extensions = {'webm': 'webm', 'mp4': 'mp4', 'wav': 'wav'};
|
||||||
'webm': 'webm',
|
|
||||||
'mp4': 'mp4',
|
|
||||||
'wav': 'wav'
|
|
||||||
};
|
|
||||||
|
|
||||||
const fileName = `recording_${Date.now()}.${extensions[format]}`;
|
const fileName = `recording_${Date.now()}.${extensions[format]}`;
|
||||||
|
|
||||||
formData.append('audio', this.recordedBlob, fileName);
|
formData.append('audio', this.recordedBlob, fileName);
|
||||||
formData.append('format', format);
|
|
||||||
formData.append('duration', this.duration.textContent);
|
|
||||||
formData.append('fileSize', this.fileSize.textContent);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 模拟上传进度
|
|
||||||
this.simulateUploadProgress();
|
this.simulateUploadProgress();
|
||||||
|
|
||||||
const response = await fetch('/api/xunFei/save-audio', {
|
const response = await fetch('/api/xunFei/save-audio', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData,
|
body: formData,
|
||||||
headers: {
|
headers: {'Accept': 'application/json'}
|
||||||
'Accept': 'application/json',
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -694,32 +775,52 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
this.uploadProgress.classList.remove('show');
|
this.uploadProgress.classList.remove('show');
|
||||||
this.updateStatus('上传成功!', 'success');
|
|
||||||
|
if (result.success) {
|
||||||
console.log('上传结果:', result);
|
this.updateStatus('评测完成!', 'success');
|
||||||
|
this.displayEvaluationResults(result.evaluation);
|
||||||
// 这里可以添加成功后的处理逻辑
|
console.log('评测结果:', result.evaluation);
|
||||||
if (result.audioUrl) {
|
|
||||||
alert(`录音上传成功!\n文件URL: ${result.audioUrl}\n文件名: ${fileName}`);
|
|
||||||
} else {
|
} else {
|
||||||
alert('录音上传成功!');
|
throw new Error(result.error || '未知错误');
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('上传错误:', error);
|
console.error('上传错误:', error);
|
||||||
this.uploadProgress.classList.remove('show');
|
this.uploadProgress.classList.remove('show');
|
||||||
this.updateStatus('上传失败: ' + error.message, 'error');
|
this.updateStatus('上传失败: ' + error.message, 'error');
|
||||||
|
alert('上传失败: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 更详细的错误处理
|
displayEvaluationResults(evaluation) {
|
||||||
if (error.name === 'TypeError' && error.message.includes('fetch')) {
|
if (!evaluation) return;
|
||||||
alert('网络连接失败,请检查网络设置');
|
|
||||||
} else if (error.message.includes('Failed to fetch')) {
|
// 显示评分卡片
|
||||||
alert('无法连接到服务器,请检查API地址是否正确');
|
this.totalScore.textContent = evaluation.total_score ? evaluation.total_score.toFixed(1) : '--';
|
||||||
} else {
|
this.accuracyScore.textContent = evaluation.accuracy_score ? evaluation.accuracy_score.toFixed(1) : '--';
|
||||||
alert('上传失败: ' + error.message);
|
this.fluencyScore.textContent = evaluation.fluency_score ? evaluation.fluency_score.toFixed(1) : '--';
|
||||||
}
|
this.completenessScore.textContent = evaluation.completeness_score ? evaluation.completeness_score.toFixed(1) : '--';
|
||||||
|
|
||||||
|
this.evaluationContainer.classList.add('show');
|
||||||
|
|
||||||
|
// 显示单词评分
|
||||||
|
if (evaluation.words && evaluation.words.length > 0) {
|
||||||
|
let wordsHTML = '';
|
||||||
|
evaluation.words.forEach((word, index) => {
|
||||||
|
const score = word.total_score ? word.total_score.toFixed(1) : '0.0';
|
||||||
|
const color = score >= 80 ? '#10b981' : score >= 60 ? '#f59e0b' : '#ef4444';
|
||||||
|
wordsHTML += `
|
||||||
|
<div class="word-item">
|
||||||
|
<span class="word-content">${word.content || `单词${index + 1}`}</span>
|
||||||
|
<span class="word-score" style="color: ${color}">${score}</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
this.wordsContent.innerHTML = wordsHTML;
|
||||||
|
} else {
|
||||||
|
this.wordsContent.innerHTML = '<p style="color: #cbd5e1; text-align: center;">无单词评分数据</p>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -728,7 +829,7 @@
|
|||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
progress += Math.random() * 15;
|
progress += Math.random() * 15;
|
||||||
if (progress >= 90) {
|
if (progress >= 90) {
|
||||||
progress = 90; // 等待实际上传完成
|
progress = 90;
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -772,7 +873,6 @@
|
|||||||
this.status.className = 'status ' + type;
|
this.status.className = 'status ' + type;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理资源
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
this.stopTimer();
|
this.stopTimer();
|
||||||
this.stopVisualization();
|
this.stopVisualization();
|
||||||
@@ -791,38 +891,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化录音器
|
|
||||||
const recorder = new AudioRecorder();
|
const recorder = new AudioRecorder();
|
||||||
|
window.addEventListener('beforeunload', () => recorder.cleanup());
|
||||||
// 页面卸载时清理资源
|
|
||||||
window.addEventListener('beforeunload', () => {
|
|
||||||
recorder.cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 处理页面可见性变化
|
|
||||||
document.addEventListener('visibilitychange', () => {
|
|
||||||
if (document.hidden && recorder.isRecording) {
|
|
||||||
console.log('页面隐藏,录音继续进行...');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 添加键盘快捷键支持
|
|
||||||
document.addEventListener('keydown', (e) => {
|
|
||||||
if (e.code === 'Space') {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!recorder.isRecording && !recorder.recordBtn.disabled) {
|
|
||||||
recorder.startRecording();
|
|
||||||
} else if (recorder.isRecording) {
|
|
||||||
recorder.stopRecording();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 添加提示信息
|
|
||||||
console.log('🎙️ 在线录音机已加载完成!');
|
|
||||||
console.log('💡 快捷键:空格键 - 开始/停止录音');
|
|
||||||
console.log('📋 支持的格式:WebM (推荐), MP4, WAV');
|
|
||||||
console.log('🔧 请确保已允许麦克风权限');
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user