业务总结之音频Audio

1,444 阅读5分钟

一、需求

  1. 不使用音频默认的进度条,仅使用UI自定义喇叭icon
  2. 开启关闭声音可控 
  3. 自动播放 

二、【方案一】使用Audio对象

👋1.创建Audio对象 

Audio对象的属性,方法,事件等可参考【原文】。

        loadAudio(){
            var x1 = document.createElement('source');
                x1.src="bgm.mp3";
                x1.type= "audio/mpeg";
            var x2 = document.createElement('source');
                x2.src="bgm.ogg";
                x2.type="audio/ogg";
                y = document.createElement("audio");
                y.appendChild(x1);
                y.appendChild(x2);//音频格式兼容
                y.preload = true;//预加载
                y.autoplay = true;//自动播放
                y.loop = true;//循环播放
            this.isBGM = y.muted;//是否关闭声音
        },

其中,创建的Audio对象并没有通过appendChild追加到DOM树中,将不会在页面中展示。

👋2.canplay监听

一般要求,音频资源下载完成,可以播放时开始播放,可监听Audio对象的canplay事件或者使用promise,回调函数等异步策略。

        playBGMbyAudio(){
            var that = this;
            that.loadAudio();//创建Audio对象
            y.oncanplay = that.prePlay();//canplay监听
        },

👋3.针对Chrome及IOS禁止自动播放的预处理

  • 已知:通过设置对象属性autoplaytrue,但Chrome及IOS平台对不符合条件的禁止自动播放,具体可【参考】,这种情况需要用户先交互(click或touch)然后再次执行play()

  • 解决方案:先通过执行play(),根据页面是否提示错误来区别设备支持与否,做出标记。

  • 实践:支持自动播放的,不执行play的任何回调【报错】;不支持自动播放的,会进入catch回调,在此设置默认声音关闭,做标记等,用户后续通过手动点击开启声音(重新触发play)。

       prePlay(){
            this.enterShow();//其他页面逻辑
    
            // play返回的是一个promise 以此来区分是否可以自动播放
            y.play().then(() => {
            }).catch(err => {
                // 不支持自动播放 需手动开启声音 后需要重新执行play()
                this.IOS = true;
                y.muted = true;
                this.isBGM = y.muted;
            }).finally(() => {
            });
        },
    

👋4.通过设置对象muted属性控制声音开启关闭

       controlBGM(value) {
            this.IOS&&value&&y.play();//针对标记为IOS的,通过交互事件触发play()
            y.muted = !value;
            this.isBGM = y.muted;
        },

👋5.挂起应用,声音停止

        //当app进入后台失活时
        y.pause();
        //当app被激活时
        y.play();
  • 只能针对不同app分别监听是否进入后台的状态,实用性不高。
  • 不受手机系统是否“静音”控制。

三、【方案二】使用audioContext

📖1.audioContext介绍

参考【audioContext】【Web Audio API

📖2.封装组件及使用

参考【原文

📖3.原理

  • XMLHttpRequest[FileReader](https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader "FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。"):通过XMLHttpRequest请求获取远程音频数据,并指定响应数据类型为arraybuffer,或者使用[FileReader](https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader "FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。")读取本地音频数据。

  • 创建AudioContext对象audio

  • audio.decodeAudioData()方法:通过该方法将获得的ArrayBuffer(音轨数据)解码成为AudioBuffer(可被识别播放的数据)。

  • audio.createBufferSource() 方法:创建播放对象sound

  • 对播放对象sound赋值设置谢系列属性:包括解码后的AudioBuffer,loop等。

  • sound.connect(audio.destination)设置播放源:表示音频图形在特定情况下的最终输出地址,通常为扬声器。

  • sound.start(0)开始播放:可定义开始时间。

📖4.优缺点

同样需要用户交互才可以播放音频。

跟随系统状态,受手机系统是否“静音”控制。

四、答疑

💡浏览器是如何播放音频的

在 2016 年,Chrome 官方团队使用“面向服务的架构”(Services Oriented Architecture,简称 SOA)的思想设计了新的 Chrome 架构。也就是说 Chrome 整体架构会朝向现代操作系统所采用的“面向服务的架构” 方向发展,原来的各种模块会被重构成独立的服务(Service),每个服务(Service)都可以在独立的进程中运行。

浏览器任务管理器中的Audio进程,

浏览器对于audio已经实现了单独进程处理,可以明白最开始仅创建了Audio对象并没有追加DOM树,没有走各个页面的“渲染进程”而是通过“Audio Service”来实现功能。

通过Audio对象和audioContextAPI播放音频都是使用的audio进程/服务,但也存在不同。通过Audio对象创建的音频在Chrome浏览器状态栏区域也有音频控制开关,可以更改音频播放状态,而使用audioContextAPI却没有,以此也可以做出区分。

💡如何理解动态创建并未追加DOM树的DOM节点

浏览器会将由DOM树以及render树产生的文档流自上而下自左而右的平铺下来,有些节点由于样式设置会脱离文档流排列但仍存在于DOM树中,通过浏览器“渲染进程”处理。未追加DOM树可能是以变量存在,会被其他进程读取执行。进程间可以通信。

💡如何理解Audio对象可以通过new创建

参考【Audio()】,可能为浏览器接下来的架构调整铺路。

var audio = new Audio(url);

五、总结

📍标签与API并用

音频也可通过API实现。

📍浏览器多进程架构

浏览器架构趋向于面向服务,audio功能已经独立出来成为一个进程,vedio暂时还没有。