hello 大家好,我是 superZidan,这篇文章想跟大家聊聊 `基于 React Flow 与 Web Audio API
今天我们来学习通过 React Flow 和 Web Audio API 来创建一个可交互的语音广场。我们将会从最小的场景开始,在学习 React Flow(包括:状态管理,实现自定义节点,添加交互能力) 之前,我们会先学习 Web Audio API。
这个教程会一步一步地带你完善这个应用,当然你也可以跳过中间的一些步骤。但如果你是一名新手,还是建议你从头到尾按顺序看完。
Web Audio API
=============
让我们来看一些 Web Audio API 。以下的高亮是你需要知道的知识点:
- Web Audio API 提供了许多不同的音频节点,包括:音频源(比如: OscillatorNode 和 MediaElementAudioSourceNode ),音频效果(比如:GainNode, DelayNode , ConvolverNode )输出(比如:AudioDestinationNode)
- 音频节点可以互相连接在一起来形成一个「图」,我们一般称之为「音源处理图」或者「信号图」或者「信号链」
- 音频处理在原生代码中是在一个单独的进程中处理的,这就意味着即使主线程正在忙于处理其他的任务,我们也可以持续进行音频任务处理
- AudioContext 充当音频处理图的大脑。 我们可以使用它来创建新的音频节点并进行暂停或恢复音频处理。
你好,声音
让我们看看这些东西的一些实际应用并构建我们的第一个网络音频应用程序!我们暂时不会做太复杂的事情:我们将制作一个简单的鼠标电子琴。我们将使用 React 来处理这些示例,并使用 vite
来打包和热更新
当然,你也可以使用其他的打包工具比如 parcel 或者 CRA ,也可以使用 Typescript 来替换 Javascript 。为了让应用足够的简单,我们暂时都不使用他们,但是 React Flow 是类型完整的(完全由 Typescript 编写)。
代码语言:shell复制npm create vite@latest
// Project name: audio-hello
// Select a framework: › React
// Select a variant: › JavaScript
Vite 会为我们创建一个简单的 React 应用,但我们可以删掉一些不需要的资源。跳转到 App.jsx
,删掉默认创建的组件内容,创建一个新的 AudioContext 并将我们需要的节点放在一起。我们需要一个 OscillatorNode 来生成一些音调和一个 GainNode 来控制音量。
src/App.jsx
// 创建音频处理图的大脑
const context = new AudioContext();
// 创建一个 oscillator 节点来生成音调
const osc = context.createOscillator();
// 创建一个 gain 节点来控制音量
const amp = context.createGain();
// 通过 gain 节点将 oscillator 的输出传递到扬声器
osc.connect(amp);
amp.connect(context.destination);
// 开始生成这些音调
osc.start();
OSCILLATOR 节点需要启动 不要忘记调用 osc.start ,否则音调不会生成
对于我们的应用程序,我们将跟踪鼠标在屏幕上的位置并使用它来设置 oscillator(振荡器) 节点的音高和 gain(增益)节点的音量。
src/App.jsx
import React from 'react';
const context = new AudioContext();
const osc = context.createOscillator();
const amp = context.createGain();
osc.connect(amp);
amp.connect(context.destination);
osc.start();
const updateValues = (e) => {
const freq = (e.clientX / window.innerWidth) * 1000;
const gain = e.clientY / window.innerHeight;
osc.frequency.value = freq;
amp.gain.value = gain;
};
export default function App() {
return <div style={{ width: '100vw', height: '100vh' }} onMouseMove={updateValues} />;
}
osc.frequency.value
amp.gain.value
Web Audio API 区分简单对象属性和音频节点参数。 这种区别以AudioParam
的形式出现。 你可以在 MDN 文档中阅读它们,但现在只需要知道使用 .value 来设置 AudioParam 的值而不是直接为属性分配值就足够了。
如果你现在尝试使用我们的应用,你会发现什么事情都没有发生。AudioContext 一直处于挂起的状态下启动,这样可以避免广告劫持我们的扬声器。我们可以在 <div>
元素上添加一个点击事件,判断如果当前 AudioContext 处于挂起状态就恢复它,这样就可以快速的修复上述问题。
const toggleAudio = () => {
if (context.state === 'suspended') {
context.resume();
} else {
context.suspend();
}
};
export default function App() {
return (
<div ...
onClick={toggleAudio}
/>
);
};
这就是我们开始使用 Web Audio API 制作声音所需的一切内容,让我们再整理一下代码,让它的可读性更高一点
src/App.jsx
import { useState } from 'react'
import './App.css'
const context = new AudioContext();
const osc = context.createOscillator();
const amp = context.createGain();
osc.connect(amp);
amp.connect(context.destination);
osc.start();
const updateValues = (e) => {
const freq = (e.clientX / window.innerWidth) * 1000;
const gain = e.clientY / window.innerHeight;
osc.frequency.value = freq;
amp.gain.value = gain;
};
export default function App() {
const [ isRunning, setIsRunning ] = useState(false)
const toggleAudio = () => {
if (context.state === 'suspended') {
context.resume();
setIsRunning(true)
} else {
context.suspend();
setIsRunning(false)
}
};
return <div
style={{ width: '100vw', height: '100vh' }}
onMouseMove={updateValues} >
<button onClick={toggleAudio}>{isRunning ? '