Commit 806181bc authored by xuchaozou's avatar xuchaozou

feat: 完成语音界面

parent 4f1a7479
Pipeline #85622 waiting for manual action with stages
......@@ -124,6 +124,7 @@
"bcryptjs": "2.4.3",
"compression": "1.7.4",
"connected-react-router": "6.4.0",
"crypto-js": "^4.2.0",
"echarts": "^5.3.0",
"echarts-for-react": "^3.0.2",
"fontfaceobserver": "2.1.0",
......
This diff was suppressed by a .gitattributes entry.
import style from './style.less'
import { QuestionCircleOutlined } from '@ant-design/icons'
import { withRouter } from '@wisdom-utils/runtime'
import {Typography} from 'antd'
import { useCallback, useEffect, useRef, useState } from 'react'
import {event} from '@wisdom-utils/utils'
import moment from 'moment'
import { Directive } from '../../core'
const Dialog = props => {
const {history , pandaRecordWebSocketRef} = props
const ulRef = useRef(null)
const directiveRef = useRef(null)
const [data , setData] = useState([])
const finish = useCallback(async ({resultText}) => {
pandaRecordWebSocketRef.setStatus("pause")
const directive = directiveRef.current.parse({
text : resultText
})
if(directive) {
sendContent({
role : "system",
content : "系统正在思考中,请稍后..."
})
const msg = await directive.excute()
updateLastData({
content : msg
})
return
}
sendContent({
role : "system",
content : "亲,我暂时还未理解你的意思,请换一种方式表达或者描述更加具体"
})
pandaRecordWebSocketRef.setStatus("playing")
}, [])
const frame = useCallback((data) => {
const {resultText , resultTextTemp} = data
setData(data => {
const length = data.length
const lastData = data.at(-1)
if(length == 0 || lastData.role == "system") {
return data.concat([{
role : "user",
time: new moment().format("HH:mm:ss"),
content : resultTextTemp
}])
} else {
return data.map((data , index) => {
if(index < length -1) {
return {
...data
}
} else {
return {
...data,
content : resultTextTemp
}
}
})
}
})
}, [])
useEffect(() => {
directiveRef.current = new Directive({
params : {
history
}
})
}, [])
useEffect(() => {
event.on("aiSound:finish", finish)
event.on("aiSound:frame", frame)
return () => {
event.off("aiSound:finish", finish)
event.off("aiSound:frame", frame)
}
}, [])
const sendContent = ({ role = "system", content, ...params }) => {
setData(i => i.concat([{
role,
time: new moment().format("HH:mm:ss"),
content,
...params
}]))
}
const updateLastData = ({content , ...params }) => {
setData(data => data.map((data , index) => {
if(index < length -1) {
return {
...data
}
} else {
return {
...data,
content,
...params,
}
}
}))
}
return (<div className={style.container}>
<div className={style.main}>
<div className={style.header}>
<p>熊猫智能语音库</p>
<div className={style.help}>
<QuestionCircleOutlined className={style.question} />
<div className={style.toolTipContent}>
<p>支持以下语音指令集</p>
<ul className={style.toolTip}>
{
[].map((name, index) => <li key={index}>{index + 1}.{name}</li>)
}
</ul>
</div>
</div>
</div>
<div className={style.content}>
<div className={style.display}>
<ul className={style.ul} ref={ulRef}>
{
data.map((item, index) => <li data-role={item.role} key={index} className={style.list}>
<Typography.Text className={style.user} mark>{item.role == "user" ? "用户" : "系统"}</Typography.Text>
<div className={style.aiContent}>
<p className={style.aiText}> {item.content}</p>
<span className={style.time}>{item.time}</span>
</div>
</li>)
}
</ul>
</div>
</div>
</div>
</div>)
}
export default withRouter(Dialog)
\ No newline at end of file
.container {
position: absolute;
right: 80px;
width: 350px;
height: 400px;
background-color: #fff;
bottom: 0;
border-radius: 5px;
box-shadow: 2px 2px #e6e0e0;
z-index: 10;
.main {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.header {
width: 100%;
font-size: 18px;
font-weight: bold;
display: flex;
align-items: center;
padding-left: 10px;
height: 40px;
border-bottom: 1px solid #eee;
justify-content: space-between;
>p {
margin-bottom: 0;
}
.help {
position: relative;
margin-right: 20px;
&:hover {
cursor: pointer;
.toolTipContent {
display: block;
}
}
.toolTipContent {
display: none;
position: absolute;
white-space: nowrap;
right: 0;
font-weight: normal;
font-size: 14px;
background: #f5f5f5;
padding: 0 10px;
border-radius: 5px;
z-index: 10;
>p {
margin-bottom: 0;
font-weight: bold;
}
.toolTip {
display: flex;
flex-direction: column;
margin-bottom: 0;
>li {
white-space: nowrap;
display: flex;
align-items: center;
height: 30px;
}
}
}
}
}
.content {
flex: 1;
height: 100%;
overflow-y: auto;
.display {
width: 100%;
height: auto;
background-color: #eee;
.ul {
padding: 5px 5px 15px 5px;
list-style: none;
display: flex;
flex-direction: column;
.list {
width: 100%;
margin-bottom: 15px;
display: flex;
flex-direction: row;
.user {
margin-right: 10px;
width: 45px;
}
.aiContent {
flex: 1;
background-color: #fff;
border-radius: 4px;
padding: 5px;
color: #000;
white-space: pre-wrap;
position: relative;
.aiText {
margin-bottom: 0;
}
.time {
position: absolute;
right: 0;
bottom: 0;
font-size: 12px;
color: #797070;
border-radius: 2px;
background-color: #dbd3d3;
}
}
&[data-role="system"] {
.user mark {
background-color: #44f449;
}
}
}
}
}
// .empty {
// width: 100%;
// height: 100%;
// display: flex;
// align-items: center;
// justify-content: center;
// }
}
// .footer {
// display: flex;
// width: 100%;
// height: 40px;
// align-items: center;
// padding-left: 5px;
// }
}
}
\ No newline at end of file
import { useCallback, useEffect, useRef, useState } from 'react'
import style from './style.less'
import image from '@/assets/images/soundAi/熊猫.gif'
import {event} from '@wisdom-utils/utils'
import { AwakenDirective , ByeDirective, Directive} from '../../core'
import Dialog from '../Dialog'
const PandaTip = props => {
const [status, setStatus] = useState("open")
const directiveRef = useRef(null)
const {pandaRecordWebSocketRef} = props
const finish = useCallback(({resultText}) => {
const directive = directiveRef.current.parse({
text : resultText
})
if(!directive) return null
if(directive instanceof AwakenDirective && status == "close") {
setStatus("open")
} else if(directive instanceof ByeDirective && status == "open") {
setStatus("close")
}
}, [])
useEffect(() => {
directiveRef.current = new Directive({
// directiveTypes : [AwakenDirective , ByeDirective],
directiveTypes : []
})
}, [])
useEffect(() => {
event.on("aiSound:finish", finish)
return () => {
event.off("aiSound:finish", finish)
}
}, [status])
return (<div className={style.container}>
<img src = {image} className={style.image} />
{
status == "open" ? <Dialog pandaRecordWebSocketRef = {pandaRecordWebSocketRef}/> : null
}
</div>)
}
export default PandaTip
\ No newline at end of file
.container{
position: absolute;
right: 50px;
bottom: 50px;
z-index: 100;
.image{
width: 80px;
height: 80px;
&:hover{
cursor: pointer;
}
}
}
\ No newline at end of file
export const FAIL_TYPE_DATA_ERRRO = 'FAIL_TYPE_DATA_ERRRO';
export const FAIL_TYPE_DATA_ERRRO_MSG = '服务器返回的数据意外终止';
export const FAIL_TYPE_WS_ERRRO = 'FAIL_TYPE_WS_ERRRO';
export const FAIL_TYPE_WS_ERRRO_MSG = 'webSocket意外中断';
export const LAST_DATA_IS_HANDLE = 'LAST_DATA_IS_HANDLE';
export const LAST_DATA_IS_HANDLE_MSG = '正在处理上一次数据';
export default {
FAIL_TYPE_DATA_ERRRO,
FAIL_TYPE_DATA_ERRRO_MSG,
FAIL_TYPE_WS_ERRRO,
FAIL_TYPE_WS_ERRRO_MSG,
LAST_DATA_IS_HANDLE,
LAST_DATA_IS_HANDLE_MSG,
};
import PandaWebSocket from './PandaWebSocket';
import CryptoJS from 'crypto-js';
import RecorderManager from '../../libs/aiSounds/index.umd1.js';
import Utils from '../../utils/Utils.js';
import { FAIL_TYPE_DATA_ERRRO_MSG, FAIL_TYPE_DATA_ERRRO } from '../../constants/index.js';
class PandaRecordWebSocket extends PandaWebSocket {
static url = 'wss://iat-api.xfyun.cn/v2/iat';
static host = 'iat-api.xfyun.cn';
static algorithm = 'hmac-sha256';
static headers = 'host date request-line';
constructor(options) {
super(options);
this.initConfig();
this.initEvents();
this.onRecorderStart = this.onRecorderStart.bind(this);
this.onRecorderFrameRecorded = this.onRecorderFrameRecorded.bind(this);
this.onRecorderStop = this.onRecorderStop.bind(this);
this.createRecorder();
this.resultText = '';
this.resultTextTemp = '';
this.count = 0;
this.initFrameBuffer = null;
this.status = "playing";
}
async start() {
const data = await super.start();
if (!data.isSuccess) {
return data;
}
this.createWs();
return data;
}
async checkParams() {
let data = super.checkParams();
if (!data.isSuccess) return data;
data = await Utils.isCanableRecorder(this.config.checkDelayDeciveTime);
if (!data.isSuccess) return data;
return data;
}
initConfig() {
//一句话间隔5s
if (this.config.vad_eos == undefined) {
this.config.vad_eos = 2000;
}
//动态修正
if (this.config.dwa == undefined) {
this.config.dwa = 'wpgs';
}
//标点符号
if (this.config.ptt == undefined) {
this.config.ptt = 1;
}
//采样
if (this.config.format == undefined) {
this.config.format = 'audio/L16;rate=16000';
}
//音频格式
if (this.config.encoding == undefined) {
this.config.encoding = 'raw';
}
//验证延迟时间
if (this.config.checkDelayDeciveTime == undefined) {
this.config.checkDelayDeciveTime = 1500;
}
//worker
if (this.config.worker == undefined) {
this.config.worker = 'https://civgis.panda-water.cn/system/pandaAiSoundWorker';
}
}
initEvents() {
if (!this.events.recorderStart) {
this.events.recorderStart = function () {};
}
if (!this.events.recorderStop) {
this.events.recorderStop = function () {};
}
}
setStatus(status) {
this.status = status
}
getWebSocketUrl() {
const { apiSecret, apiKey } = this.config;
if (!apiKey || !apiSecret) {
return null;
}
const date = new Date().toGMTString();
const signatureOrigin = `host: ${PandaRecordWebSocket.host}\ndate: ${date}\nGET /v2/iat HTTP/1.1`;
const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
const signature = CryptoJS.enc.Base64.stringify(signatureSha);
const authorizationOrigin = `api_key="${apiKey}", algorithm="${PandaRecordWebSocket.algorithm}", headers="${PandaRecordWebSocket.headers}", signature="${signature}"`;
const authorization = btoa(authorizationOrigin);
const url = `${PandaRecordWebSocket.url}?authorization=${authorization}&date=${date}&host=${PandaRecordWebSocket.host}`;
return url;
}
createRecorder() {
this.recorderManager = new RecorderManager(this.config.worker);
this.recorderManager.onStart = this.onRecorderStart;
this.recorderManager.onFrameRecorded = this.onRecorderFrameRecorded;
}
onRecorderStart(e) {
this.recorderManager.status = 'open';
this.events.recorderStart();
}
onRecorderStop() {
this.events.recorderStop();
}
getIsEmptyData(buffer, length, count) {
let _count = 0,
isEmpty = true;
const array = new Uint8Array(buffer);
for (let i = 0; i < length; ++i) {
if (array[i] != 0) {
_count++;
}
if (_count >= count) {
isEmpty = false;
break;
}
}
return isEmpty;
}
onRecorderFrameRecorded({ isLastFrame, frameBuffer }) {
const { encoding, format } = this.config;
if (!this.ws) {
const isEmptyData = this.getIsEmptyData(frameBuffer, 200, 20);
if (isEmptyData) {
this.initFrameBuffer = null;
console.log('系统檢測到輸入');
return;
}
if(this.status == "paused") return
this.initFrameBuffer = frameBuffer;
this.resultText = '';
this.resultTextTemp = '';
this.createWs();
}
if (this.ws.readyState === this.ws.OPEN) {
this.ws.send(
JSON.stringify({
data: {
format,
encoding,
status: isLastFrame ? 2 : 1,
audio: Utils.toBase64(frameBuffer),
},
}),
);
if (isLastFrame) {
this.destroy();
}
}
}
reconnect() {
this.count += 1;
this.wsClose();
}
wsOpen(e) {
if (!this.recorderManager) return;
if (this.recorderManager.status != 'open') {
this.recorderManager.start({
sampleRate: 16000,
frameSize: 1280,
});
}
const { appId, vad_eos, dwa, ptt, format, encoding } = this.config;
const data = {
format,
encoding,
status: 0,
};
if (this.initFrameBuffer) {
data.audio = Utils.toBase64(this.initFrameBuffer);
this.initFrameBuffer = null;
}
const params = {
common: {
app_id: appId,
},
business: {
dwa,
ptt,
language: 'zh_cn',
domain: 'iat',
accent: 'mandarin',
vad_eos: vad_eos,
},
data,
};
this.ws.send(JSON.stringify(params));
}
wsOnmessage(e) {
super.wsOnmessage(e);
const jsonData = JSON.parse(e.data);
if (jsonData.data && jsonData.data.result) {
let data = jsonData.data.result;
let str = '';
let ws = data.ws;
for (let i = 0; i < ws.length; i++) {
str = str + ws[i].cw[0].w;
}
// 开启wpgs会有此字段(前提:在控制台开通动态修正功能)
// 取值为 "apd"时表示该片结果是追加到前面的最终结果;取值为"rpl" 时表示替换前面的部分结果,替换范围为rg字段
if (data.pgs) {
if (data.pgs === 'apd') {
// 将resultTextTemp同步给resultText
this.resultText = this.resultTextTemp;
}
// 将结果存储在resultTextTemp中
this.resultTextTemp = this.resultText + str;
} else {
this.resultText = this.resultText + str;
}
this.events.frame({
resultTextTemp: this.resultTextTemp,
resultText: this.resultText,
pgs: data.pgs,
count: this.count,
});
}
if (jsonData.code == 0 && jsonData.data.status == 2) {
this.events.finish({
resultText: this.resultText,
});
this.reconnect();
}
if (jsonData.code != 0) {
this.events.fail({
type: FAIL_TYPE_DATA_ERRRO,
msg: FAIL_TYPE_DATA_ERRRO_MSG,
data: {
code: jsonData.code,
},
});
this.destroy();
}
}
wsOnerror(e) {
super.wsOnerror(e);
this.destroy();
}
wsOnclose(e) {
super.wsOnclose(e);
}
stopRecorderManager() {
if (this.recorderManager) {
this.recorderManager.onFrameRecorded = null;
this.recorderManager.onStart = null;
this.recorderManager.stop();
this.recorderManager = null;
}
}
async destroy() {
this.stopRecorderManager();
await Utils.delayTime(1000);
this.wsClose();
this.resultText = '';
this.resultTextTemp = '';
this.count = 0;
this.initFrameBuffer = null;
}
}
export default PandaRecordWebSocket;
import PandaWebSocket from './PandaWebSocket';
import CryptoJS from 'crypto-js';
import {
FAIL_TYPE_DATA_ERRRO,
FAIL_TYPE_DATA_ERRRO_MSG,
LAST_DATA_IS_HANDLE,
LAST_DATA_IS_HANDLE_MSG,
} from '../../constants';
import Utils from '../../utils/Utils';
class PandaSparkWebSocket extends PandaWebSocket {
static host = 'aichat.xf-yun.com';
static algorithm = 'hmac-sha256';
static headers = 'host date request-line';
constructor(options) {
super(options);
this.initConfig();
this.data = [];
this.resultText = '';
this.resultTextTemp = '';
this.isHandleData = false;
this.recordData = null;
this.resolve = null;
this.reject = null;
}
initConfig() {
if (this.config.modelVersion == undefined) {
this.config.modelVersion = '3.5';
}
if (this.config.url == undefined) {
this.config.url = `wss://spark-api.xf-yun.com/v${this.config.modelVersion}/chat`;
}
this.config.domain = `generalv${this.config.modelVersion}`;
this.uid = Math.random().toString().substring(2, 9);
this.chat_id = this.uid;
}
getWebSocketUrl() {
const { apiSecret, apiKey, url, modelVersion } = this.config;
if (!apiKey || !apiSecret) return;
const date = new Date().toGMTString();
const signatureOrigin = `host: ${PandaSparkWebSocket.host}\ndate: ${date}\nGET /v${modelVersion}/chat HTTP/1.1`;
const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
const signature = CryptoJS.enc.Base64.stringify(signatureSha);
const authorizationOrigin = `api_key="${apiKey}", algorithm="${PandaSparkWebSocket.algorithm}", headers="${PandaSparkWebSocket.headers}", signature="${signature}"`;
const authorization = btoa(authorizationOrigin);
const url_ = `${url}?authorization=${authorization}&date=${date}&host=${PandaSparkWebSocket.host}`;
return url_;
}
createWs() {
this.websocketUrl = this.getWebSocketUrl();
super.createWs();
}
wsOpen(e) {
super.wsOpen(e);
const { isLinkContext, content, role } = this.recordData;
if (this.ws.OPEN == this.ws.readyState) {
const { appId, uid, chat_id, domain } = this.config;
if (isLinkContext) {
this.data.push({
role: role || 'user',
content,
});
} else {
this.data = [
{
role: role || 'user',
content,
},
];
}
this.handlDataLength();
const json = this.data.map((i) => ({
role: i.role,
content: i.content,
}));
const params = {
header: {
app_id: appId,
uid,
},
parameter: {
chat: {
domain,
chat_id,
max_tokens: 512,
},
},
payload: {
message: {
text: json,
},
},
};
this.ws.send(JSON.stringify(params));
} else {
}
}
handlDataLength() {
let bytesLength = 0,
tempIndex;
for (let i = this.data.length - 1; i >= 0; i--) {
bytesLength += Utils.GetBytesLength(this.data[i].content);
if (bytesLength >= 8192) {
temp = i;
break;
}
}
if (tempIndex != undefined) {
this.data.splice(0, tempIndex);
}
}
sendMessag({ content, role, isLinkContext = true }) {
return new Promise((resolve, reject) => {
if (this.resolve) {
return reject({
type: LAST_DATA_IS_HANDLE,
msg: LAST_DATA_IS_HANDLE_MSG,
});
}
this.resultText = '';
this.resultTextTemp = '';
this.resolve = resolve;
this.reject = reject;
this.recordData = {
content,
role,
isLinkContext,
};
this.createWs();
});
}
getIsSendMessag() {
return !this.resolve;
}
stop() {
this.wsClose();
}
wsOnclose(e) {
super.wsOnclose(e);
this.reject = null;
this.resolve = null;
}
wsOnmessage(e) {
super.wsOnmessage(e);
const data = JSON.parse(e.data);
if (data.header.code != 0) {
this.events.fail({
type: FAIL_TYPE_DATA_ERRRO,
msg: FAIL_TYPE_DATA_ERRRO_MSG,
data: {
code: data.header.code,
},
});
this.destroy();
return;
}
const { status, text } = data.payload.choices;
const { content } = text[0];
this.resultText += content;
if (status == 1) {
this.events.frame({
resultTextTemp: content,
resultText: this.resultText,
});
} else if (status == 2) {
this.data.push({
role: 'assistant',
resultText: this.resultText,
});
this.events.finish({
resultText: this.resultText,
total_tokens: data.payload.usage.text.total_tokens,
});
this.resolve({
resultText: this.resultText,
total_tokens: data.payload.usage.text.total_tokens,
});
this.wsClose();
}
}
destroy() {
super.destroy();
this.resolve = null;
this.reject = null;
this.recordData = null;
this.data = [];
}
}
export default PandaSparkWebSocket;
import CryptoJS from 'crypto-js';
class PandaWebSocket {
static url = 'wss://iat-api.xfyun.cn/v2/iat';
static host = 'iat-api.xfyun.cn';
static algorithm = 'hmac-sha256';
static headers = 'host date request-line';
constructor(options = {}) {
this.options = options;
this.config = options.config || {};
this.webSocketInstance = null;
this.websocketUrl = null;
this.wsOpen = this.wsOpen.bind(this);
this.wsOnmessage = this.wsOnmessage.bind(this);
this.wsOnerror = this.wsOnerror.bind(this);
this.wsOnclose = this.wsOnclose.bind(this);
this.events = Object.assign(
{},
{
wsOpen: function () {},
wsOnmessage: function () {},
wsOnerror: function () {},
wsOnclose: function () {},
fail: function () {},
frame: function () {},
finish: function () {},
},
options.events || {},
);
}
async start() {
const data = await this.checkParams();
if (!data.isSuccess) {
return data;
}
return data;
}
createWs() {
this.wsClose();
this.ws = new this.webSocketInstance(this.websocketUrl);
this.ws.onopen = this.wsOpen;
this.ws.onmessage = this.wsOnmessage;
this.ws.onerror = this.wsOnerror;
this.ws.onclose = this.wsOnclose;
}
wsOpen(e) {
this.events.wsOpen(e);
}
wsOnmessage(e) {
this.events.wsOnmessage(e);
}
wsOnerror(e) {
this.events.wsOnerror(e);
}
wsOnclose(e) {
console.log(e);
this.events.wsOnclose(e);
}
wsClose() {
if (this.ws) {
this.ws.close();
this.ws = null;
}
}
checkParams() {
this.websocketUrl = this.getWebSocketUrl();
if (!this.websocketUrl) {
return {
isSuccess: false,
msg: '创建websocketUrl失败',
};
}
this.webSocketInstance = this.getWebsocketInstance();
if (!this.webSocketInstance) {
return {
isSuccess: false,
msg: '游览器版本太低,不支持websocket',
};
}
return {
isSuccess: true,
};
}
getWebsocketInstance() {
let instance = null;
if ('WebSocket' in window) {
instance = window['WebSocket'];
} else if ('MozWebSocket' in window) {
instance = window['MozWebSocket'];
}
return instance;
}
getWebSocketUrl() {
if (this.options.getWebSocketUrl) {
return this.options.getWebSocketUrl({
config: this.config,
CryptoJS,
});
}
return null;
}
destroy() {
this.wsClose();
}
}
export default PandaWebSocket;
import BaseDirective from "./BaseDirective"
class AwakenDirective extends BaseDirective{
match () {
const {text} = this
if(/熊猫熊猫/.test(text)) {
return true
}
return false
}
async excute () {
}
}
export default AwakenDirective
\ No newline at end of file
class BaseDirective {
constructor (options = {}) {
this.options = options
this.text = this.options.text
this.directives = options.directives
}
match () {
}
async excute () {
}
}
export default BaseDirective
\ No newline at end of file
import BaseDirective from "./BaseDirective";
class ByeDirective extends BaseDirective {
match () {
const {text} = this
if(/再见再见|拜拜/.test(text)) {
return true
}
return false
}
}
export default ByeDirective
\ No newline at end of file
import BaseDirective from "./BaseDirective";
class OpenMenuDirective extends BaseDirective {
match () {
}
async excute () {
}
}
export default OpenMenuDirective
\ No newline at end of file
import AwakenDirective from "./AwakenDirective"
import OpenMenuDirective from "./OpenMenuDirective"
const DirectiveTypes = [
AwakenDirective,
OpenMenuDirective
]
class Directive {
constructor (options = {}) {
this.directiveTypes = options.directiveTypes || DirectiveTypes
this.params = options.params || {}
this.directives = []
}
parse ({text}) {
let isHasSuccess = false
for(let i = 0 ; i < this.directiveTypes.length ; i++) {
const directive = new this.directiveTypes[i]({text , directives : this.directives, params : this.params})
if(directive.match()) {
isHasSuccess = true
this.directives.push(directive)
break
}
}
return isHasSuccess ? this.directives.at(-1) : null
}
async excute (directive = this.directives.at(-1)) {
if(!directive) return
const data = await directive.excute()
return data
}
destroy () {
}
}
export default Directive
\ No newline at end of file
import RecorderManager from './libs/aiSounds/index.umd';
import PandaWebSocket from './core/sockets/PandaWebSocket';
import PandaRecordWebSocket from './core/sockets/PandaRecordWebSocket';
import Utils from './utils/Utils';
import PandaSparkWebSocket from './core/sockets/PandaSparkWebSocket';
import Directive from './directive';
import AwakenDirective from './directive/AwakenDirective';
import ByeDirective from './directive/ByeDirective';
export {
RecorderManager,
PandaWebSocket,
PandaRecordWebSocket,
Utils,
PandaSparkWebSocket,
Directive,
AwakenDirective,
ByeDirective
};
'use strict';
function e(e, t, r, o) {
return new (r || (r = Promise))(function (n, a) {
function i(e) {
try {
u(o.next(e));
} catch (e) {
a(e);
}
}
function s(e) {
try {
u(o.throw(e));
} catch (e) {
a(e);
}
}
function u(e) {
var t;
e.done
? n(e.value)
: ((t = e.value),
t instanceof r
? t
: new r(function (e) {
e(t);
})).then(i, s);
}
u((o = o.apply(e, t || [])).next());
});
}
function t(e, t) {
var r,
o,
n,
a,
i = {
label: 0,
sent: function () {
if (1 & n[0]) throw n[1];
return n[1];
},
trys: [],
ops: [],
};
return (
(a = { next: s(0), throw: s(1), return: s(2) }),
'function' == typeof Symbol &&
(a[Symbol.iterator] = function () {
return this;
}),
a
);
function s(s) {
return function (u) {
return (function (s) {
if (r) throw new TypeError('Generator is already executing.');
for (; a && ((a = 0), s[0] && (i = 0)), i; )
try {
if (
((r = 1),
o &&
(n =
2 & s[0]
? o.return
: s[0]
? o.throw || ((n = o.return) && n.call(o), 0)
: o.next) &&
!(n = n.call(o, s[1])).done)
)
return n;
switch (((o = 0), n && (s = [2 & s[0], n.value]), s[0])) {
case 0:
case 1:
n = s;
break;
case 4:
return i.label++, { value: s[1], done: !1 };
case 5:
i.label++, (o = s[1]), (s = [0]);
continue;
case 7:
(s = i.ops.pop()), i.trys.pop();
continue;
default:
if (
!((n = i.trys),
(n = n.length > 0 && n[n.length - 1]) || (6 !== s[0] && 2 !== s[0]))
) {
i = 0;
continue;
}
if (3 === s[0] && (!n || (s[1] > n[0] && s[1] < n[3]))) {
i.label = s[1];
break;
}
if (6 === s[0] && i.label < n[1]) {
(i.label = n[1]), (n = s);
break;
}
if (n && i.label < n[2]) {
(i.label = n[2]), i.ops.push(s);
break;
}
n[2] && i.ops.pop(), i.trys.pop();
continue;
}
s = t.call(e, i);
} catch (e) {
(s = [6, e]), (o = 0);
} finally {
r = n = 0;
}
if (5 & s[0]) throw s[1];
return { value: s[0] ? s[1] : void 0, done: !0 };
})([s, u]);
};
}
}
var r = !AudioWorkletNode;
function o() {
var e;
return (null === (e = navigator.mediaDevices) || void 0 === e ? void 0 : e.getUserMedia)
? navigator.mediaDevices.getUserMedia({ audio: !0, video: !1 })
: navigator.getUserMedia
? new Promise(function (e, t) {
navigator.getUserMedia(
{ audio: !0, video: !1 },
function (t) {
e(t);
},
function (e) {
t(e);
},
);
})
: Promise.reject(new Error('不支持录音'));
}
function n(o, n) {
return e(this, void 0, void 0, function () {
return t(this, function (e) {
switch (e.label) {
case 0:
return r ? [4, o.audioWorklet.addModule(''.concat(n, '/processor.worklet.js'))] : [3, 2];
case 1:
return e.sent(), [2, new AudioWorkletNode(o, 'processor-worklet')];
case 2:
return [4, new Worker(''.concat(n, '/processor.worker.js'))];
case 3:
return [2, { port: e.sent() }];
}
});
});
}
var a = (function () {
function a(e) {
(this.processorPath = e), (this.audioBuffers = []);
}
return (
(a.prototype.start = function (a) {
var i,
s = a.sampleRate,
u = a.frameSize,
c = a.arrayBufferType;
return e(this, void 0, void 0, function () {
var e, a, d, l, f, p;
return t(this, function (t) {
switch (t.label) {
case 0:
return ((e = this).audioBuffers = []), [4, o()];
case 1:
return (
(a = t.sent()),
(this.audioTracks = a.getAudioTracks()),
(d = (function (e, t) {
var r;
try {
(r = new (window.AudioContext || window.webkitAudioContext)({
sampleRate: t,
})).createMediaStreamSource(e);
} catch (t) {
(r = new (window.AudioContext ||
window.webkitAudioContext)()).createMediaStreamSource(e);
}
return r;
})(a, s)),
(this.audioContext = d),
d.createMediaStreamSource(a),
(l = d.createMediaStreamSource(a)),
[4, n(d, this.processorPath)]
);
case 2:
return (
(f = t.sent()),
(this.audioWorklet = f),
f.port.postMessage({
type: 'init',
data: {
frameSize: u,
toSampleRate: s || d.sampleRate,
fromSampleRate: d.sampleRate,
arrayBufferType: c || 'short16',
},
}),
(f.port.onmessage = function (t) {
u && e.onFrameRecorded && e.onFrameRecorded(t.data),
e.onStop &&
(t.data.frameBuffer && e.audioBuffers.push(t.data.frameBuffer),
t.data.isLastFrame && !r && (null == f ? void 0 : f.port).terminate(),
t.data.isLastFrame && e.onStop(e.audioBuffers));
}),
r
? l.connect(f)
: (((p = d.createScriptProcessor(0, 1, 1)).onaudioprocess = function (e) {
f.port.postMessage({
type: 'message',
data: e.inputBuffer.getChannelData(0),
});
}),
l.connect(p),
p.connect(d.destination)),
d.resume(),
null === (i = this.onStart) || void 0 === i || i.call(this),
[2]
);
}
});
});
}),
(a.prototype.stop = function () {
var e, t, r;
null === (e = this.audioWorklet) || void 0 === e || e.port.postMessage({ type: 'stop' }),
null === (t = this.audioTracks) || void 0 === t || t[0].stop(),
null === (r = this.audioContext) || void 0 === r || r.suspend();
}),
a
);
})();
module.exports = a;
declare class RecorderManager {
/**
* 构造函数
* @param processorPath processor的文件路径,如果processor.worker.js的访问地址为`/a/b/processor.worker.js`,则processorPath 为`/a/b`
*
*/
constructor(processorPath: string);
private audioBuffers;
private processorPath;
private audioContext?;
private audioTracks?;
private audioWorklet?;
onStop?: (audioBuffers: ArrayBuffer[]) => void;
onFrameRecorded?: (params: { isLastFrame: boolean; frameBuffer: ArrayBuffer }) => void;
/**
* 监听录音开始事件
*/
onStart?: () => void;
start({
sampleRate,
frameSize,
arrayBufferType,
}: {
sampleRate?: number;
frameSize?: number;
arrayBufferType?: 'short16' | 'float32';
}): Promise<void>;
stop(): void;
}
export { RecorderManager as default };
function e(e, t, r, o) {
return new (r || (r = Promise))(function (n, a) {
function i(e) {
try {
s(o.next(e));
} catch (e) {
a(e);
}
}
function u(e) {
try {
s(o.throw(e));
} catch (e) {
a(e);
}
}
function s(e) {
var t;
e.done
? n(e.value)
: ((t = e.value),
t instanceof r
? t
: new r(function (e) {
e(t);
})).then(i, u);
}
s((o = o.apply(e, t || [])).next());
});
}
function t(e, t) {
var r,
o,
n,
a,
i = {
label: 0,
sent: function () {
if (1 & n[0]) throw n[1];
return n[1];
},
trys: [],
ops: [],
};
return (
(a = { next: u(0), throw: u(1), return: u(2) }),
'function' == typeof Symbol &&
(a[Symbol.iterator] = function () {
return this;
}),
a
);
function u(u) {
return function (s) {
return (function (u) {
if (r) throw new TypeError('Generator is already executing.');
for (; a && ((a = 0), u[0] && (i = 0)), i; )
try {
if (
((r = 1),
o &&
(n =
2 & u[0]
? o.return
: u[0]
? o.throw || ((n = o.return) && n.call(o), 0)
: o.next) &&
!(n = n.call(o, u[1])).done)
)
return n;
switch (((o = 0), n && (u = [2 & u[0], n.value]), u[0])) {
case 0:
case 1:
n = u;
break;
case 4:
return i.label++, { value: u[1], done: !1 };
case 5:
i.label++, (o = u[1]), (u = [0]);
continue;
case 7:
(u = i.ops.pop()), i.trys.pop();
continue;
default:
if (
!((n = i.trys),
(n = n.length > 0 && n[n.length - 1]) || (6 !== u[0] && 2 !== u[0]))
) {
i = 0;
continue;
}
if (3 === u[0] && (!n || (u[1] > n[0] && u[1] < n[3]))) {
i.label = u[1];
break;
}
if (6 === u[0] && i.label < n[1]) {
(i.label = n[1]), (n = u);
break;
}
if (n && i.label < n[2]) {
(i.label = n[2]), i.ops.push(u);
break;
}
n[2] && i.ops.pop(), i.trys.pop();
continue;
}
u = t.call(e, i);
} catch (e) {
(u = [6, e]), (o = 0);
} finally {
r = n = 0;
}
if (5 & u[0]) throw u[1];
return { value: u[0] ? u[1] : void 0, done: !0 };
})([u, s]);
};
}
}
var r = !AudioWorkletNode;
function o() {
var e;
return (null === (e = navigator.mediaDevices) || void 0 === e ? void 0 : e.getUserMedia)
? navigator.mediaDevices.getUserMedia({ audio: !0, video: !1 })
: navigator.getUserMedia
? new Promise(function (e, t) {
navigator.getUserMedia(
{ audio: !0, video: !1 },
function (t) {
e(t);
},
function (e) {
t(e);
},
);
})
: Promise.reject(new Error('不支持录音'));
}
function n(o, n) {
return e(this, void 0, void 0, function () {
return t(this, function (e) {
switch (e.label) {
case 0:
return r ? [4, o.audioWorklet.addModule(''.concat(n, '/processor.worklet.js'))] : [3, 2];
case 1:
return e.sent(), [2, new AudioWorkletNode(o, 'processor-worklet')];
case 2:
let worker;
if (/http/.test(n)) {
const workerBlob = new Blob(
['importScripts(' + JSON.stringify(n + '/processor.worker.js') + ')'],
{ type: 'application/javascript' },
);
const blobUrl = window.URL.createObjectURL(workerBlob);
worker = new Worker(blobUrl);
worker.__blobUrl = blobUrl;
} else {
worker = new Worker(''.concat(n, '/processor.worker.js'));
}
return [4, worker];
case 3:
return [2, { port: e.sent() }];
}
});
});
}
var a = (function () {
function a(e) {
(this.processorPath = e), (this.audioBuffers = []);
}
return (
(a.prototype.start = function (a) {
var i,
u = a.sampleRate,
s = a.frameSize,
c = a.arrayBufferType;
return e(this, void 0, void 0, function () {
var e, a, d, l, f, p;
return t(this, function (t) {
switch (t.label) {
case 0:
return ((e = this).audioBuffers = []), [4, o()];
case 1:
return (
(a = t.sent()),
(this.audioTracks = a.getAudioTracks()),
(d = (function (e, t) {
var r;
try {
(r = new (window.AudioContext || window.webkitAudioContext)({
sampleRate: t,
})).createMediaStreamSource(e);
} catch (t) {
(r = new (window.AudioContext ||
window.webkitAudioContext)()).createMediaStreamSource(e);
}
return r;
})(a, u)),
(this.audioContext = d),
d.createMediaStreamSource(a),
(l = d.createMediaStreamSource(a)),
[4, n(d, this.processorPath)]
);
case 2:
return (
(f = t.sent()),
(this.audioWorklet = f),
f.port.postMessage({
type: 'init',
data: {
frameSize: s,
toSampleRate: u || d.sampleRate,
fromSampleRate: d.sampleRate,
arrayBufferType: c || 'short16',
},
}),
(f.port.onmessage = function (t) {
if (f.port.__blobUrl) {
window.URL.revokeObjectURL(f.port.__blobUrl);
f.port.__blobUrl = null;
}
s && e.onFrameRecorded && e.onFrameRecorded(t.data),
e.onStop &&
(t.data.frameBuffer && e.audioBuffers.push(t.data.frameBuffer),
t.data.isLastFrame && !r && (null == f ? void 0 : f.port).terminate(),
t.data.isLastFrame && e.onStop(e.audioBuffers));
}),
r
? l.connect(f)
: (((p = d.createScriptProcessor(0, 1, 1)).onaudioprocess = function (e) {
f.port.postMessage({
type: 'message',
data: e.inputBuffer.getChannelData(0),
});
}),
l.connect(p),
p.connect(d.destination)),
d.resume(),
null === (i = this.onStart) || void 0 === i || i.call(this),
[2]
);
}
});
});
}),
(a.prototype.stop = function () {
var e, t, r;
if (this.audioWorklet && this.audioWorklet.port && this.audioWorklet.port.__blobUrl) {
window.URL.revokeObjectURL(this.audioWorklet.port.__blobUrl);
this.audioWorklet.port.__blobUrl = null;
}
null === (e = this.audioWorklet) || void 0 === e || e.port.postMessage({ type: 'stop' }),
null === (t = this.audioTracks) || void 0 === t || t[0].stop(),
null === (r = this.audioContext) || void 0 === r || r.suspend();
}),
a
);
})();
export { a as default };
!(function (e, t) {
'object' == typeof exports && 'undefined' != typeof module
? (module.exports = t())
: 'function' == typeof define && define.amd
? define(t)
: ((e = 'undefined' != typeof globalThis ? globalThis : e || self).RecorderManager = t());
})(this, function () {
'use strict';
function e(e, t, r, o) {
return new (r || (r = Promise))(function (n, a) {
function i(e) {
try {
u(o.next(e));
} catch (e) {
a(e);
}
}
function s(e) {
try {
u(o.throw(e));
} catch (e) {
a(e);
}
}
function u(e) {
var t;
e.done
? n(e.value)
: ((t = e.value),
t instanceof r
? t
: new r(function (e) {
e(t);
})).then(i, s);
}
u((o = o.apply(e, t || [])).next());
});
}
function t(e, t) {
var r,
o,
n,
a,
i = {
label: 0,
sent: function () {
if (1 & n[0]) throw n[1];
return n[1];
},
trys: [],
ops: [],
};
return (
(a = { next: s(0), throw: s(1), return: s(2) }),
'function' == typeof Symbol &&
(a[Symbol.iterator] = function () {
return this;
}),
a
);
function s(s) {
return function (u) {
return (function (s) {
if (r) throw new TypeError('Generator is already executing.');
for (; a && ((a = 0), s[0] && (i = 0)), i; )
try {
if (
((r = 1),
o &&
(n =
2 & s[0]
? o.return
: s[0]
? o.throw || ((n = o.return) && n.call(o), 0)
: o.next) &&
!(n = n.call(o, s[1])).done)
)
return n;
switch (((o = 0), n && (s = [2 & s[0], n.value]), s[0])) {
case 0:
case 1:
n = s;
break;
case 4:
return i.label++, { value: s[1], done: !1 };
case 5:
i.label++, (o = s[1]), (s = [0]);
continue;
case 7:
(s = i.ops.pop()), i.trys.pop();
continue;
default:
if (
!((n = i.trys),
(n = n.length > 0 && n[n.length - 1]) || (6 !== s[0] && 2 !== s[0]))
) {
i = 0;
continue;
}
if (3 === s[0] && (!n || (s[1] > n[0] && s[1] < n[3]))) {
i.label = s[1];
break;
}
if (6 === s[0] && i.label < n[1]) {
(i.label = n[1]), (n = s);
break;
}
if (n && i.label < n[2]) {
(i.label = n[2]), i.ops.push(s);
break;
}
n[2] && i.ops.pop(), i.trys.pop();
continue;
}
s = t.call(e, i);
} catch (e) {
(s = [6, e]), (o = 0);
} finally {
r = n = 0;
}
if (5 & s[0]) throw s[1];
return { value: s[0] ? s[1] : void 0, done: !0 };
})([s, u]);
};
}
}
function r() {
var e,
t = navigator,
r = t.getUserMedia || t.webkitGetUserMedia || t.mozGetUserMedia;
return (null === (e = t.mediaDevices) || void 0 === e ? void 0 : e.getUserMedia)
? t.mediaDevices.getUserMedia({ audio: !0, video: !1 })
: r
? new Promise(function (e, t) {
r.call(
navigator,
{ audio: !0, video: !1 },
function (t) {
e(t);
},
function (e) {
t(e);
},
);
})
: Promise.reject(new Error('不支持录音'));
}
var o;
function n(r, n) {
return e(this, void 0, void 0, function () {
var e;
return t(this, function (t) {
switch (t.label) {
case 0:
return [3, 2];
case 1:
return t.sent(), [2, new AudioWorkletNode(r, 'processor-worklet')];
case 2:
return (e = o)
? [3, 4]
: [
4,
(function () {
let worker;
if (/http/.test(n)) {
const workerBlob = new Blob(
['importScripts(' + JSON.stringify(n + '/processor.worker.js') + ')'],
{ type: 'application/javascript' },
);
const blobUrl = window.URL.createObjectURL(workerBlob);
worker = new Worker(blobUrl);
worker.__blobUrl = blobUrl;
} else {
worker = new Worker(''.concat(n, '/processor.worker.js'));
}
return worker;
})(),
];
case 3:
(e = t.sent()), (t.label = 4);
case 4:
return [2, { port: (o = e) }];
}
});
});
}
return (function () {
function o(e) {
(this.processorPath = e), (this.audioBuffers = []);
}
return (
(o.prototype.start = function (o) {
var a,
i = o.sampleRate,
s = o.frameSize,
u = o.arrayBufferType;
return e(this, void 0, void 0, function () {
var e, o, c, l, f, d, p;
return t(this, function (t) {
switch (t.label) {
case 0:
return t.trys.push([0, 3, , 4]), ((e = this).audioBuffers = []), [4, r()];
case 1:
return (
(o = t.sent()),
(this.audioTracks = o.getAudioTracks()),
(c = (function (e, t) {
var r;
try {
(r = new (window.AudioContext || window.webkitAudioContext)({
sampleRate: t,
})).createMediaStreamSource(e);
} catch (t) {
null == r || r.close(),
(r = new (window.AudioContext ||
window.webkitAudioContext)()).createMediaStreamSource(e);
}
return r;
})(o, i)),
(this.audioContext = c),
(l = c.createMediaStreamSource(o)),
[4, n(c, this.processorPath)]
);
case 2:
return (
(f = t.sent()),
(this.audioWorklet = f),
f.port.postMessage({
type: 'init',
data: {
frameSize: s,
toSampleRate: i || c.sampleRate,
fromSampleRate: c.sampleRate,
arrayBufferType: u || 'short16',
},
}),
(f.port.onmessage = function (t) {
var r = t.data,
o = r.frameBuffer,
n = r.isLastFrame;
if (f.port.__blobUrl) {
window.URL.revokeObjectURL(f.port.__blobUrl);
f.port.__blobUrl = null;
}
if (s && e.onFrameRecorded)
if (null == o ? void 0 : o.byteLength)
for (var a = 0; a < o.byteLength; )
e.onFrameRecorded({
isLastFrame: n && a + s >= o.byteLength,
frameBuffer: t.data.frameBuffer.slice(a, a + s),
}),
(a += s);
else e.onFrameRecorded(t.data);
e.onStop && (o && e.audioBuffers.push(o), n && e.onStop(e.audioBuffers));
}),
((d = c.createScriptProcessor(0, 1, 1)).onaudioprocess = function (e) {
f.port.postMessage({ type: 'message', data: e.inputBuffer.getChannelData(0) });
}),
l.connect(d),
d.connect(c.destination),
c.resume(),
null === (a = this.onStart) || void 0 === a || a.call(this),
[3, 4]
);
case 3:
return (p = t.sent()), console.error(p), [3, 4];
case 4:
return [2];
}
});
});
}),
(o.prototype.stop = function () {
var e, t, r, o;
if (this.audioWorklet && this.audioWorklet.port && this.audioWorklet.port.__blobUrl) {
window.URL.revokeObjectURL(this.audioWorklet.port.__blobUrl);
this.audioWorklet.port.__blobUrl = null;
}
null === (e = this.audioWorklet) || void 0 === e || e.port.postMessage({ type: 'stop' }),
null === (t = this.audioTracks) || void 0 === t || t[0].stop(),
'running' === (null === (r = this.audioContext) || void 0 === r ? void 0 : r.state) &&
(null === (o = this.audioContext) || void 0 === o || o.close());
}),
o
);
})();
});
function e(e, t, r, o) {
return new (r || (r = Promise))(function (n, a) {
function i(e) {
try {
u(o.next(e));
} catch (e) {
a(e);
}
}
function s(e) {
try {
u(o.throw(e));
} catch (e) {
a(e);
}
}
function u(e) {
var t;
e.done
? n(e.value)
: ((t = e.value),
t instanceof r
? t
: new r(function (e) {
e(t);
})).then(i, s);
}
u((o = o.apply(e, t || [])).next());
});
}
function t(e, t) {
var r,
o,
n,
a,
i = {
label: 0,
sent: function () {
if (1 & n[0]) throw n[1];
return n[1];
},
trys: [],
ops: [],
};
return (
(a = { next: s(0), throw: s(1), return: s(2) }),
'function' == typeof Symbol &&
(a[Symbol.iterator] = function () {
return this;
}),
a
);
function s(s) {
return function (u) {
return (function (s) {
if (r) throw new TypeError('Generator is already executing.');
for (; a && ((a = 0), s[0] && (i = 0)), i; )
try {
if (
((r = 1),
o &&
(n =
2 & s[0]
? o.return
: s[0]
? o.throw || ((n = o.return) && n.call(o), 0)
: o.next) &&
!(n = n.call(o, s[1])).done)
)
return n;
switch (((o = 0), n && (s = [2 & s[0], n.value]), s[0])) {
case 0:
case 1:
n = s;
break;
case 4:
return i.label++, { value: s[1], done: !1 };
case 5:
i.label++, (o = s[1]), (s = [0]);
continue;
case 7:
(s = i.ops.pop()), i.trys.pop();
continue;
default:
if (
!((n = i.trys),
(n = n.length > 0 && n[n.length - 1]) || (6 !== s[0] && 2 !== s[0]))
) {
i = 0;
continue;
}
if (3 === s[0] && (!n || (s[1] > n[0] && s[1] < n[3]))) {
i.label = s[1];
break;
}
if (6 === s[0] && i.label < n[1]) {
(i.label = n[1]), (n = s);
break;
}
if (n && i.label < n[2]) {
(i.label = n[2]), i.ops.push(s);
break;
}
n[2] && i.ops.pop(), i.trys.pop();
continue;
}
s = t.call(e, i);
} catch (e) {
(s = [6, e]), (o = 0);
} finally {
r = n = 0;
}
if (5 & s[0]) throw s[1];
return { value: s[0] ? s[1] : void 0, done: !0 };
})([s, u]);
};
}
}
function r() {
var e,
t = navigator,
r = t.getUserMedia || t.webkitGetUserMedia || t.mozGetUserMedia;
return (null === (e = t.mediaDevices) || void 0 === e ? void 0 : e.getUserMedia)
? t.mediaDevices.getUserMedia({ audio: !0, video: !1 })
: r
? new Promise(function (e, t) {
r.call(
navigator,
{ audio: !0, video: !1 },
function (t) {
e(t);
},
function (e) {
t(e);
},
);
})
: Promise.reject(new Error('不支持录音'));
}
var o;
function n(r, n) {
return e(this, void 0, void 0, function () {
var e;
return t(this, function (t) {
switch (t.label) {
case 0:
return [3, 2];
case 1:
return t.sent(), [2, new AudioWorkletNode(r, 'processor-worklet')];
case 2:
return (e = o)
? [3, 4]
: [
4,
(function () {
let worker;
if (/http/.test(n)) {
const workerBlob = new Blob(
['importScripts(' + JSON.stringify(n + '/processor.worker.js') + ')'],
{ type: 'application/javascript' },
);
const blobUrl = window.URL.createObjectURL(workerBlob);
worker = new Worker(blobUrl);
worker.__blobUrl = blobUrl;
} else {
worker = new Worker(''.concat(n, '/processor.worker.js'));
}
return worker;
})(),
];
case 3:
(e = t.sent()), (t.label = 4);
case 4:
return [2, { port: (o = e) }];
}
});
});
}
var a = (function () {
function o(e) {
(this.processorPath = e), (this.audioBuffers = []);
}
return (
(o.prototype.start = function (o) {
var a,
i = o.sampleRate,
s = o.frameSize,
u = o.arrayBufferType;
return e(this, void 0, void 0, function () {
var e, o, c, l, f, d, p;
return t(this, function (t) {
switch (t.label) {
case 0:
return t.trys.push([0, 3, , 4]), ((e = this).audioBuffers = []), [4, r()];
case 1:
return (
(o = t.sent()),
(this.audioTracks = o.getAudioTracks()),
(c = (function (e, t) {
var r;
try {
(r = new (window.AudioContext || window.webkitAudioContext)({
sampleRate: t,
})).createMediaStreamSource(e);
} catch (t) {
null == r || r.close(),
(r = new (window.AudioContext ||
window.webkitAudioContext)()).createMediaStreamSource(e);
}
return r;
})(o, i)),
(this.audioContext = c),
(l = c.createMediaStreamSource(o)),
[4, n(c, this.processorPath)]
);
case 2:
return (
(f = t.sent()),
(this.audioWorklet = f),
f.port.postMessage({
type: 'init',
data: {
frameSize: s,
toSampleRate: i || c.sampleRate,
fromSampleRate: c.sampleRate,
arrayBufferType: u || 'short16',
},
}),
(f.port.onmessage = function (t) {
var r = t.data,
o = r.frameBuffer,
n = r.isLastFrame;
if (f.port.__blobUrl) {
window.URL.revokeObjectURL(f.port.__blobUrl);
f.port.__blobUrl = null;
}
if (s && e.onFrameRecorded)
if (null == o ? void 0 : o.byteLength)
for (var a = 0; a < o.byteLength; )
e.onFrameRecorded({
isLastFrame: n && a + s >= o.byteLength,
frameBuffer: t.data.frameBuffer.slice(a, a + s),
}),
(a += s);
else e.onFrameRecorded(t.data);
e.onStop && (o && e.audioBuffers.push(o), n && e.onStop(e.audioBuffers));
}),
((d = c.createScriptProcessor(0, 1, 1)).onaudioprocess = function (e) {
f.port.postMessage({ type: 'message', data: e.inputBuffer.getChannelData(0) });
}),
l.connect(d),
d.connect(c.destination),
c.resume(),
null === (a = this.onStart) || void 0 === a || a.call(this),
[3, 4]
);
case 3:
return (p = t.sent()), console.error(p), [3, 4];
case 4:
return [2];
}
});
});
}),
(o.prototype.stop = function () {
var e, t, r, o;
if (this.audioWorklet && this.audioWorklet.port && this.audioWorklet.port.__blobUrl) {
window.URL.revokeObjectURL(this.audioWorklet.port.__blobUrl);
this.audioWorklet.port.__blobUrl = null;
}
null === (e = this.audioWorklet) || void 0 === e || e.port.postMessage({ type: 'stop' }),
null === (t = this.audioTracks) || void 0 === t || t[0].stop(),
'running' === (null === (r = this.audioContext) || void 0 === r ? void 0 : r.state) &&
(null === (o = this.audioContext) || void 0 === o || o.close());
}),
o
);
})();
export { a as default };
!(function () {
'use strict';
function t(t) {
return (
(function (t) {
if (Array.isArray(t)) return e(t);
})(t) ||
(function (t) {
if (('undefined' != typeof Symbol && null != t[Symbol.iterator]) || null != t['@@iterator'])
return Array.from(t);
})(t) ||
(function (t, r) {
if (!t) return;
if ('string' == typeof t) return e(t, r);
var i = Object.prototype.toString.call(t).slice(8, -1);
'Object' === i && t.constructor && (i = t.constructor.name);
if ('Map' === i || 'Set' === i) return Array.from(t);
if ('Arguments' === i || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)) return e(t, r);
})(t) ||
(function () {
throw new TypeError(
'Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.',
);
})()
);
}
function e(t, e) {
(null == e || e > t.length) && (e = t.length);
for (var r = 0, i = new Array(e); r < e; r++) i[r] = t[r];
return i;
}
function r(t, e, r, i) {
(this.fromSampleRate = t),
(this.toSampleRate = e),
(this.channels = 0 | r),
(this.noReturn = !!i),
this.initialize();
}
(r.prototype.initialize = function () {
if (!(this.fromSampleRate > 0 && this.toSampleRate > 0 && this.channels > 0))
throw new Error('Invalid settings specified for the resampler.');
this.fromSampleRate == this.toSampleRate
? ((this.resampler = this.bypassResampler), (this.ratioWeight = 1))
: (this.fromSampleRate < this.toSampleRate
? ((this.lastWeight = 1), (this.resampler = this.compileLinearInterpolation))
: ((this.tailExists = !1),
(this.lastWeight = 0),
(this.resampler = this.compileMultiTap)),
(this.ratioWeight = this.fromSampleRate / this.toSampleRate));
}),
(r.prototype.compileLinearInterpolation = function (t) {
var e = t.length;
this.initializeBuffers(e);
var r,
i,
s = this.outputBufferSize,
a = this.ratioWeight,
f = this.lastWeight,
n = 0,
o = 0,
h = 0,
l = this.outputBuffer;
if (e % this.channels == 0) {
if (e > 0) {
for (; f < 1; f += a)
for (n = 1 - (o = f % 1), r = 0; r < this.channels; ++r)
l[h++] = this.lastOutput[r] * n + t[r] * o;
for (f--, e -= this.channels, i = Math.floor(f) * this.channels; h < s && i < e; ) {
for (n = 1 - (o = f % 1), r = 0; r < this.channels; ++r)
l[h++] = t[i + r] * n + t[i + this.channels + r] * o;
(f += a), (i = Math.floor(f) * this.channels);
}
for (r = 0; r < this.channels; ++r) this.lastOutput[r] = t[i++];
return (this.lastWeight = f % 1), this.bufferSlice(h);
}
return this.noReturn ? 0 : [];
}
throw new Error('Buffer was of incorrect sample length.');
}),
(r.prototype.compileMultiTap = function (t) {
var e = [],
r = t.length;
this.initializeBuffers(r);
var i = this.outputBufferSize;
if (r % this.channels == 0) {
if (r > 0) {
for (var s = this.ratioWeight, a = 0, f = 0; f < this.channels; ++f) e[f] = 0;
var n = 0,
o = 0,
h = !this.tailExists;
this.tailExists = !1;
var l = this.outputBuffer,
u = 0,
p = 0;
do {
if (h) for (a = s, f = 0; f < this.channels; ++f) e[f] = 0;
else {
for (a = this.lastWeight, f = 0; f < this.channels; ++f) e[f] += this.lastOutput[f];
h = !0;
}
for (; a > 0 && n < r; ) {
if (!(a >= (o = 1 + n - p))) {
for (f = 0; f < this.channels; ++f) e[f] += t[n + f] * a;
(p += a), (a = 0);
break;
}
for (f = 0; f < this.channels; ++f) e[f] += t[n++] * o;
(p = n), (a -= o);
}
if (0 != a) {
for (this.lastWeight = a, f = 0; f < this.channels; ++f) this.lastOutput[f] = e[f];
this.tailExists = !0;
break;
}
for (f = 0; f < this.channels; ++f) l[u++] = e[f] / s;
} while (n < r && u < i);
return this.bufferSlice(u);
}
return this.noReturn ? 0 : [];
}
throw new Error('Buffer was of incorrect sample length.');
}),
(r.prototype.bypassResampler = function (t) {
return this.noReturn ? ((this.outputBuffer = t), t.length) : t;
}),
(r.prototype.bufferSlice = function (t) {
if (this.noReturn) return t;
try {
return this.outputBuffer.subarray(0, t);
} catch (e) {
try {
return (this.outputBuffer.length = t), this.outputBuffer;
} catch (e) {
return this.outputBuffer.slice(0, t);
}
}
}),
(r.prototype.initializeBuffers = function (t) {
this.outputBufferSize = Math.ceil((t * this.toSampleRate) / this.fromSampleRate);
try {
(this.outputBuffer = new Float32Array(this.outputBufferSize)),
(this.lastOutput = new Float32Array(this.channels));
} catch (t) {
(this.outputBuffer = []), (this.lastOutput = []);
}
}),
(self.transData = function (t) {
return (
'short16' === self.arrayBufferType &&
(t = (function (t) {
for (
var e = new ArrayBuffer(2 * t.length), r = new DataView(e), i = 0, s = 0;
s < t.length;
s += 1, i += 2
) {
var a = Math.max(-1, Math.min(1, t[s]));
r.setInt16(i, a < 0 ? 32768 * a : 32767 * a, !0);
}
return r.buffer;
})((t = self.resampler.resampler(t)))),
t
);
}),
(self.onmessage = function (e) {
var i = e.data,
s = i.type,
a = i.data;
if ('init' === s) {
var f = a.frameSize,
n = a.toSampleRate,
o = a.fromSampleRate,
h = a.arrayBufferType;
return (
(self.frameSize = f * Math.floor(o / n)),
(self.resampler = new r(o, n, 1)),
(self.frameBuffer = []),
void (self.arrayBufferType = h)
);
}
if (
('stop' === s &&
(self.postMessage({ frameBuffer: self.transData(self.frameBuffer), isLastFrame: !0 }),
(self.frameBuffer = [])),
'message' === s)
) {
var l,
u = a;
if (self.frameSize)
return (
(l = self.frameBuffer).push.apply(l, t(u)),
self.frameBuffer.length >= self.frameSize &&
(self.postMessage({ frameBuffer: self.transData(this.frameBuffer), isLastFrame: !1 }),
(self.frameBuffer = [])),
!0
);
u && self.postMessage({ frameBuffer: self.transData(u), isLastFrame: !1 });
}
});
})();
This diff is collapsed.
const Utils = {
toBase64(buffer) {
let binary = '';
let bytes = new Uint8Array(buffer);
let len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
},
delayTime: function (time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
},
getMediaCodeToMsg(code) {
if (/device not found/.test(code)) {
return '没有发现录音设备';
} else if (/Permission denied/.test(code)) {
return '录音权限被禁止';
}
return code;
},
async isCanableRecorder(delayTime = 1500) {
if (!(navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia))
return {
isSuccess: false,
msg: '没有发现音频设备',
};
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
stream.getTracks().map((track) => {
track.enabled = false;
track.stop();
});
await this.delayTime(delayTime);
return {
isSuccess: true,
};
} catch (e) {
return {
isSuccess: false,
msg: this.getMediaCodeToMsg(e?.toString()),
};
}
},
GetBytesLength: function (str) {
let realLength = 0;
if (!str) return realLength;
for (let i = 0; i < str.length; i++) {
const charCode = str.charCodeAt(i);
if (charCode >= 0 && charCode <= 128) realLength += 1;
else realLength += 2;
}
return realLength;
},
};
export default Utils;
import { request, event } from '@wisdom-utils/utils'
import { useCallback, useEffect } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { PandaRecordWebSocket } from './core'
import { message } from 'antd'
import PandaTip from './Ui/PandaTip'
const SoundAi = props => {
const changeSwitch = useCallback(({recordData , e : checked}) => {
if(checked) {
const pandaRecordWebSocketRef = useRef(null)
const keyRef = useRef(null)
const [isStartRecorder , setIsStartRecorder] = useState(false)
useEffect(async () => {
try {
await getIsOpenConfig()
await getKey()
event.emit("record:changeVisible", {
visible : true
})
event.on("record:changedStaus", changeSwitch)
} catch (error){
console.warn(error)
}
return () => {
event.off("record:changedStaus", changeSwitch)
}
// console.log(checked)
// debugger
// event.emit("record:changeSwicth", {
// loading : true
// })
// setTimeout(() => {
// event.emit("record:changeSwicth", {
// loading : false
// })
// }, 5000)
}, [])
useEffect(() => {
const changeSwitch = useCallback(async ({ recordData, e: checked }) => {
if (checked) {
emitSwitch({
loading : true
})
createPandaRecordWebSocket()
const data = await pandaRecordWebSocketRef.current.start()
emitSwitch({
loading : false
})
if(!data.isSuccess) {
return message.info(data.msg)
}
emitSwitch({
checked : true
})
setIsStartRecorder(true)
} else {
emitSwitch({
loading : true
})
await destroyPandaRecorderWebSocket()
emitSwitch({
loading : false,
checked : false
})
setIsStartRecorder(false)
}
}, [])
const createPandaRecordWebSocket = () => {
destroyPandaRecorderWebSocket()
pandaRecordWebSocketRef.current = new PandaRecordWebSocket({
config : keyRef.current.shortSound,
events : {
frame (data) {
event.emit("aiSound:frame", data)
},
finish (data) {
event.emit("aiSound:finish", data)
}
}
})
}
const destroyPandaRecorderWebSocket = async () => {
if(pandaRecordWebSocketRef.current) {
await pandaRecordWebSocketRef.current.destroy()
pandaRecordWebSocketRef.current = null
}
}
const emitSwitch = data => {
event.emit("record:changeSwicth", data)
}
const getIsOpenConfig = () => new Promise((resolve, reject) => {
request("/PandaOMS/OMS/DataManger/GetDicConfigs", {
params: {
ParentName: "智能语音",
......@@ -25,25 +91,41 @@ const SoundAi = props => {
}
}).then(res => {
if (res.code != 0) {
reject("没有配置智能语音此条目")
return
}
const data = res.data
const config = data.find(item => item.nodeName == "是否开启")
if(!config) return
if (!config) {
reject("没有配置是否开启字段")
}
const value = config.nodeValue
if(value != "开") return
event.emit("record:changeVisible", {
visible : true
})
event.on("record:changedStaus", changeSwitch)
if (value != "开") {
reject("字段开启字段值为空")
return
}
resolve()
}).catch(error => {
// console.log(error)
})
return () => {
event.off("record:changedStaus", changeSwitch)
reject(error)
})
})
const getKey = () => new Promise((resolve , reject) => {
fetch("https://civgis.panda-water.cn/system/pandaAiSoundWorker/key.json",{
mode : "cors"
}).then(res => res.json())
.then(res => {
keyRef.current = res
resolve(res)
}).catch(error => {
reject(error)
})
})
return (<>
{
isStartRecorder ? <PandaTip pandaRecordWebSocketRef = {pandaRecordWebSocketRef} /> : null
}
}, [])
return null
</>)
}
export default SoundAi
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment