跳转到内容

效果与淡入淡出

本指南演示如何使用 nodesfadeplay 模块实现音频效果和淡入淡出过渡。

演示

音源

使用的模块

  • nodes: 音频节点工厂函数(createGaincreatePannerchain 等)
  • fade: 淡入/淡出和交叉淡入淡出工具
  • play: 核心播放引擎(通过 through 选项路由效果链)

代码示例

1. 音量控制

使用 createGain() 创建 GainNode,并通过 play() 函数的 through 选项进行路由。使用 rampGain() 可以平滑地改变音量,不会产生爆音。

import { WaaPlayer } from "waa-play";
const waa = new WaaPlayer();
await waa.ensureRunning();
const buffer = await waa.load("/audio/track.mp3");
const gain = waa.createGain(0.5); // Initial volume 50%
const playback = waa.play(buffer, { through: [gain] });
// Ramp to 100% volume over 3 seconds
waa.rampGain(gain, 1.0, 3);

当您将 GainNode 传递给 through 选项时,play() 会自动将音源连接到增益节点,然后再连接到目标。

函数 API 版
import { createContext, ensureRunning } from "waa-play/context";
import { loadBuffer } from "waa-play/buffer";
import { createGain, rampGain } from "waa-play/nodes";
import { play } from "waa-play/play";
const ctx = createContext();
await ensureRunning(ctx);
const buffer = await loadBuffer(ctx, "/audio/track.mp3");
const gain = createGain(ctx, 0.5);
const playback = play(ctx, buffer, { through: [gain] });
rampGain(gain, 1.0, 3);

2. 立体声声像

使用 createPanner() 创建 StereoPannerNode 以控制左右声像。值的范围从 -1(左)到 1(右)。

import { WaaPlayer } from "waa-play";
const waa = new WaaPlayer();
await waa.ensureRunning();
const buffer = await waa.load("/audio/track.mp3");
const panner = waa.createPanner(0); // Center
const playback = waa.play(buffer, { through: [panner] });
// Pan left
panner.pan.value = -1;
// Pan right
panner.pan.value = 1;
// Back to center
panner.pan.value = 0;
函数 API 版
import { createContext, ensureRunning } from "waa-play/context";
import { loadBuffer } from "waa-play/buffer";
import { createPanner } from "waa-play/nodes";
import { play } from "waa-play/play";
const ctx = createContext();
await ensureRunning(ctx);
const buffer = await loadBuffer(ctx, "/audio/track.mp3");
const panner = createPanner(ctx, 0);
const playback = play(ctx, buffer, { through: [panner] });
panner.pan.value = -1; // Left

3. 效果链

组合多个效果以实现丰富的声音处理。将节点数组传递给 through 以自动将它们链接在一起。

import { WaaPlayer } from "waa-play";
const waa = new WaaPlayer();
await waa.ensureRunning();
const buffer = await waa.load("/audio/track.mp3");
// Build effect chain
const gain = waa.createGain(0.8);
const filter = waa.createFilter({ type: "lowpass", frequency: 1000 });
const compressor = waa.createCompressor();
const panner = waa.createPanner(0.5);
// Auto-chain: gain → filter → compressor → panner → destination
const playback = waa.play(buffer, {
through: [gain, filter, compressor, panner],
});
// Adjust filter frequency
filter.frequency.value = 500;

使用 through 选项时,play() 会自动连接节点,因此不需要 chain()。在 play() 之外构建节点图时使用 chain()

函数 API 版

使用 chain() 显式构建节点图的示例:

import { createContext, ensureRunning } from "waa-play/context";
import { loadBuffer } from "waa-play/buffer";
import {
createGain,
createFilter,
createCompressor,
createPanner,
chain,
} from "waa-play/nodes";
import { play } from "waa-play/play";
const ctx = createContext();
await ensureRunning(ctx);
const buffer = await loadBuffer(ctx, "/audio/track.mp3");
const gain = createGain(ctx, 0.8);
const filter = createFilter(ctx, { type: "lowpass", frequency: 1000 });
const compressor = createCompressor(ctx);
const panner = createPanner(ctx, 0.5);
// Explicitly connect with chain()
chain(gain, filter, compressor, panner);
const playback = play(ctx, buffer, { through: [gain] });

