写点什么

探索 js 让你的网页“自己开口说话”

用户头像
云小梦
关注
发布于: 2021 年 04 月 01 日

最近一直在研究音视频流,正好想要做一个“有声提示”,增强页面交互和用户体验的功能。(以后打算引入前端 AI,让整个页面真正实现“语音控制”,嘿嘿)。这一想,顺便就想到了让一众网友为难的“网页自动播放音视频”。


不说废话,实现过程中倒是遇到了一点小问题:




本来嘛以为是很简单的:就像一般给网页添加背景音乐,先动态创建一个 audio 元素,让其隐藏起来,然后用 js 添加一个 event 事件,并触发(事实上现在普遍认为的是:不能给网页添加背景音乐。但经过猜想和实践发现,也可通过下文第一种解决方式实现):


浏览器厂商为了用户体验考虑禁止自动播放出发点是好的,but 为什么要做这么绝情呢?有一句非常著名的话:“不要试图让用户做自己不熟悉的操作”,难道就不能在 DOMContentLoaded 或者 document.readyState==="complete" 前让渲染线程暂停并给出一个显式提示让用户去抉择?你就在 console 里报个错是几个意思


let event = new MouseEvent("click"); // 创建一个单击事件//给触发元素添加属性或者回调事件//比如:a.download = xxx  /  div.onclick=function(){}触发元素.dispatchEvent(event); // 触发单击事件
复制代码


但是这时候发现:浏览器现在全面禁止 audio/video 自动播放了:autoplay 被禁用了! 除非用户主动打开设置


好吧听着就有点匪夷所思,准确地说是——


  1. 在 controls 属性存在时 video 允许 autoplay 控制自动播放,controls 不存在时其 autoplay 属性不起作用;(Google)

  2. audio 元素的 autoplay 属性永远不起作用(必须用户手动触发);(Google 和 Firefox)

  3. video 元素是支持 source 兼容的!但是如果 source 中 type 为音频相关格式,那么 autoplay 大概率也不起作用;(Google 和 Firefox 等)

  4. 文档中要求的“手动触发”很严格——按道理讲上面代码创建一个鼠标事件其意义和用户手动按下鼠标按钮是一样的,但是,浏览器不支持!


所以我们可以换句话说:mp3 等音频格式的 autoplay 被 Google 禁用了!


没有办法,我只能将目光移向“元素 click 事件”,后来想来是我多想了——因为上面第四点的缘故,任何非用户手动操作的“动作”都会被浏览器禁止。


Google 浏览器为其取了一个很有意义的名字 —— 自动播放阻止。




但是我还是发现了文档中的一个“新”成员:web Audio API。虽然文档中没有描述什么相关原理,但是通过这段描述:



这让我突然的就想到了 HTML5 的另一个“大杀器”:canvas。他也是通过一个上下文对象 context 凌驾于浏览器图像/视频流之上。


web audio api 怎么说呢,感觉至少目前对大多数人用处真不大——文档中 &被广大开发者发掘的各种骚操作 canvas 能做的都用 canvas 了,canvas 不能做的对绝大多数开发者来说也不重要。


顺着这个思路,我想到了“创建一个音轨上下文,在其中用 createBufferSource() 方法用于创建一个新的 AudioBufferSourceNode 接口—— 该接口可以通过 AudioBuffer 对象 来播放音频数据,以突破浏览器限制”,AudioBuffer 对象怎么获取?web audio API 为了解决音频源的问题,提出了“向音频 url 发送一个请求,将数据以 arraybuffer 返回,再解码得到元数据”这样看似复杂的方法,具体过程是这样的:


方法使用 XHR 加载一个音轨,设置请求的 responsetype 为 ArrayBuffer 使它返回一个 arraybuffer 数据,然后存储在 audioData 变量中。然后我们将这个 arraybuffer 数据置于 decodeAudioData()方法中使用,当成功解码 PCM Data 后通过 promise 回调返回, 将返回的结果通过 AudioContext.createBufferSource()接口进行处理并获得一个 AudioBufferSourceNode,,将源连接至 AudioContext.destination 形成一个完整的音频流。


