feat: 新增🖕中止暂停按钮

This commit is contained in:
white 2024-10-15 19:00:11 +08:00
parent 88d37f262e
commit 1674bbee2e
11 changed files with 356 additions and 306 deletions

View File

@ -46,8 +46,11 @@
<!-- 录制 -->
<div class="buttons startRec">点击说话</div>
<!-- 暂停 -->
<div class="buttons endRec">中止</div>
<!-- 停止 -->
<div class="buttons endRec">停止</div>
<div class="buttons stopRec">暂停</div>
</div>
<!-- 讯飞测试 -->
@ -185,6 +188,7 @@
// 获取 audio 元素的引用
var audioElement = document.getElementById('myAudio');
var isPlaying = false; // 是否播放
audioElement.muted = true; // 先静音
// 获取页面元素
@ -196,7 +200,11 @@
// 点击事件
var startRec = document.getElementsByClassName('startRec')[0];
var endRec = document.getElementsByClassName('endRec')[0];
var stopRec = document.getElementsByClassName('stopRec')[0];
endRec.style.display = "none";
stopRec.style.display = "none";
var token = null;
let times = null;
@ -450,15 +458,35 @@
startShibie();
});
// 关闭识别
endRec.addEventListener("click", function() {
closeShibie();
});
// 关闭识别
stopRec.addEventListener("click", function() {
stopPlay();
});
function stopPlay() {
/**暂停播放**/
if (audioElement.paused) {
audioElement.play();
videoElement.play();
isPlaying = true;
stopRec.textContent = '暂停';
} else {
audioElement.pause();
videoElement.pause();
isPlaying = false;
stopRec.textContent = '继续播放';
}
};
function startShibie() {
/**开始识别**/
voiceTxt.innerText = '';
stopRec.textContent = '暂停';
voice.start();
isCallbackExecuted = false;
@ -469,6 +497,7 @@
startRec.style.display = 'none';
endRec.style.display = 'none';
stopRec.style.display = 'none';
showModal();
}
@ -476,6 +505,7 @@
/**关闭识别**/
voiceTxt.innerText = '';
statusTxt.value = '';
stopRec.textContent = '暂停';
voice.stop();
@ -489,10 +519,10 @@
startRec.style.display = 'block';
endRec.style.display = 'none';
stopRec.style.display = 'none';
hideModal()
}
// 显示弹窗和遮罩
function showModal() {
modal.style.display = 'block';
@ -557,6 +587,7 @@
}).then(async(response) => {
startRec.style.display = "none";
endRec.style.display = "block";
stopRec.style.display = "block";
NextPlayVideo(response, filterString(Subtitles, ['*', ' ']));
}).catch(e => {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -79,6 +79,7 @@
<script>
var Items = ['血糖', '睡眠', '血氧', '血压', '尿酸', '梅拖', '心率', '体温', '心电图', '身体成份', '运动', '血脂', '血液成分'];
var Question = '';
var Subtitles = '';
function getURLParameter(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
@ -137,31 +138,6 @@
// 如果没有找到 finish_reason 为 "stop" 的事件,则返回 null 或其他默认值
return stopEvent;
}
// 筛选关键词
function containsKeywordRegex(text) {
var index = Items.findIndex(item => text.includes(item));
if (index !== -1) {
return index
} else {
return 99999
}
}
// 语音所需时间
function calculateSpeakingTime(text) {
// 假设平均每分钟说230个单词
const wordsPerMinute = 230;
// 计算文字的长度
const wordCount = text.trim().length;
// 计算所需时间(以分钟为单位)
const speakingTime = wordCount / wordsPerMinute;
// 转换为秒数
const speakingTimeInSeconds = speakingTime * 60;
console.log(speakingTimeInSeconds, '说完需要多少秒')
return speakingTimeInSeconds;
}
</script>
<!-- 讯飞语音识别 -->
@ -300,7 +276,6 @@
if (result) {
DetailDay(result.dataKey, result.date).then(res => {
let TargetData = res.data.data;
var QSindex = containsKeywordRegex(params.msg);
if (result.dataKey == 'ECGData') {
TargetData.map(item => {
@ -320,118 +295,148 @@
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的${result.dataKey}数据为${JSON.stringify(TargetData)}, #提示data_msg为值hour_minute为检测时间。#提示:“[]”表示数据为空,请在小程序上传数据。#限制:回复不要带英文,要都转化成汉语。#限制回复内容控制在150字。#限制:忽略“压力指数、疲劳指数、心肌炎风险、冠心病风险和动脉硬化”等数据。`
console.log(Question, '=========================');
console.log(Question, '问题=========================问题');
/*
* 调用接口 传递关键信息 文字转语音
*/
const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://sc2.agrimedia.cn:8787/api/user/ask', true);
const url = 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation';
const apiKey = 'sk-cbb9b5ff44374fa2a8a258160ebb292d';
// 打开请求,设置为异步
xhr.open('POST', url, true);
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Bearer ' + apiKey);
xhr.setRequestHeader('X-DashScope-SSE', 'enable');
// 处理流式数据的接收(使用 progress 事件)
xhr.onprogress = function () {
const data = xhr.responseText;
// SSE 数据解析处理
const lines = data.split('\n');
lines.forEach(line => {
if (line.startsWith('data:')) {
const jsonStr = line.substring(5); // 去掉 'data:' 前缀
const parsedData = JSON.parse(jsonStr);
// 解析 content 内容
const content = parsedData.output.choices[0].message.content;
if (content) {
// 检查内容是否已经存在
if (!Subtitles.includes(content)) {
Subtitles += content;
}
};
}
});
};
// 检查请求完成
xhr.onload = function () {
if (xhr.status === 200) {
statusTxt.value = '正在说话';
RequestMicrosoft();
}
};
// 错误处理
xhr.onerror = function () {
console.error('An error occurred during the transaction', xhr.statusText);
};
/*
* 关键字转换
*/
var data = JSON.stringify({
"messages": [
{"role": "system", "content": Question},
{"role": "user", "content": `请问我${params.msg}正常吗`}
]
});
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
const chunk = xhr.responseText;
const str = extractStopEvent(chunk);
statusTxt.value = '正在说话';
/*
* 微软接口识别
*/
fetch("https://eastasia.tts.speech.microsoft.com/cognitiveservices/v1", {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Ocp-Apim-Subscription-Key': '58e9b39b8f6f48fe8d01f85b727ff737',
'Content-Type': 'application/ssml+xml',
'X-Microsoft-OutputFormat': 'audio-24khz-48kbitrate-mono-mp3'
},
responseType: 'arraybuffer',
body: `<speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" version="1.0" xml:lang="en-US">
<voice name="zh-CN-XiaoxiaoNeural">
<mstts:express-as style="Default" >
<prosody rate="0%" pitch="0%">
${filterString(str.output.text, ['*', ' '])}
</prosody>
</mstts:express-as>
</voice>
</speak> `,
}).then(async(response) => {
startRec.style.display = "none";
endRec.style.display = "block";
NextPlayVideo(response, filterString(str.output.text, ['*', ' ']));
}).catch(e => {
hideModal();
});
}
const requestBody = {
model: 'qwen-turbo',
input: {
messages: [
{"role": "system", "content": Question},
{"role": "user", "content": `请问我${params.msg}正常吗`}
]
},
parameters: {
result_format: 'message',
incremental_output: true
}
};
xhr.send(data);
xhr.send(JSON.stringify(requestBody));
isCallbackExecuted = true;
return
return;
})
} else {
// 如果不存在关键字,就直接走下面
const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://sc2.agrimedia.cn:8787/api/user/ask', true);
var data = JSON.stringify({
"messages": [
{"role": "system", "content": `请模仿全科医生的口吻与我对话`},
{"role": "user", "content": params.msg}
]
})
const url = 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation';
const apiKey = 'sk-cbb9b5ff44374fa2a8a258160ebb292d';
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
const chunk = xhr.responseText;
const str = extractStopEvent(chunk);
// 打开请求,设置为异步
xhr.open('POST', url, true);
statusTxt.value = '正在说话';
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Bearer ' + apiKey);
xhr.setRequestHeader('X-DashScope-SSE', 'enable');
/*
* 微软接口识别
*/
fetch("https://eastasia.tts.speech.microsoft.com/cognitiveservices/v1", {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Ocp-Apim-Subscription-Key': '58e9b39b8f6f48fe8d01f85b727ff737',
'Content-Type': 'application/ssml+xml',
'X-Microsoft-OutputFormat': 'audio-24khz-48kbitrate-mono-mp3'
},
responseType: 'arraybuffer',
body: `<speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" version="1.0" xml:lang="en-US">
<voice name="zh-CN-XiaoxiaoNeural">
<mstts:express-as style="Default" >
<prosody rate="0%" pitch="0%">
${filterString(str.output.text, ['*', ' '])}
</prosody>
</mstts:express-as>
</voice>
</speak> `,
}).then(async(response) => {
startRec.style.display = "none";
endRec.style.display = "block";
// 处理流式数据的接收(使用 progress 事件)
xhr.onprogress = function () {
const data = xhr.responseText;
NextPlayVideo(response, filterString(str.output.text, ['*', ' ']));
}).catch(e => {
hideModal();
// SSE 数据解析处理
const lines = data.split('\n');
lines.forEach(line => {
if (line.startsWith('data:')) {
const jsonStr = line.substring(5); // 去掉 'data:' 前缀
const parsedData = JSON.parse(jsonStr);
// 解析 content 内容
const content = parsedData.output.choices[0].message.content;
if (content) {
// 检查内容是否已经存在
if (!Subtitles.includes(content)) {
Subtitles += content;
}
};
}
});
}
};
xhr.send(data);
// 检查请求完成
xhr.onload = function () {
if (xhr.status === 200) {
statusTxt.value = '正在说话';
RequestMicrosoft();
}
};
// 错误处理
xhr.onerror = function () {
console.error('An error occurred during the transaction', xhr.statusText);
};
// 发送请求
const requestBody = {
model: 'qwen-turbo',
input: {
messages: [
{"role": "system", "content": `请模仿全科医生的口吻与我对话`},
{"role": "user", "content": params.msg}
]
},
parameters: {
result_format: 'message',
incremental_output: true
}
};
xhr.send(JSON.stringify(requestBody));
isCallbackExecuted = true;
return
return;
}
}, 3000);
}
@ -464,7 +469,7 @@
startRec.style.display = 'none';
endRec.style.display = 'none';
showModal()
showModal();
}
function closeShibie() {
@ -529,10 +534,8 @@
}
}
// 语音输出
function speed(str) {
console.log(str)
// 请求微软文字转语音
function RequestMicrosoft() {
fetch("https://eastasia.tts.speech.microsoft.com/cognitiveservices/v1", {
method: 'POST',
headers: {
@ -546,19 +549,22 @@
<voice name="zh-CN-XiaoxiaoNeural">
<mstts:express-as style="Default" >
<prosody rate="0%" pitch="0%">
${str}
${filterString(Subtitles, ['*', ' '])}
</prosody>
</mstts:express-as>
</voice>
</speak> `,
}).then(async(response) => {
NextPlayVideo(response, str);
startRec.style.display = "none";
endRec.style.display = "block";
NextPlayVideo(response, filterString(Subtitles, ['*', ' ']));
}).catch(e => {
hideModal();
Subtitles = "";
});
}
// 字幕播放视频等操作
async function NextPlayVideo(response, str) {
const content_bytes = await response.arrayBuffer();
@ -590,6 +596,8 @@
replayVideoSegment(0, 60);
hideModal()
});
Subtitles = "";
}
// 防抖
@ -607,20 +615,6 @@
};
}
// 分钟转小时
function minutesToHoursMinutesStringSimplified(minutes) {
let hours = Math.floor(minutes / 60);
let remainingMinutes = minutes % 60;
let formattedMinutes = remainingMinutes.toString().padStart(2, '0');
if (hours > 0) {
return `${hours}小时${formattedMinutes}分`;
} else {
return `${formattedMinutes}分`;
}
}
// 拿到的数据移除*
function filterString(str, charsToRemove) {
// 这里的正则表达式是通过将charsToRemove数组中的字符转换为字符类character class来构建的

View File

@ -1 +1 @@
{"@platforms":["android","iPhone","iPad"],"id":"__UNI__24DA8DD","name":"中鼎云医","version":{"name":"1.2.5","code":125},"description":"中鼎云医","launch_path":"__uniappview.html","developer":{"name":"","email":"","url":""},"permissions":{"VideoPlayer":{},"Record":{},"UniNView":{"description":"UniNView原生渲染"}},"plus":{"useragent":{"value":"uni-app","concatenate":true},"splashscreen":{"autoclose":true,"delay":0,"target":"id:1","waiting":true},"popGesture":"close","launchwebview":{"render":"always","id":"1","kernel":"WKWebview"},"statusbar":{"immersed":"supportedDevice","style":"dark","background":"#F8F8F8"},"usingComponents":true,"nvueStyleCompiler":"uni-app","compilerVersion":3,"distribute":{"icons":{"android":{"hdpi":"icon-android-hdpi.png","xhdpi":"icon-android-xhdpi.png","xxhdpi":"icon-android-xxhdpi.png","xxxhdpi":"icon-android-xxxhdpi.png"},"ios":{"appstore":"unpackage/res/icons/1024x1024.png","ipad":{"app":"unpackage/res/icons/76x76.png","app@2x":"unpackage/res/icons/152x152.png","notification":"unpackage/res/icons/20x20.png","notification@2x":"unpackage/res/icons/40x40.png","proapp@2x":"unpackage/res/icons/167x167.png","settings":"unpackage/res/icons/29x29.png","settings@2x":"unpackage/res/icons/58x58.png","spotlight":"unpackage/res/icons/40x40.png","spotlight@2x":"unpackage/res/icons/80x80.png"},"iphone":{"app@2x":"unpackage/res/icons/120x120.png","app@3x":"unpackage/res/icons/180x180.png","notification@2x":"unpackage/res/icons/40x40.png","notification@3x":"unpackage/res/icons/60x60.png","settings@2x":"unpackage/res/icons/58x58.png","settings@3x":"unpackage/res/icons/87x87.png","spotlight@2x":"unpackage/res/icons/80x80.png","spotlight@3x":"unpackage/res/icons/120x120.png"},"prerendered":"false"}},"google":{"permissions":["<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>","<uses-permission android:name=\"android.permission.VIBRATE\"/>","<uses-permission android:name=\"android.permission.READ_LOGS\"/>","<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>","<uses-feature android:name=\"android.hardware.camera.autofocus\"/>","<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.CAMERA\"/>","<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>","<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>","<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>","<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>","<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>","<uses-feature android:name=\"android.hardware.camera\"/>","<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>","<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>"],"packagename":"uni.UNI24DA8DD","aliasname":"platform","password":"aB6ADjKOYCOnZRmpzM7ptg==","keystore":"google-keystore.keystore","custompermissions":true},"apple":{"dSYMs":false,"devices":"universal"},"plugins":{"ad":{},"audio":{"mp3":{"description":"Android平台录音支持MP3格式文件"}}},"orientation":"portrait-primary"},"uniStatistics":{"enable":false},"allowsInlineMediaPlayback":true,"uni-app":{"compilerVersion":"4.15","control":"uni-v3","nvueCompiler":"uni-app","renderer":"auto","nvue":{"flex-direction":"column"},"nvueLaunchMode":"normal"},"launch_path":"__uniappview.html","adid":"123262070412"},"screenOrientation":["portrait-primary","portrait-secondary"]}
{"@platforms":["android","iPhone","iPad"],"id":"__UNI__24DA8DD","name":"中鼎云医","version":{"name":"1.2.5","code":125},"description":"中鼎云医","launch_path":"__uniappview.html","developer":{"name":"","email":"","url":""},"permissions":{"VideoPlayer":{},"Record":{},"UniNView":{"description":"UniNView原生渲染"}},"plus":{"useragent":{"value":"uni-app","concatenate":true},"splashscreen":{"autoclose":true,"delay":0,"target":"id:1","waiting":true},"popGesture":"close","launchwebview":{"render":"always","id":"1","kernel":"WKWebview"},"statusbar":{"immersed":"supportedDevice","style":"dark","background":"#F8F8F8"},"usingComponents":true,"nvueStyleCompiler":"uni-app","compilerVersion":3,"distribute":{"screenOrientation":"portrait","icons":{"android":{"hdpi":"icon-android-hdpi.png","xhdpi":"icon-android-xhdpi.png","xxhdpi":"icon-android-xxhdpi.png","xxxhdpi":"icon-android-xxxhdpi.png"},"ios":{"appstore":"unpackage/res/icons/1024x1024.png","ipad":{"app":"unpackage/res/icons/76x76.png","app@2x":"unpackage/res/icons/152x152.png","notification":"unpackage/res/icons/20x20.png","notification@2x":"unpackage/res/icons/40x40.png","proapp@2x":"unpackage/res/icons/167x167.png","settings":"unpackage/res/icons/29x29.png","settings@2x":"unpackage/res/icons/58x58.png","spotlight":"unpackage/res/icons/40x40.png","spotlight@2x":"unpackage/res/icons/80x80.png"},"iphone":{"app@2x":"unpackage/res/icons/120x120.png","app@3x":"unpackage/res/icons/180x180.png","notification@2x":"unpackage/res/icons/40x40.png","notification@3x":"unpackage/res/icons/60x60.png","settings@2x":"unpackage/res/icons/58x58.png","settings@3x":"unpackage/res/icons/87x87.png","spotlight@2x":"unpackage/res/icons/80x80.png","spotlight@3x":"unpackage/res/icons/120x120.png"},"prerendered":"false"}},"google":{"permissions":["<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>","<uses-permission android:name=\"android.permission.VIBRATE\"/>","<uses-permission android:name=\"android.permission.READ_LOGS\"/>","<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>","<uses-feature android:name=\"android.hardware.camera.autofocus\"/>","<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.CAMERA\"/>","<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>","<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>","<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>","<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>","<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>","<uses-feature android:name=\"android.hardware.camera\"/>","<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>","<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>"],"packagename":"uni.UNI24DA8DD","aliasname":"platform","password":"aB6ADjKOYCOnZRmpzM7ptg==","keystore":"google-keystore.keystore","custompermissions":true},"apple":{"dSYMs":false,"devices":"universal"},"plugins":{"ad":{},"audio":{"mp3":{"description":"Android平台录音支持MP3格式文件"}}},"orientation":"portrait-primary"},"uniStatistics":{"enable":false},"allowsInlineMediaPlayback":true,"uni-app":{"compilerVersion":"4.15","control":"uni-v3","nvueCompiler":"uni-app","renderer":"auto","nvue":{"flex-direction":"column"},"nvueLaunchMode":"normal"},"launch_path":"__uniappview.html","adid":"123262070412"},"screenOrientation":["portrait-primary","portrait-secondary"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -79,6 +79,7 @@
<script>
var Items = ['血糖', '睡眠', '血氧', '血压', '尿酸', '梅拖', '心率', '体温', '心电图', '身体成份', '运动', '血脂', '血液成分'];
var Question = '';
var Subtitles = '';
function getURLParameter(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
@ -137,31 +138,6 @@
// 如果没有找到 finish_reason 为 "stop" 的事件,则返回 null 或其他默认值
return stopEvent;
}
// 筛选关键词
function containsKeywordRegex(text) {
var index = Items.findIndex(item => text.includes(item));
if (index !== -1) {
return index
} else {
return 99999
}
}
// 语音所需时间
function calculateSpeakingTime(text) {
// 假设平均每分钟说230个单词
const wordsPerMinute = 230;
// 计算文字的长度
const wordCount = text.trim().length;
// 计算所需时间(以分钟为单位)
const speakingTime = wordCount / wordsPerMinute;
// 转换为秒数
const speakingTimeInSeconds = speakingTime * 60;
console.log(speakingTimeInSeconds, '说完需要多少秒')
return speakingTimeInSeconds;
}
</script>
<!-- 讯飞语音识别 -->
@ -300,7 +276,6 @@
if (result) {
DetailDay(result.dataKey, result.date).then(res => {
let TargetData = res.data.data;
var QSindex = containsKeywordRegex(params.msg);
if (result.dataKey == 'ECGData') {
TargetData.map(item => {
@ -320,118 +295,148 @@
Question = `请模仿全科医生的口吻与我: ${user}对话,我最近测量的${result.dataKey}数据为${JSON.stringify(TargetData)}, #提示data_msg为值hour_minute为检测时间。#提示:“[]”表示数据为空,请在小程序上传数据。#限制:回复不要带英文,要都转化成汉语。#限制回复内容控制在150字。#限制:忽略“压力指数、疲劳指数、心肌炎风险、冠心病风险和动脉硬化”等数据。`
console.log(Question, '=========================');
console.log(Question, '问题=========================问题');
/*
* 调用接口 传递关键信息 文字转语音
*/
const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://sc2.agrimedia.cn:8787/api/user/ask', true);
const url = 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation';
const apiKey = 'sk-cbb9b5ff44374fa2a8a258160ebb292d';
// 打开请求,设置为异步
xhr.open('POST', url, true);
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Bearer ' + apiKey);
xhr.setRequestHeader('X-DashScope-SSE', 'enable');
// 处理流式数据的接收(使用 progress 事件)
xhr.onprogress = function () {
const data = xhr.responseText;
// SSE 数据解析处理
const lines = data.split('\n');
lines.forEach(line => {
if (line.startsWith('data:')) {
const jsonStr = line.substring(5); // 去掉 'data:' 前缀
const parsedData = JSON.parse(jsonStr);
// 解析 content 内容
const content = parsedData.output.choices[0].message.content;
if (content) {
// 检查内容是否已经存在
if (!Subtitles.includes(content)) {
Subtitles += content;
}
};
}
});
};
// 检查请求完成
xhr.onload = function () {
if (xhr.status === 200) {
statusTxt.value = '正在说话';
RequestMicrosoft();
}
};
// 错误处理
xhr.onerror = function () {
console.error('An error occurred during the transaction', xhr.statusText);
};
/*
* 关键字转换
*/
var data = JSON.stringify({
"messages": [
{"role": "system", "content": Question},
{"role": "user", "content": `请问我${params.msg}正常吗`}
]
});
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
const chunk = xhr.responseText;
const str = extractStopEvent(chunk);
statusTxt.value = '正在说话';
/*
* 微软接口识别
*/
fetch("https://eastasia.tts.speech.microsoft.com/cognitiveservices/v1", {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Ocp-Apim-Subscription-Key': '58e9b39b8f6f48fe8d01f85b727ff737',
'Content-Type': 'application/ssml+xml',
'X-Microsoft-OutputFormat': 'audio-24khz-48kbitrate-mono-mp3'
},
responseType: 'arraybuffer',
body: `<speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" version="1.0" xml:lang="en-US">
<voice name="zh-CN-XiaoxiaoNeural">
<mstts:express-as style="Default" >
<prosody rate="0%" pitch="0%">
${filterString(str.output.text, ['*', ' '])}
</prosody>
</mstts:express-as>
</voice>
</speak> `,
}).then(async(response) => {
startRec.style.display = "none";
endRec.style.display = "block";
NextPlayVideo(response, filterString(str.output.text, ['*', ' ']));
}).catch(e => {
hideModal();
});
}
const requestBody = {
model: 'qwen-turbo',
input: {
messages: [
{"role": "system", "content": Question},
{"role": "user", "content": `请问我${params.msg}正常吗`}
]
},
parameters: {
result_format: 'message',
incremental_output: true
}
};
xhr.send(data);
xhr.send(JSON.stringify(requestBody));
isCallbackExecuted = true;
return
return;
})
} else {
// 如果不存在关键字,就直接走下面
const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://sc2.agrimedia.cn:8787/api/user/ask', true);
var data = JSON.stringify({
"messages": [
{"role": "system", "content": `请模仿全科医生的口吻与我对话`},
{"role": "user", "content": params.msg}
]
})
const url = 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation';
const apiKey = 'sk-cbb9b5ff44374fa2a8a258160ebb292d';
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
const chunk = xhr.responseText;
const str = extractStopEvent(chunk);
// 打开请求,设置为异步
xhr.open('POST', url, true);
statusTxt.value = '正在说话';
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Bearer ' + apiKey);
xhr.setRequestHeader('X-DashScope-SSE', 'enable');
/*
* 微软接口识别
*/
fetch("https://eastasia.tts.speech.microsoft.com/cognitiveservices/v1", {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Ocp-Apim-Subscription-Key': '58e9b39b8f6f48fe8d01f85b727ff737',
'Content-Type': 'application/ssml+xml',
'X-Microsoft-OutputFormat': 'audio-24khz-48kbitrate-mono-mp3'
},
responseType: 'arraybuffer',
body: `<speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" version="1.0" xml:lang="en-US">
<voice name="zh-CN-XiaoxiaoNeural">
<mstts:express-as style="Default" >
<prosody rate="0%" pitch="0%">
${filterString(str.output.text, ['*', ' '])}
</prosody>
</mstts:express-as>
</voice>
</speak> `,
}).then(async(response) => {
startRec.style.display = "none";
endRec.style.display = "block";
// 处理流式数据的接收(使用 progress 事件)
xhr.onprogress = function () {
const data = xhr.responseText;
NextPlayVideo(response, filterString(str.output.text, ['*', ' ']));
}).catch(e => {
hideModal();
// SSE 数据解析处理
const lines = data.split('\n');
lines.forEach(line => {
if (line.startsWith('data:')) {
const jsonStr = line.substring(5); // 去掉 'data:' 前缀
const parsedData = JSON.parse(jsonStr);
// 解析 content 内容
const content = parsedData.output.choices[0].message.content;
if (content) {
// 检查内容是否已经存在
if (!Subtitles.includes(content)) {
Subtitles += content;
}
};
}
});
}
};
xhr.send(data);
// 检查请求完成
xhr.onload = function () {
if (xhr.status === 200) {
statusTxt.value = '正在说话';
RequestMicrosoft();
}
};
// 错误处理
xhr.onerror = function () {
console.error('An error occurred during the transaction', xhr.statusText);
};
// 发送请求
const requestBody = {
model: 'qwen-turbo',
input: {
messages: [
{"role": "system", "content": `请模仿全科医生的口吻与我对话`},
{"role": "user", "content": params.msg}
]
},
parameters: {
result_format: 'message',
incremental_output: true
}
};
xhr.send(JSON.stringify(requestBody));
isCallbackExecuted = true;
return
return;
}
}, 3000);
}
@ -464,7 +469,7 @@
startRec.style.display = 'none';
endRec.style.display = 'none';
showModal()
showModal();
}
function closeShibie() {
@ -529,10 +534,8 @@
}
}
// 语音输出
function speed(str) {
console.log(str)
// 请求微软文字转语音
function RequestMicrosoft() {
fetch("https://eastasia.tts.speech.microsoft.com/cognitiveservices/v1", {
method: 'POST',
headers: {
@ -546,19 +549,22 @@
<voice name="zh-CN-XiaoxiaoNeural">
<mstts:express-as style="Default" >
<prosody rate="0%" pitch="0%">
${str}
${filterString(Subtitles, ['*', ' '])}
</prosody>
</mstts:express-as>
</voice>
</speak> `,
}).then(async(response) => {
NextPlayVideo(response, str);
startRec.style.display = "none";
endRec.style.display = "block";
NextPlayVideo(response, filterString(Subtitles, ['*', ' ']));
}).catch(e => {
hideModal();
Subtitles = "";
});
}
// 字幕播放视频等操作
async function NextPlayVideo(response, str) {
const content_bytes = await response.arrayBuffer();
@ -590,6 +596,8 @@
replayVideoSegment(0, 60);
hideModal()
});
Subtitles = "";
}
// 防抖
@ -607,20 +615,6 @@
};
}
// 分钟转小时
function minutesToHoursMinutesStringSimplified(minutes) {
let hours = Math.floor(minutes / 60);
let remainingMinutes = minutes % 60;
let formattedMinutes = remainingMinutes.toString().padStart(2, '0');
if (hours > 0) {
return `${hours}小时${formattedMinutes}分`;
} else {
return `${formattedMinutes}分`;
}
}
// 拿到的数据移除*
function filterString(str, charsToRemove) {
// 这里的正则表达式是通过将charsToRemove数组中的字符转换为字符类character class来构建的

View File

@ -1 +1 @@
{"@platforms":["android","iPhone","iPad"],"id":"__UNI__24DA8DD","name":"中鼎云医","version":{"name":"1.2.5","code":125},"description":"中鼎云医","launch_path":"__uniappview.html","developer":{"name":"","email":"","url":""},"permissions":{"VideoPlayer":{},"Record":{},"UniNView":{"description":"UniNView原生渲染"}},"plus":{"useragent":{"value":"uni-app","concatenate":true},"splashscreen":{"target":"id:1","autoclose":true,"waiting":true,"delay":0},"popGesture":"close","launchwebview":{"render":"always","id":"1","kernel":"WKWebview"},"statusbar":{"immersed":"supportedDevice","style":"dark","background":"#F8F8F8"},"usingComponents":true,"nvueStyleCompiler":"uni-app","compilerVersion":3,"distribute":{"icons":{"android":{"hdpi":"unpackage/res/icons/72x72.png","xhdpi":"unpackage/res/icons/96x96.png","xxhdpi":"unpackage/res/icons/144x144.png","xxxhdpi":"unpackage/res/icons/192x192.png"},"ios":{"appstore":"unpackage/res/icons/1024x1024.png","ipad":{"app":"unpackage/res/icons/76x76.png","app@2x":"unpackage/res/icons/152x152.png","notification":"unpackage/res/icons/20x20.png","notification@2x":"unpackage/res/icons/40x40.png","proapp@2x":"unpackage/res/icons/167x167.png","settings":"unpackage/res/icons/29x29.png","settings@2x":"unpackage/res/icons/58x58.png","spotlight":"unpackage/res/icons/40x40.png","spotlight@2x":"unpackage/res/icons/80x80.png"},"iphone":{"app@2x":"unpackage/res/icons/120x120.png","app@3x":"unpackage/res/icons/180x180.png","notification@2x":"unpackage/res/icons/40x40.png","notification@3x":"unpackage/res/icons/60x60.png","settings@2x":"unpackage/res/icons/58x58.png","settings@3x":"unpackage/res/icons/87x87.png","spotlight@2x":"unpackage/res/icons/80x80.png","spotlight@3x":"unpackage/res/icons/120x120.png"}}},"google":{"permissions":["<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>","<uses-permission android:name=\"android.permission.VIBRATE\"/>","<uses-permission android:name=\"android.permission.READ_LOGS\"/>","<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>","<uses-feature android:name=\"android.hardware.camera.autofocus\"/>","<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.CAMERA\"/>","<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>","<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>","<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>","<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>","<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>","<uses-feature android:name=\"android.hardware.camera\"/>","<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>","<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>"]},"apple":{"dSYMs":false},"plugins":{"ad":{},"audio":{"mp3":{"description":"Android平台录音支持MP3格式文件"}}}},"uniStatistics":{"enable":false},"allowsInlineMediaPlayback":true,"uni-app":{"compilerVersion":"4.15","control":"uni-v3","nvueCompiler":"uni-app","renderer":"auto","nvue":{"flex-direction":"column"},"nvueLaunchMode":"normal"},"launch_path":"__uniappview.html"},"screenOrientation":["portrait-primary","portrait-secondary"]}
{"@platforms":["android","iPhone","iPad"],"id":"__UNI__24DA8DD","name":"中鼎云医","version":{"name":"1.2.5","code":125},"description":"中鼎云医","launch_path":"__uniappview.html","developer":{"name":"","email":"","url":""},"permissions":{"VideoPlayer":{},"Record":{},"UniNView":{"description":"UniNView原生渲染"}},"plus":{"useragent":{"value":"uni-app","concatenate":true},"splashscreen":{"target":"id:1","autoclose":true,"waiting":true,"delay":0},"popGesture":"close","launchwebview":{"render":"always","id":"1","kernel":"WKWebview"},"statusbar":{"immersed":"supportedDevice","style":"dark","background":"#F8F8F8"},"usingComponents":true,"nvueStyleCompiler":"uni-app","compilerVersion":3,"distribute":{"screenOrientation":"portrait","icons":{"android":{"hdpi":"unpackage/res/icons/72x72.png","xhdpi":"unpackage/res/icons/96x96.png","xxhdpi":"unpackage/res/icons/144x144.png","xxxhdpi":"unpackage/res/icons/192x192.png"},"ios":{"appstore":"unpackage/res/icons/1024x1024.png","ipad":{"app":"unpackage/res/icons/76x76.png","app@2x":"unpackage/res/icons/152x152.png","notification":"unpackage/res/icons/20x20.png","notification@2x":"unpackage/res/icons/40x40.png","proapp@2x":"unpackage/res/icons/167x167.png","settings":"unpackage/res/icons/29x29.png","settings@2x":"unpackage/res/icons/58x58.png","spotlight":"unpackage/res/icons/40x40.png","spotlight@2x":"unpackage/res/icons/80x80.png"},"iphone":{"app@2x":"unpackage/res/icons/120x120.png","app@3x":"unpackage/res/icons/180x180.png","notification@2x":"unpackage/res/icons/40x40.png","notification@3x":"unpackage/res/icons/60x60.png","settings@2x":"unpackage/res/icons/58x58.png","settings@3x":"unpackage/res/icons/87x87.png","spotlight@2x":"unpackage/res/icons/80x80.png","spotlight@3x":"unpackage/res/icons/120x120.png"}}},"google":{"permissions":["<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>","<uses-permission android:name=\"android.permission.VIBRATE\"/>","<uses-permission android:name=\"android.permission.READ_LOGS\"/>","<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>","<uses-feature android:name=\"android.hardware.camera.autofocus\"/>","<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.CAMERA\"/>","<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>","<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>","<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>","<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>","<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>","<uses-feature android:name=\"android.hardware.camera\"/>","<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>","<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>"]},"apple":{"dSYMs":false},"plugins":{"ad":{},"audio":{"mp3":{"description":"Android平台录音支持MP3格式文件"}}}},"uniStatistics":{"enable":false},"allowsInlineMediaPlayback":true,"uni-app":{"compilerVersion":"4.15","control":"uni-v3","nvueCompiler":"uni-app","renderer":"auto","nvue":{"flex-direction":"column"},"nvueLaunchMode":"normal"},"launch_path":"__uniappview.html"},"screenOrientation":["portrait-primary","portrait-secondary"]}

View File

@ -46,8 +46,11 @@
<!-- 录制 -->
<div class="buttons startRec">点击说话</div>
<!-- 暂停 -->
<div class="buttons endRec">中止</div>
<!-- 停止 -->
<div class="buttons endRec">停止</div>
<div class="buttons stopRec">暂停</div>
</div>
<!-- 讯飞测试 -->
@ -185,6 +188,7 @@
// 获取 audio 元素的引用
var audioElement = document.getElementById('myAudio');
var isPlaying = false; // 是否播放
audioElement.muted = true; // 先静音
// 获取页面元素
@ -196,7 +200,11 @@
// 点击事件
var startRec = document.getElementsByClassName('startRec')[0];
var endRec = document.getElementsByClassName('endRec')[0];
var stopRec = document.getElementsByClassName('stopRec')[0];
endRec.style.display = "none";
stopRec.style.display = "none";
var token = null;
let times = null;
@ -450,15 +458,35 @@
startShibie();
});
// 关闭识别
endRec.addEventListener("click", function() {
closeShibie();
});
// 关闭识别
stopRec.addEventListener("click", function() {
stopPlay();
});
function stopPlay() {
/**暂停播放**/
if (audioElement.paused) {
audioElement.play();
videoElement.play();
isPlaying = true;
stopRec.textContent = '暂停';
} else {
audioElement.pause();
videoElement.pause();
isPlaying = false;
stopRec.textContent = '继续播放';
}
};
function startShibie() {
/**开始识别**/
voiceTxt.innerText = '';
stopRec.textContent = '暂停';
voice.start();
isCallbackExecuted = false;
@ -469,6 +497,7 @@
startRec.style.display = 'none';
endRec.style.display = 'none';
stopRec.style.display = 'none';
showModal();
}
@ -476,6 +505,7 @@
/**关闭识别**/
voiceTxt.innerText = '';
statusTxt.value = '';
stopRec.textContent = '暂停';
voice.stop();
@ -489,10 +519,10 @@
startRec.style.display = 'block';
endRec.style.display = 'none';
stopRec.style.display = 'none';
hideModal()
}
// 显示弹窗和遮罩
function showModal() {
modal.style.display = 'block';
@ -557,6 +587,7 @@
}).then(async(response) => {
startRec.style.display = "none";
endRec.style.display = "block";
stopRec.style.display = "block";
NextPlayVideo(response, filterString(Subtitles, ['*', ' ']));
}).catch(e => {