实现 SFX V2 T1 契约与提示词基础

统一前后端 SFX Prompt Unicode canonicalization、字符计数和 2048 边界
固化 52 个音效预设、追加规则及跨语言测试向量
演进共享音频请求响应、duration、Loop 和 V2 metadata 契约
增加 External model 输入矩阵纯函数并保持正式接线归属 T5
补齐前端提交、读取、深拷贝与请求边界回归测试
更新共享计划并标记 T1 完成且当前不可发布
This commit is contained in:
2026-08-06 13:05:48 +00:00
parent 6fd731f56c
commit cd80c085ff
24 changed files with 1559 additions and 97 deletions
@@ -0,0 +1,423 @@
import { canonicalizeSoundEffectPrompt } from './ImageCanvasSoundEffectPromptModel';
export type SoundEffectPromptPresetCategory = 'event' | 'requirement';
export type SoundEffectPromptPresetGroup =
| 'ui-operation'
| 'pickup-reward'
| 'growth-result'
| 'character-combat'
| 'skill-status'
| 'mechanism-scene-interaction'
| 'style-direction'
| 'feedback-requirement';
export type SoundEffectPromptPreset = {
id: string;
category: SoundEffectPromptPresetCategory;
group: SoundEffectPromptPresetGroup;
label: string;
prompt: string;
};
const UNICODE_PUNCTUATION_CODE_POINT = /^\p{Punctuation}$/u;
const SOUND_EFFECT_PRESET_SEPARATOR = ',';
export const SOUND_EFFECT_PROMPT_PRESET_GROUPS = [
'ui-operation',
'pickup-reward',
'growth-result',
'character-combat',
'skill-status',
'mechanism-scene-interaction',
'style-direction',
'feedback-requirement',
] as const satisfies readonly SoundEffectPromptPresetGroup[];
/** 权威设计「SFX 生成优化 V2.0」冻结的 40 个事件预设与 12 个补充要求。 */
export const SOUND_EFFECT_PROMPT_PRESETS = [
{
id: 'button-tap',
category: 'event',
group: 'ui-operation',
label: '轻触按钮',
prompt: '柔和的按钮点击声',
},
{
id: 'operation-confirm',
category: 'event',
group: 'ui-operation',
label: '确认操作',
prompt: '明亮的确认提示音',
},
{
id: 'back-cancel',
category: 'event',
group: 'ui-operation',
label: '返回取消',
prompt: '轻微下降的取消提示音',
},
{
id: 'page-transition',
category: 'event',
group: 'ui-operation',
label: '页面切换',
prompt: '快速掠过的界面切换声',
},
{
id: 'notification',
category: 'event',
group: 'ui-operation',
label: '通知提醒',
prompt: '清晰柔和的通知提示音',
},
{
id: 'operation-error',
category: 'event',
group: 'ui-operation',
label: '操作错误',
prompt: '短促克制的错误提示音',
},
{
id: 'coin-pickup',
category: 'event',
group: 'pickup-reward',
label: '金币拾取',
prompt: '金币拾取时清脆的金属叮当声',
},
{
id: 'item-pickup',
category: 'event',
group: 'pickup-reward',
label: '道具拾取',
prompt: '拾取道具时轻快的提示音',
},
{
id: 'reward-received',
category: 'event',
group: 'pickup-reward',
label: '获得奖励',
prompt: '奖励出现时明亮的提示音',
},
{
id: 'chest-open',
category: 'event',
group: 'pickup-reward',
label: '宝箱开启',
prompt: '金属锁扣弹开,随后响起明亮的奖励提示音',
},
{
id: 'content-unlock',
category: 'event',
group: 'pickup-reward',
label: '解锁内容',
prompt: '锁定状态解除,随后响起解锁提示音',
},
{
id: 'rare-drop',
category: 'event',
group: 'pickup-reward',
label: '稀有掉落',
prompt: '稀有物品出现时闪耀的奖励提示音',
},
{
id: 'item-craft',
category: 'event',
group: 'growth-result',
label: '物品合成',
prompt: '两件物品融合,随后响起明亮的完成提示音',
},
{
id: 'character-level-up',
category: 'event',
group: 'growth-result',
label: '角色升级',
prompt: '能量快速上升,随后响起明亮的升级提示音',
},
{
id: 'quest-complete',
category: 'event',
group: 'growth-result',
label: '任务完成',
prompt: '任务完成提示音,随后响起简短的奖励音符',
},
{
id: 'achievement-unlocked',
category: 'event',
group: 'growth-result',
label: '成就达成',
prompt: '明亮的成就提示音,随后响起简短的庆祝音符',
},
{
id: 'challenge-victory',
category: 'event',
group: 'growth-result',
label: '挑战胜利',
prompt: '明亮的胜利提示音,随后响起短暂的庆祝音符',
},
{
id: 'challenge-defeat',
category: 'event',
group: 'growth-result',
label: '挑战失败',
prompt: '低沉的失败提示音',
},
{
id: 'character-jump',
category: 'event',
group: 'character-combat',
label: '角色跳跃',
prompt: '角色轻盈跳起的声音',
},
{
id: 'character-land',
category: 'event',
group: 'character-combat',
label: '角色落地',
prompt: '角色落地时轻微的撞击声',
},
{
id: 'light-hit',
category: 'event',
group: 'character-combat',
label: '轻度受击',
prompt: '轻微撞击的受击声',
},
{
id: 'heavy-hit',
category: 'event',
group: 'character-combat',
label: '重度受击',
prompt: '沉重有力的撞击声',
},
{
id: 'attack-swing',
category: 'event',
group: 'character-combat',
label: '攻击挥动',
prompt: '武器快速挥过空气的呼啸声',
},
{
id: 'attack-hit',
category: 'event',
group: 'character-combat',
label: '攻击命中',
prompt: '武器击中目标的清晰撞击声',
},
{
id: 'block-success',
category: 'event',
group: 'character-combat',
label: '格挡成功',
prompt: '武器碰撞,随后被挡开的金属声',
},
{
id: 'object-break',
category: 'event',
group: 'character-combat',
label: '物体破碎',
prompt: '物体撞击地面后快速破碎的声音',
},
{
id: 'skill-charge',
category: 'event',
group: 'skill-status',
label: '技能蓄力',
prompt: '能量逐渐聚集的低沉嗡鸣声',
},
{
id: 'magic-cast',
category: 'event',
group: 'skill-status',
label: '魔法释放',
prompt: '柔和的魔法能量扩散,带有圆润空灵的闪光声',
},
{
id: 'healing',
category: 'event',
group: 'skill-status',
label: '治疗恢复',
prompt: '柔和能量扩散,带有温暖圆润的提示音',
},
{
id: 'shield-create',
category: 'event',
group: 'skill-status',
label: '护盾生成',
prompt: '能量向外展开,形成稳定的护盾声',
},
{
id: 'teleport',
category: 'event',
group: 'skill-status',
label: '瞬间移动',
prompt: '能量快速收缩,随后以短促的空气抽离声消失',
},
{
id: 'ice-skill',
category: 'event',
group: 'skill-status',
label: '冰冻技能',
prompt: '冰霜能量扩散,随后响起清脆的冻结声',
},
{
id: 'fire-skill',
category: 'event',
group: 'skill-status',
label: '火焰技能',
prompt: '火焰迅速喷发,带有短促的燃烧声',
},
{
id: 'status-buff',
category: 'event',
group: 'skill-status',
label: '状态强化',
prompt: '能量逐渐上升,形成稳定明亮的提示音',
},
{
id: 'door-open',
category: 'event',
group: 'mechanism-scene-interaction',
label: '门开启',
prompt: '门锁解除,随后厚重的木门缓慢打开',
},
{
id: 'mechanism-start',
category: 'event',
group: 'mechanism-scene-interaction',
label: '机关启动',
prompt: '机关解锁,随后齿轮开始转动',
},
{
id: 'lever-trigger',
category: 'event',
group: 'mechanism-scene-interaction',
label: '拉杆触发',
prompt: '拉杆被扳动,随后远处机关启动',
},
{
id: 'stone-move',
category: 'event',
group: 'mechanism-scene-interaction',
label: '石块移动',
prompt: '大型石块缓慢移动时低沉的摩擦声',
},
{
id: 'portal-open',
category: 'event',
group: 'mechanism-scene-interaction',
label: '传送门开启',
prompt: '能量旋转聚集,随后响起持续、空灵的传送门展开声',
},
{
id: 'countdown-warning',
category: 'event',
group: 'mechanism-scene-interaction',
label: '倒计时警告',
prompt: '逐渐加快的倒计时提示音',
},
{
id: 'casual-cute',
category: 'requirement',
group: 'style-direction',
label: '休闲可爱',
prompt: '轻快可爱的卡通风格',
},
{
id: 'retro-arcade',
category: 'requirement',
group: 'style-direction',
label: '复古街机',
prompt: '复古街机风格',
},
{
id: 'sci-fi-electronic',
category: 'requirement',
group: 'style-direction',
label: '科幻电子',
prompt: '干净的科幻电子音色',
},
{
id: 'fantasy-magic',
category: 'requirement',
group: 'style-direction',
label: '奇幻魔法',
prompt: '柔和梦幻的魔法音色',
},
{
id: 'realistic-natural',
category: 'requirement',
group: 'style-direction',
label: '写实自然',
prompt: '自然真实的声音质感',
},
{
id: 'cartoon-exaggerated',
category: 'requirement',
group: 'style-direction',
label: '卡通夸张',
prompt: '夸张鲜明的卡通风格',
},
{
id: 'gentle-feedback',
category: 'requirement',
group: 'feedback-requirement',
label: '轻柔反馈',
prompt: '轻柔克制',
},
{
id: 'strong-feedback',
category: 'requirement',
group: 'feedback-requirement',
label: '有力反馈',
prompt: '更有力的撞击感',
},
{
id: 'short-feedback',
category: 'requirement',
group: 'feedback-requirement',
label: '短促反馈',
prompt: '短促的单次声音',
},
{
id: 'two-stage-rise',
category: 'requirement',
group: 'feedback-requirement',
label: '两段递进',
prompt: '由弱到强的两段变化',
},
{
id: 'clean-prominent',
category: 'requirement',
group: 'feedback-requirement',
label: '干净突出',
prompt: '主体声音清晰,减少杂音',
},
{
id: 'soft-not-harsh',
category: 'requirement',
group: 'feedback-requirement',
label: '柔和不刺耳',
prompt: '圆润柔和,避免尖锐高频',
},
] as const satisfies readonly SoundEffectPromptPreset[];
function readLastCodePoint(value: string) {
const codePoints = Array.from(value);
return codePoints[codePoints.length - 1];
}
export function appendSoundEffectPromptPreset(
currentPrompt: string,
preset: SoundEffectPromptPreset,
) {
const canonicalPrompt = canonicalizeSoundEffectPrompt(currentPrompt);
if (!canonicalPrompt) {
return preset.prompt;
}
const lastCodePoint = readLastCodePoint(canonicalPrompt);
if (lastCodePoint && UNICODE_PUNCTUATION_CODE_POINT.test(lastCodePoint)) {
return `${canonicalPrompt}${preset.prompt}`;
}
return `${canonicalPrompt}${SOUND_EFFECT_PRESET_SEPARATOR}${preset.prompt}`;
}