var context = new (window.AudioContext || window.webkitAudioContext)();var soundBuffer = null;
function loadSound(url) { var request = new XMLHttpRequest(); request.open('GET', url, true); request.responseType = 'arraybuffer';
request.onload = function() { context.decodeAudioData(request.response).then((buffer)=>{ soundBuffer = buffer; playSound(soundBuffer); }); }; request.send();}
function playSound(buffer) { var source = context.createBufferSource(); source.buffer = buffer; source.connect(context.destination); source[source.start?"start":"noteOn"](0);}//你也可以将init放在某个按钮触发事件上window.addEventListener('DOMContentLoaded', init, false);
function init() { try { var url = "audio/1.mp3"; loadSound(url); } catch (e) { alert('你的浏览器不支持Web Audio API'); }}
复制代码


这里有两个注意点:


  1. 倒数第六行 url 那里如果是个音频的话最好还是手动下载到本地,不然极有可能涉及到跨域问题

  2. 如果是要“语音提示某一段话”,则可以为代码中 init() 函数增加 txt 参数,并调用百度转化接口,将文字先用 encodeURI API 转化为 uri 格式,再接入百度接口 var url = "http://tts.baidu.com/text2audio?lan=zh&ie=UTF-8&text=" + encodeURI('这里是字符串文本'); 转化为 url 链接


这段代码很神奇:google 不支持,但是控制台里面“万恶的报错”也没有了,会给你一个提示:



总之就是我不给你播放。


所以,在除了 Google 的其余浏览器中,建议将上面的 API 和下面这段 HTML 代码一起写上:


<video controls autoplay style="display:none;">   /** 或者将controls和style都去掉 */  <source src="这里写要播放的音频路径,如:http://m10.music.126.net/20200926133756/cba79f37e90871df07cd582befe27723/ymusic/obj/w5zDlMODwrDDiGjCn8Ky/2920523350/fd2a/c111/aae2/5542b627692d3df8d63bbaeb1c73711a.mp3" type="audio/mp3"></source></video>
复制代码


然后你应该就可以听到美妙动听的背景音了!


Firefox 和 Edge 虽然禁掉了 autoplay,但是它也是支持 AudioContext API 的!Google 中禁止了一切不符合规范的 API,而且可能是因为某些不知名原因,上面这段 HTML 代码在 Google 中是时而可以时而不行的,就很迷惑。。。




补充其实这个 API 最大的作用是用于音频可视化领域——它有一个函数是这样的:createAnalyser() 用来创建一个音域可视化对象,可以将它连接到 context 流上:


var gainNode=context[context.createGain?"createGain":"createGainNode"]();gainNode.connect(context.destination);//操作完上一步后,我们已经将音域加载到context流上,以后的操作就可以直接连接这个音域对象var analyser=context.createAnalyser();analyser.fftSize=512;analyser.connect(gainNode);
复制代码




发现浏览器中有了部分实现的相关 API——它曾经一直在逃避浏览器音频播放政策:


function speak(sentence,pitch,volume) {   //使用时调用这个函数即可    const utterance = new SpeechSynthesisUtterance(sentence);    //音调    utterance.pitch = pitch;    //音量    utterance.volume = volume;    window.speechSynthesis.speak(utterance)}
复制代码


参数 sentence 是一个字符串格式文本。


只要调用了这个函数并传入参数,你就能在浏览器中听到动听的、梦寐以求的声音了!(还是个女声,嘿嘿嘿)目前,Chrome70 已经废弃这个 API 了。。。(因为它可以不经过用户主动触发而直接播放)





当然还有稳妥一些的做法,也是个让人比较眼前一亮的操作:是 MDN 文档中提到的 Feature-Policy 头——HTTP 头信息,可设置自动播放相关:这个属性是给后端用的:在 response 中设置 header!

发布于: 2021 年 04 月 01 日阅读数: 225
用户头像

云小梦

关注

求知若渴,虚心若愚 2019.05.11 加入

江湖人称“云小梦”。一个大前端路上还未“毕业”的“小学生”。爱好分享、执着探索、乐于开源;着迷于vue、node、css、可视化、前端智能化以及原生js技术。csdn链接:https://yunxiaomeng.blog.csdn.net/

评论 (2 条评论)

发布
用户头像
可以可以
2021 年 04 月 16 日 18:04
回复
用户头像
太赞了!
2021 年 04 月 01 日 17:04
回复
没有更多了
探索js让你的网页“自己开口说话”