@easyv/biz-components 0.0.17 → 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
- import { XunFeiStreamVoiceManager, XunFeiStreamVoiceManagerConfig } from '../utils';
1
+ import { XFStreamVoiceManager, XFStreamVoiceManagerConfig } from '../utils';
2
2
 
3
3
  /**
4
4
  * 讯飞流式语音识别的 hook,注意 config 只在初识化时注册一次,后续更改,请使用 manager.setConfig 方法更新
5
5
  * @param config XunFeiStreamVoiceManagerConfig
6
6
  * @returns
7
7
  */
8
- export declare const useXunFeiSteamVoiceManager: (config: XunFeiStreamVoiceManagerConfig) => XunFeiStreamVoiceManager | null;
8
+ export declare const useXunFeiSteamVoiceManager: (config: XFStreamVoiceManagerConfig) => XFStreamVoiceManager | null;
@@ -1,10 +1,10 @@
1
- import { useState as o, useEffect as a } from "react";
2
- import { XunFeiStreamVoiceManager as s } from "../utils/xunFeiVoice/XunFeiStreamVoiceManager.es.js";
1
+ import { useState as n, useEffect as a } from "react";
2
+ import { XFStreamVoiceManager as s } from "../utils/xunFeiVoice/XFStreamVoiceManager.es.js";
3
3
  const c = (t) => {
4
- const [r, n] = o(null);
4
+ const [r, o] = n(null);
5
5
  return a(() => {
6
6
  const e = new s(t);
7
- return n(e), () => {
7
+ return o(e), () => {
8
8
  e == null || e.destroy();
9
9
  };
10
10
  }, []), r;
@@ -1 +1 @@
1
- {"version":3,"file":"useXunFeiSteamVoiceManager.es.js","sources":["../../src/hooks/useXunFeiSteamVoiceManager.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { XunFeiStreamVoiceManager, XunFeiStreamVoiceManagerConfig } from '@/utils';\n\n/**\n * 讯飞流式语音识别的 hook,注意 config 只在初识化时注册一次,后续更改,请使用 manager.setConfig 方法更新\n * @param config XunFeiStreamVoiceManagerConfig\n * @returns\n */\nexport const useXunFeiSteamVoiceManager = (config: XunFeiStreamVoiceManagerConfig) => {\n const [manager, setManager] = useState<XunFeiStreamVoiceManager | null>(null);\n\n useEffect(() => {\n const newManager = new XunFeiStreamVoiceManager(config);\n setManager(newManager);\n return () => {\n newManager?.destroy();\n };\n }, []);\n return manager;\n};\n"],"names":["useXunFeiSteamVoiceManager","config","manager","setManager","useState","useEffect","newManager","XunFeiStreamVoiceManager"],"mappings":";;AAQa,MAAAA,IAA6B,CAACC,MAA2C;AACpF,QAAM,CAACC,GAASC,CAAU,IAAIC,EAA0C,IAAI;AAE5E,SAAAC,EAAU,MAAM;AACR,UAAAC,IAAa,IAAIC,EAAyBN,CAAM;AACtD,WAAAE,EAAWG,CAAU,GACd,MAAM;AACX,MAAAA,KAAA,QAAAA,EAAY;AAAA,IACd;AAAA,EACF,GAAG,EAAE,GACEJ;AACT;"}
1
+ {"version":3,"file":"useXunFeiSteamVoiceManager.es.js","sources":["../../src/hooks/useXunFeiSteamVoiceManager.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { XFStreamVoiceManager, XFStreamVoiceManagerConfig } from '@/utils';\n\n/**\n * 讯飞流式语音识别的 hook,注意 config 只在初识化时注册一次,后续更改,请使用 manager.setConfig 方法更新\n * @param config XunFeiStreamVoiceManagerConfig\n * @returns\n */\nexport const useXunFeiSteamVoiceManager = (config: XFStreamVoiceManagerConfig) => {\n const [manager, setManager] = useState<XFStreamVoiceManager | null>(null);\n\n useEffect(() => {\n const newManager = new XFStreamVoiceManager(config);\n setManager(newManager);\n return () => {\n newManager?.destroy();\n };\n }, []);\n return manager;\n};\n"],"names":["useXunFeiSteamVoiceManager","config","manager","setManager","useState","useEffect","newManager","XFStreamVoiceManager"],"mappings":";;AAQa,MAAAA,IAA6B,CAACC,MAAuC;AAChF,QAAM,CAACC,GAASC,CAAU,IAAIC,EAAsC,IAAI;AAExE,SAAAC,EAAU,MAAM;AACR,UAAAC,IAAa,IAAIC,EAAqBN,CAAM;AAClD,WAAAE,EAAWG,CAAU,GACd,MAAM;AACX,MAAAA,KAAA,QAAAA,EAAY;AAAA,IACd;AAAA,EACF,GAAG,EAAE,GACEJ;AACT;"}
package/dist/index.es.js CHANGED
@@ -1,23 +1,23 @@
1
1
  /* empty css */
2
2
  import { AiMessageRender as t } from "./components/AiMessageRender/AiMessageRender.es.js";
3
- import { ShadowDom as n } from "./components/ShadowDom/ShadowDom.es.js";
4
- import { ScrollController as i } from "./components/ScrollController/ScrollController.es.js";
5
- import { VoiceAnimation as f } from "./components/VoiceAnimation/VoiceAnimation.es.js";
3
+ import { ShadowDom as a } from "./components/ShadowDom/ShadowDom.es.js";
4
+ import { ScrollController as p } from "./components/ScrollController/ScrollController.es.js";
5
+ import { VoiceAnimation as i } from "./components/VoiceAnimation/VoiceAnimation.es.js";
6
6
  import { useDivAutoScroll as c } from "./hooks/useDivAutoScroll.es.js";
7
- import { useXunFeiSteamVoiceManager as u } from "./hooks/useXunFeiSteamVoiceManager.es.js";
8
- import { FunASRManager as l, getWebSocketConnectForFunASR as A } from "./utils/funASR/funASRManager.es.js";
9
- import { XunFeiStreamVoiceManager as M } from "./utils/xunFeiVoice/XunFeiStreamVoiceManager.es.js";
7
+ import { useXunFeiSteamVoiceManager as g } from "./hooks/useXunFeiSteamVoiceManager.es.js";
8
+ import { FunASRManager as u, getWebSocketConnectForFunASR as A } from "./utils/funASR/funASRManager.es.js";
9
+ import { XFStreamVoiceManager as M } from "./utils/xunFeiVoice/XFStreamVoiceManager.es.js";
10
10
  import { RecorderManager as R } from "./utils/xunFeiVoice/RecorderManager/RecorderManager.es.js";
11
11
  export {
12
12
  t as AiMessageRender,
13
- l as FunASRManager,
13
+ u as FunASRManager,
14
14
  R as RecorderManager,
15
- i as ScrollController,
16
- n as ShadowDom,
17
- f as VoiceAnimation,
18
- M as XunFeiStreamVoiceManager,
15
+ p as ScrollController,
16
+ a as ShadowDom,
17
+ i as VoiceAnimation,
18
+ M as XFStreamVoiceManager,
19
19
  A as getWebSocketConnectForFunASR,
20
20
  c as useDivAutoScroll,
21
- u as useXunFeiSteamVoiceManager
21
+ g as useXunFeiSteamVoiceManager
22
22
  };
23
23
  //# sourceMappingURL=index.es.js.map
@@ -1,26 +1,26 @@
1
1
  var S = Object.defineProperty;
2
- var g = (r, e, t) => e in r ? S(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
3
- var c = (r, e, t) => g(r, typeof e != "symbol" ? e + "" : e, t);
2
+ var g = (o, e, t) => e in o ? S(o, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[e] = t;
3
+ var c = (o, e, t) => g(o, typeof e != "symbol" ? e + "" : e, t);
4
4
  import k from "../../../node_modules/.pnpm/co-web-worker@1.0.1/node_modules/co-web-worker/index.es.js";
5
5
  import y from "./processorSource/processor.worker.es.js";
6
6
  import R from "./processorSource/processor.worklet.es.js";
7
7
  const { AudioWorkletNode: h } = window, u = !!h;
8
8
  async function M() {
9
- const r = navigator.mediaDevices ?? {};
10
- if (typeof r.getUserMedia == "function")
11
- return r.getUserMedia({ audio: !0, video: !1 });
9
+ const o = navigator.mediaDevices ?? {};
10
+ if (typeof o.getUserMedia == "function")
11
+ return await o.getUserMedia({ audio: !0, video: !1 });
12
12
  if (typeof navigator.getUserMedia == "function")
13
13
  return new Promise((e, t) => {
14
14
  navigator.getUserMedia({ audio: !0, video: !1 }, e, t);
15
15
  });
16
16
  throw new Error("浏览器不支持录音!");
17
17
  }
18
- const w = (r) => {
19
- const e = r === "worker" ? y : R, t = new Blob([e], { type: "application/javascript" });
18
+ const w = (o) => {
19
+ const e = o === "worker" ? y : R, t = new Blob([e], { type: "application/javascript" });
20
20
  return URL.createObjectURL(t);
21
21
  };
22
- async function A(r) {
23
- return u ? (await r.audioWorklet.addModule(w("worklet")), new h(r, "processor-worklet")) : { port: new k(w("worker")) };
22
+ async function A(o) {
23
+ return u ? (await o.audioWorklet.addModule(w("worklet")), new h(o, "processor-worklet")) : { port: new k(w("worker")) };
24
24
  }
25
25
  const d = () => {
26
26
  };
@@ -43,13 +43,13 @@ class v {
43
43
  * }} config 录音配置
44
44
  */
45
45
  async start(e) {
46
- var t, s;
46
+ var t, r;
47
47
  try {
48
48
  this.audioBuffers = [];
49
- const o = await M(), i = this.createAudioContext(o, e.sampleRate);
50
- this.audioWorkletNode = await A(i), this.setupAudioProcessing(i, o, this.audioWorkletNode, e), (t = this.audioContext) == null || t.resume(), (s = this.onStart) == null || s.call(this);
51
- } catch (o) {
52
- throw console.error("Failed to start recording:", o), o;
49
+ const s = await M(), i = this.createAudioContext(s, e.sampleRate);
50
+ this.audioWorkletNode = await A(i), this.setupAudioProcessing(i, s, this.audioWorkletNode, e), (t = this.audioContext) == null || t.resume(), (r = this.onStart) == null || r.call(this);
51
+ } catch (s) {
52
+ throw console.error("Failed to start recording:", s), s;
53
53
  }
54
54
  }
55
55
  /**
@@ -58,39 +58,39 @@ class v {
58
58
  */
59
59
  createAudioContext(e, t) {
60
60
  try {
61
- const s = new (window.AudioContext || window.webkitAudioContext)({
61
+ const r = new (window.AudioContext || window.webkitAudioContext)({
62
62
  sampleRate: t
63
63
  });
64
- return s.createMediaStreamSource(e), s;
64
+ return r.createMediaStreamSource(e), r;
65
65
  } catch {
66
- const o = new (window.AudioContext || window.webkitAudioContext)();
67
- return console.warn(`Using default sample rate: ${o.sampleRate}`), o.createMediaStreamSource(e), o;
66
+ const s = new (window.AudioContext || window.webkitAudioContext)();
67
+ return console.warn(`Using default sample rate: ${s.sampleRate}`), s.createMediaStreamSource(e), s;
68
68
  }
69
69
  }
70
70
  /**
71
71
  * 配置音频处理管线
72
72
  * @private
73
73
  */
74
- setupAudioProcessing(e, t, s, o) {
74
+ setupAudioProcessing(e, t, r, s) {
75
75
  const i = e.createMediaStreamSource(t);
76
- if (this.mediaSource = i, s.port.postMessage({
76
+ if (this.mediaSource = i, r.port.postMessage({
77
77
  type: "init",
78
78
  data: {
79
- frameSize: o.frameSize,
80
- toSampleRate: o.sampleRate || e.sampleRate,
79
+ frameSize: s.frameSize,
80
+ toSampleRate: s.sampleRate || e.sampleRate,
81
81
  fromSampleRate: e.sampleRate,
82
- arrayBufferType: o.arrayBufferType || "short16"
82
+ arrayBufferType: s.arrayBufferType || "short16"
83
83
  }
84
- }), s.port.onmessage = (n) => {
85
- var p, f, l, m;
84
+ }), r.port.onmessage = (n) => {
85
+ var p, f, m, l;
86
86
  const { data: a } = n;
87
- o.frameSize && this.onFrameRecorded && this.onFrameRecorded(a), a.frameBuffer && this.audioBuffers.push(a.frameBuffer), a.isLastFrame && (u || (p = s.port) == null || p.terminate(), (f = this.mediaSource) == null || f.disconnect(), (l = this.audioContext) == null || l.close(), (m = this.onStop) == null || m.call(this, this.audioBuffers));
87
+ s.frameSize && this.onFrameRecorded && this.onFrameRecorded(a), a.frameBuffer && this.audioBuffers.push(a.frameBuffer), a.isLastFrame && (u || (p = r.port) == null || p.terminate(), (f = this.mediaSource) == null || f.disconnect(), (m = this.audioContext) == null || m.close(), (l = this.onStop) == null || l.call(this, this.audioBuffers));
88
88
  }, u)
89
- i.connect(s);
89
+ i.connect(r);
90
90
  else {
91
91
  const n = e.createScriptProcessor(0, 1, 1);
92
92
  n.onaudioprocess = ({ inputBuffer: a }) => {
93
- s.port.postMessage({
93
+ r.port.postMessage({
94
94
  type: "message",
95
95
  data: a.getChannelData(0)
96
96
  });
@@ -107,9 +107,9 @@ class v {
107
107
  this.audioWorkletNode && this.audioWorkletNode.port.postMessage({ type: "stop" });
108
108
  }
109
109
  close() {
110
- var e, t;
111
- if (this.audioBuffers = [], ((e = this.audioContext) == null ? void 0 : e.state) !== "closed")
112
- return (t = this.audioContext) == null ? void 0 : t.close();
110
+ var e, t, r;
111
+ if (this.audioBuffers = [], (e = this.mediaSource) == null || e.disconnect(), ((t = this.audioContext) == null ? void 0 : t.state) !== "closed")
112
+ return (r = this.audioContext) == null ? void 0 : r.close();
113
113
  }
114
114
  }
115
115
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"RecorderManager.es.js","sources":["../../../../src/utils/xunFeiVoice/RecorderManager/RecorderManager.js"],"sourcesContent":["/**\n * 录音管理器 - 用于处理音频录制及处理\n */\nimport CrossOriginWorker from 'co-web-worker';\nimport workerJsString from './processorSource/processor.worker.js?raw';\nimport workletJsString from './processorSource/processor.worklet.js?raw';\n\n//------------------------------ 音频处理核心类 ------------------------------\nconst { AudioWorkletNode } = window;\nconst isAudioWorkletSupported = !!AudioWorkletNode;\n\n/**\n * 获取用户媒体设备权限\n * @returns {Promise<MediaStream>}\n */\nasync function requestMicrophonePermission() {\n const mediaDevices = navigator.mediaDevices ?? {};\n\n if (typeof mediaDevices.getUserMedia === 'function') {\n return mediaDevices.getUserMedia({ audio: true, video: false });\n }\n\n if (typeof navigator.getUserMedia === 'function') {\n return new Promise((resolve, reject) => {\n navigator.getUserMedia({ audio: true, video: false }, resolve, reject);\n });\n }\n\n throw new Error('浏览器不支持录音!');\n}\n\n/** @type {(type: 'worker' | 'worklet') => string} */\nconst generateWorkletUrl = (type) => {\n const jsStr = type === 'worker' ? workerJsString : workletJsString;\n const blob = new Blob([jsStr], { type: 'application/javascript' });\n const scriptUrl = URL.createObjectURL(blob);\n return scriptUrl;\n};\n\n/**\n * 初始化音频处理器\n * @param {AudioContext} audioContext\n * @returns {Promise<AudioWorkletNode|{port: Worker}>}\n */\nasync function initializeAudioProcessor(audioContext) {\n if (isAudioWorkletSupported) {\n await audioContext.audioWorklet.addModule(generateWorkletUrl('worklet'));\n return new AudioWorkletNode(audioContext, 'processor-worklet');\n }\n\n const worker = new CrossOriginWorker(generateWorkletUrl('worker'));\n return { port: worker };\n}\n\nconst NOOP = () => {};\n/**\n * 录音管理器\n */\n//------------------------------ 录音管理器类 ------------------------------\nexport class RecorderManager {\n /** @type {Array<ArrayBuffer>} */\n audioBuffers = [];\n /** @type {(params: RecordFrameInfo) => void} */\n onFrameRecorded = NOOP;\n onStart = NOOP;\n /** @type {(data: Array<ArrayBuffer>) => void} */\n onStop = NOOP;\n\n constructor() {}\n\n /**\n * 开始录音\n * @param {{\n * sampleRate?: number,\n * frameSize?: number,\n * arrayBufferType?: string\n * }} config 录音配置\n */\n async start(config) {\n try {\n this.audioBuffers = [];\n\n // 获取音频流并创建处理节点\n const mediaStream = await requestMicrophonePermission();\n const audioContext = this.createAudioContext(mediaStream, config.sampleRate);\n this.audioWorkletNode = await initializeAudioProcessor(audioContext);\n\n // 初始化音频处理\n this.setupAudioProcessing(audioContext, mediaStream, this.audioWorkletNode, config);\n /** 恢复之前暂停播放的音频。 */\n this.audioContext?.resume();\n\n this.onStart?.();\n } catch (error) {\n console.error('Failed to start recording:', error);\n throw error;\n }\n }\n\n /**\n * 创建音频上下文\n * @private\n */\n createAudioContext(mediaStream, targetSampleRate) {\n try {\n const context = new (window.AudioContext || window.webkitAudioContext)({\n sampleRate: targetSampleRate,\n });\n context.createMediaStreamSource(mediaStream);\n return context;\n } catch (error) {\n // 回退处理:当目标采样率不支持时使用默认采样率\n const fallbackContext = new (window.AudioContext || window.webkitAudioContext)();\n console.warn(`Using default sample rate: ${fallbackContext.sampleRate}`);\n fallbackContext.createMediaStreamSource(mediaStream);\n return fallbackContext;\n }\n }\n\n /**\n * 配置音频处理管线\n * @private\n */\n setupAudioProcessing(audioContext, mediaStream, audioWorkletNode, config) {\n // 创建媒体流源节点\n const mediaSource = audioContext.createMediaStreamSource(mediaStream);\n this.mediaSource = mediaSource;\n\n // 配置处理器通信\n audioWorkletNode.port.postMessage({\n type: 'init',\n data: {\n frameSize: config.frameSize,\n toSampleRate: config.sampleRate || audioContext.sampleRate,\n fromSampleRate: audioContext.sampleRate,\n arrayBufferType: config.arrayBufferType || 'short16',\n },\n });\n\n // 处理音频数据回调\n audioWorkletNode.port.onmessage = (e) => {\n const { data } = e;\n if (config.frameSize && this.onFrameRecorded) {\n this.onFrameRecorded(data);\n }\n\n if (data.frameBuffer) {\n this.audioBuffers.push(data.frameBuffer);\n }\n\n if (data.isLastFrame) {\n if (!isAudioWorkletSupported) {\n audioWorkletNode.port?.terminate();\n }\n this.mediaSource?.disconnect();\n this.audioContext?.close();\n this.onStop?.(this.audioBuffers);\n }\n };\n\n // 连接音频节点\n if (isAudioWorkletSupported) {\n mediaSource.connect(audioWorkletNode);\n } else {\n const scriptProcessor = audioContext.createScriptProcessor(0, 1, 1);\n scriptProcessor.onaudioprocess = ({ inputBuffer }) => {\n audioWorkletNode.port.postMessage({\n type: 'message',\n data: inputBuffer.getChannelData(0),\n });\n };\n mediaSource.connect(scriptProcessor);\n scriptProcessor.connect(audioContext.destination);\n }\n }\n\n /** 停止录音 */\n stop() {\n /** 暂停音频上下文对象中的进度,并暂时剥离进程对音频设备硬件的访问权限,减少 CPU 和电池的使用 */\n this.audioContext?.suspend();\n if (!this.audioWorkletNode) {\n console.error('Recorder is not started.');\n return;\n }\n if (this.audioWorkletNode) {\n this.audioWorkletNode.port.postMessage({ type: 'stop' });\n }\n }\n\n close() {\n this.audioBuffers = [];\n if (this.audioContext?.state !== 'closed') {\n return this.audioContext?.close();\n }\n }\n}\n"],"names":["AudioWorkletNode","isAudioWorkletSupported","requestMicrophonePermission","mediaDevices","resolve","reject","generateWorkletUrl","type","jsStr","workerJsString","workletJsString","blob","initializeAudioProcessor","audioContext","CrossOriginWorker","NOOP","RecorderManager","__publicField","config","mediaStream","_a","_b","error","targetSampleRate","context","fallbackContext","audioWorkletNode","mediaSource","e","data","_c","_d","scriptProcessor","inputBuffer"],"mappings":"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"}
1
+ {"version":3,"file":"RecorderManager.es.js","sources":["../../../../src/utils/xunFeiVoice/RecorderManager/RecorderManager.js"],"sourcesContent":["/**\n * 录音管理器 - 用于处理音频录制及处理\n */\nimport CrossOriginWorker from 'co-web-worker';\nimport workerJsString from './processorSource/processor.worker.js?raw';\nimport workletJsString from './processorSource/processor.worklet.js?raw';\n\n//------------------------------ 音频处理核心类 ------------------------------\nconst { AudioWorkletNode } = window;\nconst isAudioWorkletSupported = !!AudioWorkletNode;\n\n/**\n * 获取用户媒体设备权限\n * @returns {Promise<MediaStream>}\n */\nasync function requestMicrophonePermission() {\n const mediaDevices = navigator.mediaDevices ?? {};\n\n if (typeof mediaDevices.getUserMedia === 'function') {\n const stream = await mediaDevices.getUserMedia({ audio: true, video: false });\n return stream;\n }\n\n if (typeof navigator.getUserMedia === 'function') {\n return new Promise((resolve, reject) => {\n navigator.getUserMedia({ audio: true, video: false }, resolve, reject);\n });\n }\n\n throw new Error('浏览器不支持录音!');\n}\n\n/** @type {(type: 'worker' | 'worklet') => string} */\nconst generateWorkletUrl = (type) => {\n const jsStr = type === 'worker' ? workerJsString : workletJsString;\n const blob = new Blob([jsStr], { type: 'application/javascript' });\n const scriptUrl = URL.createObjectURL(blob);\n return scriptUrl;\n};\n\n/**\n * 初始化音频处理器\n * @param {AudioContext} audioContext\n * @returns {Promise<AudioWorkletNode|{port: Worker}>}\n */\nasync function initializeAudioProcessor(audioContext) {\n if (isAudioWorkletSupported) {\n await audioContext.audioWorklet.addModule(generateWorkletUrl('worklet'));\n return new AudioWorkletNode(audioContext, 'processor-worklet');\n }\n\n const worker = new CrossOriginWorker(generateWorkletUrl('worker'));\n return { port: worker };\n}\n\nconst NOOP = () => {};\n/**\n * 录音管理器\n */\n//------------------------------ 录音管理器类 ------------------------------\nexport class RecorderManager {\n /** @type {Array<ArrayBuffer>} */\n audioBuffers = [];\n /** @type {(params: RecordFrameInfo) => void} */\n onFrameRecorded = NOOP;\n onStart = NOOP;\n /** @type {(data: Array<ArrayBuffer>) => void} */\n onStop = NOOP;\n\n constructor() {}\n\n /**\n * 开始录音\n * @param {{\n * sampleRate?: number,\n * frameSize?: number,\n * arrayBufferType?: string\n * }} config 录音配置\n */\n async start(config) {\n try {\n this.audioBuffers = [];\n // 获取音频流并创建处理节点\n const mediaStream = await requestMicrophonePermission();\n const audioContext = this.createAudioContext(mediaStream, config.sampleRate);\n this.audioWorkletNode = await initializeAudioProcessor(audioContext);\n\n // 初始化音频处理\n this.setupAudioProcessing(audioContext, mediaStream, this.audioWorkletNode, config);\n /** 恢复之前暂停播放的音频。 */\n this.audioContext?.resume();\n this.onStart?.();\n } catch (error) {\n console.error('Failed to start recording:', error);\n throw error;\n }\n }\n\n /**\n * 创建音频上下文\n * @private\n */\n createAudioContext(mediaStream, targetSampleRate) {\n try {\n const context = new (window.AudioContext || window.webkitAudioContext)({\n sampleRate: targetSampleRate,\n });\n context.createMediaStreamSource(mediaStream);\n return context;\n } catch (error) {\n // 回退处理:当目标采样率不支持时使用默认采样率\n const fallbackContext = new (window.AudioContext || window.webkitAudioContext)();\n console.warn(`Using default sample rate: ${fallbackContext.sampleRate}`);\n fallbackContext.createMediaStreamSource(mediaStream);\n return fallbackContext;\n }\n }\n\n /**\n * 配置音频处理管线\n * @private\n */\n setupAudioProcessing(audioContext, mediaStream, audioWorkletNode, config) {\n // 创建媒体流源节点\n const mediaSource = audioContext.createMediaStreamSource(mediaStream);\n this.mediaSource = mediaSource;\n\n // 配置处理器通信\n audioWorkletNode.port.postMessage({\n type: 'init',\n data: {\n frameSize: config.frameSize,\n toSampleRate: config.sampleRate || audioContext.sampleRate,\n fromSampleRate: audioContext.sampleRate,\n arrayBufferType: config.arrayBufferType || 'short16',\n },\n });\n\n // 处理音频数据回调\n audioWorkletNode.port.onmessage = (e) => {\n const { data } = e;\n if (config.frameSize && this.onFrameRecorded) {\n this.onFrameRecorded(data);\n }\n\n if (data.frameBuffer) {\n this.audioBuffers.push(data.frameBuffer);\n }\n\n if (data.isLastFrame) {\n if (!isAudioWorkletSupported) {\n audioWorkletNode.port?.terminate();\n }\n this.mediaSource?.disconnect();\n this.audioContext?.close();\n this.onStop?.(this.audioBuffers);\n }\n };\n\n // 连接音频节点\n if (isAudioWorkletSupported) {\n mediaSource.connect(audioWorkletNode);\n } else {\n const scriptProcessor = audioContext.createScriptProcessor(0, 1, 1);\n scriptProcessor.onaudioprocess = ({ inputBuffer }) => {\n audioWorkletNode.port.postMessage({\n type: 'message',\n data: inputBuffer.getChannelData(0),\n });\n };\n mediaSource.connect(scriptProcessor);\n scriptProcessor.connect(audioContext.destination);\n }\n }\n\n /** 停止录音 */\n stop() {\n /** 暂停音频上下文对象中的进度,并暂时剥离进程对音频设备硬件的访问权限,减少 CPU 和电池的使用 */\n this.audioContext?.suspend();\n if (!this.audioWorkletNode) {\n console.error('Recorder is not started.');\n return;\n }\n if (this.audioWorkletNode) {\n this.audioWorkletNode.port.postMessage({ type: 'stop' });\n }\n }\n\n close() {\n this.audioBuffers = [];\n this.mediaSource?.disconnect();\n if (this.audioContext?.state !== 'closed') {\n return this.audioContext?.close();\n }\n }\n}\n"],"names":["AudioWorkletNode","isAudioWorkletSupported","requestMicrophonePermission","mediaDevices","resolve","reject","generateWorkletUrl","type","jsStr","workerJsString","workletJsString","blob","initializeAudioProcessor","audioContext","CrossOriginWorker","NOOP","RecorderManager","__publicField","config","mediaStream","_a","_b","error","targetSampleRate","context","fallbackContext","audioWorkletNode","mediaSource","e","data","_c","_d","scriptProcessor","inputBuffer"],"mappings":"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"}
@@ -1,32 +1,25 @@
1
- const n = `!(function () {
2
- "use strict";
1
+ const t = `!(function () {
2
+ 'use strict';
3
3
  function t(t) {
4
4
  return (
5
5
  (function (t) {
6
6
  if (Array.isArray(t)) return e(t);
7
7
  })(t) ||
8
8
  (function (t) {
9
- if (
10
- ("undefined" != typeof Symbol && null != t[Symbol.iterator]) ||
11
- null != t["@@iterator"]
12
- )
9
+ if (('undefined' != typeof Symbol && null != t[Symbol.iterator]) || null != t['@@iterator'])
13
10
  return Array.from(t);
14
11
  })(t) ||
15
12
  (function (t, r) {
16
13
  if (!t) return;
17
- if ("string" == typeof t) return e(t, r);
14
+ if ('string' == typeof t) return e(t, r);
18
15
  var i = Object.prototype.toString.call(t).slice(8, -1);
19
- "Object" === i && t.constructor && (i = t.constructor.name);
20
- if ("Map" === i || "Set" === i) return Array.from(t);
21
- if (
22
- "Arguments" === i ||
23
- /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)
24
- )
25
- return e(t, r);
16
+ 'Object' === i && t.constructor && (i = t.constructor.name);
17
+ if ('Map' === i || 'Set' === i) return Array.from(t);
18
+ if ('Arguments' === i || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)) return e(t, r);
26
19
  })(t) ||
27
20
  (function () {
28
21
  throw new TypeError(
29
- "Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."
22
+ 'Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.',
30
23
  );
31
24
  })()
32
25
  );
@@ -44,15 +37,12 @@ const n = `!(function () {
44
37
  this.initialize();
45
38
  }
46
39
  (r.prototype.initialize = function () {
47
- if (
48
- !(this.fromSampleRate > 0 && this.toSampleRate > 0 && this.channels > 0)
49
- )
50
- throw new Error("Invalid settings specified for the resampler.");
40
+ if (!(this.fromSampleRate > 0 && this.toSampleRate > 0 && this.channels > 0))
41
+ throw new Error('Invalid settings specified for the resampler.');
51
42
  this.fromSampleRate == this.toSampleRate
52
43
  ? ((this.resampler = this.bypassResampler), (this.ratioWeight = 1))
53
44
  : (this.fromSampleRate < this.toSampleRate
54
- ? ((this.lastWeight = 1),
55
- (this.resampler = this.compileLinearInterpolation))
45
+ ? ((this.lastWeight = 1), (this.resampler = this.compileLinearInterpolation))
56
46
  : ((this.tailExists = !1),
57
47
  (this.lastWeight = 0),
58
48
  (this.resampler = this.compileMultiTap)),
@@ -75,11 +65,7 @@ const n = `!(function () {
75
65
  for (; f < 1; f += a)
76
66
  for (n = 1 - (o = f % 1), r = 0; r < this.channels; ++r)
77
67
  l[h++] = this.lastOutput[r] * n + t[r] * o;
78
- for (
79
- f--, e -= this.channels, i = Math.floor(f) * this.channels;
80
- h < s && i < e;
81
-
82
- ) {
68
+ for (f--, e -= this.channels, i = Math.floor(f) * this.channels; h < s && i < e; ) {
83
69
  for (n = 1 - (o = f % 1), r = 0; r < this.channels; ++r)
84
70
  l[h++] = t[i + r] * n + t[i + this.channels + r] * o;
85
71
  (f += a), (i = Math.floor(f) * this.channels);
@@ -89,7 +75,7 @@ const n = `!(function () {
89
75
  }
90
76
  return this.noReturn ? 0 : [];
91
77
  }
92
- throw new Error("Buffer was of incorrect sample length.");
78
+ throw new Error('Buffer was of incorrect sample length.');
93
79
  }),
94
80
  (r.prototype.compileMultiTap = function (t) {
95
81
  var e = [],
@@ -98,8 +84,7 @@ const n = `!(function () {
98
84
  var i = this.outputBufferSize;
99
85
  if (r % this.channels == 0) {
100
86
  if (r > 0) {
101
- for (var s = this.ratioWeight, a = 0, f = 0; f < this.channels; ++f)
102
- e[f] = 0;
87
+ for (var s = this.ratioWeight, a = 0, f = 0; f < this.channels; ++f) e[f] = 0;
103
88
  var n = 0,
104
89
  o = 0,
105
90
  h = !this.tailExists;
@@ -110,8 +95,7 @@ const n = `!(function () {
110
95
  do {
111
96
  if (h) for (a = s, f = 0; f < this.channels; ++f) e[f] = 0;
112
97
  else {
113
- for (a = this.lastWeight, f = 0; f < this.channels; ++f)
114
- e[f] += this.lastOutput[f];
98
+ for (a = this.lastWeight, f = 0; f < this.channels; ++f) e[f] += this.lastOutput[f];
115
99
  h = !0;
116
100
  }
117
101
  for (; a > 0 && n < r; ) {
@@ -124,8 +108,7 @@ const n = `!(function () {
124
108
  (p = n), (a -= o);
125
109
  }
126
110
  if (0 != a) {
127
- for (this.lastWeight = a, f = 0; f < this.channels; ++f)
128
- this.lastOutput[f] = e[f];
111
+ for (this.lastWeight = a, f = 0; f < this.channels; ++f) this.lastOutput[f] = e[f];
129
112
  this.tailExists = !0;
130
113
  break;
131
114
  }
@@ -135,7 +118,7 @@ const n = `!(function () {
135
118
  }
136
119
  return this.noReturn ? 0 : [];
137
120
  }
138
- throw new Error("Buffer was of incorrect sample length.");
121
+ throw new Error('Buffer was of incorrect sample length.');
139
122
  }),
140
123
  (r.prototype.bypassResampler = function (t) {
141
124
  return this.noReturn ? ((this.outputBuffer = t), t.length) : t;
@@ -153,9 +136,7 @@ const n = `!(function () {
153
136
  }
154
137
  }),
155
138
  (r.prototype.initializeBuffers = function (t) {
156
- this.outputBufferSize = Math.ceil(
157
- (t * this.toSampleRate) / this.fromSampleRate
158
- );
139
+ this.outputBufferSize = Math.ceil((t * this.toSampleRate) / this.fromSampleRate);
159
140
  try {
160
141
  (this.outputBuffer = new Float32Array(this.outputBufferSize)),
161
142
  (this.lastOutput = new Float32Array(this.channels));
@@ -165,13 +146,10 @@ const n = `!(function () {
165
146
  }),
166
147
  (self.transData = function (t) {
167
148
  return (
168
- "short16" === self.arrayBufferType &&
149
+ 'short16' === self.arrayBufferType &&
169
150
  (t = (function (t) {
170
151
  for (
171
- var e = new ArrayBuffer(2 * t.length),
172
- r = new DataView(e),
173
- i = 0,
174
- s = 0;
152
+ var e = new ArrayBuffer(2 * t.length), r = new DataView(e), i = 0, s = 0;
175
153
  s < t.length;
176
154
  s += 1, i += 2
177
155
  ) {
@@ -187,7 +165,7 @@ const n = `!(function () {
187
165
  var i = e.data,
188
166
  s = i.type,
189
167
  a = i.data;
190
- if ("init" === s) {
168
+ if ('init' === s) {
191
169
  var f = a.frameSize,
192
170
  n = a.toSampleRate,
193
171
  o = a.fromSampleRate,
@@ -200,13 +178,13 @@ const n = `!(function () {
200
178
  );
201
179
  }
202
180
  if (
203
- ("stop" === s &&
181
+ ('stop' === s &&
204
182
  (self.postMessage({
205
183
  frameBuffer: self.transData(self.frameBuffer),
206
184
  isLastFrame: !0,
207
185
  }),
208
186
  (self.frameBuffer = [])),
209
- "message" === s)
187
+ 'message' === s)
210
188
  ) {
211
189
  var l,
212
190
  u = a;
@@ -221,13 +199,12 @@ const n = `!(function () {
221
199
  (self.frameBuffer = [])),
222
200
  !0
223
201
  );
224
- u &&
225
- self.postMessage({ frameBuffer: self.transData(u), isLastFrame: !1 });
202
+ u && self.postMessage({ frameBuffer: self.transData(u), isLastFrame: !1 });
226
203
  }
227
204
  });
228
205
  })();
229
206
  `;
230
207
  export {
231
- n as default
208
+ t as default
232
209
  };
233
210
  //# sourceMappingURL=processor.worker.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"processor.worker.es.js","sources":["../../../../../src/utils/xunFeiVoice/RecorderManager/processorSource/processor.worker.js?raw"],"sourcesContent":["export default \"!(function () {\\n \\\"use strict\\\";\\n function t(t) {\\n return (\\n (function (t) {\\n if (Array.isArray(t)) return e(t);\\n })(t) ||\\n (function (t) {\\n if (\\n (\\\"undefined\\\" != typeof Symbol && null != t[Symbol.iterator]) ||\\n null != t[\\\"@@iterator\\\"]\\n )\\n return Array.from(t);\\n })(t) ||\\n (function (t, r) {\\n if (!t) return;\\n if (\\\"string\\\" == typeof t) return e(t, r);\\n var i = Object.prototype.toString.call(t).slice(8, -1);\\n \\\"Object\\\" === i && t.constructor && (i = t.constructor.name);\\n if (\\\"Map\\\" === i || \\\"Set\\\" === i) return Array.from(t);\\n if (\\n \\\"Arguments\\\" === i ||\\n /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)\\n )\\n return e(t, r);\\n })(t) ||\\n (function () {\\n throw new TypeError(\\n \\\"Invalid attempt to spread non-iterable instance.\\\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\\\"\\n );\\n })()\\n );\\n }\\n function e(t, e) {\\n (null == e || e > t.length) && (e = t.length);\\n for (var r = 0, i = new Array(e); r < e; r++) i[r] = t[r];\\n return i;\\n }\\n function r(t, e, r, i) {\\n (this.fromSampleRate = t),\\n (this.toSampleRate = e),\\n (this.channels = 0 | r),\\n (this.noReturn = !!i),\\n this.initialize();\\n }\\n (r.prototype.initialize = function () {\\n if (\\n !(this.fromSampleRate > 0 && this.toSampleRate > 0 && this.channels > 0)\\n )\\n throw new Error(\\\"Invalid settings specified for the resampler.\\\");\\n this.fromSampleRate == this.toSampleRate\\n ? ((this.resampler = this.bypassResampler), (this.ratioWeight = 1))\\n : (this.fromSampleRate < this.toSampleRate\\n ? ((this.lastWeight = 1),\\n (this.resampler = this.compileLinearInterpolation))\\n : ((this.tailExists = !1),\\n (this.lastWeight = 0),\\n (this.resampler = this.compileMultiTap)),\\n (this.ratioWeight = this.fromSampleRate / this.toSampleRate));\\n }),\\n (r.prototype.compileLinearInterpolation = function (t) {\\n var e = t.length;\\n this.initializeBuffers(e);\\n var r,\\n i,\\n s = this.outputBufferSize,\\n a = this.ratioWeight,\\n f = this.lastWeight,\\n n = 0,\\n o = 0,\\n h = 0,\\n l = this.outputBuffer;\\n if (e % this.channels == 0) {\\n if (e > 0) {\\n for (; f < 1; f += a)\\n for (n = 1 - (o = f % 1), r = 0; r < this.channels; ++r)\\n l[h++] = this.lastOutput[r] * n + t[r] * o;\\n for (\\n f--, e -= this.channels, i = Math.floor(f) * this.channels;\\n h < s && i < e;\\n\\n ) {\\n for (n = 1 - (o = f % 1), r = 0; r < this.channels; ++r)\\n l[h++] = t[i + r] * n + t[i + this.channels + r] * o;\\n (f += a), (i = Math.floor(f) * this.channels);\\n }\\n for (r = 0; r < this.channels; ++r) this.lastOutput[r] = t[i++];\\n return (this.lastWeight = f % 1), this.bufferSlice(h);\\n }\\n return this.noReturn ? 0 : [];\\n }\\n throw new Error(\\\"Buffer was of incorrect sample length.\\\");\\n }),\\n (r.prototype.compileMultiTap = function (t) {\\n var e = [],\\n r = t.length;\\n this.initializeBuffers(r);\\n var i = this.outputBufferSize;\\n if (r % this.channels == 0) {\\n if (r > 0) {\\n for (var s = this.ratioWeight, a = 0, f = 0; f < this.channels; ++f)\\n e[f] = 0;\\n var n = 0,\\n o = 0,\\n h = !this.tailExists;\\n this.tailExists = !1;\\n var l = this.outputBuffer,\\n u = 0,\\n p = 0;\\n do {\\n if (h) for (a = s, f = 0; f < this.channels; ++f) e[f] = 0;\\n else {\\n for (a = this.lastWeight, f = 0; f < this.channels; ++f)\\n e[f] += this.lastOutput[f];\\n h = !0;\\n }\\n for (; a > 0 && n < r; ) {\\n if (!(a >= (o = 1 + n - p))) {\\n for (f = 0; f < this.channels; ++f) e[f] += t[n + f] * a;\\n (p += a), (a = 0);\\n break;\\n }\\n for (f = 0; f < this.channels; ++f) e[f] += t[n++] * o;\\n (p = n), (a -= o);\\n }\\n if (0 != a) {\\n for (this.lastWeight = a, f = 0; f < this.channels; ++f)\\n this.lastOutput[f] = e[f];\\n this.tailExists = !0;\\n break;\\n }\\n for (f = 0; f < this.channels; ++f) l[u++] = e[f] / s;\\n } while (n < r && u < i);\\n return this.bufferSlice(u);\\n }\\n return this.noReturn ? 0 : [];\\n }\\n throw new Error(\\\"Buffer was of incorrect sample length.\\\");\\n }),\\n (r.prototype.bypassResampler = function (t) {\\n return this.noReturn ? ((this.outputBuffer = t), t.length) : t;\\n }),\\n (r.prototype.bufferSlice = function (t) {\\n if (this.noReturn) return t;\\n try {\\n return this.outputBuffer.subarray(0, t);\\n } catch (e) {\\n try {\\n return (this.outputBuffer.length = t), this.outputBuffer;\\n } catch (e) {\\n return this.outputBuffer.slice(0, t);\\n }\\n }\\n }),\\n (r.prototype.initializeBuffers = function (t) {\\n this.outputBufferSize = Math.ceil(\\n (t * this.toSampleRate) / this.fromSampleRate\\n );\\n try {\\n (this.outputBuffer = new Float32Array(this.outputBufferSize)),\\n (this.lastOutput = new Float32Array(this.channels));\\n } catch (t) {\\n (this.outputBuffer = []), (this.lastOutput = []);\\n }\\n }),\\n (self.transData = function (t) {\\n return (\\n \\\"short16\\\" === self.arrayBufferType &&\\n (t = (function (t) {\\n for (\\n var e = new ArrayBuffer(2 * t.length),\\n r = new DataView(e),\\n i = 0,\\n s = 0;\\n s < t.length;\\n s += 1, i += 2\\n ) {\\n var a = Math.max(-1, Math.min(1, t[s]));\\n r.setInt16(i, a < 0 ? 32768 * a : 32767 * a, !0);\\n }\\n return r.buffer;\\n })((t = self.resampler.resampler(t)))),\\n t\\n );\\n }),\\n (self.onmessage = function (e) {\\n var i = e.data,\\n s = i.type,\\n a = i.data;\\n if (\\\"init\\\" === s) {\\n var f = a.frameSize,\\n n = a.toSampleRate,\\n o = a.fromSampleRate,\\n h = a.arrayBufferType;\\n return (\\n (self.frameSize = f * Math.floor(o / n)),\\n (self.resampler = new r(o, n, 1)),\\n (self.frameBuffer = []),\\n void (self.arrayBufferType = h)\\n );\\n }\\n if (\\n (\\\"stop\\\" === s &&\\n (self.postMessage({\\n frameBuffer: self.transData(self.frameBuffer),\\n isLastFrame: !0,\\n }),\\n (self.frameBuffer = [])),\\n \\\"message\\\" === s)\\n ) {\\n var l,\\n u = a;\\n if (self.frameSize)\\n return (\\n (l = self.frameBuffer).push.apply(l, t(u)),\\n self.frameBuffer.length >= self.frameSize &&\\n (self.postMessage({\\n frameBuffer: self.transData(this.frameBuffer),\\n isLastFrame: !1,\\n }),\\n (self.frameBuffer = [])),\\n !0\\n );\\n u &&\\n self.postMessage({ frameBuffer: self.transData(u), isLastFrame: !1 });\\n }\\n });\\n})();\\n\""],"names":["workerJsString"],"mappings":"AAAA,MAAeA,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;"}
1
+ {"version":3,"file":"processor.worker.es.js","sources":["../../../../../src/utils/xunFeiVoice/RecorderManager/processorSource/processor.worker.js?raw"],"sourcesContent":["export default \"!(function () {\\n 'use strict';\\n function t(t) {\\n return (\\n (function (t) {\\n if (Array.isArray(t)) return e(t);\\n })(t) ||\\n (function (t) {\\n if (('undefined' != typeof Symbol && null != t[Symbol.iterator]) || null != t['@@iterator'])\\n return Array.from(t);\\n })(t) ||\\n (function (t, r) {\\n if (!t) return;\\n if ('string' == typeof t) return e(t, r);\\n var i = Object.prototype.toString.call(t).slice(8, -1);\\n 'Object' === i && t.constructor && (i = t.constructor.name);\\n if ('Map' === i || 'Set' === i) return Array.from(t);\\n if ('Arguments' === i || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)) return e(t, r);\\n })(t) ||\\n (function () {\\n throw new TypeError(\\n 'Invalid attempt to spread non-iterable instance.\\\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.',\\n );\\n })()\\n );\\n }\\n function e(t, e) {\\n (null == e || e > t.length) && (e = t.length);\\n for (var r = 0, i = new Array(e); r < e; r++) i[r] = t[r];\\n return i;\\n }\\n function r(t, e, r, i) {\\n (this.fromSampleRate = t),\\n (this.toSampleRate = e),\\n (this.channels = 0 | r),\\n (this.noReturn = !!i),\\n this.initialize();\\n }\\n (r.prototype.initialize = function () {\\n if (!(this.fromSampleRate > 0 && this.toSampleRate > 0 && this.channels > 0))\\n throw new Error('Invalid settings specified for the resampler.');\\n this.fromSampleRate == this.toSampleRate\\n ? ((this.resampler = this.bypassResampler), (this.ratioWeight = 1))\\n : (this.fromSampleRate < this.toSampleRate\\n ? ((this.lastWeight = 1), (this.resampler = this.compileLinearInterpolation))\\n : ((this.tailExists = !1),\\n (this.lastWeight = 0),\\n (this.resampler = this.compileMultiTap)),\\n (this.ratioWeight = this.fromSampleRate / this.toSampleRate));\\n }),\\n (r.prototype.compileLinearInterpolation = function (t) {\\n var e = t.length;\\n this.initializeBuffers(e);\\n var r,\\n i,\\n s = this.outputBufferSize,\\n a = this.ratioWeight,\\n f = this.lastWeight,\\n n = 0,\\n o = 0,\\n h = 0,\\n l = this.outputBuffer;\\n if (e % this.channels == 0) {\\n if (e > 0) {\\n for (; f < 1; f += a)\\n for (n = 1 - (o = f % 1), r = 0; r < this.channels; ++r)\\n l[h++] = this.lastOutput[r] * n + t[r] * o;\\n for (f--, e -= this.channels, i = Math.floor(f) * this.channels; h < s && i < e; ) {\\n for (n = 1 - (o = f % 1), r = 0; r < this.channels; ++r)\\n l[h++] = t[i + r] * n + t[i + this.channels + r] * o;\\n (f += a), (i = Math.floor(f) * this.channels);\\n }\\n for (r = 0; r < this.channels; ++r) this.lastOutput[r] = t[i++];\\n return (this.lastWeight = f % 1), this.bufferSlice(h);\\n }\\n return this.noReturn ? 0 : [];\\n }\\n throw new Error('Buffer was of incorrect sample length.');\\n }),\\n (r.prototype.compileMultiTap = function (t) {\\n var e = [],\\n r = t.length;\\n this.initializeBuffers(r);\\n var i = this.outputBufferSize;\\n if (r % this.channels == 0) {\\n if (r > 0) {\\n for (var s = this.ratioWeight, a = 0, f = 0; f < this.channels; ++f) e[f] = 0;\\n var n = 0,\\n o = 0,\\n h = !this.tailExists;\\n this.tailExists = !1;\\n var l = this.outputBuffer,\\n u = 0,\\n p = 0;\\n do {\\n if (h) for (a = s, f = 0; f < this.channels; ++f) e[f] = 0;\\n else {\\n for (a = this.lastWeight, f = 0; f < this.channels; ++f) e[f] += this.lastOutput[f];\\n h = !0;\\n }\\n for (; a > 0 && n < r; ) {\\n if (!(a >= (o = 1 + n - p))) {\\n for (f = 0; f < this.channels; ++f) e[f] += t[n + f] * a;\\n (p += a), (a = 0);\\n break;\\n }\\n for (f = 0; f < this.channels; ++f) e[f] += t[n++] * o;\\n (p = n), (a -= o);\\n }\\n if (0 != a) {\\n for (this.lastWeight = a, f = 0; f < this.channels; ++f) this.lastOutput[f] = e[f];\\n this.tailExists = !0;\\n break;\\n }\\n for (f = 0; f < this.channels; ++f) l[u++] = e[f] / s;\\n } while (n < r && u < i);\\n return this.bufferSlice(u);\\n }\\n return this.noReturn ? 0 : [];\\n }\\n throw new Error('Buffer was of incorrect sample length.');\\n }),\\n (r.prototype.bypassResampler = function (t) {\\n return this.noReturn ? ((this.outputBuffer = t), t.length) : t;\\n }),\\n (r.prototype.bufferSlice = function (t) {\\n if (this.noReturn) return t;\\n try {\\n return this.outputBuffer.subarray(0, t);\\n } catch (e) {\\n try {\\n return (this.outputBuffer.length = t), this.outputBuffer;\\n } catch (e) {\\n return this.outputBuffer.slice(0, t);\\n }\\n }\\n }),\\n (r.prototype.initializeBuffers = function (t) {\\n this.outputBufferSize = Math.ceil((t * this.toSampleRate) / this.fromSampleRate);\\n try {\\n (this.outputBuffer = new Float32Array(this.outputBufferSize)),\\n (this.lastOutput = new Float32Array(this.channels));\\n } catch (t) {\\n (this.outputBuffer = []), (this.lastOutput = []);\\n }\\n }),\\n (self.transData = function (t) {\\n return (\\n 'short16' === self.arrayBufferType &&\\n (t = (function (t) {\\n for (\\n var e = new ArrayBuffer(2 * t.length), r = new DataView(e), i = 0, s = 0;\\n s < t.length;\\n s += 1, i += 2\\n ) {\\n var a = Math.max(-1, Math.min(1, t[s]));\\n r.setInt16(i, a < 0 ? 32768 * a : 32767 * a, !0);\\n }\\n return r.buffer;\\n })((t = self.resampler.resampler(t)))),\\n t\\n );\\n }),\\n (self.onmessage = function (e) {\\n var i = e.data,\\n s = i.type,\\n a = i.data;\\n if ('init' === s) {\\n var f = a.frameSize,\\n n = a.toSampleRate,\\n o = a.fromSampleRate,\\n h = a.arrayBufferType;\\n return (\\n (self.frameSize = f * Math.floor(o / n)),\\n (self.resampler = new r(o, n, 1)),\\n (self.frameBuffer = []),\\n void (self.arrayBufferType = h)\\n );\\n }\\n if (\\n ('stop' === s &&\\n (self.postMessage({\\n frameBuffer: self.transData(self.frameBuffer),\\n isLastFrame: !0,\\n }),\\n (self.frameBuffer = [])),\\n 'message' === s)\\n ) {\\n var l,\\n u = a;\\n if (self.frameSize)\\n return (\\n (l = self.frameBuffer).push.apply(l, t(u)),\\n self.frameBuffer.length >= self.frameSize &&\\n (self.postMessage({\\n frameBuffer: self.transData(this.frameBuffer),\\n isLastFrame: !1,\\n }),\\n (self.frameBuffer = [])),\\n !0\\n );\\n u && self.postMessage({ frameBuffer: self.transData(u), isLastFrame: !1 });\\n }\\n });\\n})();\\n\""],"names":["workerJsString"],"mappings":"AAAA,MAAeA,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;"}
@@ -2,7 +2,7 @@ import { RecorderManager } from './RecorderManager';
2
2
  import { OnMessageInfo, RecordFrameInfo } from './types';
3
3
 
4
4
  type errorType = 'socketConnect' | 'message' | 'record';
5
- export interface XunFeiStreamVoiceManagerConfig {
5
+ export interface XFStreamVoiceManagerConfig {
6
6
  onError: (errInfo: {
7
7
  type: errorType;
8
8
  message: string;
@@ -16,6 +16,14 @@ export interface XunFeiStreamVoiceManagerConfig {
16
16
  * 每次录音都会新建一个 webSocket 连接。录音结束后关闭 webSocket
17
17
  */
18
18
  onWsClose: () => void;
19
+ /**
20
+ * 录音开始的回调
21
+ */
22
+ onRecordStart?: () => void;
23
+ /**
24
+ * 录音结束的回调
25
+ */
26
+ onRecordStop?: (data: ArrayBuffer[]) => void;
19
27
  /**
20
28
  * 获取链接讯飞语音识别地址的 url
21
29
  * @returns websocket url。详情查看讯飞文档里的鉴权方法:https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B
@@ -26,8 +34,8 @@ type SocketStatus = 'UNDEFINED' | 'CONNECTING' | 'OPEN' | 'CLOSING' | 'CLOSED';
26
34
  /**
27
35
  * 讯飞流式语音识别
28
36
  */
29
- export declare class XunFeiStreamVoiceManager {
30
- config: XunFeiStreamVoiceManagerConfig;
37
+ export declare class XFStreamVoiceManager {
38
+ config: XFStreamVoiceManagerConfig;
31
39
  wsInstance: WebSocket | null;
32
40
  status: SocketStatus;
33
41
  recorder: RecorderManager | null;
@@ -38,9 +46,9 @@ export declare class XunFeiStreamVoiceManager {
38
46
  startTimeStamp: number;
39
47
  recordStatus: 'start' | 'stop';
40
48
  readyToSend: boolean;
41
- constructor(config: XunFeiStreamVoiceManagerConfig);
49
+ constructor(config: XFStreamVoiceManagerConfig);
42
50
  renderResult(resultData: string): void;
43
- setConfig(newConfig: XunFeiStreamVoiceManagerConfig): void;
51
+ setConfig(newConfig: XFStreamVoiceManagerConfig): void;
44
52
  sendMessage(message: string): void;
45
53
  recordConfigSend(): Promise<void>;
46
54
  startNewSocket(): void;
@@ -94,7 +94,7 @@ class C {
94
94
  startNewSocket() {
95
95
  var e, s;
96
96
  console.log(
97
- "%c ❤️ love ==== start new socket ",
97
+ "%c ❤️ love ==== start new socket:",
98
98
  "color: red; font-size: 16px;",
99
99
  u,
100
100
  (/* @__PURE__ */ new Date()).toUTCString()
@@ -133,8 +133,11 @@ class C {
133
133
  this.recorder = new w(), this.recorder && (this.recorder.onFrameRecorded = (t) => {
134
134
  this.recordProcess(t);
135
135
  }, this.recorder.onStop = (t) => {
136
+ var e, s;
137
+ (s = (e = this.config).onRecordStop) == null || s.call(e, t);
136
138
  }, this.recorder.onStart = () => {
137
- console.log("%c ❤️ love ==== recorder onStart:", "color: red; font-size: 16px;"), this.changeBtnStatus("OPEN");
139
+ var t, e;
140
+ console.log("%c ❤️ love ==== recorder onStart:", "color: red; font-size: 16px;"), (e = (t = this.config).onRecordStart) == null || e.call(t), this.changeBtnStatus("OPEN");
138
141
  });
139
142
  }
140
143
  changeBtnStatus(t) {
@@ -168,6 +171,6 @@ class C {
168
171
  }
169
172
  }
170
173
  export {
171
- C as XunFeiStreamVoiceManager
174
+ C as XFStreamVoiceManager
172
175
  };
173
- //# sourceMappingURL=XunFeiStreamVoiceManager.es.js.map
176
+ //# sourceMappingURL=XFStreamVoiceManager.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"XFStreamVoiceManager.es.js","sources":["../../../src/utils/xunFeiVoice/XFStreamVoiceManager.ts"],"sourcesContent":["import CryptoJS from 'crypto-js';\nimport { RecorderManager } from './RecorderManager';\nimport type { OnMessageInfo, RecordFrameInfo } from './types';\nimport { toBase64 } from './utils';\n\ntype errorType = 'socketConnect' | 'message' | 'record';\nlet startCount = 0;\n\nexport interface XFStreamVoiceManagerConfig {\n onError: (errInfo: { type: errorType; message: string }) => void;\n /** ws 收到消息的回调\n * @param info.message 消息内容\n * @param info.isLatest 是否是最后一条消息。 isLatest 为 true 时, message 为完整的语音识别结果。\n */\n onMessage: (info: OnMessageInfo) => void;\n /** webSocket 关闭的回调。\n * 每次录音都会新建一个 webSocket 连接。录音结束后关闭 webSocket\n */\n onWsClose: () => void;\n /**\n * 录音开始的回调\n */\n onRecordStart?: () => void;\n /**\n * 录音结束的回调\n */\n onRecordStop?: (data: ArrayBuffer[]) => void;\n /**\n * 获取链接讯飞语音识别地址的 url\n * @returns websocket url。详情查看讯飞文档里的鉴权方法:https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B\n */\n getWebSocketUrl?: () => string;\n}\ntype SocketStatus = 'UNDEFINED' | 'CONNECTING' | 'OPEN' | 'CLOSING' | 'CLOSED';\n\n/** 现在前端mock,后续移到接口下发 */\nconst APPID = '93b73e33';\nconst API_SECRET = 'ZGJhMzQ5ZTJlMDgyYmE1ZTFlZDlmYjg0';\nconst API_KEY = 'fe43de7a071e3a32ec03c6f09fe7bedf';\n/**\n * 获取websocket url\n * 该接口需要后端提供,这里为了方便前端处理\n */\nfunction getWebSocketUrl() {\n // 请求地址根据语种不同变化\n const url = 'wss://iat-api.xfyun.cn/v2/iat';\n const host = 'iat-api.xfyun.cn';\n const apiKey = API_KEY;\n const apiSecret = API_SECRET;\n const date = new Date().toUTCString();\n const algorithm = 'hmac-sha256';\n const headers = 'host date request-line';\n const signatureOrigin = `host: ${host}\\ndate: ${date}\\nGET /v2/iat HTTP/1.1`;\n const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);\n const signature = CryptoJS.enc.Base64.stringify(signatureSha);\n const authorizationOrigin = `api_key=\"${apiKey}\", algorithm=\"${algorithm}\", headers=\"${headers}\", signature=\"${signature}\"`;\n const authorization = window.btoa(authorizationOrigin);\n return `${url}?authorization=${authorization}&date=${date}&host=${host}`;\n}\n\n/**\n * 讯飞流式语音识别\n */\nexport class XFStreamVoiceManager {\n wsInstance: WebSocket | null = null;\n status: SocketStatus = 'UNDEFINED';\n recorder: RecorderManager | null = null;\n isDestroy = false;\n resultText = '';\n resultTextTemp = '';\n continueRecord = false;\n startTimeStamp = 0;\n recordStatus: 'start' | 'stop' = 'stop';\n readyToSend = false;\n\n constructor(public config: XFStreamVoiceManagerConfig) {\n this.initializeRecorder();\n }\n renderResult(resultData: string) {\n // 识别结束\n const jsonData = JSON.parse(resultData);\n if (jsonData.data && jsonData.data.result) {\n const data = jsonData.data.result;\n let str = '';\n const ws = data.ws;\n for (let i = 0; i < ws.length; i++) {\n str = str + ws[i].cw[0].w;\n }\n // 开启 wpgs 会有此字段(前提:在控制台开通动态修正功能)\n // 取值为 \"apd\"时表示该片结果是追加到前面的最终结果;取值为\"rpl\" 时表示替换前面的部分结果,替换范围为rg字段\n if (data.pgs) {\n if (data.pgs === 'apd') {\n // 将resultTextTemp同步给resultText\n this.resultText = this.resultTextTemp;\n }\n // 将结果存储在resultTextTemp中\n this.resultTextTemp = this.resultText + str;\n } else {\n this.resultText = this.resultText + str;\n }\n }\n /**\n * 识别结果是否结束标识:\n * 0:识别的第一块结果。\n * 1:识别中间结果。\n * 2:识别最后一块结果\n */\n if (jsonData.code === 0 && jsonData.data.status === 2) {\n this.config.onMessage({\n message: this.resultText,\n isLatest: true,\n tempMessage: this.resultTextTemp,\n });\n this.wsInstance?.close();\n return;\n }\n if (jsonData.code === 0 && jsonData.data.status === 1) {\n this.config.onMessage({\n message: this.resultText,\n isLatest: false,\n tempMessage: this.resultTextTemp,\n });\n }\n // code 不为0时表示出错\n if (jsonData.code !== 0) {\n this.wsInstance?.close();\n this.config.onError({ type: 'message', message: resultData });\n }\n }\n\n setConfig(newConfig: XFStreamVoiceManagerConfig) {\n this.config = {\n ...this.config,\n ...newConfig,\n };\n this.initializeRecorder();\n }\n sendMessage(message: string) {\n if (this.wsInstance?.readyState === WebSocket.OPEN && this.readyToSend) {\n this.wsInstance.send(message);\n }\n }\n\n async recordConfigSend() {\n if (this.isDestroy) {\n return;\n }\n try {\n // 开始录音\n await this.recorder?.start({\n sampleRate: 16000,\n frameSize: 1280,\n });\n const params = {\n common: {\n app_id: APPID,\n },\n business: {\n language: 'zh_cn',\n domain: 'iat',\n accent: 'mandarin',\n vad_eos: 5000,\n dwa: 'wpgs',\n },\n data: {\n status: 0,\n format: 'audio/L16;rate=16000',\n encoding: 'raw',\n },\n };\n this.readyToSend = true;\n this.sendMessage(JSON.stringify(params));\n } catch (error) {\n console.error('record error:', error);\n this.config.onError({ type: 'record', message: '录音失败, 请检查权限!' });\n }\n }\n\n startNewSocket() {\n console.log(\n '%c ❤️ love ==== start new socket:',\n 'color: red; font-size: 16px;',\n startCount,\n new Date().toUTCString(),\n );\n startCount++;\n const websocketUrl = this.config.getWebSocketUrl?.() || getWebSocketUrl();\n if ('WebSocket' in window) {\n this.wsInstance = new WebSocket(websocketUrl);\n } else {\n this.config.onError({ type: 'socketConnect', message: '浏览器不支持 WebSocket' });\n return;\n }\n this.wsInstance.onmessage = (e) => {\n // 处理返回数据\n this.renderResult(e.data);\n };\n this.wsInstance.onerror = (e) => {\n console.error('socket error:', e);\n this.changeBtnStatus('CLOSED');\n };\n this.wsInstance.onclose = async () => {\n this.changeBtnStatus('CLOSED');\n this.config.onWsClose();\n this.recorder?.stop();\n if (this.continueRecord) {\n this.readyToSend = false;\n await this.recorder?.close();\n this.startRecord();\n }\n };\n this.wsInstance.onopen = () => {\n this.changeBtnStatus('OPEN');\n this.recordConfigSend();\n };\n }\n\n recordProcess({ isLastFrame, frameBuffer }: RecordFrameInfo) {\n if (this.isDestroy) {\n return;\n }\n if (!this.wsInstance) {\n return;\n }\n if (this.wsInstance.readyState === this.wsInstance.OPEN) {\n this.sendMessage(\n JSON.stringify({\n data: {\n status: isLastFrame ? 2 : 1,\n format: 'audio/L16;rate=16000',\n encoding: 'raw',\n audio: toBase64(frameBuffer),\n },\n }),\n );\n }\n if (isLastFrame) {\n this.readyToSend = false;\n this.changeBtnStatus('CLOSING');\n }\n }\n\n initializeRecorder() {\n this.recorder = new RecorderManager();\n if (!this.recorder) {\n return;\n }\n this.recorder.onFrameRecorded = (data: RecordFrameInfo) => {\n this.recordProcess(data);\n };\n this.recorder.onStop = (data: ArrayBuffer[]) => {\n this.config.onRecordStop?.(data);\n };\n this.recorder.onStart = () => {\n console.log('%c ❤️ love ==== recorder onStart:', 'color: red; font-size: 16px;');\n this.config.onRecordStart?.();\n this.changeBtnStatus('OPEN');\n };\n }\n\n changeBtnStatus(status: SocketStatus) {\n this.status = status;\n }\n\n startRecord() {\n if (this.isDestroy) {\n return;\n }\n this.startTimeStamp = Date.now();\n this.resultText = '';\n this.resultTextTemp = '';\n this.startNewSocket();\n }\n\n async stopRecord() {\n console.log('stop record ====', Date.now() - this.startTimeStamp);\n // 结束录音,为了防止刚开始就关闭\n if (Date.now() - this.startTimeStamp < 500) {\n setTimeout(() => {\n this.recorder?.stop();\n }, 100);\n } else {\n this.recorder?.stop();\n }\n // 这里为了防止ws没有返回结束的数据,导致 ws 一直没关,所以加个定时器,5s 后手动关闭\n setTimeout(() => {\n if (this.wsInstance?.readyState === WebSocket.OPEN) {\n this.wsInstance?.close();\n }\n }, 5000);\n }\n\n /** 开启持续监听 */\n startContinueRecord() {\n this.continueRecord = true;\n this.startRecord();\n }\n\n stopContinueRecord() {\n this.continueRecord = false;\n this.stopRecord();\n }\n\n destroy() {\n this.isDestroy = true;\n setTimeout(() => {\n if (this.wsInstance?.readyState === WebSocket.OPEN) {\n this.wsInstance?.close();\n }\n this.recorder?.stop();\n }, 1000);\n }\n}\n"],"names":["startCount","APPID","API_SECRET","API_KEY","getWebSocketUrl","url","host","apiKey","apiSecret","date","algorithm","headers","signatureOrigin","signatureSha","CryptoJS","signature","authorizationOrigin","authorization","XFStreamVoiceManager","config","__publicField","resultData","jsonData","data","str","ws","i","_a","_b","newConfig","message","params","error","websocketUrl","e","isLastFrame","frameBuffer","toBase64","RecorderManager","status","_c"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- export * from './XunFeiStreamVoiceManager';
1
+ export * from './XFStreamVoiceManager';
2
2
  export * from './RecorderManager';
3
3
  export * from './types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easyv/biz-components",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "type": "module",
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",
@@ -1 +0,0 @@
1
- {"version":3,"file":"XunFeiStreamVoiceManager.es.js","sources":["../../../src/utils/xunFeiVoice/XunFeiStreamVoiceManager.ts"],"sourcesContent":["import CryptoJS from 'crypto-js';\nimport { RecorderManager } from './RecorderManager';\nimport type { OnMessageInfo, RecordFrameInfo } from './types';\nimport { toBase64 } from './utils';\n\ntype errorType = 'socketConnect' | 'message' | 'record';\nlet startCount = 0;\n\nexport interface XunFeiStreamVoiceManagerConfig {\n onError: (errInfo: { type: errorType; message: string }) => void;\n /** ws 收到消息的回调\n * @param info.message 消息内容\n * @param info.isLatest 是否是最后一条消息。 isLatest 为 true 时, message 为完整的语音识别结果。\n */\n onMessage: (info: OnMessageInfo) => void;\n /** webSocket 关闭的回调。\n * 每次录音都会新建一个 webSocket 连接。录音结束后关闭 webSocket\n */\n onWsClose: () => void;\n /**\n * 获取链接讯飞语音识别地址的 url\n * @returns websocket url。详情查看讯飞文档里的鉴权方法:https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B\n */\n getWebSocketUrl?: () => string;\n}\ntype SocketStatus = 'UNDEFINED' | 'CONNECTING' | 'OPEN' | 'CLOSING' | 'CLOSED';\n\n/** 现在前端mock,后续移到接口下发 */\nconst APPID = '93b73e33';\nconst API_SECRET = 'ZGJhMzQ5ZTJlMDgyYmE1ZTFlZDlmYjg0';\nconst API_KEY = 'fe43de7a071e3a32ec03c6f09fe7bedf';\n/**\n * 获取websocket url\n * 该接口需要后端提供,这里为了方便前端处理\n */\nfunction getWebSocketUrl() {\n // 请求地址根据语种不同变化\n const url = 'wss://iat-api.xfyun.cn/v2/iat';\n const host = 'iat-api.xfyun.cn';\n const apiKey = API_KEY;\n const apiSecret = API_SECRET;\n const date = new Date().toUTCString();\n const algorithm = 'hmac-sha256';\n const headers = 'host date request-line';\n const signatureOrigin = `host: ${host}\\ndate: ${date}\\nGET /v2/iat HTTP/1.1`;\n const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);\n const signature = CryptoJS.enc.Base64.stringify(signatureSha);\n const authorizationOrigin = `api_key=\"${apiKey}\", algorithm=\"${algorithm}\", headers=\"${headers}\", signature=\"${signature}\"`;\n const authorization = window.btoa(authorizationOrigin);\n return `${url}?authorization=${authorization}&date=${date}&host=${host}`;\n}\n\n/**\n * 讯飞流式语音识别\n */\nexport class XunFeiStreamVoiceManager {\n wsInstance: WebSocket | null = null;\n status: SocketStatus = 'UNDEFINED';\n recorder: RecorderManager | null = null;\n isDestroy = false;\n resultText = '';\n resultTextTemp = '';\n continueRecord = false;\n startTimeStamp = 0;\n recordStatus: 'start' | 'stop' = 'stop';\n readyToSend = false;\n\n constructor(public config: XunFeiStreamVoiceManagerConfig) {\n this.initializeRecorder();\n }\n renderResult(resultData: string) {\n // 识别结束\n const jsonData = JSON.parse(resultData);\n if (jsonData.data && jsonData.data.result) {\n const data = jsonData.data.result;\n let str = '';\n const ws = data.ws;\n for (let i = 0; i < ws.length; i++) {\n str = str + ws[i].cw[0].w;\n }\n // 开启 wpgs 会有此字段(前提:在控制台开通动态修正功能)\n // 取值为 \"apd\"时表示该片结果是追加到前面的最终结果;取值为\"rpl\" 时表示替换前面的部分结果,替换范围为rg字段\n if (data.pgs) {\n if (data.pgs === 'apd') {\n // 将resultTextTemp同步给resultText\n this.resultText = this.resultTextTemp;\n }\n // 将结果存储在resultTextTemp中\n this.resultTextTemp = this.resultText + str;\n } else {\n this.resultText = this.resultText + str;\n }\n }\n /**\n * 识别结果是否结束标识:\n * 0:识别的第一块结果。\n * 1:识别中间结果。\n * 2:识别最后一块结果\n */\n if (jsonData.code === 0 && jsonData.data.status === 2) {\n this.config.onMessage({\n message: this.resultText,\n isLatest: true,\n tempMessage: this.resultTextTemp,\n });\n this.wsInstance?.close();\n return;\n }\n if (jsonData.code === 0 && jsonData.data.status === 1) {\n this.config.onMessage({\n message: this.resultText,\n isLatest: false,\n tempMessage: this.resultTextTemp,\n });\n }\n // code 不为0时表示出错\n if (jsonData.code !== 0) {\n this.wsInstance?.close();\n this.config.onError({ type: 'message', message: resultData });\n }\n }\n\n setConfig(newConfig: XunFeiStreamVoiceManagerConfig) {\n this.config = {\n ...this.config,\n ...newConfig,\n };\n this.initializeRecorder();\n }\n sendMessage(message: string) {\n if (this.wsInstance?.readyState === WebSocket.OPEN && this.readyToSend) {\n this.wsInstance.send(message);\n }\n }\n\n async recordConfigSend() {\n if (this.isDestroy) {\n return;\n }\n try {\n // 开始录音\n await this.recorder?.start({\n sampleRate: 16000,\n frameSize: 1280,\n });\n const params = {\n common: {\n app_id: APPID,\n },\n business: {\n language: 'zh_cn',\n domain: 'iat',\n accent: 'mandarin',\n vad_eos: 5000,\n dwa: 'wpgs',\n },\n data: {\n status: 0,\n format: 'audio/L16;rate=16000',\n encoding: 'raw',\n },\n };\n this.readyToSend = true;\n this.sendMessage(JSON.stringify(params));\n } catch (error) {\n console.error('record error:', error);\n this.config.onError({ type: 'record', message: '录音失败, 请检查权限!' });\n }\n }\n\n startNewSocket() {\n console.log(\n '%c ❤️ love ==== start new socket ',\n 'color: red; font-size: 16px;',\n startCount,\n new Date().toUTCString(),\n );\n startCount++;\n const websocketUrl = this.config.getWebSocketUrl?.() || getWebSocketUrl();\n if ('WebSocket' in window) {\n this.wsInstance = new WebSocket(websocketUrl);\n } else {\n this.config.onError({ type: 'socketConnect', message: '浏览器不支持 WebSocket' });\n return;\n }\n this.wsInstance.onmessage = (e) => {\n // 处理返回数据\n this.renderResult(e.data);\n };\n this.wsInstance.onerror = (e) => {\n console.error('socket error:', e);\n this.changeBtnStatus('CLOSED');\n };\n this.wsInstance.onclose = async () => {\n this.changeBtnStatus('CLOSED');\n this.config.onWsClose();\n this.recorder?.stop();\n if (this.continueRecord) {\n this.readyToSend = false;\n await this.recorder?.close();\n this.startRecord();\n }\n };\n this.wsInstance.onopen = () => {\n this.changeBtnStatus('OPEN');\n this.recordConfigSend();\n };\n }\n\n recordProcess({ isLastFrame, frameBuffer }: RecordFrameInfo) {\n if (this.isDestroy) {\n return;\n }\n if (!this.wsInstance) {\n return;\n }\n if (this.wsInstance.readyState === this.wsInstance.OPEN) {\n this.sendMessage(\n JSON.stringify({\n data: {\n status: isLastFrame ? 2 : 1,\n format: 'audio/L16;rate=16000',\n encoding: 'raw',\n audio: toBase64(frameBuffer),\n },\n }),\n );\n }\n if (isLastFrame) {\n this.readyToSend = false;\n this.changeBtnStatus('CLOSING');\n }\n }\n\n initializeRecorder() {\n this.recorder = new RecorderManager();\n if (!this.recorder) {\n return;\n }\n this.recorder.onFrameRecorded = (data: RecordFrameInfo) => {\n this.recordProcess(data);\n };\n this.recorder.onStop = (_data: ArrayBuffer[]) => {\n // do nothing\n };\n this.recorder.onStart = () => {\n console.log('%c ❤️ love ==== recorder onStart:', 'color: red; font-size: 16px;');\n this.changeBtnStatus('OPEN');\n };\n }\n\n changeBtnStatus(status: SocketStatus) {\n this.status = status;\n }\n\n startRecord() {\n if (this.isDestroy) {\n return;\n }\n this.startTimeStamp = Date.now();\n this.resultText = '';\n this.resultTextTemp = '';\n this.startNewSocket();\n }\n\n async stopRecord() {\n console.log('stop record ====', Date.now() - this.startTimeStamp);\n // 结束录音,为了防止刚开始就关闭\n if (Date.now() - this.startTimeStamp < 500) {\n setTimeout(() => {\n this.recorder?.stop();\n }, 100);\n } else {\n this.recorder?.stop();\n }\n // 这里为了防止ws没有返回结束的数据,导致 ws 一直没关,所以加个定时器,5s 后手动关闭\n setTimeout(() => {\n if (this.wsInstance?.readyState === WebSocket.OPEN) {\n this.wsInstance?.close();\n }\n }, 5000);\n }\n\n /** 开启持续监听 */\n startContinueRecord() {\n this.continueRecord = true;\n this.startRecord();\n }\n\n stopContinueRecord() {\n this.continueRecord = false;\n this.stopRecord();\n }\n\n destroy() {\n this.isDestroy = true;\n setTimeout(() => {\n if (this.wsInstance?.readyState === WebSocket.OPEN) {\n this.wsInstance?.close();\n }\n this.recorder?.stop();\n }, 1000);\n }\n}\n"],"names":["startCount","APPID","API_SECRET","API_KEY","getWebSocketUrl","url","host","apiKey","apiSecret","date","algorithm","headers","signatureOrigin","signatureSha","CryptoJS","signature","authorizationOrigin","authorization","XunFeiStreamVoiceManager","config","__publicField","resultData","jsonData","data","str","ws","i","_a","_b","newConfig","message","params","error","websocketUrl","e","isLastFrame","frameBuffer","toBase64","RecorderManager","_data","status","_c"],"mappings":"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"}