| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- class VolumeTTS {
- // 默认TTS参数
- static DEFAULT_PARAMS = {
- lang: "zh-CN",
- speed: 1,
- pitch: 1,
- volume: 1,
- };
- // 数字到中文的映射
- static DIGIT_MAP = {
- 0: "零", 1: "一", 2: "二", 3: "三", 4: "四",
- 5: "五", 6: "六", 7: "七", 8: "八", 9: "九"
- };
- constructor(opts = {}) {
- this.ttsParams = this._initializeParams(opts);
- this.TTSModule = null;
- this.customOpts = null;
- this.platform = uni.getSystemInfoSync().platform;
- this.isInitialized = false;
- this.isSpeaking = false;
-
- // #ifdef APP-PLUS
- this._initTTS();
- // #endif
- }
- /**
- * 初始化TTS参数
- * @private
- */
- _initializeParams(opts) {
- const audioConfig = uni.getStorageSync("audioConfig");
-
- if (opts && Object.keys(opts).length > 0) {
- return { ...VolumeTTS.DEFAULT_PARAMS, ...opts };
- }
-
- if (audioConfig) {
- return {
- lang: "zh-CN",
- speed: Math.max(0.1, Math.min(2.0, audioConfig.audioSpeed / 10)),
- pitch: Math.max(0.1, Math.min(2.0, audioConfig.audioVolume)),
- volume: 1,
- };
- }
-
- return { ...VolumeTTS.DEFAULT_PARAMS };
- }
- /**
- * 初始化TTS模块
- * @private
- */
- _initTTS() {
- try {
- this.TTSModule = uni.requireNativePlugin("nrb-tts-plugin");
-
- if (!this.TTSModule) {
- console.warn('TTS模块加载失败');
- return;
- }
- this.TTSModule.init(
- {
- lang: "zh",
- country: "CN",
- },
- (res) => {
- if (res.success === 0) {
- this.isInitialized = true;
- console.log('TTS初始化成功');
- // 移除调试弹窗,改为控制台日志
- } else {
- console.error('TTS初始化失败:', res);
- }
- }
- );
- } catch (error) {
- console.error('TTS模块初始化异常:', error);
- }
- }
- /**
- * 预处理文本,将数字转换为中文
- * @param {string} text - 待处理的文本
- * @returns {string} 处理后的文本
- */
- preprocessText(text) {
- if (!text || typeof text !== 'string') {
- return '';
- }
- return text.replace(/\d+/g, (match) => {
- return match
- .split("")
- .map((digit) => VolumeTTS.DIGIT_MAP[digit] || digit)
- .join("");
- });
- }
- /**
- * 设置自定义的opts参数
- * @param {Object} opts - 自定义的opts参数
- */
- setCustomOpts(opts) {
- if (opts && typeof opts === 'object') {
- this.customOpts = { ...opts };
- }
- }
- /**
- * 获取当前的自定义opts参数
- * @returns {Object|null} 当前的自定义opts参数
- */
- getCustomOpts() {
- return this.customOpts ? { ...this.customOpts } : null;
- }
- /**
- * 清除自定义的opts参数,恢复默认行为
- */
- clearCustomOpts() {
- this.customOpts = null;
- }
- /**
- * 获取平台特定的TTS选项
- * @private
- */
- _getPlatformOpts() {
- if (this.platform === "ios") {
- return {
- rate: this.ttsParams.speed,
- lang: "zh-CN",
- volume: this.ttsParams.volume,
- };
- } else {
- return {
- pitch: this.ttsParams.pitch,
- speechRate: this.ttsParams.speed,
- queueMode: 1,
- };
- }
- }
- /**
- * 语音合成
- * @param {string} text - 要合成的文本
- * @param {Object} customOpts - 自定义选项
- * @returns {Promise<boolean>} 是否成功开始合成
- */
- async speak(text, customOpts = null) {
- if (!text || typeof text !== 'string') {
- console.warn('TTS: 无效的文本输入');
- return false;
- }
- if (this.isSpeaking) {
- console.log('TTS: 正在播放中,停止当前播放');
- this.stop();
- }
- const processedText = this.preprocessText(text);
- console.log('TTS: 开始合成文本:', processedText);
- // #ifdef H5
- return this._speakH5(processedText);
- // #endif
- // #ifdef APP-PLUS
- return this._speakApp(processedText, customOpts);
- // #endif
- return false;
- }
- /**
- * H5端语音合成
- * @private
- */
- _speakH5(text) {
- if ("speechSynthesis" in window) {
- try {
- const utterance = new SpeechSynthesisUtterance(text);
- utterance.lang = this.ttsParams.lang;
- utterance.rate = this.ttsParams.speed;
- utterance.pitch = this.ttsParams.pitch;
- utterance.volume = this.ttsParams.volume;
-
- utterance.onstart = () => {
- this.isSpeaking = true;
- console.log('TTS: H5开始播放');
- };
-
- utterance.onend = () => {
- this.isSpeaking = false;
- console.log('TTS: H5播放完成');
- };
-
- utterance.onerror = (error) => {
- this.isSpeaking = false;
- console.error('TTS: H5播放错误:', error);
- };
-
- speechSynthesis.speak(utterance);
- return true;
- } catch (error) {
- console.error('TTS: H5播放异常:', error);
- return false;
- }
- }
- return false;
- }
- /**
- * APP端语音合成
- * @private
- */
- _speakApp(text, customOpts) {
- if (!this.TTSModule || Object.keys(this.TTSModule).length === 0) {
- console.log('TTS: 重新初始化模块');
- this._initTTS();
- return false;
- }
- try {
- let opts;
-
- // 优先级:方法参数传入的customOpts > setCustomOpts设置的customOpts > 默认opts
- if (customOpts && typeof customOpts === 'object') {
- opts = customOpts;
- } else if (this.customOpts) {
- opts = this.customOpts;
- } else {
- opts = this._getPlatformOpts();
- }
- this.TTSModule.speak(text, opts, (result) => {
- if (result && result.success === 0) {
- this.isSpeaking = true;
- console.log('TTS: APP开始播放');
- } else {
- console.error('TTS: APP播放失败:', result);
- }
- });
- return true;
- } catch (error) {
- console.error('TTS: APP播放异常:', error);
- return false;
- }
- }
- /**
- * 停止语音合成
- */
- stop() {
- this.isSpeaking = false;
-
- // #ifdef H5
- if ("speechSynthesis" in window) {
- speechSynthesis.cancel();
- }
- // #endif
- // #ifdef APP-PLUS
- if (this.TTSModule && typeof this.TTSModule.stop === 'function') {
- try {
- this.TTSModule.stop();
- } catch (error) {
- console.error('TTS: 停止播放异常:', error);
- }
- }
- // #endif
- console.log('TTS: 已停止播放');
- }
- /**
- * 暂停语音合成
- */
- pause() {
- // #ifdef H5
- if ("speechSynthesis" in window) {
- speechSynthesis.pause();
- }
- // #endif
-
- console.log('TTS: 已暂停播放');
- }
- /**
- * 恢复语音合成
- */
- resume() {
- // #ifdef H5
- if ("speechSynthesis" in window) {
- speechSynthesis.resume();
- }
- // #endif
-
- console.log('TTS: 已恢复播放');
- }
- /**
- * 检查是否正在播放
- * @returns {boolean} 是否正在播放
- */
- isPlaying() {
- return this.isSpeaking;
- }
- /**
- * 检查TTS是否已初始化
- * @returns {boolean} 是否已初始化
- */
- isReady() {
- return this.isInitialized;
- }
- /**
- * 销毁TTS实例
- */
- destroy() {
- this.stop();
- this.isInitialized = false;
- this.TTSModule = null;
- this.customOpts = null;
- this.ttsParams = { ...VolumeTTS.DEFAULT_PARAMS };
- console.log('TTS: 实例已销毁');
- }
- /**
- * 更新TTS参数
- * @param {Object} newParams - 新的参数
- */
- updateParams(newParams) {
- if (newParams && typeof newParams === 'object') {
- this.ttsParams = { ...this.ttsParams, ...newParams };
- console.log('TTS: 参数已更新:', this.ttsParams);
- }
- }
- /**
- * 获取当前TTS参数
- * @returns {Object} 当前TTS参数
- */
- getParams() {
- return { ...this.ttsParams };
- }
- }
- export default VolumeTTS;
|