学习参考来源:
https://zhuanlan.zhihu.com/p/634848804
https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Audio_API
实际效果:
前言
本文内容可结合上面学习参考来源,结合学习。
之前我遇到的需求主要是对人声进行变音处理,以确保无法通过声音识别出是谁说的这个话,保护隐私。通过Web Audio API即可实现该变声效果。
1. 获取Audio上下文
javascript
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
2. 用 audioCtx 与音频源做关联
各个音频源关联方法:
(1) 与audio,vidio标签 或 Audio实例
javascript
const audio = new Audio()
const sourceNode = audioCtx.createMediaElementSource(audio)
(2) 获取麦克风
javascript
const stream = await navigator.mediaDevices
.getUserMedia({ audio: true})
.catch(function (error) {
console.log(error);
});
const sourceNode = audioCtx.createMediaStreamSource(stream)
(3) 使用 缓冲器
javascript
const loadAudioBuffer = async (url) => {
const response = await fetch(url);
const arrayBuffer = await response.arrayBuffer();
return await audioCtx.decodeAudioData(arrayBuffer);
}
// 获取 audioBuffer
loadAudioBuffer(props.url).then((audioBuffer) => {
audioCtx.audioBuffer = audioBuffer;
}).catch((error) => {
console.error("Failed to load audio buffer:", error);
});
// 创建缓冲区
const bufferNode = audioCtx.createBufferSource()
需要注意:缓冲区只能播放一次,所以创建缓冲区逻辑要与播放逻辑放在一起
3. 使用缓冲区后,改写原有音频播放、暂停
声音流添加处理效果就像穿项链一样,一个接一个(引自学习参考来源1)
javascript
// 创建多个不同作用功能的node节点
var analyser = audioCtx.createAnalyser();
var distortion = audioCtx.createWaveShaper();
var gainNode = audioCtx.createGain();
var biquadFilter = audioCtx.createBiquadFilter();
var convolver = audioCtx.createConvolver();
// 将所有节点连接在一起
source = audioCtx.createMediaStreamSource(stream);
source.connect(analyser);
analyser.connect(distortion);
distortion.connect(biquadFilter);
biquadFilter.connect(convolver);
convolver.connect(gainNode);
gainNode.connect(audioCtx.destination);
上述代码是帮助你理解 AudioContext 的处理节点如何添加,不是实现代码。
audioCtx.destination 是 AudioContext 的输出源
播放
javascript
if (flag) {
// 创建缓冲区
bufferNode = audioCtx.createBufferSource()
bufferNode.buffer = audioCtx.audioBuffer;
bufferNode.playbackRate.value = umlautValue;
// 用于修改音量
gainNode = audioCtx.createGain();
bufferNode.connect(gainNode);
gainNode.connect(audioCtx.destination);
gainNode.gain.value = audioData.volume / 100;
bufferNode.startTime = audioCtx.currentTime - audioData.currentTime;
bufferNode.start(0, audioData.currentTime);
bufferNode.onended = () => {
audioData.status = "play";
cancelAnimationFrame(animationFrameId);
};
} else {
audio.play()
}
暂停
javascript
if (flag) {
bufferNode.stop();
audioData.currentTime = audioCtx.currentTime - bufferNode.startTime;
cancelAnimationFrame(animationFrameId);
} else {
audio.pause()
}
4. 使用 requestAnimationFrame 代替 timeupdate 获取实时信息
javascript
const updateCurrentTime = () => {
if (bufferNode && bufferNode.playbackState === bufferNode.PLAYING_STATE) {
audioData.currentTime = audioCtx.currentTime - bufferNode.startTime;
audioData.currentValue = audioData.currentTime / audioData.duration * 100 * umlautValue;
audioData.currentFormat = `${moment.utc(audioData.currentTime * 1000 * umlautValue).format("mm:ss")} / ${moment.utc(audioData.duration * 1000).format("mm:ss")}`;
}
animationFrameId = requestAnimationFrame(updateCurrentTime);
}
在需要关闭的地方
javascript
cancelAnimationFrame(animationFrameId);
5. 音频变音核心逻辑
本文走的是 变速变调 的路子,改变声音播放速率情况下,音调音色也会随着改变,例如玩过磁带的都知道,按快进功能会使声音变尖提高音调,慢放功能使声音变粗,降低音调。
为了让音频进度条与 加速或减速 的速率保持一致,需创建个变量做统一管理
javascript
const umlautValue = 1.5
6. 结尾
在销毁前记得销毁这些节点
javascript
if (bufferNode) {
bufferNode.stop();
}
if (gainNode) {
gainNode.disconnect();
}
cancelAnimationFrame(animationFrameId);
喜欢的话不妨点个小小的赞与关注,您的赞与关注将是我源源不断的前进动力。