feat(polaris): 《夜市一条街》厨房音效 CC0 真采样——6 事件换公有领域采样(创始人拍夜市采样)
创始人拍夜市 SFX 走 CC0 真采样(烤肉滋滋/收银声真录音比合成贴)。7 个真实世界 foley 事件:6 换核验过的 CC0/公有领域真采样、1(切菜 chop)如实退回合成(无干净 CC0、Commons 搜空/freesound 要 OAuth,不硬凑)。 采样(逐个机读 Wikimedia Commons license 字段核验、下载+定稿两次复核,只收 CC0/PD 弃 CC-BY/CC-BY-SA): 烤肉滋滋 sizzle(PD)/收银钱箱 cash(PD,选硬币落金属钱箱真声)/金币叮当 coin(PD)/下单铃 bell(CC0)/ 顾客满意 applause(PD)/失败差评 buzzer(CC0);curl 直下→ffmpeg 裁≤2s 单声道淡入淡出→mp3 96k,共 106KB。 授权逐文件留痕 assets/audio/sfx/manifest.json+批次二音效CC0采样台账.md(clean-room/备案证据链)。 接线:采样懒加载(首手势后 decodeAudioData 不进首屏)、缺文件/解码失败/node 无 fetch 逐事件回落合成不崩。 证据:回归 logic-smoke 95/tree-check 15/单测 30 零倒退+两本账不破门(总 4.336MB≤10MB/首屏 1.13MB 一字不差、 采样懒加载不计首屏);成本 ¥0。真播听感待 mini-desktop 窗口人耳评。抽象/UI/设计音 20 类保留合成。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cccedd9ac5
commit
d91d857c17
55
docs-design/夜市风格版-批次二/音效CC0采样台账.md
Normal file
55
docs-design/夜市风格版-批次二/音效CC0采样台账.md
Normal file
@ -0,0 +1,55 @@
|
||||
# 音效 CC0 真采样台账 · 批次二(2026-07-07)
|
||||
|
||||
《夜市一条街》的真实世界厨房 foley 从分层合成换成 CC0/公有领域真采样。起因是创始人 2026-07-06 拍定夜市 SFX 走真采样而非合成——烤肉的滋滋声、收银的钱箱声,真录音的质感比振荡器合成贴得多。本批把七个经营内容事件逐个去找干净、可核验授权的真录音,找得到就换采样,找不到就如实退回合成,绝不为了凑齐而糊弄授权。
|
||||
|
||||
机器可读的逐文件授权清单是采样目录里的 `manifest.json`(源页 URL、直链、license、作者、字节、裁剪窗全留痕);本页是给人看的一页数与决策留痕。
|
||||
|
||||
## 采样来源与授权(逐条核验)
|
||||
|
||||
七个目标里六个拿到了干净的 CC0 或公有领域真录音,全部来自 Wikimedia Commons(其 license 字段可经 API 机器核验,`extmetadata.LicenseShortName` + `License` 两栏在下载当刻复核过一次,不是笼统的“免费”)。授权口径只收 CC0 与 Public domain(免授权、免署名);过程中命中的几个 CC-BY / CC-BY-SA 素材(一段现成的 “Cha Ching” 收银机、一段掌声、一个压电蜂鸣器)因为要求署名,一律不取。
|
||||
|
||||
| 音效 | 文件 | 采样内容 | License | 作者 | 源页 | 字节 / 时长 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 烤肉滋滋 | `sizzle.mp3` | 油锅煎炸 | **Public domain** | ezwa | [Frying fries…ogg](https://commons.wikimedia.org/wiki/File:Frying_fries_going_in_and_out_of_oil.ogg) | 22.3 KB / 1.79s |
|
||||
| 收银钱箱 | `cash.mp3` | 硬币落入金属钱箱 | **Public domain** | ezwa | [Coins…metallic moneybox.ogg](https://commons.wikimedia.org/wiki/File:Coins_dropped_in_metallic_moneybox.ogg) | 17.0 KB / 1.35s |
|
||||
| 金币叮当 | `coin.mp3` | 硬币落入木钱箱 | **Public domain** | ezwa | [Coins…wooden moneybox.ogg](https://commons.wikimedia.org/wiki/File:Coins_dropped_in_wooden_moneybox.ogg) | 13.5 KB / 1.05s |
|
||||
| 下单铃 | `bell.mp3` | 自行车铃清脆一响 | **CC0 1.0** | qubodup | [Bell-ring.flac](https://commons.wikimedia.org/wiki/File:Bell-ring.flac) | 17.8 KB / 1.40s |
|
||||
| 顾客满意 | `applause.mp3` | 鼓掌加欢呼 | **Public domain** | starlite | [Clapping hurray.ogg](https://commons.wikimedia.org/wiki/File:Clapping_hurray.ogg) | 24.8 KB / 2.00s |
|
||||
| 失败差评 | `buzzer.mp3` | 蜂鸣器 | **CC0 1.0** | David Pride | [Buzzer.wav](https://commons.wikimedia.org/wiki/File:Buzzer.wav) | 11.0 KB / 0.85s |
|
||||
|
||||
四个来自 pdsounds.org 早年释入公有领域的现场录音(ezwa、starlite),两个来自 freesound 与 Cotswold Jam 的 CC0 释出(qubodup 的自行车铃、David Pride 的蜂鸣器),均经 Wikimedia Commons 转存并保留原始出处。收银用的其实是“硬币落金属钱箱”的真声——比一段合成 cha-ching 更贴夜市小摊那种把零钱往铁盒里一撂的动作,也正好对上工单里“收银 cha-ching/钱箱”的口径。
|
||||
|
||||
每个文件下载后统一走一道加工:curl 直下原始 ogg/flac/wav,ffmpeg 裁到两秒以内的活跃窗口(连续声如煎炸、鼓掌取最响的一段,脉冲声如落币、铃响、蜂鸣取起音到自然衰减),下混单声道 44.1kHz,首尾各加几十毫秒淡入淡出消除裁剪爆音,最后 libmp3lame 96k 编码。六个文件合计 106 KB,加清单 111 KB。
|
||||
|
||||
## 采样 vs 合成的分布
|
||||
|
||||
真实世界 foley 事件换采样,抽象与 UI 反馈音保留合成——后者本就是设计音,没有“真录音更贴”的诉求。
|
||||
|
||||
- **改播采样(6 类事件 → 6 个样本)**:下锅 `order`(下单铃 + 起锅滋滋两层走采样)、收银三档 `cash_low/mid/high`(高档额外叠一层散币叮当)、小费 `tip`、好评 `praise`、差评 `blame`。
|
||||
- **保留合成(切菜 chop,1 类)**:见下节,如实退回。
|
||||
- **本就合成、不在采样范围**:翻台、连击、断链、进货、缺货、解锁号角、升级、研发、雇佣、技能、潮汐、评论家登场、雨声、任务、成就 fanfare、图鉴、UI 点击、弹窗、广告位金光,以及出餐 `serve_ready`。这些是设计音或抽象反馈,合成足够。
|
||||
|
||||
接线上采样是懒加载:首个用户手势解锁 AudioContext 后才 fetch 解码六个样本,不进首屏字节账;解码完成前、文件缺失或加载失败,对应事件自动回落到原有合成声部,一行都没删,缺音不崩的红线不破。node 与 headless 环境没有 fetch 与 AudioContext,整条采样加载跳过,纯逻辑与门的契约不变。
|
||||
|
||||
## 未竟:切菜 chop 退回合成(如实)
|
||||
|
||||
切菜没找到干净的 CC0 真录音,退回合成。Wikimedia Commons 搜遍 chopping / cutting / cleaver / knife-cutting-board 全部落空;freesound 上确有 CC0 的切菜录音,但其原文件下载要 OAuth token,内网凭据档里没有,不硬凑;OpenGameArt 的 CC0 合集要逐层下钻且难以在无法试听的机器上核验音色。唯一现成的公有领域近似物是一段 “Knocking on wood or door”,但它是连续几下敲击、带空心门腔体的共鸣、没有切割的嘶声,在 6c6g 上没法试听确认它读起来像不像“刀落砧板”——按诚实协议,音色不可核验的样本不采。保留的合成 chop 本来就是照刀落砧板建模的(短噪声穿切 + 低频木墩 thock),比一段敲门声更贴,作为这一个音的兜底站得住。
|
||||
|
||||
结论:七个目标,六个真采样、一个合成兜底。
|
||||
|
||||
## 成本
|
||||
|
||||
零采买。六个样本都是 CC0 或公有领域,无授权费、无素材费。开销只有下载与转码的本机算力和时间,没有调用任何付费模型或接口(本批音效线与批次二美术线的 mmx 生图预算无关,不占其额度)。
|
||||
|
||||
## 验收
|
||||
|
||||
| 门 | 结果 |
|
||||
|---|---|
|
||||
| logic-smoke(95 断言,纯逻辑核) | exit 0,全过 |
|
||||
| tree-check(15 静态可达查) | exit 0,全过 |
|
||||
| 单测(30 用例:经济骨架/九门 driver/富三门/M2 系统/M3 注入缝) | 30 pass / 0 fail / 0 skipped |
|
||||
| 构建 + 两本包体账 | build exit 0;账一总 4.336 MB ≤ 10 MB(过),首屏 1.13 MB ≤ 2 MB(过) |
|
||||
|
||||
采样落在 `assets/audio/` 下,构建记账里本就归 BGM 同级的懒加载项、不计首屏,所以首屏字节与改动前一字不差(1.13 MB),总账只多了这 111 KB。三个纯逻辑门与单测都不碰音效模块,零倒退。
|
||||
|
||||
浏览器里真实的 decodeAudioData 播放与听感未在本机验证——6c6g 禁 chrome,音频真播归 mini-desktop 的 CDP 窗口,与 M4 换装的九门回归一并排。样本文件本身由 ffmpeg 编码、ffprobe 复核为合法 MP3,路径解析沿用 Phaser 既有的相对 `assets/…` 惯例(M2 CDP 已 ACCEPT 过同款路径),接线正确性有据。
|
||||
BIN
tier2/games/night-market/assets/audio/sfx/applause.mp3
Normal file
BIN
tier2/games/night-market/assets/audio/sfx/applause.mp3
Normal file
Binary file not shown.
BIN
tier2/games/night-market/assets/audio/sfx/bell.mp3
Normal file
BIN
tier2/games/night-market/assets/audio/sfx/bell.mp3
Normal file
Binary file not shown.
BIN
tier2/games/night-market/assets/audio/sfx/buzzer.mp3
Normal file
BIN
tier2/games/night-market/assets/audio/sfx/buzzer.mp3
Normal file
Binary file not shown.
BIN
tier2/games/night-market/assets/audio/sfx/cash.mp3
Normal file
BIN
tier2/games/night-market/assets/audio/sfx/cash.mp3
Normal file
Binary file not shown.
BIN
tier2/games/night-market/assets/audio/sfx/coin.mp3
Normal file
BIN
tier2/games/night-market/assets/audio/sfx/coin.mp3
Normal file
Binary file not shown.
59
tier2/games/night-market/assets/audio/sfx/manifest.json
Normal file
59
tier2/games/night-market/assets/audio/sfx/manifest.json
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"_说明": "《夜市一条街》真实世界 foley 事件的 CC0/公有领域真采样清单(逐个记 source URL + license,授权干净留痕红线)。抽象/UI 反馈音(点击/弹层/连击/解锁号角等)仍走 src/audio/sfx.js 分层合成,不在此清单。采样懒加载(首个用户手势后 fetch+decodeAudioData),缺采样回退合成。",
|
||||
"_生成": { "日期": "2026-07-07", "工序": "curl 直下(Wikimedia upload)→ ffmpeg trim ≤2s + 单声道 44.1k + 首尾淡入淡出 → libmp3lame 96k", "口径": "只收 CC0 或 Public domain(免授权免署名);CC-BY / CC-BY-SA 一律不取(不满足免署名)" },
|
||||
"samples": [
|
||||
{
|
||||
"key": "sizzle", "file": "sizzle.mp3", "音效": "烤肉滋滋(起锅煎炸)", "用于事件": ["order 下锅第三层"],
|
||||
"license": "Public domain (PD)", "licenseCode": "pd", "author": "ezwa",
|
||||
"sourcePage": "https://commons.wikimedia.org/wiki/File:Frying_fries_going_in_and_out_of_oil.ogg",
|
||||
"downloadUrl": "https://upload.wikimedia.org/wikipedia/commons/4/4e/Frying_fries_going_in_and_out_of_oil.ogg",
|
||||
"origin": "pdsounds.org/audio/download/150 → Wikimedia Commons",
|
||||
"trim": "源 2.0–3.8s(取活跃煎炸窗)", "durationS": 1.79, "bytes": 22301
|
||||
},
|
||||
{
|
||||
"key": "cash", "file": "cash.mp3", "音效": "收银/钱箱(硬币入金属钱箱)", "用于事件": ["cash_low", "cash_mid", "cash_high"],
|
||||
"license": "Public domain (PD)", "licenseCode": "pd", "author": "ezwa",
|
||||
"sourcePage": "https://commons.wikimedia.org/wiki/File:Coins_dropped_in_metallic_moneybox.ogg",
|
||||
"downloadUrl": "https://upload.wikimedia.org/wikipedia/commons/2/2d/Coins_dropped_in_metallic_moneybox.ogg",
|
||||
"origin": "pdsounds.org/audio/download/135 → Wikimedia Commons",
|
||||
"trim": "源 0.0–1.35s", "durationS": 1.35, "bytes": 16972
|
||||
},
|
||||
{
|
||||
"key": "coin", "file": "coin.mp3", "音效": "金币叮当(硬币入木钱箱)", "用于事件": ["tip", "cash_high 散币层"],
|
||||
"license": "Public domain (PD)", "licenseCode": "pd", "author": "ezwa",
|
||||
"sourcePage": "https://commons.wikimedia.org/wiki/File:Coins_dropped_in_wooden_moneybox.ogg",
|
||||
"downloadUrl": "https://upload.wikimedia.org/wikipedia/commons/c/c1/Coins_dropped_in_wooden_moneybox.ogg",
|
||||
"origin": "pdsounds.org/audio/download/136 → Wikimedia Commons",
|
||||
"trim": "源 0.28–1.33s(去前导静音)", "durationS": 1.05, "bytes": 13524
|
||||
},
|
||||
{
|
||||
"key": "bell", "file": "bell.mp3", "音效": "下单铃(自行车铃清脆一响)", "用于事件": ["order 下单铃层"],
|
||||
"license": "CC0 1.0", "licenseCode": "cc0", "author": "qubodup",
|
||||
"sourcePage": "https://commons.wikimedia.org/wiki/File:Bell-ring.flac",
|
||||
"downloadUrl": "https://upload.wikimedia.org/wikipedia/commons/b/b2/Bell-ring.flac",
|
||||
"origin": "freesound.org/people/qubodup/sounds/168709 → Wikimedia Commons",
|
||||
"trim": "源 0.0–1.4s(响+自然衰减)", "durationS": 1.4, "bytes": 17761
|
||||
},
|
||||
{
|
||||
"key": "applause", "file": "applause.mp3", "音效": "顾客满意(鼓掌+欢呼)", "用于事件": ["praise 好评欢呼"],
|
||||
"license": "Public domain (PD)", "licenseCode": "pd", "author": "starlite",
|
||||
"sourcePage": "https://commons.wikimedia.org/wiki/File:Clapping_hurray.ogg",
|
||||
"downloadUrl": "https://upload.wikimedia.org/wikipedia/commons/a/a8/Clapping_hurray.ogg",
|
||||
"origin": "pdsounds.org/audio/download/38 → Wikimedia Commons",
|
||||
"trim": "源 1.8–3.8s(密集鼓掌窗)", "durationS": 2.0, "bytes": 24809
|
||||
},
|
||||
{
|
||||
"key": "buzzer", "file": "buzzer.mp3", "音效": "失败/差评(蜂鸣器)", "用于事件": ["blame 差评"],
|
||||
"license": "CC0 1.0", "licenseCode": "cc0", "author": "David Pride",
|
||||
"sourcePage": "https://commons.wikimedia.org/wiki/File:Buzzer.wav",
|
||||
"downloadUrl": "https://upload.wikimedia.org/wikipedia/commons/5/55/Buzzer.wav",
|
||||
"origin": "cotswoldjam.org(David Pride)→ Wikimedia Commons",
|
||||
"trim": "源 0.0–0.85s", "durationS": 0.85, "bytes": 11016
|
||||
}
|
||||
],
|
||||
"fallbackSynth": [
|
||||
{
|
||||
"音效": "切菜 chop", "退回原因": "Wikimedia Commons 无真实切菜/刀落砧板录音(chopping/cutting/cleaver/knife-cutting-board 全空);freesound CC0 需 OAuth token(内网凭据档无);OpenGameArt CC0 合集需逐层下钻且难眼验。仅有 PD『Knocking on wood or door』(敲门/敲木)——为多下连敲、空心门腔体、无切割嘶声,不能听验其读作『刀落砧板』,按诚实协议不采不可核验音色的样本。保留 src/audio/sfx.js 既有合成 chop(短噪声穿切+低频木墩 thock,本就贴刀落砧板)。"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
tier2/games/night-market/assets/audio/sfx/sizzle.mp3
Normal file
BIN
tier2/games/night-market/assets/audio/sfx/sizzle.mp3
Normal file
Binary file not shown.
@ -1,13 +1,17 @@
|
||||
/**
|
||||
* src/audio/sfx.js —— 音效系统(M4 内容化:分层 WebAudio foley 合成,零二进制资产)
|
||||
* src/audio/sfx.js —— 音效系统(M4 内容化:CC0/PD 真采样 + 分层 WebAudio 合成兜底)
|
||||
* owner:北极星顶级线①(W-POLARIS)· 设计书 §6 音效 24 类
|
||||
*
|
||||
* 【为什么自著分层合成而非 CC0 采样】设计书要求内容贴合音效(烤肉滋滋/切菜/下单铃/收银/顾客满意…),
|
||||
* 旧实现是 ZzFX 单振荡器芯片音(几十年前 8-bit 味,不贴内容)。CC0 采样评估结论:保证 CC0 的源
|
||||
* (Kenney)偏 UI 蜂鸣、缺真实厨房 foley;真实 foley 源(freesound)需逐条 OAuth + 逐条授权核验,
|
||||
* 触「零外采 = CC0 授权干净留痕」红线。故按设计书 fallback 走「分层 WebAudio 合成(噪声+滤波+包络
|
||||
* 近 foley,非单振荡器)」:自著 = 零授权风险、零二进制字节(不吃包体账)、延迟最低、headless 静默。
|
||||
* 【合成分层】每个音由多路声部叠加:滤波噪声(滋滋/切菜质感)+ 非谐分音钟(下单/收银铃)+
|
||||
* 【采样 vs 合成分工】创始人 2026-07-06 拍定:真实世界厨房 foley 走 CC0 真采样(真录音比合成贴)。
|
||||
* 6 类经营内容事件改播 assets/audio/sfx/ 下的 CC0/公有领域真采样(清单 + 逐条授权见同目录 manifest.json):
|
||||
* 烤肉滋滋 sizzle(order)/ 收银钱箱 cash(cash_*)/ 金币叮当 coin(tip)/ 下单铃 bell(order)/
|
||||
* 顾客满意掌声 applause(praise)/ 差评蜂鸣 buzzer(blame)。切菜 chop 未找到干净 CC0 真录音
|
||||
* (Commons 无切菜录音、freesound 需 OAuth token、见 manifest.fallbackSynth)→ 退回合成;抽象/UI
|
||||
* 反馈音(点击/弹层/连击/解锁号角/成就 fanfare 等)本是设计音、无"真采样更贴"诉求,全部保留合成。
|
||||
* 【采样懒加载】首个用户手势(initAudio)后 fetch + decodeAudioData 六个样本,非首屏(不进首屏字节账,
|
||||
* assets/audio 在构建 §5 记账里本就归懒加载);解码前/文件缺失/加载失败 → 该事件 playBuf 返 false
|
||||
* 回退合成,绝不阻塞可玩。node/headless 无 fetch/AudioContext,采样加载整体跳过,契约不变。
|
||||
* 【合成兜底分层】每音由多路声部叠加:滤波噪声(滋滋/切菜质感)+ 非谐分音钟(下单/收银铃)+
|
||||
* 包络塑形(上升=正反馈/下降=负反馈)。总线经压缩器防叠加削波。
|
||||
* 【手势解锁】浏览器策略要求 AudioContext 在用户手势后 resume;initAudio() 挂首个 pointerdown。
|
||||
* 【静默降级】无 WebAudio(headless/门)或未解锁时 play 静默返回,绝不抛错阻塞可玩(缺音不崩红线)。
|
||||
@ -39,10 +43,55 @@ export function initAudio() {
|
||||
for (let i = 0; i < d.length; i++) d[i] = Math.random() * 2 - 1;
|
||||
}
|
||||
if (X.state === 'suspended') X.resume();
|
||||
loadSamples(); // 手势解锁后懒加载 CC0 真采样(非首屏;失败静默回退合成)
|
||||
unlocked = true;
|
||||
} catch (_) { /* 解锁失败:静默降级,不阻塞 */ }
|
||||
}
|
||||
|
||||
// ── CC0/PD 真采样(懒加载;缺样本回退合成;逐条授权见 assets/audio/sfx/manifest.json)────────────
|
||||
const SAMPLE_FILES = Object.freeze({
|
||||
sizzle: 'sizzle.mp3', // 烤肉滋滋(PD · ezwa)
|
||||
cash: 'cash.mp3', // 收银/钱箱(PD · ezwa)
|
||||
coin: 'coin.mp3', // 金币叮当(PD · ezwa)
|
||||
bell: 'bell.mp3', // 下单铃(CC0 · qubodup)
|
||||
applause: 'applause.mp3', // 顾客满意/掌声(PD · starlite)
|
||||
buzzer: 'buzzer.mp3', // 差评蜂鸣(CC0 · David Pride)
|
||||
});
|
||||
// 各样本相对增益:样本近满幅,而合成 peak 刻意压低(0.1–0.28);此处把样本拉回同量级(再过 master 0.34 + 压缩器)
|
||||
const SAMPLE_GAIN = Object.freeze({ sizzle: 0.85, cash: 0.42, coin: 0.25, bell: 0.35, applause: 0.22, buzzer: 0.24 });
|
||||
const buffers = Object.create(null); // key → 解码后 AudioBuffer 缓存
|
||||
let samplesKicked = false;
|
||||
|
||||
/** 懒加载六个真采样(首个手势后触发,非首屏);缺文件/解码失败该键静默回退合成——外部资源加载失败不阻塞。 */
|
||||
function loadSamples() {
|
||||
if (samplesKicked || !X || typeof fetch === 'undefined') return; // 已加载过 / node·headless 无 fetch:整体跳过
|
||||
samplesKicked = true;
|
||||
const base = 'assets/audio/sfx/'; // 相对文档根(index.html 同 Phaser 资源惯例;正式装载随 GamePackage 同源)
|
||||
for (const key of Object.keys(SAMPLE_FILES)) {
|
||||
// 外部资源:网络失败(404/离线)与解码错都 catch 静默,该键 playBuf 返 false → 合成兜底(缺音不崩红线)
|
||||
Promise.resolve()
|
||||
.then(() => fetch(base + SAMPLE_FILES[key]))
|
||||
.then((r) => (r && r.ok ? r.arrayBuffer() : Promise.reject(new Error('sfx http ' + (r && r.status)))))
|
||||
.then((ab) => X.decodeAudioData(ab))
|
||||
.then((buf) => { buffers[key] = buf; })
|
||||
.catch(() => { /* 采样不可用:保持 buffers[key] 缺席 → 合成兜底 */ });
|
||||
}
|
||||
}
|
||||
|
||||
/** 播放一个采样声部(缓冲就绪且成功起播返 true;否则 false → 调用方合成兜底)。t 绝对秒,gainMul 叠加系数,rate 变速。 */
|
||||
function playBuf(key, t, gainMul = 1, rate = 1) {
|
||||
const buf = buffers[key];
|
||||
if (!buf || !X || !master) return false;
|
||||
try {
|
||||
const src = X.createBufferSource(); src.buffer = buf;
|
||||
if (rate !== 1) src.playbackRate.value = rate;
|
||||
const g = X.createGain(); g.gain.value = (SAMPLE_GAIN[key] || 0.5) * gainMul;
|
||||
src.connect(g); g.connect(master);
|
||||
src.start(t);
|
||||
return true;
|
||||
} catch (_) { return false; } // 起播异常:回退合成
|
||||
}
|
||||
|
||||
// ── 声部原语(全部连到 master;t 为绝对起始秒)──────────────────────────────
|
||||
|
||||
/** 带 ADSR 包络的振荡器声部(下单/收银铃、连击 pluck、号角)。 */
|
||||
@ -100,15 +149,19 @@ function coins(t, n = 4, peak = 0.14) {
|
||||
// ── 24 类音效(设计书 §6;每类分层 foley,语义:高频清脆/低频沉/上升正反馈/下降负反馈)──
|
||||
const SFX = Object.freeze({
|
||||
// 下锅 = 下单铃 + 切菜 + 起锅滋滋(核心经营动作,内容三合一)
|
||||
order: (t) => { bell(1050, t, { peak: 0.2, dur: 0.28, partials: [1, 2.4, 4.1] }); chop(t + 0.06, 0.22); sizzle(t + 0.12, 0.34, 0.1); },
|
||||
order: (t) => { // 下单铃(采样→合成)+ 切菜(无 CC0,合成)+ 起锅滋滋(采样→合成)
|
||||
if (!playBuf('bell', t, 0.8)) bell(1050, t, { peak: 0.2, dur: 0.28, partials: [1, 2.4, 4.1] });
|
||||
chop(t + 0.06, 0.22);
|
||||
if (!playBuf('sizzle', t + 0.12)) sizzle(t + 0.12, 0.34, 0.1);
|
||||
},
|
||||
serve_ready: (t) => { chop(t, 0.18); osc('sine', 720, t + 0.04, { a: 0.002, d: 0.09, s: 0, r: 0.05, peak: 0.22 }); }, // 出餐嗒
|
||||
// 收银 cha-ching(两声钟由低到高 + 抽屉机械 clack;客单越高越亮)
|
||||
cash_low: (t) => { bell(560, t, { peak: 0.22, dur: 0.22, partials: [1, 2.7] }); fnoise(t + 0.02, 0.05, { type: 'bandpass', freq: 1400, q: 1.5, peak: 0.12, a: 0.001, r: 0.05 }); },
|
||||
cash_mid: (t) => { bell(620, t, { peak: 0.22, dur: 0.2, partials: [1, 2.7] }); bell(900, t + 0.1, { peak: 0.22, dur: 0.34, partials: [1, 2.76, 5.4] }); fnoise(t + 0.02, 0.05, { type: 'bandpass', freq: 1500, q: 1.4, peak: 0.12, a: 0.001, r: 0.05 }); },
|
||||
cash_high: (t) => { bell(680, t, { peak: 0.22, dur: 0.2, partials: [1, 2.7] }); bell(1020, t + 0.1, { peak: 0.24, dur: 0.4, partials: [1, 2.76, 5.4, 8.9] }); coins(t + 0.18, 4, 0.12); fnoise(t + 0.02, 0.05, { type: 'bandpass', freq: 1600, q: 1.4, peak: 0.12, a: 0.001, r: 0.05 }); },
|
||||
tip: (t) => coins(t, 5, 0.15), // 小费叮当
|
||||
praise: (t) => { [523, 659, 784, 1047].forEach((f, i) => osc('triangle', f, t + i * 0.07, { a: 0.004, d: 0.12, s: 0.1, r: 0.12, peak: 0.2 })); }, // 好评欢呼(上行琶音)
|
||||
blame: (t) => { osc('sawtooth', 240, t, { a: 0.005, d: 0.18, s: 0.2, r: 0.14, peak: 0.16, slideTo: 150 }); fnoise(t, 0.16, { type: 'lowpass', freq: 600, q: 0.7, peak: 0.08 }); }, // 差评低鸣
|
||||
cash_low: (t) => { if (playBuf('cash', t, 0.9)) return; bell(560, t, { peak: 0.22, dur: 0.22, partials: [1, 2.7] }); fnoise(t + 0.02, 0.05, { type: 'bandpass', freq: 1400, q: 1.5, peak: 0.12, a: 0.001, r: 0.05 }); },
|
||||
cash_mid: (t) => { if (playBuf('cash', t, 1)) return; bell(620, t, { peak: 0.22, dur: 0.2, partials: [1, 2.7] }); bell(900, t + 0.1, { peak: 0.22, dur: 0.34, partials: [1, 2.76, 5.4] }); fnoise(t + 0.02, 0.05, { type: 'bandpass', freq: 1500, q: 1.4, peak: 0.12, a: 0.001, r: 0.05 }); },
|
||||
cash_high: (t) => { if (playBuf('cash', t, 1.05)) { playBuf('coin', t + 0.22, 0.7); return; } bell(680, t, { peak: 0.22, dur: 0.2, partials: [1, 2.7] }); bell(1020, t + 0.1, { peak: 0.24, dur: 0.4, partials: [1, 2.76, 5.4, 8.9] }); coins(t + 0.18, 4, 0.12); fnoise(t + 0.02, 0.05, { type: 'bandpass', freq: 1600, q: 1.4, peak: 0.12, a: 0.001, r: 0.05 }); },
|
||||
tip: (t) => { if (!playBuf('coin', t)) coins(t, 5, 0.15); }, // 小费叮当(采样→合成)
|
||||
praise: (t) => { if (playBuf('applause', t)) return; [523, 659, 784, 1047].forEach((f, i) => osc('triangle', f, t + i * 0.07, { a: 0.004, d: 0.12, s: 0.1, r: 0.12, peak: 0.2 })); }, // 好评(采样掌声→合成上行琶音)
|
||||
blame: (t) => { if (playBuf('buzzer', t)) return; osc('sawtooth', 240, t, { a: 0.005, d: 0.18, s: 0.2, r: 0.14, peak: 0.16, slideTo: 150 }); fnoise(t, 0.16, { type: 'lowpass', freq: 600, q: 0.7, peak: 0.08 }); }, // 差评(采样蜂鸣→合成低鸣)
|
||||
leave: (t) => { osc('sine', 420, t, { a: 0.003, d: 0.16, s: 0, r: 0.12, peak: 0.2, slideTo: 180 }); osc('sine', 280, t + 0.08, { a: 0.003, d: 0.14, s: 0, r: 0.12, peak: 0.16, slideTo: 120 }); }, // 翻台走人(下滑)
|
||||
combo: (t) => { osc('triangle', 880, t, { a: 0.002, d: 0.08, s: 0, r: 0.06, peak: 0.22, slideTo: 1200 }); coins(t + 0.02, 2, 0.08); }, // 连击(上跳)
|
||||
combo_break: (t) => osc('sine', 520, t, { a: 0.004, d: 0.14, s: 0, r: 0.1, peak: 0.18, slideTo: 300 }), // 断链
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user