SpeechSynthesisUtterance 语音合成使用 文字语音播报

2023-11-13

一、关于HTML5语音Web Speech API

HTML5中和Web Speech相关的API实际上有两类,一类是“语音识别(Speech Recognition)”,另外一个就是“语音合成(Speech Synthesis)”,这两个名词听上去很高大上,实际上指的分别是“语音转文字”,和“文字变语音”。

而本文要介绍的就是这里的“语音合成-文字变语音”。为什么称为“合成”呢?比方说你Siri发音“你好,世界!” 实际上是把“你”、“好”、“世”、“界”这4个字的读音给合并在一起,因此,称为“语音合成”。

“语音识别”和“语音合成”看上去像是正反两方面,应该带有镜面气质,实际上,至少从兼容性来看,两者并无法直接对等。“语音识别(Speech Recognition)”目前的就Chrome浏览器和Opera浏览器默认支持,但是,“语音合成(Speech Synthesis)”的兼容性要好上太多了Chrome,FF,Edge,Safari等等都是支持的。

使用的基本套路如下:

  • 创建SpeechRecognition的新实例。由于到目前为止,浏览器还没有广泛支持,所以需要webKit的前缀:

var newRecognition = webkitSpeechRecognition();
  • 设置是持续听还是听到声音之后就关闭接收。通过设置continuous属性值实现。一般聊天沟通使用false属性值,如果是写文章写公众号之类的则可以设置为true,如下示意:
newRecognition.continuous = true;
  • 控制语音识别的开启和停止,可以使用start()和stop()方法:
// 开启
newRecognition.start();
// 停止
newRecognition.stop();
  • 对识别到的结果进行处理,可以使用一些事件方法,比方说onresult:
newRecognition.onresult = function(event) { 
    console.log(event);
}

除了result事件外,还有其他一些事件,例如,soundstart、speechstart、error等。

二、关于语音合成Speech Synthesis API

先从最简单的例子说起,如果想让浏览器读出“你好,世界!”的声音,可以下面的JS代码:

var utterThis = new window.SpeechSynthesisUtterance('你好,世界!');
window.speechSynthesis.speak(utterThis);

没错,只需要这么一点代码就足够了,大家可以在自己浏览器的控制台里面运行上面两行代码,看看有没有读出声音。

上面代码出现了两个长长的对象,SpeechSynthesisUtterancespeechSynthesis,就是语音合成Speech Synthesis API的核心。

首先是SpeechSynthesisUtterance对象,主要用来构建语音合成实例,例如上面代码中的实例对象utterThis。我们可以直接在构建的时候就把要读的文字内容写进去:

var utterThis = new window.SpeechSynthesisUtterance('你好,世界!');

又或者是使用实例对象的一些属性,包括:

  • text – 要合成的文字内容,字符串。
  • lang – 使用的语言,字符串, 例如:"zh-cn"
  • voiceURI – 指定希望使用的声音和服务,字符串。
  • volume – 声音的音量,区间范围是01,默认是1
  • rate – 语速,数值,默认值是1,范围是0.110,表示语速的倍数,例如2表示正常语速的两倍。
  • pitch – 表示说话的音高,数值,范围从0(最小)到2(最大)。默认值为1

因此上面的代码也可以写作:

var utterThis = new window.SpeechSynthesisUtterance();
utterThis.text = '你好,世界!';

不仅如此,该实例对象还暴露了一些方法:

  • onstart – 语音合成开始时候的回调。
  • onpause – 语音合成暂停时候的回调。
  • onresume – 语音合成重新开始时候的回调。
  • onend – 语音合成结束时候的回调。

接下来是speechSynthesis对象,主要作用是触发行为,例如读,停,还原等:

  • speak() – 只能接收SpeechSynthesisUtterance作为唯一的参数,作用是读合成的话语。

  • cancel() –  删除队列中所有的语音.如果正在播放,则直接停止。

  • pause() – 暂停合成过程。

  • resume() – 重新开始合成过程。

  • getVoices – 此方法不接受任何参数,用来返回浏览器支持的语音包列表注意:必须添加在voiceschanged事件中才能生效,是个数组

三、项目源码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title></title>
</head>
<body>
<button onclick='playRadio()' id="played">播报语音</button>
<button onclick='pauseRadio()' id="pasued">暂停</button>
<button onclick='resumeRadio()' id="resumed">重新播报</button>
<button onclick='stopRadio()' id="stoped">停止</button>

