ChiptuneSynth 完整参考指南 — 基于 Web Audio API 的 4 轨芯片音乐合成器
// 1. Include the library
<script src="chiptune-synth.js"></script>
// 2. Create and initialize
const synth = new ChiptuneSynth();
await synth.init();
// 3. Play a note
synth.playNoteByName('C', 4, 0, 0.5);
// 4. Or play a sound effect
synth.playPreset('coin');
// 5. Load an instrument
synth.loadInstrument('violin', 0);
synth.playNoteByName('A', 4, 0, 1.0); init()`。创建一个包含 4 个音轨(主音、低音、鼓、效果音)、默认包络以及空的颤音设置的新合成器实例。
const synth = new ChiptuneSynth(); 初始化 Web Audio API 上下文,创建主增益、分析器、各音轨增益节点以及噪声缓冲区。必须在任何播放操作之前调用一次。
await synth.init(); 停止所有音符并关闭 AudioContext。当不再使用该合成器时调用此方法。
在指定音轨上播放频率为 (Hz) 的音符。
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| frequency | 数字 | — | 频率(单位:赫兹)(例如:A4 为 440) |
| trackIndex | 数字 | 0 | 音轨索引:0=主音,1=贝斯,2=鼓,3=效果 |
| 时长 | 数字 | 10 | 时长(单位:秒)。值≥10将生成持续音 |
| startTime | 数字 | 现在 | AudioContext 开始时间(用于调度) |
const noteId = synth.playNote(440, 0, 0.5); // A4, Lead, 0.5s stopNote()` 手动控制延音音符,请使用 `duration = 10` 或更高版本。键盘和 MIDI 即是通过此方式工作的。根据音名播放音符。
| 参数 | 类型 | 默认 | 描述 |
|---|---|---|---|
| 音符 | 字符串 | — | 音符名称:C、C#、D、D#、E、F、F#、G、G#、A、A#、B(也包括 Db、Eb、Gb、Ab、Bb) |
| 八度 | 数字 | 4 | 八度 (1-7) |
| 轨道索引 | 编号 | 0 | 目标曲目 |
| 时长 | 编号 | 10 | 时长(秒) |
synth.playNoteByName('C', 4, 0, 0.5); // Middle C on Lead
synth.playNoteByName('Eb', 3, 1, 1.0); // Eb3 on Bass 停止特定音符及其释放包络。请使用playNote()或playNoteByName()返回的noteId。
立即停止所有音轨中所有正在播放的音符。
通过一次调用加载并播放内置的 SFX 预设。非常适合游戏音效。
synth.playPreset('coin');
synth.playPreset('explosion');
synth.playPreset('jump'); | 名称 | 描述 | 使用场景 |
|---|---|---|
| 激光 | 向下射出的电击光束 | 射击、光束 |
| 硬币 | 上升的明亮叮声 | 收集物品 |
| 跳跃 | 快速上升扫射 | 跳跃、弹跳 |
| 爆炸 | 噪音爆发衰减 | 毁灭、死亡 |
| 能量提升 | 上升扫荡号角 | 强化道具、升级 |
| 击中 | 尖锐的撞击声 | 伤害、碰撞 |
| 哔 | 短促的UI点击声 | 菜单选择、UI |
| 低音 | 深沉的三角铁重击 | 低音下沉、冲击 |
| 射击 | 快速电击射击 | 连发武器 |
| 1UP | 升腾的号角 | 额外生命、奖励 |
将预设的配置(波形、包络、效果)加载到音轨上,但不播放。适用于在手动触发音符之前设置音轨。
将完整的乐器预设加载到指定音轨,配置波形、包络、颤音和调制。
synth.loadInstrument('violin', 0);
synth.playNoteByName('A', 4, 0, 2.0); | 名称 | 类型 | 描述 |
|---|---|---|
| 钢琴 | 三角铁 | 明亮的打击乐音色,衰减迅速 |
| 小提琴 | 锯齿波 | 温暖的弓弦音色,带有颤音 |
| 大提琴 | 锯齿波 | 低音区深沉的弓弦声 |
| 长笛 | 正弦波 | 柔和的、带有颤音的呼吸音色 |
| 管风琴 | 方波 | 长音管风琴音色 |
| 铜管 | 锯齿波 | 粗犷的号角/小号音色 |
| 口琴 | 方波 | 带颤音的簧片PWM音色 |
| 合成器主音 | 方波 | 经典芯片音乐主音 |
| 合成器垫音 | 锯齿波 | 宽广的齐奏垫音(带滤波器) |
| 合成贝斯 | 锯齿波 | 强劲的合成贝斯 |
| 马林巴琴 | 正弦波 | 打击乐槌击音色 |
| 电吉他 | 方波 | 过载吉他模拟 |
更新音轨的一个或多个属性。更改将实时应用于当前正在播放的音符。
synth.updateTrack(0, {
type: 'sawtooth',
volume: 0.4,
unisonVoices: 4,
unisonDetune: 20
}); | 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| type | 字符串 | '方波' | 波形:'方波'、'三角波'、'锯齿波'、'正弦波'、'噪声' |
| 音量 | 数字 | 0.3 | 音量 (0-1) |
| 占空比 | 数值 | 0.5 | 方波脉冲宽度 (0-1) |
| 失谐 | 数值 | 0 | 以分音为单位的微调(1200 = 1 个八度) |
| 八度偏移 | 数字 | 0 | 八度偏移 (±) |
| 半音偏移 | 数字 | 0 | 半音偏移 (±) |
| 音高包络 | 数字 | 0 | 音高包络深度(半音) |
| 滑音 | 数值 | 0 | 滑音时间(秒) |
您还可以直接设置轨道对象的属性:
synth.tracks[0].type = 'sawtooth';
synth.tracks[0].volume = 0.4;
synth.updateLiveNotes(0); // Apply to active notes 设置音轨的幅度包络。控制音符的渐入、保持和渐出效果。
synth.updateEnvelope(0, {
attack: 0.1, // Fade-in time (seconds)
decay: 0.2, // Decay to sustain level
sustain: 0.6, // Held level (0-1)
release: 0.5 // Fade-out after note stops
}); | 属性 | 类型 | 范围 | 描述 |
|---|---|---|---|
| attack | 数值 | 0-5秒 | 达到最大音量所需时间 |
| 衰减 | 数值 | 0-5秒 | 从峰值降至持续音量所需的时间 |
| sustain | 数值 | 0-1 | 音符保持期间的音量级别(0 = 衰减后关闭) |
| 释放 | 数值 | 0-10 秒 | 音符停止后的淡出时间 |
| 音轨 | 起音 | 衰减 | 延音 | 释放 |
|---|---|---|---|---|
| 0 - 主音 | 0.01 | 0.10 | 0.7 | 0.20 |
| 1 - 低音 | 0.01 | 0.20 | 0.8 | 0.15 |
| 2 - 鼓 | 0.001 | 0.10 | 0.0 | 0.05 |
| 3 - 音效 | 0.005 | 0.30 | 0.0 | 0.20 |
配置音轨的音高调制(颤音)。
synth.updateVibrato(0, {
rate: 5, // Speed in Hz
depth: 12 // Amount in cents
}); | 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| 速率 | 数字 | 0 | LFO 速度(单位:Hz)(0 = 关闭) |
| 深度 | 数值 | 0 | 音高偏移(以分表示)(0 = 关闭) |
每条音轨都配有一个可选滤波器,用于塑造声音的频率成分。
synth.tracks[0].filterEnabled = true;
synth.tracks[0].filterType = 'lowpass';
synth.tracks[0].filterCutoff = 2000;
synth.tracks[0].filterQ = 5;
synth.updateLiveNotes(0); | 属性 | 类型 | 默认 | 描述 |
|---|---|---|---|
| filterEnabled | 布尔值 | false | 启用/禁用过滤器 |
| filterType | 字符串 | '低通' | '低通', '高通', '带通', '陷波' |
| 滤波器截止频率 | 数字 | 20000 | 截止频率(单位:Hz) |
| filterQ | 数字 | 0.1 | 共振 / Q 因子 |
| 滤波器键轨 | 数字 | 0 | 键跟踪 0-100(截止频率随音高变化) |
| 滤波器包络量 | 数字 | 0 | 滤波器包络深度(半音) |
| 滤波器包络起音 | 数字 | 0.01 | 滤波器包络攻击时间(秒) |
| filterEnvRelease | 数值 | 0.2 | 滤波器包络释放(秒) |
叠加多个失谐振荡器,营造厚实、宽广的声音。对于垫音和主奏音色至关重要。
synth.tracks[0].unisonVoices = 8;
synth.tracks[0].unisonDetune = 25; // cents between voices
synth.tracks[0].unisonSpread = 80; // stereo spread % | 属性 | 类型 | 默认 | 描述 |
|---|---|---|---|
| unisonVoices | 数字 | 1 | 振荡器数量 (1-16) |
| unisonDetune | 数字 | 0 | 音高偏移范围(以分音为单位) |
| 齐奏音程 | 数字 | 0 | 立体声声像范围(0-100%) |
synth.tracks[0].tremoloRate = 6; // Hz
synth.tracks[0].tremoloDepth = 50; // 0-100 synth.tracks[0].filterEnabled = true;
synth.tracks[0].filterCutoff = 1000;
synth.tracks[0].lfoFilterRate = 3; // Hz
synth.tracks[0].lfoFilterDepth = 2000; // Hz range | 属性 | 类型 | 描述 |
|---|---|---|
| tremoloRate | 数字 | 颤音速度(单位:Hz)(0 = 关闭) |
| tremoloDepth | 数字 | 颤音量(0-100) |
| lfoFilterRate | 数字 | 滤波器 LFO 速度(单位:Hz)(0 = 关闭) |
| lfoFilterDepth | 数字 | 滤波器 LFO 范围(单位:Hz) |
启用来自连接控制器的 MIDI 输入。返回一个检测到的 MIDI 输入设备名称数组。
const inputs = await synth.enableMIDI({
track: 0,
channel: 0, // 0 = all channels
onConnect: (name) => console.log('Connected:', name),
onDisconnect: (name) => console.log('Disconnected:', name),
onNoteOn: (note, vel, ch) => { /* MIDI note 0-127 */ },
onNoteOff: (note, ch) => { /* ... */ },
onCC: (cc, value, ch) => { /* CC 0-127 */ }
}); | 属性 | 类型 | 默认 | 描述 |
|---|---|---|---|
| track | 编号 | 0 | MIDI 音符的目标音轨 |
| 通道 | 编号 | 0 | MIDI 通道过滤器(1-16,0 = 全部) |
| onConnect | onConnect | — | 当 MIDI 设备连接时调用 |
| onDisconnect | onDisconnect | — | 当 MIDI 设备断开连接时调用 |
| onNoteOn | 函数 | — | 在音符开启时调用 (note, velocity, channel) |
| onNoteOff | 函数 | — | 在音符关闭时调用 (note, channel) |
| onCC | onNoteOff | — | 在控制变化时调用 (cc, value, channel) |
| CC | 名称 | 效果 |
|---|---|---|
| 1 | 调制轮 | 控制颤音深度 |
| 7 | 音量 | 控制音轨音量 |
| 64 | 延音踏板 | 延音踏板 |
| 120 | 全部静音 | 停止所有音符 |
| 123 | 关闭所有音符 | 停止所有音符 |
禁用 MIDI 输入并停止所有正在发声的 MIDI 音符。
更改接收 MIDI 输入的轨道。
设置 MIDI 通道过滤器(0 表示所有通道)。
返回当前 MIDI 是否处于活动状态。
返回用于绘制示波器样式可视化的时域波形数据。值范围为 0-255(128 = 中心)。
function draw() {
const data = synth.getWaveformData();
// Draw on canvas...
ctx.beginPath();
data.forEach((v, i) => {
const x = (i / data.length) * width;
const y = (v / 255) * height;
i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
});
ctx.stroke();
requestAnimationFrame(draw);
} 返回用于频谱可视化的时域(FFT)数据。数值范围为 0-255。
设置主输出音量(0-1)。
返回当前主音量。
将所有音轨、包络和颤音重置为默认值。
将音符名称和八度音转换为以赫兹为单位的频率。
ChiptuneSynth.noteToFrequency('A', 4); // → 440
ChiptuneSynth.noteToFrequency('C', 4); // → 261.63 将 MIDI 音符编号(0-127)转换为频率(单位:Hz)。
将频率转换为最接近的 MIDI 音符编号。
返回一个包含所有可用 SFX 预设名称的数组。
ChiptuneSynth.getPresetNames();
// → ['laser','coin','jump','explosion','powerup','hit','blip','bass','shoot','1up'] 返回一个包含所有可用乐器预设名称的数组。
返回一个以乐器名称为键的对象,每个值包含完整的定义(音轨设置、包络、颤音、滤波器等)。
const instruments = ChiptuneSynth.getInstruments();
// { piano: { name, label, icon, type, volume, ... }, violin: { ... }, ... }
// Access a specific instrument definition
const piano = instruments.piano;
console.log(piano.type); // 'triangle'
console.log(piano.label); // 'Piano' 您可以通过组合音轨设置、包络、颤音和滤波器来制作自己的乐器。以下是从零开始构建声音的方法。
手动配置音轨,然后在其上演奏音符:
// Create a retro pluck bass
synth.updateTrack(1, {
type: 'sawtooth',
volume: 0.5,
octaveOffset: -1
});
synth.updateEnvelope(1, {
attack: 0.005,
decay: 0.3,
sustain: 0.0,
release: 0.1
});
synth.playNoteByName('E', 2, 1, 0.4); 复现预设模式——配置音轨、弹奏音符,其余工作由包络线自动处理:
// Custom "warp" SFX on FX track (3)
function playWarp() {
synth.updateTrack(3, {
type: 'square',
volume: 0.3,
dutyCycle: 0.25,
pitchEnv: -24 // pitch drops 2 octaves
});
synth.updateEnvelope(3, {
attack: 0.01,
decay: 0.4,
sustain: 0.0,
release: 0.1
});
synth.playNote(880, 3, 0.5);
}
// Now call playWarp() anytime! 结合多种功能,打造富有表现力的乐器:
// Dreamy synth pad with filter sweep
function setupDreamPad(track) {
synth.updateTrack(track, {
type: 'square',
volume: 0.25,
dutyCycle: 0.3,
unisonVoices: 6,
unisonDetune: 18,
unisonSpread: 70
});
synth.updateEnvelope(track, {
attack: 0.8,
decay: 0.5,
sustain: 0.6,
release: 2.0
});
synth.updateVibrato(track, {
rate: 4,
depth: 8
});
// Add filter with LFO modulation
synth.tracks[track].filterEnabled = true;
synth.tracks[track].filterType = 'lowpass';
synth.tracks[track].filterCutoff = 1200;
synth.tracks[track].filterQ = 3;
synth.tracks[track].lfoFilterRate = 0.3;
synth.tracks[track].lfoFilterDepth = 800;
}
setupDreamPad(0);
synth.playNoteByName('C', 4, 0, 3.0); 叠加多条音轨以生成复杂音色:
// Layered "epic hit" — noise + bass + lead
function playEpicHit() {
// Layer 1: noise crash
synth.updateTrack(2, { type: 'noise', volume: 0.4 });
synth.updateEnvelope(2, { attack:0.001, decay:0.3, sustain:0, release:0.1 });
synth.playNote(200, 2, 0.4);
// Layer 2: sub bass thump
synth.updateTrack(1, { type: 'sine', volume: 0.6, pitchEnv: 12 });
synth.updateEnvelope(1, { attack:0.001, decay:0.25, sustain:0, release:0.1 });
synth.playNote(60, 1, 0.3);
// Layer 3: bright accent
synth.updateTrack(0, { type: 'square', volume: 0.2 });
synth.updateEnvelope(0, { attack:0.001, decay:0.1, sustain:0, release:0.2 });
synth.playNote(440, 0, 0.15);
} 将自定义乐器组织为配置对象:
// Define instrument configs
const myInstruments = {
retroLead: {
track: { type: 'square', dutyCycle: 0.25, volume: 0.3 },
env: { attack: 0.02, decay: 0.15, sustain: 0.5, release: 0.3 },
vib: { rate: 5.5, depth: 10 }
},
fatBass: {
track: { type: 'sawtooth', volume: 0.5, unisonVoices: 3, unisonDetune: 10 },
env: { attack: 0.01, decay: 0.2, sustain: 0.7, release: 0.1 },
vib: { rate: 0, depth: 0 }
}
};
// Apply an instrument to a track
function applyInstrument(name, trackIndex) {
const inst = myInstruments[name];
synth.updateTrack(trackIndex, inst.track);
synth.updateEnvelope(trackIndex, inst.env);
synth.updateVibrato(trackIndex, inst.vib);
}
applyInstrument('retroLead', 0);
synth.playNoteByName('C', 5, 0, 0.5); | 音轨 | 索引 | 波形 | 音量 |
|---|---|---|---|
| 主音 | 0 | 方形 | 0.30 |
| 低音 | 1 | 三角铁 | 0.40 |
| 鼓 | 2 | 噪音 | 0.50 |
| 音效 | 3 | 锯齿波 | 0.25 |
每个音符的gainNode仅处理ADSR包络(归一化0→1→保持)。音轨音量由独立的_trackGains[]节点控制。这种架构允许实时更改参数,同时不干扰正在播放音符的ADSR进程。