4. 淡入/淡出

使用 fadeIn()fadeOut() 实现平滑的淡入淡出效果。autoFade() 可以在播放开始和结束时自动淡入淡出。

import { WaaPlayer } from "waa-play";
const waa = new WaaPlayer();
await waa.ensureRunning();
const buffer = await waa.load("/audio/track.mp3");
const gain = waa.createGain(0); // Start at 0 volume
const playback = waa.play(buffer, { through: [gain] });
// Fade in over 2 seconds (equal-power curve)
waa.fadeIn(gain, 1, { duration: 2, curve: "equal-power" });
// Fade out over 2 seconds
setTimeout(() => {
waa.fadeOut(gain, { duration: 2 });
}, 5000);

使用 autoFade() 可以自动设置播放开始和结束时的淡入淡出。

import { WaaPlayer } from "waa-play";
const waa = new WaaPlayer();
await waa.ensureRunning();
const buffer = await waa.load("/audio/track.mp3");
const gain = waa.createGain(0);
const playback = waa.play(buffer, { through: [gain] });
// Fade in 1s at start, fade out 2s over the last 2 seconds
waa.autoFade(playback, gain, { fadeIn: 1, fadeOut: 2 });
函数 API 版
import { createContext, ensureRunning } from "waa-play/context";
import { loadBuffer } from "waa-play/buffer";
import { createGain } from "waa-play/nodes";
import { play } from "waa-play/play";
import { fadeIn, fadeOut, autoFade } from "waa-play/fade";
const ctx = createContext();
await ensureRunning(ctx);
const buffer = await loadBuffer(ctx, "/audio/track.mp3");
const gain = createGain(ctx, 0);
const playback = play(ctx, buffer, { through: [gain] });
// Fade in
fadeIn(gain, 1, { duration: 2, curve: "equal-power" });
// Auto fade
autoFade(playback, gain, { fadeIn: 1, fadeOut: 2 });

5. DJ 交叉淡入淡出

在两个音轨之间交叉淡入淡出,实现无缝的 DJ 风格过渡。

import { WaaPlayer } from "waa-play";
const waa = new WaaPlayer();
await waa.ensureRunning();
const bufferA = await waa.load("/audio/track-a.mp3");
const bufferB = await waa.load("/audio/track-b.mp3");
// Track A (full volume)
const gainA = waa.createGain(1);
const playbackA = waa.play(bufferA, { through: [gainA], loop: true });
// Track B (muted)
const gainB = waa.createGain(0);
const playbackB = waa.play(bufferB, { through: [gainB], loop: true });
// Crossfade from track A to track B over 3 seconds
setTimeout(() => {
waa.crossfade(gainA, gainB, { duration: 3, curve: "equal-power" });
}, 5000);

crossfade() 同时将一个 GainNode 从 1 淡出到 0,将另一个从 0 淡入到 1。使用 curve: "equal-power" 可以确保在交叉淡入淡出期间音量感知均匀。

函数 API 版
import { createContext, ensureRunning } from "waa-play/context";
import { loadBuffer } from "waa-play/buffer";
import { createGain } from "waa-play/nodes";
import { play } from "waa-play/play";
import { crossfade } from "waa-play/fade";
const ctx = createContext();
await ensureRunning(ctx);
const bufferA = await loadBuffer(ctx, "/audio/track-a.mp3");
const bufferB = await loadBuffer(ctx, "/audio/track-b.mp3");
const gainA = createGain(ctx, 1);
const playbackA = play(ctx, bufferA, { through: [gainA], loop: true });
const gainB = createGain(ctx, 0);
const playbackB = play(ctx, bufferB, { through: [gainB], loop: true });
setTimeout(() => {
crossfade(gainA, gainB, { duration: 3, curve: "equal-power" });
}, 5000);

相关 API