如果想做一个网页端的小钢琴,可能最先想到的都是用很多个video标签,js直接控制这些video的播放和暂停,不过不仅很麻烦要录制每个琴键的声音,多个按键同时按下同时播放的兼容性也很拉
既然确定了不用video,那自然是用AudioContext
创建一个音频上下文了。让我们看一下MDN中怎么介绍AudioContext
:
AudioContext
接口表示由链接在一起的音频模块构建的音频处理图,每个模块由一个AudioNode
表示。音频上下文控制它包含的节点的创建和音频处理或解码的执行。
好嘛,还挺难懂。简单地说,AudioContext
创建了一条无限长的时间轴,时间轴上分布着声音信息(可以理解为频谱),并且不可以停止。我们可以通过时间点改编这些信息,从而控制频率、音色等等。
认识AudioContext
那么就来看看AudioContext
:
构造方法:直接new,接受一个参数,一般不写参数,默认就可以了
注:必须存储音频源。有 3 种主要类型的音频源。
- 振荡器: 用于产生数学计算的声音,今天的钢琴就是用振荡器
- 音频样本: 从各种文件中获取音频
- 音频流: 从网络摄像头或麦克风获取音频
属性:继承BaseAudioContext,里面比较常用的有:
属性 | 意义 |
---|---|
currentTime | 当前时间 |
state | 当前状态 |
desination | 音频播放的扬声器 |
控制音频的部分方法:
属性 | 意义 |
---|---|
AudioContext.close() | 释放AudioContext控制的资源(比如扬声器)可以理解为停止 |
AudioContext.createMediaElementSource() | 控制与标签 |
AudioContext.createMediaStreamSource() | 处理麦克风 |
AudioContext.createMediaStreamDestination() | 处理本地文件 |
AudioContext.createMediaStreamTrackSource() | 跟踪media stream |
AudioContext.getOutputTimestamp() | 返回时间戳 |
AudioContext.resume() | 暂停后的播放 |
AudioContext.suspend() | 暂停(有人翻译做挂起,不过suspend也有暂停的意思,应该更合适一些) |
于是我们可以构建出来一个利用AudioContext
的播放链:
开始播放new AudioContext => 暂停AudioContext.suspend()=> 继续播放AudioContext.resume()=>停止AudioContext.close()
并且用AudioContext.currentTime控制播放时间
获取音频源
我们之前已经提到过,AudioContext
必须要有音频源,接下来有一些定式的操作,获取音频源:
createGain()
AudioContext.createGain()
也是AudioContext
的一个方法,创建一个GainNode,控制音频的总音量
1.绑定扬声器
我们要将他绑定在一个扬声器上。使用connect
方法绑定扬声器,我们就绑定默认扬声器connect(AudioContext.desination)
2.设置音高
直接Gain.value就好了
3.动态修改音高linearRampToValueAtTime()
gain.linearRampToValueAtTime(0.6,AudioContext.currentTime + 0.01)
//在0.01s内从音高从value值变到0.6
这里提一点,linearRampToValueAtTime的参数中第二个是一个时间点,表示从现在这个点到参数中的时间点内修改音高。
综上,我们创建好了一个舒舒服服的AudioContext(似乎并不舒服)
我们获取了一个AudioContext,但只有一个AudioContext有什么用(嫌弃)
我们来给他绑定音频源。因为要做钢琴,肯定是绑定振荡器了
振荡器
振荡器由createOscillator
方法构造AudioContext.createOscillator()
即可创建。
创建完之后,需要连接上文创建的Gain,否则没有音量控制器,不会出声。连接用connect
方法,非常容易:Oscillator.connect(Gain)
振荡器的属性type,代表了音频的波形,有这几个参数:
- sine:默认值,正弦波
- square:方形波
- sawtooth:锯齿波
- triangle:三角波
除此之外的属性frequency.value
代表了声音的频率,确定了波形和频率,即可确定某个声音。
在这里补充一下乐理基础:
- 频率为波形的一个最小重复单元的长度,单位为Hz
- 第一国际音高为机械波440Hz,对应中音la,高音la为440折半,220,低音la为880(这里的高和低特指的是高八度和低八度),不过纯数字算出来的频率会让人听上去音调偏低
- 按照第一国际音高,从低8dao,到高8dao的频率约等于[130,147,165,175,196,220,246,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1047](加粗标出了基准点)
有了频率,我们就能直接获得声音了。让我们先写几个最简单的练练手:
var son1=new AudioContext();
//创建AudioContext
var osc = son1.createOscillator()
//创建音频振荡器
var g = son1.createGain()
//获得音量控制器Gain
osc.connect(g)
//振荡器连接Gain
osc.type = 'sine'
//设置波形
osc.frequency.value = 440
//设置频率为440Hz,即中音la
g.connect(son1.destination)
//连接扬声器
g.gain.value = 1
//初始音高为1
osc.start();
//从时间轴的此时此刻当前开始发生
直接执行这段,会是一个非常刺耳的无限长的laaaaaa—————,我们再加一个截止时间
var stoptime = 1
osc.stop(stoptime);
就只响一秒了。
到这里就已经基本结束了(大概),我们来封装一下:
JS Bin - Collaborative JavaScript Debugging
代码都放这里啦
(对!就是光遇哈哈哈哈哈哈哈)