Skip to content

语音合成

设置音高
设置速度
设置声音
设置音量
<script setup lang="ts">
import { reactive, ref, onMounted } from "vue";
const msg = ref(
	`路上只我一个人,背着手踱着。这一片天地好像是我的;我也像超出了平常旳自己,到了另一世界里。
    我爱热闹,也爱冷静;爱群居,也爱独处。像今晚上,一个人在这苍茫旳月下,什么都可以想,什么都可以不想,
    便觉是个自由的人。白天里一定要做的事,一定要说的话,现在都可不理。这是独处的妙处,我且受用这无边的荷香月色好了。`.replace(
		/\s|\r|\n|\t/g,
		""
	)
);
const voices = ref();

const loading = ref(false);
const model = reactive({
	lang: "zh-CN",
	pitch: 1,
	rate: 1,
	voice: "zh-CN",
	volume: 1,
});

const utterance = ref();
const toVoice = (type: string) => {
	try {
		loading.value = true;
		// 初始化
		// @ts-ignore
		utterance.value = new SpeechSynthesisUtterance(msg.value);
		//语言
		utterance.value.lang = model.lang;
		// 音高
		utterance.value.pitch = model.pitch;
		// 速度
		utterance.value.rate = model.rate;
		// 声音
		utterance.value.voice =
			voices.value.find(i => i.lang === model.voice) || null;
		// 音量
		utterance.value.volume = model.volume;
		// 执行
		speechSynthesis[type](utterance.value);
		loading.value = false;
	} catch (error) {
		console.log(error);
	}
};

onMounted(() => {
	try {
		voices.value = speechSynthesis.getVoices();
		// speechSynthesis.onvoiceschanged = e => {
		// 	// @ts-ignore
		// 	voices.value = speechSynthesis.getVoices();
		// };
	} catch (error) {
		console.log(error);
	}
});
</script>

<template>
	<div class="container">
		<el-form label-width="100px" label-position="left">
			<el-form-item>
				<el-input
					v-model.trim="msg"
					placeholder="请输入内容..."
					show-word-limit
					type="textarea"
					:rows="5"
				/>
			</el-form-item>

			<el-form-item label="设置音高">
				<el-slider v-model="model.pitch" :step="0.5" :max="2" show-stops />
			</el-form-item>
			<el-form-item label="设置速度">
				<el-slider v-model="model.rate" :step="0.5" :max="3" show-stops />
			</el-form-item>
			<el-form-item label="设置声音">
				<el-select v-model="model.voice" placeholder="请选择声音">
					<el-option
						v-for="item in voices"
						:key="item.lang"
						:label="item.name"
						:value="item.lang"
					>
					</el-option>
				</el-select>
			</el-form-item>
			<el-form-item label="设置音量">
				<el-slider v-model="model.volume" :step="0.2" :max="1" show-stops />
			</el-form-item>

			<el-form-item>
				<el-button type="primary" @click="toVoice('speak')" :loading="loading"
					>播放</el-button
				>
				<el-button type="danger" @click="toVoice('cancel')" :loading="loading"
					>停止</el-button
				>
				<el-button type="warning" @click="toVoice('pause')" :loading="loading"
					>暂停</el-button
				>
				<el-button type="success" @click="toVoice('resume')" :loading="loading">
					继续播放
				</el-button>
			</el-form-item>
		</el-form>
	</div>
</template>

<style lang="scss" scoped>
.container {
	width: 100%;
	:deep(textarea) {
		resize: none;
	}
}
</style>

语音识别

结果
临时结果
最大数
<script setup lang="ts">
import { reactive, ref, onMounted } from "vue";
import { ElMessage } from "element-plus";
const msg = ref("");
const voices = ref();

const loading = ref(false);
const model = reactive({
	lang: "zh-CN",
	continuous: false,
	interimResults: false,
	maxAlternatives: 1,
});

const speechRecognition = ref();

const toVoice = (type: string) => {
	try {
		loading.value = true;
		// 语言
		speechRecognition.value.lang = model.lang || "en-US";
		// 控制每次识别是返回连续结果,还是只返回单个结果
		speechRecognition.value.continuous = model.continuous;
		// 控制是否应返回临时结果(true)或不(false)临时结果是尚未最终的结果
		speechRecognition.value.interimResults = model.interimResults;
		//置每个结果提供的最大数
		speechRecognition.value.maxAlternatives = 1;
		msg.value = "";
		speechRecognition.value.onresult = function (event) {
			msg.value = event.results[0][0].transcript;
		};
		speechRecognition.value.onerror = function (error) {
			ElMessage.error(error.message || "该浏览器不支持");
		};
		speechRecognition.value[type]();
		loading.value = false;
	} catch (error) {
		console.log(error);
	}
};

onMounted(() => {
	try {
		voices.value = speechSynthesis.getVoices();
		//@ts-ignore
		speechRecognition.value = new webkitSpeechRecognition();
	} catch (error) {
		console.log(error);
	}
});
</script>

<template>
	<div class="container">
		<el-form label-width="100px" label-position="left">
			<el-form-item>
				<el-input
					v-model.trim="msg"
					placeholder="请输入内容..."
					show-word-limit
					type="textarea"
					:rows="5"
				/>
			</el-form-item>

			<el-form-item label="结果">
				<el-radio-group v-model="model.continuous">
					<el-radio :value="true" :label="true">连续</el-radio>
					<el-radio :value="false" :label="false">单个</el-radio>
				</el-radio-group>
			</el-form-item>

			<el-form-item label="临时结果">
				<el-radio-group v-model="model.interimResults">
					<el-radio :value="true" :label="true">是</el-radio>
					<el-radio :value="false" :label="false">否</el-radio>
				</el-radio-group>
			</el-form-item>

			<el-form-item label="最大数">
				<el-slider
					v-model="model.maxAlternatives"
					:step="1"
					:max="3"
					show-stops
				/>
			</el-form-item>

			<el-form-item>
				<el-button type="primary" @click="toVoice('start')" :loading="loading"
					>识别</el-button
				>
				<el-button type="danger" @click="toVoice('stop')" :loading="loading"
					>结束</el-button
				>
			</el-form-item>
		</el-form>
	</div>
</template>

<style lang="scss" scoped>
.container {
	width: 100%;
	:deep(textarea) {
		resize: none;
	}
}
</style>

wangxiaoze | MIT License.