当前位置: 首页 > article >正文

uniapp 文本转语音

在这里插入图片描述

uniapp 文本转语音

  • 基于 Minimax API 的 UniApp 文本转语音工具,支持文本分段、队列播放、暂停恢复等功能。
  • 目前只内置了 Minimax文本转语音
  • Minimax 的语音生成技术以其自然、情感丰富和实时性强而著称
API_KEY、GroupId 获取方法

https://platform.minimaxi.com/user-center/basic-information/interface-key

NPM 地址

特性

  • 🎯 自动文本分段处理
  • 🔄 队列播放管理
  • ⏯️ 支持暂停/恢复
  • 📦 轻量级封装
  • 🎨 完整的事件系统
  • 💫 支持长文本处理

安装 (推荐 npm 安装,而不是导入)

npm install uniapp-text-to-speech

基础使用

import SpeechSynthesisUtil from 'uniapp-text-to-speech';
// 初始化
const tts = new SpeechSynthesisUtil({
	API_KEY: 'your_minimax_api_key', // Minimax API密钥
	GroupId: 'your_group_id', // Minimax 组ID
	MAX_QUEUE_LENGTH: 3// 可选:音频队列最大长度
	modelConfig: { // 可选:音频生成配置
		model: 'speech-01-240228',
		voice_setting: {
			"voice_id": "female-tianmei",
			"speed": 1,
			"vol": 1,
			}
		},
		// 其他配置...
	}
});

// 基础播放
try {
	await tts.textToSpeech('你好,世界!');
} catch (error) {
	console.error('语音合成失败:', error);
}

分段使用

  • 模拟 AI 大模型流式返回数据,自动会处理合成分段:[“你好”,“我是一个ai机器人”,“我的名字叫做阿强”]
import SpeechSynthesisUtil from "uniapp-text-to-speech";
// 初始化
const tts = new SpeechSynthesisUtil({
  API_KEY: "your_minimax_api_key", // Minimax API密钥
  GroupId: "your_group_id", // Minimax 组ID
});

const mockTexts = ['你好,', '我是', '人工智能助手,', '很高兴认识你!'];

try {
	for (const text of mockTexts) {
		await tts.processText(text);
	}
	await tts.flushRemainingText();
} catch (error) {
	addLog(`分段播放失败: ${error.message}`);
}

高级功能

1. 事件监听

import { EventType } from "uniapp-text-to-speech";
// 监听合成开始
tts.on(EventType.SYNTHESIS_START, ({ text }) => {
  console.log(`开始合成文本: ${text}`);
});

// 监听播放开始
tts.on(EventType.AUDIO_PLAY, ({ currentText }) => {
  console.log(`正在播放: ${currentText}`);
  status.value = "播放中";
});

// 监听播放结束
tts.on(EventType.AUDIO_END, ({ finishedText }) => {
  console.log(`播放完成: ${finishedText}`);
  status.value = "就绪";
  progress.value = 100;
});

// 监听错误
tts.on(EventType.ERROR, ({ error }) => {
  console.log(`错误: ${error.message}`);
  status.value = "错误";
});

// 监听暂停
tts.on(EventType.PAUSE, () => {
  console.log("播放已暂停");
  status.value = "已暂停";
  isPaused.value = true;
});

// 监听恢复
tts.on(EventType.RESUME, () => {
  console.log("播放已恢复");
  status.value = "播放中";
  isPaused.value = false;
});

2. 暂停和恢复

// 暂停播放
tts.pause();
// 恢复播放
tts.resume();
// 切换播放/暂停状态
tts.togglePlay();

3. 长文本分段处理

// 自动按标点符号分段处理长文本
await tts.processText("这是第一句话。这是第二句话!这是第三句话?");
// 强制处理剩余未播放的文本
await tts.flushRemainingText();
// 重置文本处理器
tts.resetTextProcessor();

4. 状态管理

// 获取当前状态
const state = tts.getState();
console.log("是否正在播放:", state.isPlaying);
console.log("是否已暂停:", state.isPaused);
// 重置所有状态
tts.reset();

API 文档

构造函数选项

参数类型必填说明
API_KEYstringMinimax API 密钥
GroupIdstringMinimax 组 ID
MAX_QUEUE_LENGTHnumber音频队列最大长度,默认为 3
modelConfigobject合成语音配置,参考minimaxi

事件类型

事件名说明回调参数
SYNTHESIS_START开始合成{ text: string }
SYNTHESIS_END合成结束{ text: string }
AUDIO_PLAY开始播放单个音频片段{ text: string }
AUDIO_END所有音频播放完成{ text: string }
PAUSE暂停播放-
RESUME恢复播放-
ERROR发生错误{ error: Error }

事件说明

  • AUDIO_PLAY: 每个音频片段开始播放时触发
  • AUDIO_END: 仅在所有音频片段都播放完成后触发一次

使用示例