<script>
	function playRadio(){
	   updataVoice();
	}
	function pauseRadio(){
	   window.speechSynthesis.pause();

	}
	function resumeRadio(){
		window.speechSynthesis.resume();
	}
	function stopRadio(){
		window.speechSynthesis.cancel();
	}
	
    //获取最新数据的定时器
    var voiceUpdatatimer; //获取最新数据的定时器
    var newVoiceText; //最新数据
    var newVoiceArray; //深拷贝后的最新数据防止修改影响原数组
    //备注:这是根据设置设置每级告警要播放几次 我这个有4个等级告警,可以根据ajax获取设置的告警次数去更改默认的次数
    var alarmLevelOnePlayback = 2; //这是根据1级告警的播放次数 默认播放4遍
    var alarmLevelTowPlayback = 2; //这是根据2级告警的播放次数 默认播放3遍
    var alarmLevelThreePlayback = 2; //这是根据3级告警的播放次数 默认播放3遍
    var alarmLevelFourPlayback = 2; //这是根据4级告警的播放次数 默认播放2遍
    var voiceState = true; //这是一个卡锁在播放本条数据时新来数据不能影响到当前的播报,播放时为false,播放完为true
    var to_speak; //谷歌阅读声明全局变量防止函数执行完销毁造成阅读不全

    //更新告警语音
    function updataVoice() {
        //每次新数据来时需要清理存在的定时器
        clearInterval(voiceUpdatatimer)
        //获取最新数据的定时器
        voiceUpdatatimer = setTimeout(function () {
            //alarmVoice数组结构: [{areaName:"区域",stationName:"局站",deviceName:"设备",alarmLevel:"告警级别", beginTime"开始告警时间",platNo:"121253"}]
            //测试数据
            var alarmVoice = [{
                areaName: "中国",
                stationName: "天津环保科技股份有限公司",
                deviceName: "测试设备1",
                alarmLevel: "2",
                beginTime: "2020-02-20 16:04:48",
                platNo: "121253"
            }];
            //这个是个深拷贝为了不影响原数组,而且根据alarmLevel进行从小到大的排序(compareVoiceArray函数是排序数组对象),告警等级高的肯定在最前面(alarmLevel(告警的等级),是你数组对象中的一个属性)
            newVoiceArray = JSON.parse(JSON.stringify(compareVoiceArray(alarmVoice, "alarmLevel")));
            if (newVoiceArray.length > 0) {
                //去执行声音播报模块
                voiceAnnouncements()
            } else {
                newVoiceText = ""
            }
        }, 1000)
    }

    //排序数据
    function compareVoiceArray(alarmVoice, compareVal) {
        function compare(voiceVal) {
            return function (obj1, obj2) {
                var value1 = obj1[voiceVal];
                var value2 = obj2[voiceVal];
                return value1 - value2;
            }
        }
        return (alarmVoice.sort(compare(compareVal)))
    }

    //判断浏览器 没做细的判断
    var userAgent = navigator.userAgent;
    try {
        //简单判断是ie浏览器 如果想要更细致再加判断
        if (userAgent.indexOf('Trident') > -1) {
            //声明成全局变量 如果放在函数内部 函数执行完会销毁,就会造成语音播放不全就停止了
            var voiceObj = new ActiveXObject("Sapi.SpVoice");
        }
    } catch (err) {
        var voiceObj = ''
    }

    //声音播报函数
    function voiceAnnouncements() {
        var voicLevel; //win7读2发音liang win10正常 这是做个处理让发音一致
        var voiceTimeArry; //win7读取时间 在读到最后时会拉掉秒不读 这是事单独取出时间的字符串拼接出出秒 ;win10正常
        var voiceTime;
        var useVoiceText = "" //阅读的文本
        //为0不阅读 ,唯一阅读 项目上还有声音的播放 两个只能使用一个所以添加这个变量 如果只有声音播报大于1就可以
        var numberOfPlayback = 1; //阅读的次数
        //对2做替换处理替换成汉字二(对win7差异做处理)
        if (newVoiceArray[0].alarmLevel == 2) {
            voicLevel = "二级:"
        } else {
            voicLevel = newVoiceArray[0].alarmLevel + "级:"
        }
        //对时间处理 年月日阅读都正常可以直接使用 把时间的字符串取出单独拼接
        voiceTimeArry = newVoiceArray[0].beginTime.split(" ")
        voiceTime = voiceTimeArry[1].split(":")
        //用:做的停顿, win10可以\r停顿就可以
        newVoiceText = [":::区域:" + newVoiceArray[0].areaName + ":: 局站:" + newVoiceArray[0].stationName + "::: 设备:" + newVoiceArray[0].deviceName + "::: 告警级别:" + voicLevel + " ::: 开始告警时间:" + voiceTimeArry[0] + ":" + voiceTime[0] + "点" + voiceTime[1] + "分" + voiceTime[2] + "秒", newVoiceArray[0].platNo, newVoiceArray[0].alarmLevel]
        if (voiceState) {
            if (newVoiceText[2] == 1) {
                numberOfPlayback = alarmLevelOnePlayback; //1级告警播放次数
            }
            if (newVoiceText[2] == 2) {
                numberOfPlayback = alarmLevelTowPlayback; //2级告警播放次数
            }
            if (newVoiceText[2] == 3) {
                numberOfPlayback = alarmLevelThreePlayback; //3级告警播放次数
            }
            if (newVoiceText[2] == 4) {
                numberOfPlayback = alarmLevelFourPlayback; //4级告警播放次数
			}
			//要播放几次都循环拼接到一起
			for (var i = 0; i < numberOfPlayback; i++) {
				useVoiceText += newVoiceText[0]
				console.log(newVoiceText[0])
			}
			//注意ie需要设置activeX的权限 否则播放不出来
			//这ie的播放模 (目前只做了ie跟谷歌)
			if (!('speechSynthesis' in window)) {
				try {
					voiceState = false; //播放时为false
					voiceObj.Speak(useVoiceText, 1); //播放语音
					//定时器获取是否播放结束 播放时voiceObj.Status.RunningState=2 判断不紧密,如果有结束回调函数最好用结束回调函数,我目前没找到
					voiceTimer = setInterval(function () {
						if (voiceObj.Status.RunningState != 2) {
							clearInterval(voiceTimer)
							//阅读完去拿新数据
							voiceState = true;
							for (var j = 0; j < newVoiceArray.length; j++) {
								if (newVoiceArray[j].platNo == newVoiceText[1]) {
									newVoiceArray.splice(j, 1);
								}
							}
							if (newVoiceArray.length > 0) {
								voiceAnnouncements()
							}
						}
					}, 1000)
				} catch (err) {
					alert("你的浏览器不支持语音(activeX权限设置)")
				}

			} else {
				voiceState = false; //播放时不能让影响
				to_speak = window.speechSynthesis,
					to_speak = new SpeechSynthesisUtterance(useVoiceText);
				to_speak.onend = function (event) {
					//播放完新数据可以进来
					voiceState = true;
					//删除刚刚播放完的数据
					for (var j = 0; j < newVoiceArray.length; j++) {
						if (newVoiceArray[j].platNo == newVoiceText[1]) {
							newVoiceArray.splice(j, 1);
						}
					}
					//重新获取
					if (newVoiceArray.length > 0) {
						voiceAnnouncements()
					}
				}
				//开始阅读
				window.speechSynthesis.speak(to_speak);
				//window.speechSynthesis.cancel();
			}
        }
    }
