Web Audio 概览

Web Audio提供了一个强大的音频处理系统,在我们现有的业务场景中,很少有使用到Web Audio,很多时候用到也仅限于播放一段音频。 除此之外,还能实现丰富的功能,比如:可视化、音色合成器、动态混音、声音特效、3D空间音频、均衡器、环境混响等,可以应用在音乐播放器、电子音乐软件、游戏音效、音乐游戏、直播互动等领域。 这篇文章是我在学习Web Audio的过程中写的一些总结和Demo,简单介绍一些API基础用法。 文章中所有示例:https://web-audio.johnsonlee.site/ AudioContext AudioContext为音频处理提供一个上下文环境,相当于一个中央控制器,控制着音频路由图中的各个音频模块。 在开始音频处理之前,都需要创建一个AudioContext实例,并且可以全局共享同一个。所有(相关)的音频节点都需要包含在同一个AudioContext中,每个音频节点,只能关联一个AudioContext。 音频节点 音频节点即AudioNode,它是一个基类,作为一个音频路由图中的基本单位,它的工作依赖于AudioContext。 音频节点拥有自己的输入/输出,可以通过connect方法将一个节点的输出连接至另一个节点的输入。比如我们可以将一个音频节点连接至audioContext.destination节点来进行音频播放。 audioBufferSourceNode.connect(audioContext.destination) 上面的audioContext.destination是音频节点中的一种,音频节点可以分为三类: Source Node:能产生音频的节点,只有输出,没有输入。 Process Node:对音频进行处理的节点,有输入(可能有多个)和输出。 Destination Node:通常为音频播放设备,如扬声器。 有的音频处理节点会有多个输出,比如ChannelSplitterNode,可以将音频拆分为多个声道,对应的,也有一个合并声道的节点ChannelMergerNode,有多个输入和一个输出。 路由图 Web Audio 提供的是模块化的API,在AudioContext中,各个音频节点的连接,构建了一个路由图,audioContext控制着整个路由图的运转。比如下面一个简单的音频播放示例 const ac = new AudioContext() const $audio = document.querySelector('#audio'); const sourceNode = ac.createMediaElementSource($audio); // 从audio标签创建一个音频源节点 const gainNode = ac.createGain(); // 创建一个增益节点 gainNode.gain.value = 0; // 将增益设置为0(相当于音量设置为0) $audio.addEventListener('play', () => { gainNode.gain.exponentialRampToValueAtTime(1, 1); // 在1秒的时间内指数增长到1,实现一个播放渐入效果 }); sourceNode.connect(gainNode); // 音频源节点连接到增益节点 gainNode.connect(ac.destination); // 增益节点连接到destination进行播放 音频源 web中音频源包括: audio节点 网络加载的音频文件 实时音频流(webRTC、麦克风) 能产生音频信号的音频节点(如:OscillatorNode) 从标签加载音频源 网络加载的音频文件,需要将其转换成音频源节点,才能连接到路由图中,比如我们经常使用的<audio>标签,它是不能直接连接到其它音频节点的...

June 15, 2021 · 3 min · 461 words · Johnson

给你的库集成单元测试和CI/CD

