cd80c085ff
统一前后端 SFX Prompt Unicode canonicalization、字符计数和 2048 边界 固化 52 个音效预设、追加规则及跨语言测试向量 演进共享音频请求响应、duration、Loop 和 V2 metadata 契约 增加 External model 输入矩阵纯函数并保持正式接线归属 T5 补齐前端提交、读取、深拷贝与请求边界回归测试 更新共享计划并标记 T1 完成且当前不可发布
424 lines
10 KiB
TypeScript
424 lines
10 KiB
TypeScript
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}`;
|
||
}
|