import SpeechSynthesisUtil, { EventType } from "uniapp-text-to-speech";

const tts = new SpeechSynthesisUtil({
  API_KEY: "your_minimax_api_key",
  GroupId: "your_group_id",
  modelConfig: {
    model: "speech-01-240228",
    voice_setting: {
      voice_id: "female-yujie", // 默认使用悦姐声音
      speed: 1.2,
      vol: 1,
    },
  },
});

// 监听播放完成事件
tts.on(EventType.AUDIO_END, ({ text }) => {
  console.log("所有音频播放完成,最后播放的文本:", text);
});

// 分段播放示例
async function playMultipleTexts() {
  await tts.processText("第一段文本");
  await tts.processText("第二段文本");
  await tts.flushRemainingText(); // 确保所有文本都被处理
}

// 重置播放状态
tts.reset();

注意事项

  1. 需要先在 Minimax 申请 API_KEY 和 GroupId
  2. 文本会自动按标点符号分段处理,支持的标点符号优先级:
    • 高优先级:。!?
    • 中优先级:;:
    • 低优先级:,、
  3. 音频队列最大长度默认为 3,可以通过构造函数参数修改
  4. AUDIO_END 事件只会在所有音频片段播放完成后触发一次
  5. 使用 reset() 方法可以重置所有播放状态和计数器

主要的方

方法名说明参数返回值
textToSpeech文本转语音text: stringPromise
processText处理长文本text: stringPromise
pause暂停播放-void
resume恢复播放-void
togglePlay切换播放状态-void
reset重置所有状态-void
on添加事件监听event: EventType, callback: Functionvoid
off移除事件监听event: EventType, callback: Functionvoid

完整的示例代码

<template>
	<div class="speech-demo">
		<!-- 基础演示区域 -->
		<section class="demo-section">
			<h3>基础演示</h3>
			<textarea v-model="basicText" placeholder="请输入要转换的文本"></textarea>
			<button @click="handleBasicSpeech">开始播放</button>
		</section>

		<!-- 分段演示区域 -->
		<section class="demo-section">
			<h3>分段播放演示</h3>
			<div class="segment-container">
				<div v-for="(text, index) in mockTexts" :key="index" class="segment">
					<span>{{ text }}</span>
				</div>
			</div>
			<button @click="handleSegmentSpeech">分段播放</button>
		</section>

		<!-- 高级功能演示区域 -->
		<section class="demo-section">
			<h3>高级功能演示</h3>
			<div class="controls">
				<button @click="handleTogglePlay">{{ isPaused ? '继续' : '暂停' }}</button>
				<button @click="handleReset">重置</button>
			</div>
			<div class="status">
				<p>当前状态: {{ status }}</p>
				<p>播放进度: {{ progress }}%</p>
			</div>
		</section>

		<!-- 事件日志区域 -->
		<section class="demo-section">
			<h3>事件日志</h3>
			<div class="log-container">
				<div v-for="(log, index) in eventLogs" :key="index" class="log-item">
					{{ log }}
				</div>
			</div>
		</section>
	</div>
</template>

<script setup lang="ts">
	import { ref, onMounted, onBeforeUnmount } from 'vue';
	import SpeechSynthesisUtil, { EventType } from 'uniapp-text-to-speech';

	// 响应式状态
	const basicText = ref('你好,这是一个基础示例。');
	const mockTexts = ref(['你好,', '我是', '人工智能助手,', '很高兴认识你!']);
	const status = ref('就绪');
	const progress = ref(0);
	const isPaused = ref(false);
	const eventLogs = ref<string[]>([]);

	// 初始化语音工具
	const tts = new SpeechSynthesisUtil({
		API_KEY: 'your_minimax_api_key', // Minimax API密钥
		GroupId: 'your_group_id', // Minimax 组ID
		modelConfig: {
			model: 'speech-01-240228',
			voice_setting: {
				voice_id: "female-yujie",
				speed: 1,
				vol: 1
			}
		}
	});

	// 添加日志
	const addLog = (message : string) => {
		eventLogs.value.unshift(`${new Date().toLocaleTimeString()}: ${message}`);
		if (eventLogs.value.length > 10) {
			eventLogs.value.pop();
		}
	};

	// 设置事件监听
	const setupEventListeners = () => {
		// 监听合成开始
		tts.on(EventType.SYNTHESIS_START, ({ text }) => {
			addLog(`开始合成文本: ${text}`);
		});

		// 监听播放开始
		tts.on(EventType.AUDIO_PLAY, ({ currentText }) => {
			addLog(`正在播放: ${currentText}`);
			status.value = '播放中';
		});

		// 监听播放结束
		tts.on(EventType.AUDIO_END, ({ finishedText }) => {
			addLog(`播放完成: ${finishedText}`);
			status.value = '就绪';
			progress.value = 100;
		});

		// 监听错误
		tts.on(EventType.ERROR, ({ error }) => {
			addLog(`错误: ${error.message}`);
			status.value = '错误';
		});

		// 监听暂停
		tts.on(EventType.PAUSE, () => {
			addLog('播放已暂停');
			status.value = '已暂停';
			isPaused.value = true;
		});

		// 监听恢复
		tts.on(EventType.RESUME, () => {
			addLog('播放已恢复');
			status.value = '播放中';
			isPaused.value = false;
		});
	};

	// 基础播放示例
	const handleBasicSpeech = async () => {
		try {
			await tts.textToSpeech(basicText.value);
		} catch (error) {
			addLog(`播放失败: ${error.message}`);
		}
	};

	// 分段播放示例
	const handleSegmentSpeech = async () => {
		try {
			for (const text of mockTexts.value) {
				await tts.processText(text);
			}
			await tts.flushRemainingText();
		} catch (error) {
			addLog(`分段播放失败: ${error.message}`);
		}
	};

	// 切换播放/暂停
	const handleTogglePlay = () => {
		tts.togglePlay();
	};

	// 重置播放
	const handleReset = () => {
		tts.reset();
		status.value = '就绪';
		progress.value = 0;
		isPaused.value = false;
		addLog('已重置所有状态');
	};

	// 生命周期钩子
	onMounted(() => {
		setupEventListeners();
	});

	onBeforeUnmount(() => {
		tts.reset();
	});