在开发工具库的时候,为保证代码的稳定性和健壮性,我们需要编写完善的测试程序。同时也会集成 CI/CD 等工具优化工作流,提高开发效率。 如果你还不清楚如何开发一个 js 库,可以阅读《typescript 开发工具库入门》 对于一个成熟的 JavaScript 库,单元测试是必要的。现有的 JavaScript 测试框架有很多,karma, mocha, sinon, jasmine, jest等,其中jest是 Facebook 开源的一个开箱即用的测试框架,使用起来非常简单方便。本文将为《typescript 开发工具库入门》中的示例库natulu集成 jest 单元测试。 安装测试框架 我们的项目使用的是 typescript,所以需要配置 jest 以支持 typescript。幸运的是已经有提供了ts-jest库来解决这个事情,我们只需要简单的配置就能使用。 yarn add -D jest ts-jest @types/jest 创建配置文件 执行以下命令自动创建一个 jest 配置文件 yarn ts-jest config:init 生成的配置文件如下 module.exports = { preset: 'ts-jest', testEnvironment: 'node', // 如果是浏览器环境,可以设置为jest-environment-jsdom或删除该配置项 } 也可以使用 jest 来创建配置文件 yarn jest --init 按照步骤选择合适的选项后,将preset设置为ts-jest module.exports = { coverageDirectory: 'coverage', // 单元测试覆盖率生成目录 preset: 'ts-jest', testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'], } 然后我们在 package....

September 2, 2020 · 1 min · 174 words · Johnson

typescript开发工具库入门

写了这么久前端,一起来学习一下如何开发一个自己的工具库吧。我们可以将经常使用的一些业务逻辑封装成通用的函数库,并发布到 npm 上。 本文适合阅读对象为入门级前端工程师,需要具有一定的 typescript 基础,建议先学习typescript 选择 Typescript 是因为其具有完善的类型系统,编译后也能够自动生成声明文件(.d.ts 文件),为使用者提供类型提示,不用再去手写声明文件。同时静态的类型检查能降低代码的出错率,提高开发效率。 本文以一个简单的示例来学习一个 JavaScript 工具库的创建、开发、测试、发布的全生命周期。 相关技术 本文的示例中将使用到以下技术,如有不熟悉的需自行查阅相关文档 typescript rollup - 打包工具 创建项目 创建一个名为’natulu’的空目录,你也可以自己起个名字。然后打开终端进入目录下,执行npm init初始化 npm 包,一阵回车之后,得到一个自动创建的文件package.json { "name": "natulu", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } 先不管里面这里面的内容,接下来我们先组织一下目录结构 目录结构 一般情况都建议将核心的源码放到src目录下管理,我们采用常见的目录结构 natulu ├─ dist ├─ src │ └─ index.ts ├─ package.json ├─ rollup.config.js └─ tsconfig.json src 中存放核心的源码,dist 里面为编译打包后的产物,是直接提供给用户使用的代码。上面的目录中有两个文件,分别是 rollup 配置文件和 ts 配置文件,这两个文件会在后续的步骤中创建。...

August 17, 2020 · 3 min · 576 words · Johnson

Vue Composition API 初探

Composition API是 vue-next 中的逻辑复用解决方案,它是一组基于函数式编程的组合式 API。与之前的逻辑复用方案mixin相比,带来了更友好的类型检查,更方便的测试等优点。 Composition API的详细用法和 API 可以参考官方文档,这里我们做一个简单的初步的上手体验。 安装 Composition API被单独抽离为一个库,让我们可以在 vue2.x 中体验该特性。 npm install @vue/composition-api # or yarn add @vue/composition-api 在Vue中注册@vue/composition-api // main.js import Vue from 'vue' import CompositionApi from '@vue/composition-api' Vue.use(CompositionApi) 得益于 vue 的插件机制,通过一行简单的代码,我们就给项目添加了Composition API特性。 开始 Composition API给 vue 组件增加了一个名为setup的生命周期函数,它要早于befororeCreate执行,在 vue-next 中是最早执行的生命周期钩子。 注意: 和befororeCreate一样,setup实例还没创建,因此无法使用this。 <template> <!-- 自动展开 --> <div>{{message}}<div> <!-- 也可以手动取值 --> <div>{{message.value}}</div> </template> <script> import { ref } from '@vue/composition-api' export default { setup () { const message = ref('hello vue') return { message, setMessage: (msg) => initialMessage....

August 12, 2020 · 1 min · 175 words · Johnson

GraphQL 接口设计

graphql 是一种用于 API 的查询语言。它提供了一套完整的和易于理解的 API 接口数据描述,给客户端权力去精准查询他们需要的数据,而不用再去实现其他更多的代码,使 API 接口开发变得更简单高效。 最近在用Gatsby开发一版静态博客,感觉和这个框架真是相见恨晚。因为这个框架使用到了 graphql 技术,所以我花了点时间去学习了一下,并且记录了一下学习和思考过程。 本文主要讲解如何理解 graphql 以及基于关系型数据库设计 graphql 的思路。如果需要学习 graphql 基础知识,请移步官方文档 本文包含Nestjs + graphql 的示例项目:https://github.com/YES-Lee/nestjs-graphql-starter 理解 graphql graphql 是一个用于描述数据及其关系的查询语言,官方文档中描述了 graphql 的标准,具体的实现依靠第三方,目前主流的是Apollo提供的解决方案。 graphql 并不关心具体我们是怎么获取数据的,我们只需要提供获取数据的方法(resolver)以及如何组装数据(schema)。类似于 Java 开发过程中的接口设计模式,Graphql 定义了一套标准,我们按照标准实现接口。下面以用户-角色模型举例。 下面的代码定义了两个数据结构,与 JSON 类似,应该很容易理解。它描述了每个类型(type)的名称,以及其包含的属性。属性除了可以是基本类型外,也可以是数组、其他引用类型,从而建立起所有数据模型及相互的关系图。 type Role { name: String note: String } type User { id: Int name: String gender: Int role: Role } 上面代码用于描述Role和User的数据结构,那么我们具体要怎么使用这个东西呢?先从前端的角度来看,可以从官方文档学习到前端的基本使用,请求体中的数据描述和上面定义类型的代码有些许差别,比如我们要查询用户数据: query userInfo { user(id: Int) { id name gender role { name note } } } 从上面的代码可以大概猜测出,如果我们不需要查询role数据是,只需要将其从请求中去掉...

March 25, 2020 · 3 min · 464 words · Johnson