</script>
</body>
</html>

注:

1.voiceAnnouncements()方法,对播报的语音进行解析拼接,将级别2,改为二级,将时间字符串拼接后再播放;可设置循环播放次数;

2.兼容ie浏览器,谷歌浏览器、360浏览器

3.离线状态下也能用

4.是HTML5的特性,浏览器要支持HTML5.

来源:https://zhuanlan.zhihu.com/p/41179191
来源:https://www.jianshu.com/p/92dec635f6c5

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpeechSynthesisUtterance 语音合成使用 文字语音播报 的相关文章

随机推荐

  • gojs 流程图框架-节点装饰器模板(二)

    上一章我们了解了如何使用 gojs 完成基本的节点和连接线的绘制 gojs 中还可以对节点或边进行自由拖动 编辑等功能 本章将基于上一章编写的流程图代码 为这些节点设置装饰器模板 完成后的效果图 建议下载源码 对照本文进行学习 源码地址 g
  • 【11月比赛合集】13场可报名的创新应用、数据分析和程序设计大奖赛,任君挑选!

    CompHub 实时聚合多平台的数据类 Kaggle 天池 和OJ类 Leetcode 牛客 比赛 本账号同时会推送最新的比赛消息 欢迎关注 更多比赛信息见 CompHub主页 或 点击文末阅读原文 以下信息仅供参考 以比赛官网为准 目录
  • 性能优化:虚拟列表,如何渲染10万条数据的dom,页面同时不卡顿

    最近做的一个需求 当列表大概有2万条数据 又不让做成分页 如果页面直接渲染2万条数据 在一些低配电脑上可能会照成页面卡死 基于这个需求 我们来手写一个虚拟列表 思路 列表中固定只显示少量的数据 比如60条 在列表滚动的时候不断的去插入删除d
  • GMP初探

    G Goroutine 协程 用户级的轻量级线程 M 对内核线程的封装 P 为G和M的调度对象 主要用途是用来执行goroutine 维护了一个goroutine队列 即runqueue 由来 单进程时代 这个时代不需要调度器 早起的操作系
  • PMS-adb install安装应用流程(Android L)

    第一次画流程图画的不好 通过adb install安装应用时对framework来说会首先调用Pm java的runInstall 方法 private int runInstall int installFlags 0 int userI
  • mesa调试技巧

    技术关键字 mesa log系统 环境变量 目录 前言 一 gdb或vscode的断点调试 二 mesa log 系统的使用 总结 前言 软件调试技术是要求软件开发人员必备的一项技能 不同的问题具有不同的调试手段和方法 本文从mesa库的实
  • xcode报错:Cycle inside *******

    xcode报错 Cycle inside building could produce unreliable results This usually can be resolved by moving the target s Heade
  • 基于pytorch实现的Auto-encoder模型

    最近因为在自己论文当中可能要用到Auto encoder 这个东西 学了点皮毛之后想着先按照别人的解释实现一下 然后在MNIST数据集上跑了下测试看看效果 话不多说直接贴代码 Author Media 2020 10 23 import t
  • ci/cd 流程图_如何在整个CI / CD工作流程中衡量软件的可靠性

    ci cd 流程图 克服具有持续可靠性的CI CD工作流程中保持代码质量的挑战 CI CD的做法鼓励在开发中频繁进行代码集成 加快新版本的准备工作并自动化部署 借助这种新工具 软件开发生命周期的这些部分都得到了改善和加速 同时 我们用于评估
  • Spring MVC(Boot) Servlet 3.0异步处理,DeferredResult和Callable(续篇)

    目录 背景 意外发现 结论 背景 上篇Spring MVC Boot Servlet 3 0异步处理 DeferredResult和Callable 我把WebMvcConfig 代码 继承WebMvcConfigurationSuppor
  • 搜索引擎和知识图谱那些事 (上).基础篇

    这是一篇基础性文章 主要介绍搜索引擎和知识图谱的一些原理 发展经历和应用等知识 希望文章对你有所帮助 如果有错误或不足之处 还请海涵 参考资料见后 一 搜索引擎 一 搜索引擎的四个时代 根据张俊林大神的 这就是搜索引擎 这本书中描述 推荐大
  • C++ stl容器

    1 std string string constructor include
  • 有哪些通俗易懂的例子可以解释 IaaS、PaaS、SaaS 的区别?

    首先 什么 云 很多年前 我们家住一小平房 喝水就喝院子里的井水 冬天取暖自己烧煤炉 后来经济状况好了 搬进了楼房 喝水喝上了集中供应的自来水 冬季用上了集中供应的暖气 再也不用打水和掏黑煤球 这就是 云 的基本概念 过去企业数据维护需要恒
  • 声纹识别中pooling总结

    1 Statistics Pooling http danielpovey com files 2017 interspeech embeddings pdf The statistics pooling layer calculates
  • Python手册(Standard Library)--math+random

    文章目录 math random math math import math math truck x 取整 math ceil x 天花板 math floor x 地板 math exp x math log x b math e 计算
  • 机器学习与数学基础知识(一)

    最近 朋友分享给我一套 七月在线 的机器学习视频 我几经思量之后 决定从视频量最少的数学基础部分开始看起 今天学习完了第一个视频 长达2小时 感觉老师讲的挺不错的 以前自己就对机器学习很感兴趣 做了一些了解和尝试性地学习 也看了一点经典的林
  • 专升本数学——极限与连续(二)笔记

    一 无穷大量与无穷小量 1 定义 无穷小量 如果 lim f x 0 则称 f x 是此极限条件下的无穷小量 本质 以 0 为极限的函数 x 2 是 x gt 0 时的无穷小量 无穷大量 如果 lim f x 无穷 则称 f x 是此极限条
  • 详解grep(一)grep基础、语法格式、常用选项与退出状态码的案例解析

    目录 一 GREP基础 1 1 grep的全称 1 2 grep语法格式 二 grep常用选项 2 1 长短选项的说明 2 2 显示常用信息 2 3 控制匹配模式的选项 2 4 控制输出内容的选项 2 5 控制输出行前缀的选项 2 6 控制
  • java学习笔记——JDBC 中 ResultSet、ResultSetMetaData配置对象的属性、批处理

    使用ResultSet ResultSetMetaData操作数据表 SELECT public void test1 1 获取连接 Connection conn null PreparedStatement ps null 4 执行 S
  • SpeechSynthesisUtterance 语音合成使用 文字语音播报

    一 关于HTML5语音Web Speech API HTML5中和Web Speech相关的API实际上有两类 一类是 语音识别 Speech Recognition 另外一个就是 语音合成 Speech Synthesis 这两个名词听上