目的:通过对 audio 和 audioContext 的使用,加深对音频的处理
- 使用 标签播放音频
- 使用 AudioContext 对象播放音频
- <进阶>通过 AudioContext 对音频进行精细化处理:失真、滤波,变调
- <进阶>通过 AudioContext.createBuffer()生成一段音频
使用 audio 标签播放音频
使用 audio 标签播放音乐, 加载音频文件可以通过直接在标签上的 src 写好,
或通过 audio.setAttribute(“src”,"./love105.mp3");此方法来设置
<audio id="audio" src="./love105.mp3"></audio>
<button onclick="play()">play</button>
<button onclick="suspend()">suspend</button>
<script>
var audio = document.getElementById("audio");
function play() {
audio.play();
}
function suspend() {
audio.pause();
}
</script>
限制,chrome 已经不支持自动播放音频,规定只有用户与页面交互之后,比如点击了页面任意地方之后才会自动播放。而自动播放的需求非常常见,因此需要设计师根据场景去设计交互,交互之后进行音频得播放
示例:每秒检测是否已经开始播放,没有那么一直检测下去,直到播放。
在示例中,你会发现一直检测,一直报错,直到你与页面交互(点击)之后,那么播放限制解除,才会真正开始播放。
<audio id="audio" src="./love105.mp3" auto loop></audio>
<button onclick="play()">play</button>
<button onclick="suspend()">suspend</button>
<script>
var audio = document.getElementById("audio");
var isPlaying = false;
checkIsPlaying();
function checkIsPlaying() {
setTimeout(() => {
if (!isPlaying) {
console.log("检测中,未播放");
play();
checkIsPlaying();
}
}, 1000);
}
// 使用异步确保开始播放
async function play() {
await audio.play();
isPlaying = true;
console.log("开始播放");
}
function suspend() {
audio.pause();
}
</script>
使用 audioContext 对象播放音频
使用 audioContext 播放也并不复杂,主要需要理解这一个方法 audioContext.createBufferSource()方法.
可以将音频视作
竹子
,一段完整得音频,由一个又一个节点相连而成。
audioContext 上有一个方法
var source = ctx.createBufferSource()
用于生成音频头部节点.
audioContext 上有一个属性叫做ctx.destination
,标识音频尾节点.
source 上的一个方法source.connet()
,可以对头尾进行链接source.connet(ctx.destination)
,
接下来,头尾链接,就成为了一根完整的竹子,也就是一个完整的音频。
<button onclick="playAudio()">playAudio</button>
<button onclick="resumeAudio()">resumeAudio</button>
<button onclick="stopAudio()">stopAudio</button>
<script>
var ctx = new (window.AudioContext || window.webkitAudioContext())();
let source = ctx.createBufferSource(); // 创建音频源头姐点
// 播放
async function playAudio() {
const audioBuffer = await loadAudio();
playSound(audioBuffer);
}
// 暂停
async function resumeAudio() {
if (ctx.state === "running") {
ctx.suspend();
} else if (ctx.state === "suspended") {
ctx.resume();
}
}
// 停止
async function stopAudio() {
source.stop();
}
async function loadAudio() {
const audioUrl = "love105.mp3";
const res = await fetch(audioUrl);
const arrayBuffer = await res.arrayBuffer(); // byte array字节数组
const audioBuffer = await ctx.decodeAudioData(arrayBuffer, function(
decodeData
) {
return decodeData;
});
return audioBuffer;
}
async function playSound(audioBuffer) {
source.buffer = audioBuffer; // 设置数据
source.loop = true; //设置,循环播放
source.connect(ctx.destination); // 头尾相连
// 可以对音频做任何控制
source.start(0); //立即播放
}
</script>
使用 audioContext 对象,对音频进行细化处理
以下内容非专业音乐人士或感兴趣者可以跳过了.
以下内容用到了这几个 API
var gainNode = ctx.createGain();。音量控制节点,控制音量变大变小
var biquadFilter = ctx.createBiquadFilter();。滤波节点,过滤指定频段波形
var distortion = ctx.createWaveShaper();。非线性失真节点,又称之为畸变节点
当我们挺一段音频(音乐)时,调子有时很高,譬如歌曲<青藏高原>,有时又很低<慢慢>,这在音频上时如何标识的呢?我们使用一段频谱均匀的音频来演示
- 生成一段音频,生成一段最简单的白噪音音频,也就是很熟悉的电视雪花音
在上面 audioContext 代码的基础上添加一个按钮和一个方法
async function playSound(audioBuffer) {
var myArrayBuffer = getDefineBuffer(); // 数据源使用刚生成的白噪音
source.buffer = myArrayBuffer;
source.loop = true; //设置,循环播放
source.connect(ctx.destination); // 头尾相连
// 可以对音频做任何控制
source.start(0); //立即播放
}
function getDefineBuffer() {
// 立体声
var channels = 2;
// 创建一个 采样率与音频环境(AudioContext)相同的 时长5秒的 音频片段。
var frameCount = ctx.sampleRate * 5.0; // ctx.sampleRate:缓存区内,PCM数据每秒钟的采样率
var myArrayBuffer = ctx.createBuffer(channels, frameCount, ctx.sampleRate);
// 使用白噪声填充;就是 -1.0 到 1.0 之间的随机数
for (var channel = 0; channel < channels; channel++) {
// 向myArrayBuffer的频道中填充数据。
// 此方法myArrayBuffer.getChannelData()设计的与常见理解相反,nowBuffering内一旦填充了数据,myArrayBuffer内部评到也就填充了
var nowBuffering = myArrayBuffer.getChannelData(channel);
for (var i = 0; i < frameCount; i++) {
nowBuffering[i] = Math.random() * 2 - 1;
}
}
return myArrayBuffer;
}
音频细化处理方法
- 细化处理之 对音频进行音量设置
<script>
var ctx = new (window.AudioContext || window.webkitAudioContext())();
let source = ctx.createBufferSource();
let gainNode = ctx.createGain(); // 创建音量控制节点
addEventListenerlGainNode();
async function playSound(audioBuffer) {
var myArrayBuffer = getDefineBuffer(); // 数据源使用刚生成的白噪音
source.buffer = myArrayBuffer;
source.loop = true; //循环播放
// 音频头 ->链接到 ->音量控制节点
// 音量控制节点 ->链接到 -> 音频尾
source.connect(gainNode);
gainNode.connect(ctx.destination);
source.start(0); //立即播放
}
function addEventListenerlGainNode() {
var max = window.innerHeight;
document.addEventListener("mousemove", function(event) {
var y = event.clientY; //取得纵坐标
var volume = (y / max).toFixed(2);
gainNode.gain.value = volume;
});
}
</script>
- 细化处理之 对音频进行滤波处理
在上面 audioContext 代码的基础上创建一个低频滤波器,并连接到头尾节点
滤波器,默认是含有默认滤波处理值得,所以一旦链接,再播放音频,能明显听出声音有所变化。
<script>
var ctx = new (window.AudioContext || window.webkitAudioContext())();
let source = ctx.createBufferSource();
let gainNode = ctx.createGain();
let biquadFilter = ctx.createBiquadFilter(); // 创建低频滤波器, 返回 BiquadFilterNode。
async function playSound(audioBuffer) {
var myArrayBuffer = getDefineBuffer();
source.buffer = myArrayBuffer;
source.loop = true;
// 音频头 ->链接到 ->音量控制节点
// 音量控制节点 ->链接到 ->滤波器
// 滤波器 ->链接到 ->音频尾
source.connect(gainNode);
gainNode.connect(biquadFilter);
biquadFilter.connect(ctx.destination);
source.start(0);
}
</script>
- 细化处理之 对音频进行失真
在上面代码的基础上创建一个双二阶滤波器,并连接到头尾节点.
添加一个点击按钮,点击则让音频失真, makeDistortionCurve()
细心得同学发现了,使用 connect()得时候,只需要向首尾得中间添加各种控制节点,就可以对音频进行处理并返回
<button onclick="controlDistortion()">失真</button>
<script>
var ctx = new (window.AudioContext || window.webkitAudioContext())();
let source = ctx.createBufferSource();
let gainNode = ctx.createGain();
let biquadFilter = ctx.createBiquadFilter();
let distortion = ctx.createWaveShaper(); // 创建双二阶滤波器,返回 WaveShaperNode.常用于添加失真效果
async function playSound(audioBuffer) {
var myArrayBuffer = getDefineBuffer();
source.buffer = myArrayBuffer;
source.loop = true;
// 音频头 ->链接到 ->滤波器
// 滤波器 ->链接到 ->失真器
// 失真器 ->链接到 ->音频尾
source.connect(gainNode);
gainNode.connect(biquadFilter);
biquadFilter.connect(distortion);
distortion.connect(ctx.destination);
source.start(0); //立即播放
}
let controlDistortion = function(value) {
distortion.curve = makeDistortionCurve(400);
};
// 失真
function makeDistortionCurve(amount) {
var k = typeof amount === "number" ? amount : 50,
n_samples = 44100,
curve = new Float32Array(n_samples),
deg = Math.PI / 180,
i = 0,
x;
for (; i < n_samples; ++i) {
x = (i * 2) / n_samples - 1;
curve[i] = ((3 + k) * x * 20 * deg) / (Math.PI + k * Math.abs(x));
}
return curve;
}
</script>
- 细化处理之 对音频进行混音,也就是常见得大厅效果,厕所,客厅等场景效果
混音比较特殊,能够创造距离,空间感
<button onclick="controlConvolver()">混音</button>
<script>
var ctx = new (window.AudioContext || window.webkitAudioContext())();
let source = ctx.createBufferSource();
let gainNode = ctx.createGain();
let biquadFilter = ctx.createBiquadFilter();
let distortion = ctx.createWaveShaper();
var convolver = ctx.createConvolver(); // 创建一个ConvolverNode,通常用来应用混响效果
// 添加混响
let controlConvolver = function (value) {
biquadFilter.disconnect(0);
biquadFilter.connect(convolver)
}
async function playSound(audioBuffer) {
var myArrayBuffer = getDefineBuffer(); // 数据源使用刚生成的白噪音
source.buffer = myArrayBuffer;
// 混音节点必须要设置音频数据!!!
convolver.buffer = myArrayBuffer;
source.loop = true; //循环播放
// 节点顺序改变
source.connect(distortion);
distortion.connect(biquadFilter);
biquadFilter.connect(gainNode);
// 节点顺序会音响混音效果
convolver.connect(gainNode);
gainNode.connect(ctx.destination);
source.start(0); //立即播放
}
</script>
扩展
音响分类为:1.0,2.0,2.1, 5.1, 7.1,其实就是混音得区别。所以只有电影院才能真正感受到环绕音,电影院设置5个以上喇叭环绕在观众周围是标准配置,家里没有那么多喇叭。
- 1.0 单声道 :单声道
- 2.0 双声道 :left, right。 左前,右前 各一个喇叭
- 2.1 立体音 : left, right。 在 2.0 得基础上加了一个低音喇叭
- 5.1 环绕音 : left, right,left around, right around,left behind,right behind 。 在 2.1 得基础上细分了一下,左前,右前,左侧,右侧 ,低音喇叭 共 5 喇叭放声音
- 7.1 环绕音 : left, right,left around, right around,left behind,right behind .再 5.1 得基础上细分了一下,左前,右前,左侧,右侧,左后,右后 ,低音喇叭 共 7 喇叭放声音
参考资料
MDN AudioBuffer
失真
voice-change-o-matic
voice-change-o-matic srouce
------ 如果文章对你有用,感谢右上角 >>>点赞 | 收藏 <<<