402 lines
12 KiB
HTML
402 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
|
||
<head>
|
||
<script type="text/javascript" src="js/webView.js"></script>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
||
<title reclang="XvFp">远程喊话</title>
|
||
<link rel="stylesheet" href="./css/voice_style.css">
|
||
<script>
|
||
var PageLM = "2023-12-08 20:41";
|
||
</script>
|
||
</head>
|
||
|
||
<body>
|
||
<script src="js/recorder.core.js"></script>
|
||
<script src="js/mp3.js"></script>
|
||
<script src="js/mp3-engine.js"></script>
|
||
<script src="js/MQTT_port.js"></script>
|
||
|
||
<!-- 【2】构建界面 Build the web interface -->
|
||
<div class="content">
|
||
<div class="record-container">
|
||
<button class="break_btn" onclick="return_function()">返回</button>
|
||
<div id="record-popup" class="record-popup" style="display: none;">
|
||
<!-- 录音动效和提示信息 -->
|
||
<div id="recording-animation" style="display: none;">录音中...</div>
|
||
<div id="cancel-tip" style="display: none;">松手发送</div>
|
||
</div>
|
||
<div id="voice-wave-animation" class="voice-line-wrap" style="display: none;">
|
||
<div class="voice-line"></div>
|
||
<div class="voice-line"></div>
|
||
<div class="voice-line"></div>
|
||
<div class="voice-line"></div>
|
||
<div class="voice-line"></div>
|
||
<div class="voice-line"></div>
|
||
<div class="voice-line"></div>
|
||
</div>
|
||
<div class="voice-btn-wrap">
|
||
<button id="record-btn">喊话</button>
|
||
</div>
|
||
<audio id="audio-playback" controls style="display: none;"></audio>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 【3】实现录音逻辑 Implement recording logic -->
|
||
<script>
|
||
document.getElementById('record-btn').addEventListener('mousedown', recStart);
|
||
document.getElementById('record-btn').addEventListener('mouseup', recStop);
|
||
|
||
document.getElementById('record-btn').addEventListener("touchstart", recStart); // 移动端
|
||
document.getElementById('record-btn').addEventListener("touchend", recStop); // 移动端
|
||
|
||
//-----------------------------------------------------------------------//
|
||
var voice_reply_parse_fun;
|
||
function return_function() {
|
||
//location.href='screen_main.html';
|
||
window.history.go(-1);
|
||
}
|
||
function voice_reply_parse(json_str) {
|
||
let json = jQuery.parseJSON(json_str);
|
||
if ("sound_card" in json) {
|
||
switch (json.sound_card.state) {
|
||
case "succeed":
|
||
console.log("---succeed---");
|
||
break;
|
||
case "playing":
|
||
console.log("---playing---");
|
||
break;
|
||
case "downloadin":
|
||
console.log("---downloadin---");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
voice_reply_parse_fun = voice_reply_parse;
|
||
function send_voice_function(url, interrupt) {
|
||
var send_json = {};
|
||
send_json.online_play = new Object();
|
||
send_json.online_play.url = url;
|
||
if (interrupt) {
|
||
send_json.interrupt = interrupt;
|
||
}
|
||
var str = JSON.stringify(send_json);
|
||
console.log(str);
|
||
MQTT_json_send(str, "sound_card");
|
||
}
|
||
//-----------------------------------------------------------------------//
|
||
var rec, recBlob;
|
||
/**调用open打开录音请求好录音权限 Call open to open the recording and request the recording permission**/
|
||
function recOpen() {
|
||
//一般在显示出录音按钮或相关的录音界面时进行此方法调用,后面用户点击开始录音时就能畅通无阻了
|
||
rec = null;
|
||
recBlob = null;
|
||
var newRec = Recorder({
|
||
type: "mp3",
|
||
sampleRate: 16000,
|
||
bitRate: 16, //mp3格式,指定采样率hz、比特率kbps,其他参数使用默认配置;注意:是数字的参数必须提供数字,不要用字符串;需要使用的type类型,需提前把格式支持文件加载进来,比如使用wav格式需要提前加载wav.js编码引擎
|
||
onProcess: function (
|
||
buffers,
|
||
powerLevel,
|
||
bufferDuration,
|
||
bufferSampleRate,
|
||
newBufferIdx,
|
||
asyncEnd
|
||
) {
|
||
//录音实时回调,大约1秒调用12次本回调
|
||
// document.querySelector(".recpowerx").style.width=powerLevel+"%";
|
||
// document.querySelector(".recpowert").innerText=formatMs(bufferDuration,1)+" / "+powerLevel;
|
||
},
|
||
});
|
||
|
||
newRec.open(
|
||
function () {
|
||
//打开麦克风授权获得相关资源
|
||
rec = newRec;
|
||
},
|
||
function (msg, isUserNotAllow) {
|
||
//用户拒绝未授权或不支持
|
||
}
|
||
);
|
||
}
|
||
|
||
/**关闭录音,释放资源 Close recording, release resources**/
|
||
function recClose() {
|
||
if (rec) {
|
||
rec.close();
|
||
reclog(Html_$T("jqOs::已关闭"));
|
||
} else {
|
||
reclog(Html_$T("VOOw::未打开录音"), 1);
|
||
}
|
||
}
|
||
|
||
/**开始录音 Start recording**/
|
||
function recStart() {
|
||
// this.preventDefault();
|
||
//打开了录音后才能进行start、stop调用
|
||
if (rec && Recorder.IsOpen()) {
|
||
recBlob = null;
|
||
rec.start();
|
||
|
||
document.getElementById('voice-wave-animation').style.display = 'flex';
|
||
|
||
// 显示录音动效
|
||
document.getElementById('record-popup').style.display = 'block';
|
||
document.getElementById('recording-animation').style.display = 'block';
|
||
document.getElementById('cancel-tip').style.display = 'block';
|
||
} else {
|
||
}
|
||
}
|
||
|
||
/**结束录音,得到音频文件 Stop recording and get audio files**/
|
||
function recStop() {
|
||
if (!(rec && Recorder.IsOpen())) {
|
||
return;
|
||
}
|
||
rec.stop(
|
||
function (blob, duration) {
|
||
recBlob = blob;
|
||
let playAudio = document.getElementById("audio-playback");
|
||
// 将音频 URL 赋值给第二个 audio 元素
|
||
playAudio.src = (window.URL || webkitURL).createObjectURL(recBlob);
|
||
// send_voice_function(playAudio.src, 0);
|
||
// playAudio.play();
|
||
document.getElementById('voice-wave-animation').style.display = 'none';
|
||
document.getElementById('record-popup').style.display = 'none';
|
||
// 隐藏录音动效
|
||
document.getElementById('recording-animation').style.display = 'none';
|
||
document.getElementById('audio-playback').style.display = 'block';
|
||
recUpload();
|
||
},
|
||
function (msg) { }
|
||
);
|
||
}
|
||
|
||
/**播放 Play**/
|
||
function recPlay() {
|
||
if (!recBlob) {
|
||
reclog(Html_$T("tIke::请先录音,然后停止后再播放"), 1);
|
||
return;
|
||
}
|
||
let playAudio = document.getElementById("audioPlay");
|
||
// 将音频 URL 赋值给第二个 audio 元素
|
||
playAudio.src = (window.URL || webkitURL).createObjectURL(recBlob);
|
||
// send_voice_function(playAudio.src, 0);
|
||
playAudio.play();
|
||
setTimeout(function () {
|
||
(window.URL || webkitURL).revokeObjectURL(audio.src);
|
||
}, 5000);
|
||
}
|
||
function getQueryString(name) {
|
||
let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
|
||
let r = window.location.search.substr(1).match(reg);
|
||
if (r != null) return unescape(decodeURI(r[2]));
|
||
return null;
|
||
}
|
||
/**上传 Upload**/
|
||
function recUpload() {
|
||
var blob = recBlob;
|
||
if (!blob) {
|
||
reclog(Html_$T("DUTn::请先录音,然后停止后再上传"), 1);
|
||
return;
|
||
}
|
||
// 创建 FormData 对象
|
||
let formData = new FormData();
|
||
|
||
// 将 Blob 对象添加到 FormData 中,假设字段名为 'audioFile'
|
||
formData.append("file", blob, "recording.mp3");
|
||
|
||
// 发送 POST 请求到服务器
|
||
fetch("http://iot.pcpnb.cn/prod-api/iot/tool/upload", {
|
||
method: "POST",
|
||
headers: {
|
||
Authorization: getQueryString("token"),
|
||
},
|
||
body: formData,
|
||
})
|
||
.then((response) => {
|
||
console.log(response);
|
||
if (response.ok) {
|
||
return response.json(); // 或者 response.text() 如果服务器返回的是文本
|
||
}
|
||
throw new Error("Network response was not ok.");
|
||
})
|
||
.then((data) => {
|
||
console.log("Success:", data);
|
||
// 处理服务器返回的数据
|
||
send_voice_function("http://iot.pcpnb.cn/prod-api" + data.fileName, 99);
|
||
})
|
||
.catch((error) => {
|
||
console.error("Error:", error);
|
||
});
|
||
}
|
||
|
||
/**本地下载 Local download**/
|
||
function recLocalDown() {
|
||
if (!recBlob) {
|
||
reclog(Html_$T("M86h::请先录音,然后停止后再下载"), 1);
|
||
return;
|
||
}
|
||
var cls = ("a" + Math.random()).replace(".", "");
|
||
recdown64.lastCls = cls;
|
||
reclog(
|
||
Html_$T("vJPl::点击 ") +
|
||
'<span class="' +
|
||
cls +
|
||
'"></span>' +
|
||
Html_$T("Whtc:: 下载,或复制文本") +
|
||
"<button onclick=\"recdown64('" +
|
||
cls +
|
||
"')\">" +
|
||
Html_$T("XK4l::生成Base64文本") +
|
||
'</button><span class="' +
|
||
cls +
|
||
'_b64"></span>'
|
||
);
|
||
|
||
var fileName = "recorder-" + Date.now() + ".mp3";
|
||
var downA = document.createElement("A");
|
||
downA.innerHTML = Html_$T("g8Fy::下载 ") + fileName;
|
||
downA.href = (window.URL || webkitURL).createObjectURL(recBlob);
|
||
downA.download = fileName;
|
||
document.querySelector("." + cls).appendChild(downA);
|
||
if (/mobile/i.test(navigator.userAgent)) {
|
||
alert(
|
||
Html_xT(
|
||
Html_$T(
|
||
"DIEK::因移动端绝大部分国产浏览器未适配Blob Url的下载,所以本demo代码在移动端未调用downA.click()。请尝试点击日志中显示的下载链接下载"
|
||
)
|
||
)
|
||
);
|
||
} else {
|
||
downA.click();
|
||
}
|
||
|
||
//不用了时需要revokeObjectURL,否则霸占内存
|
||
//(window.URL||webkitURL).revokeObjectURL(downA.href);
|
||
}
|
||
function recdown64(cls) {
|
||
var el = document.querySelector("." + cls + "_b64");
|
||
if (recdown64.lastCls != cls) {
|
||
el.innerHTML =
|
||
'<span style="color:red">' +
|
||
Html_$T("eKKx::老的数据没有保存,只支持最新的一条") +
|
||
"</span>";
|
||
return;
|
||
}
|
||
var reader = new FileReader();
|
||
reader.onloadend = function () {
|
||
el.innerHTML = "<textarea></textarea>";
|
||
el.querySelector("textarea").value = reader.result;
|
||
};
|
||
reader.readAsDataURL(recBlob);
|
||
}
|
||
|
||
var formatMs = function (ms, all) {
|
||
var ss = ms % 1000;
|
||
ms = (ms - ss) / 1000;
|
||
var s = ms % 60;
|
||
ms = (ms - s) / 60;
|
||
var m = ms % 60;
|
||
ms = (ms - m) / 60;
|
||
var h = ms;
|
||
var t =
|
||
(h ? h + ":" : "") +
|
||
(all || h + m ? ("0" + m).substr(-2) + ":" : "") +
|
||
(all || h + m + s ? ("0" + s).substr(-2) + "″" : "") +
|
||
("00" + ss).substr(-3);
|
||
return t;
|
||
};
|
||
|
||
recOpen();
|
||
</script>
|
||
|
||
<!--以下这坨可以忽略 The following can be ignored-->
|
||
<script>
|
||
function reclog(s, color) { }
|
||
function Html_$T(s) {
|
||
return s;
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
body {
|
||
word-wrap: break-word;
|
||
background: #f5f5f5 center top no-repeat;
|
||
background-size: auto 680px;
|
||
}
|
||
|
||
pre {
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
a {
|
||
text-decoration: none;
|
||
color: #06c;
|
||
}
|
||
|
||
a:hover {
|
||
color: #f00;
|
||
}
|
||
|
||
.main {
|
||
max-width: 700px;
|
||
margin: 0 auto;
|
||
padding-bottom: 80px;
|
||
}
|
||
|
||
.mainBox {
|
||
margin-top: 12px;
|
||
padding: 12px;
|
||
border-radius: 6px;
|
||
background: #fff;
|
||
--border: 1px solid #f60;
|
||
box-shadow: 2px 2px 3px #aaa;
|
||
}
|
||
|
||
.btns button {
|
||
display: inline-block;
|
||
cursor: pointer;
|
||
border: none;
|
||
border-radius: 3px;
|
||
background: #f60;
|
||
color: #fff;
|
||
padding: 0 15px;
|
||
margin: 3px 20px 3px 0;
|
||
line-height: 36px;
|
||
height: 36px;
|
||
overflow: hidden;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.btns button:active {
|
||
background: #f00;
|
||
}
|
||
|
||
.pd {
|
||
padding: 0 0 6px 0;
|
||
}
|
||
|
||
.lb {
|
||
display: inline-block;
|
||
vertical-align: middle;
|
||
background: #00940e;
|
||
color: #fff;
|
||
font-size: 14px;
|
||
padding: 2px 8px;
|
||
border-radius: 99px;
|
||
}
|
||
|
||
.voice-btn-wrap button {
|
||
-ms-user-select: none;
|
||
-webkit-user-select: none;
|
||
-moz-user-select: none;
|
||
user-select: none;
|
||
}
|
||
</style>
|
||
</body>
|
||
|
||
</html> |