</script>

<style scoped>
	.speech-demo {
		padding: 20px;
		max-width: 800px;
		margin: 0 auto;
	}

	.demo-section {
		margin-bottom: 30px;
		padding: 20px;
		border: 1px solid #eee;
		border-radius: 8px;
	}

	h3 {
		margin-top: 0;
		margin-bottom: 15px;
		color: #333;
	}

	textarea {
		width: 100%;
		height: 100px;
		padding: 10px;
		margin-bottom: 10px;
		border: 1px solid #ddd;
		border-radius: 4px;
		resize: vertical;
	}

	button {
		padding: 8px 16px;
		margin-right: 10px;
		border: none;
		border-radius: 4px;
		background-color: #4CAF50;
		color: white;
		cursor: pointer;
	}

	button:disabled {
		background-color: #cccccc;
	}

	.segment-container {
		margin-bottom: 15px;
	}

	.segment {
		display: inline-block;
		padding: 5px 10px;
		margin: 5px;
		background-color: #f5f5f5;
		border-radius: 4px;
	}

	.controls {
		margin-bottom: 15px;
	}

	.status {
		padding: 10px;
		background-color: #f9f9f9;
		border-radius: 4px;
	}

	.log-container {
		height: 200px;
		overflow-y: auto;
		padding: 10px;
		background-color: #f5f5f5;
		border-radius: 4px;
	}

	.log-item {
		padding: 5px;
		border-bottom: 1px solid #eee;
		font-family: monospace;
	}
</style>

许可证

MIT

作者

乔振 qiaozhenleve@gmail.com


http://www.kler.cn/a/454638.html

相关文章:

  • RockyLinux介绍及初始化
  • Kubernetes 安装 Nginx以及配置自动补全
  • 《解析 MXNet 的 C++版本在分布式训练中的机遇与挑战》
  • Elasticsearch:normalizer
  • 大模型的实践应用33-关于大模型中的Qwen2与Llama3具体架构的差异全解析
  • MySQL基础-常见的增删改查操作语句总结
  • 挑战一个月基本掌握C++(第十二天)了解命名空间,模板,预处理器
  • 前端Python应用指南(五)用FastAPI快速构建高性能API
  • 同步异步日志系统:设计模式
  • ubuntu 账号从文本中的1000,改成0,后五笔输入法等中文输入法不可用,如何改回来
  • 【Ubuntu 20.4安装截图软件 flameshot 】
  • 全面Kafka监控方案:从配置到指标
  • 【自由能系列(初级),论文解读】神经网络中,熵代表系统的不确定性,自由能则引导系统向更低能量的状态演化,而动力学则描述了系统状态随时间的变化。
  • flask后端开发(11):User模型创建+注册页面模板渲染
  • 使用Python实现自动化文档生成工具:提升文档编写效率的利器
  • STM32F103RCT6学习之一:基本开发流程
  • FileLink为企业打造了一站式的跨网安全文件共享解决方案
  • Docker使用——国内Docker的安装办法
  • 前端面试题合集(一)——HTML/CSS/Javascript/ES6
  • Kubernetes Gateway API-2-跨命名空间路由
  • 鸿蒙Next自定义相机开发时,如何解决相机在全屏预览的时候,画面会有变形和拉伸?
  • 【Agent】Chatbot、Copilot与Agent如何帮助我们的提升效率?
  • 【js】记录预览pdf文件
  • 如何从 0 到 1 ,打造全新一代分布式数据架构
  • Jenkins 命令行多线程并发下载制品包
  • PPT画图——如何设置导致图片为600dpi