diff --git a/audio/batch_tts_voxcpm.py b/audio/batch_tts_voxcpm.py index 196ad57..a6f84c8 100644 --- a/audio/batch_tts_voxcpm.py +++ b/audio/batch_tts_voxcpm.py @@ -46,6 +46,10 @@ BASE_URL = f"http://{HOST}:{PORT}" # speed: 语速倍率(通过 ffmpeg atempo 后处理) # ────────────────────────────────────────────────────────────── ROLES = { + "linxia": { + "dir": "linxia", + "speed": 1.00, + }, "mom": { "dir": "mom", "speed": 0.92, @@ -91,7 +95,7 @@ ROLES = { "speed": 1.20, }, "self_3y": { - "dir": "xiaomei", + "dir": "linxia", "speed": 1.15, }, } @@ -219,6 +223,204 @@ LINES = [ # ("lv11_0030_self-3y_voice_01", "self_3y", "(明朗版台词)"), # ("lv11_0030_self-3y_voice_02", "self_3y", "(迷茫版台词)"), # ("lv11_0030_self-3y_voice_03", "self_3y", "(温柔版台词)"), + + # ══════════════════════════════════════════════════════════ + # v2: 林夏回应 + NPC 反应(Round 2 & 3) + # ══════════════════════════════════════════════════════════ + + # ── 01 · 林夏自动回应 ── + ("v2_msg01_linxia_auto", "linxia", "嗯……好。"), + + # ── 02 · 小美 ── + # 林夏回应 + ("v2_msg02_linxia_warm", "linxia", "好啊,明天不见不散。"), + ("v2_msg02_linxia_cold", "linxia", "再说吧。"), + ("v2_msg02_linxia_curious", "linxia", "你怎么知道的啊?"), + ("v2_msg02_linxia_tap", "linxia", "嗯。"), + # 小美反应 + ("v2_msg02_react_warm", "xiaomei", "行!那我叫上安安!老地方见!别放鸽子啊!"), + ("v2_msg02_react_cold", "xiaomei", "……好吧。你要是改主意了随时说。"), + ("v2_msg02_react_curious", "xiaomei", + "还不是阿哲自己发朋友圈!你不知道吗?他发了又秒删……"), + ("v2_msg02_react_silent", "xiaomei", "??你还活着吗哈哈。好了不烦你了,有事找我。"), + ("v2_msg02_react_tap", "xiaomei", "嗯是什么意思啊哈哈!行吧你休息。"), + # Round 3 (追问路径) + ("v2_msg02_r3_linxia_curious", "linxia", "他……发了什么?"), + ("v2_msg02_r3_react", "xiaomei", + "就发了一张你们以前的照片,配了一句'值得'。两分钟就删了。"), + + # ── 03 · 外卖 · 林夏自动回应 ── + ("v2_msg03_linxia_auto", "linxia", "好,我下来拿。"), + + # ── 04 · 阿哲 ── + # 林夏回应 + ("v2_msg04_linxia_warm", "linxia", "你那边……还好吗。"), + ("v2_msg04_linxia_cold", "linxia", "知道了。明天寄。"), + ("v2_msg04_linxia_curious", "linxia", "你怎么不自己来拿。"), + ("v2_msg04_linxia_tap", "linxia", "好。"), + # 阿哲反应 + ("v2_msg04_react_warm", "azhe", "我……挺好的。你呢?"), + ("v2_msg04_react_cold", "azhe", "嗯……谢了。"), + ("v2_msg04_react_curious", "azhe", "我……不太方便过去。"), + ("v2_msg04_react_silent", "azhe", "那……我晚点再说吧。"), + ("v2_msg04_react_tap", "azhe", "谢谢。"), + # Round 3 温暖路径 (阿哲问"你呢?"后) + ("v2_msg04_r3w_linxia_warm", "linxia", "不太好。"), + ("v2_msg04_r3w_linxia_cold", "linxia", "挺好的。"), + ("v2_msg04_r3w_react_warm", "azhe", "……对不起。"), + ("v2_msg04_r3w_react_cold", "azhe", "那就好。"), + ("v2_msg04_r3w_react_silent", "azhe", "……那,晚安。"), + # Round 3 追问路径 (阿哲说"不太方便"后) + ("v2_msg04_r3c_linxia_curious", "linxia", "为什么。"), + ("v2_msg04_r3c_linxia_warm", "linxia", "算了,不问了。"), + ("v2_msg04_r3c_react_curious", "azhe", "我……没什么。你寄吧。"), + + # ── 05 · HR 王姐 ── + # 林夏回应 + ("v2_msg05_linxia_warm", "linxia", "谢谢王姐。"), + ("v2_msg05_linxia_cold", "linxia", "好的,知道了。"), + ("v2_msg05_linxia_curious", "linxia", "王姐……有什么建议吗?"), + ("v2_msg05_linxia_tap", "linxia", "好。"), + # 王姐反应 + ("v2_msg05_react_warm", "hr", "别客气。你还年轻,出去看看也好。这行业你知道的。"), + ("v2_msg05_react_curious", "hr", "你想听真话?别在这行耗了。你有本事,去试试别的。"), + + # ── 06 · 安安 ── + # 林夏回应 + ("v2_msg06_linxia_warm", "linxia", "在哪儿啊?我看看吧。"), + ("v2_msg06_linxia_cold", "linxia", "不了,你们玩好。"), + ("v2_msg06_linxia_curious", "linxia", "你喝了多少了?"), + ("v2_msg06_linxia_tap", "linxia", "嗯。"), + # 安安反应 + ("v2_msg06_react_warm", "anan", "对面那个!我给你留了位置!赶紧赶紧!"), + ("v2_msg06_react_cold", "anan", "好吧!那我给你寄一杯!"), + ("v2_msg06_react_curious", "anan", "才两杯!我清醒得很!……好吧三杯。快来!"), + ("v2_msg06_react_silent", "anan", "喂?信号不好吗?好吧以后再约!"), + ("v2_msg06_react_tap", "anan", "嗯是来还是不来啊!算了回头聊!"), + + # ── 07 · 寝室群 ── + # 林夏回应 + ("v2_msg07_linxia_warm", "linxia", "恭喜恭喜!太厉害了!"), + ("v2_msg07_linxia_cold", "linxia", "恭喜。"), + ("v2_msg07_linxia_curious", "linxia", "怎么升的啊?讲讲!"), + # 群反应 + ("v2_msg07_react_warm", "dorm_a", "哈哈谢谢!我请客!你最近怎么样啊?"), + ("v2_msg07_react_curious", "dorm_a", "说来话长!我们老板走了我就顶上了哈哈"), + ("v2_msg07_react_tap", "dorm_b", "林夏发表情了哈哈"), + # Round 3 温暖路径 (室友A问"你最近怎么样"后) + ("v2_msg07_r3_linxia_warm", "linxia", "挺好的~你请客我去!"), + ("v2_msg07_r3_linxia_cold", "linxia", "还行。"), + ("v2_msg07_r3_react_cold", "dorm_b", "你是不是有事啊?没事可以跟我们说"), + + # ── 09 · 小美(醉)── + # 林夏回应 + ("v2_msg09_linxia_warm", "linxia", "小美……你没事吧。"), + ("v2_msg09_linxia_cold", "linxia", "我知道了。"), + ("v2_msg09_linxia_curious", "linxia", "你……为什么现在才说。"), + ("v2_msg09_linxia_tap", "linxia", "嗯。"), + # 小美反应 + ("v2_msg09_react_warm", "xiaomei_drunk", + "我没事……我就是喝多了想告诉你……你别怪我。"), + ("v2_msg09_react_curious", "xiaomei_drunk", + "因为你今天……我怕你以后恨我……"), + ("v2_msg09_react_tap", "xiaomei_drunk", + "你不生气吗?算了……当我没说。"), + # Round 3 追问路径 (小美说"怕你恨我"后) + ("v2_msg09_r3_linxia_warm", "linxia", "我不恨你。"), + ("v2_msg09_r3_linxia_cold", "linxia", "我得想想。"), + ("v2_msg09_r3_react_warm", "xiaomei_drunk", "谢谢你……谢谢你林夏。"), + ("v2_msg09_r3_react_cold", "xiaomei_drunk", "好……对不起。"), + ("v2_msg09_r3_react_silent_q", "xiaomei_drunk", "你还在吗?"), + + # ── 10 · 妈妈文字 ── + # 林夏回应 + ("v2_msg10_linxia_warm", "linxia", "没有,怎么了?"), + ("v2_msg10_linxia_cold", "linxia", "嗯,要睡了。"), + ("v2_msg10_linxia_curious", "linxia", "妈……你能打电话给我吗?"), + ("v2_msg10_linxia_tap", "linxia", "嗯。"), + # 妈妈反应 + ("v2_msg10_react_warm", "mom", "没事,就是想起来问一下。今天怎么样?"), + ("v2_msg10_react_cold", "mom", "好,早点睡。"), + ("v2_msg10_react_silent", "mom", "看到了回一下。"), + ("v2_msg10_react_tap", "mom", "嗯就是睡了?晚安。"), + # 追问路径:妈妈打来电话 + ("v2_msg10_call_mom", "mom", "怎么了夏夏?你声音不对。"), + ("v2_msg10_call_linxia_warm", "linxia", "没什么……就是有点累。"), + ("v2_msg10_call_linxia_curious", "linxia", "妈,我辞职了。"), + ("v2_msg10_call_react_warm", "mom", "累了就回家歇几天。妈煮了排骨。"), + ("v2_msg10_call_react_curious", "mom", "辞了就辞了。回来再说。"), + ("v2_msg10_call_react_silent", "mom", "不想说就不说。妈在这呢。"), + # 温暖路径 Round 3 (妈妈问"今天怎么样"后) + ("v2_msg10_rw_linxia_warm", "linxia", "还好。"), + ("v2_msg10_rw_linxia_curious", "linxia", "不太好。"), + ("v2_msg10_rw_react_warm", "mom", "那就好。别太晚了。"), + ("v2_msg10_rw_react_curious", "mom", "怎么了?"), + + # ── 11 · 阿哲电话 ── + # 林夏回应 + ("v2_msg11_linxia_warm", "linxia", "可以。但只有5分钟。"), + ("v2_msg11_linxia_cold", "linxia", "不方便。我明天寄给你。"), + ("v2_msg11_linxia_curious", "linxia", "为什么非要今天来?"), + ("v2_msg11_linxia_tap", "linxia", "不了。"), + # 阿哲反应 + ("v2_msg11_react_warm", "azhe", "好,我马上过来。谢谢。"), + ("v2_msg11_react_cold", "azhe", "好吧……那谢了。"), + ("v2_msg11_react_curious", "azhe", "我……明天要出差。就5分钟。"), + ("v2_msg11_react_tap", "azhe", "好。"), + + # ── 12 · 周南 ── + # 林夏回应 + ("v2_msg12_linxia_warm", "linxia", "周南!你还记得我啊!"), + ("v2_msg12_linxia_cold", "linxia", "嗯,你好。"), + ("v2_msg12_linxia_curious", "linxia", "你怎么有我微信的?"), + ("v2_msg12_linxia_tap", "linxia", "嗯……好久不见。"), + # 周南反应 + ("v2_msg12_react_warm", "zhounan", "当然记得。你是我们班最安静的那个。"), + ("v2_msg12_react_cold", "zhounan", "嗯……好久不见。你现在在哪呢?"), + ("v2_msg12_react_curious", "zhounan", + "你忘了吗?毕业那天你加的我。不过你可能早忘了哈哈。"), + ("v2_msg12_react_silent", "zhounan", + "你是不是不记得我了?没关系的。就是突然想说声嗨。"), + ("v2_msg12_react_tap", "zhounan", "是啊,好久不见。"), + + # ── 13 · 周南长语音 ── + # 林夏回应 + ("v2_msg13_linxia_warm", "linxia", "一个人在北京,还好吗?"), + ("v2_msg13_linxia_cold", "linxia", "你讲了好多。"), + ("v2_msg13_linxia_curious", "linxia", "……生日快乐。"), + ("v2_msg13_linxia_tap", "linxia", "嗯,我都听到了。"), + # 周南反应 + ("v2_msg13_react_warm", "zhounan", "还行吧。习惯了。你呢?你在北京过得好吗?"), + ("v2_msg13_react_cold", "zhounan", "是吗?不好意思,话太多了。"), + ("v2_msg13_react_curious", "zhounan", "谢谢你。你是今天第一个跟我说的。"), + ("v2_msg13_react_silent", "zhounan", "不好意思突然说这么多……那,晚安。"), + ("v2_msg13_react_tap", "zhounan", "嗯。谢谢你听我说。"), + # Round 3 温暖路径 (周南问"你在北京过得好吗"后) + ("v2_msg13_r3_linxia_warm", "linxia", "不太好。今天也是我的坏日子。"), + ("v2_msg13_r3_linxia_cold", "linxia", "还行吧。"), + ("v2_msg13_r3_linxia_curious", "linxia", "你为什么突然联系我?"), + ("v2_msg13_r3_react_warm", "zhounan", "那我们算是难兄难妹了。"), + ("v2_msg13_r3_react_cold", "zhounan", "那就好。"), + ("v2_msg13_r3_react_curious", "zhounan", + "因为……今天生日嘛,想起以前的事。想起你。"), + + # ── 14 · 妈妈第二次电话 ── + ("v2_msg14_linxia_auto", "linxia", "嗯,我没事。"), + ("v2_msg14_mom_ask", "mom", "真的没事?"), + ("v2_msg14_react_linked", "mom", "好。那早点睡。妈爱你。"), + ("v2_msg14_react_unlinked", "mom", "你今天怎么都不理妈妈?出什么事了吗?"), + + # ── 结局 E · 周南秒回 ── + ("v2_ending_e_zhounan", "zhounan", "那我下次出差来北京,请你吃饭好不好。"), + + # ── 通用林夏短句(备用) ── + ("v2_linxia_generic_hmm", "linxia", "嗯。"), + ("v2_linxia_generic_ok", "linxia", "好。"), + ("v2_linxia_generic_warm_ok", "linxia", "嗯~好啊。"), + ("v2_linxia_generic_cold_know", "linxia", "知道了。"), + ("v2_linxia_generic_cold_fine", "linxia", "行吧。"), + ("v2_linxia_generic_curious", "linxia", "嗯?"), + ("v2_linxia_generic_then", "linxia", "然后呢?"), ] diff --git a/audio/mp3/tts/v2_ending_e_zhounan_wav_v1.mp3 b/audio/mp3/tts/v2_ending_e_zhounan_wav_v1.mp3 new file mode 100644 index 0000000..fd06467 Binary files /dev/null and b/audio/mp3/tts/v2_ending_e_zhounan_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_linxia_generic_cold_fine_wav_v1.mp3 b/audio/mp3/tts/v2_linxia_generic_cold_fine_wav_v1.mp3 new file mode 100644 index 0000000..540ef98 Binary files /dev/null and b/audio/mp3/tts/v2_linxia_generic_cold_fine_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_linxia_generic_cold_know_wav_v1.mp3 b/audio/mp3/tts/v2_linxia_generic_cold_know_wav_v1.mp3 new file mode 100644 index 0000000..dacadd6 Binary files /dev/null and b/audio/mp3/tts/v2_linxia_generic_cold_know_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_linxia_generic_curious_wav_v1.mp3 b/audio/mp3/tts/v2_linxia_generic_curious_wav_v1.mp3 new file mode 100644 index 0000000..3f493c2 Binary files /dev/null and b/audio/mp3/tts/v2_linxia_generic_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_linxia_generic_hmm_wav_v1.mp3 b/audio/mp3/tts/v2_linxia_generic_hmm_wav_v1.mp3 new file mode 100644 index 0000000..52dc308 Binary files /dev/null and b/audio/mp3/tts/v2_linxia_generic_hmm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_linxia_generic_ok_wav_v1.mp3 b/audio/mp3/tts/v2_linxia_generic_ok_wav_v1.mp3 new file mode 100644 index 0000000..1c72afa Binary files /dev/null and b/audio/mp3/tts/v2_linxia_generic_ok_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_linxia_generic_then_wav_v1.mp3 b/audio/mp3/tts/v2_linxia_generic_then_wav_v1.mp3 new file mode 100644 index 0000000..ca24de4 Binary files /dev/null and b/audio/mp3/tts/v2_linxia_generic_then_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_linxia_generic_warm_ok_wav_v1.mp3 b/audio/mp3/tts/v2_linxia_generic_warm_ok_wav_v1.mp3 new file mode 100644 index 0000000..025341d Binary files /dev/null and b/audio/mp3/tts/v2_linxia_generic_warm_ok_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg01_linxia_auto_wav_v1.mp3 b/audio/mp3/tts/v2_msg01_linxia_auto_wav_v1.mp3 new file mode 100644 index 0000000..6bff4d7 Binary files /dev/null and b/audio/mp3/tts/v2_msg01_linxia_auto_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg02_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg02_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..565e946 Binary files /dev/null and b/audio/mp3/tts/v2_msg02_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg02_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg02_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..ae7209d Binary files /dev/null and b/audio/mp3/tts/v2_msg02_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg02_linxia_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg02_linxia_tap_wav_v1.mp3 new file mode 100644 index 0000000..a8744f1 Binary files /dev/null and b/audio/mp3/tts/v2_msg02_linxia_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg02_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg02_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..e938d20 Binary files /dev/null and b/audio/mp3/tts/v2_msg02_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg02_r3_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg02_r3_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..af2562f Binary files /dev/null and b/audio/mp3/tts/v2_msg02_r3_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg02_r3_react_wav_v1.mp3 b/audio/mp3/tts/v2_msg02_r3_react_wav_v1.mp3 new file mode 100644 index 0000000..7d6111f Binary files /dev/null and b/audio/mp3/tts/v2_msg02_r3_react_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg02_react_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg02_react_cold_wav_v1.mp3 new file mode 100644 index 0000000..c6fb9b9 Binary files /dev/null and b/audio/mp3/tts/v2_msg02_react_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg02_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg02_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..5252455 Binary files /dev/null and b/audio/mp3/tts/v2_msg02_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg02_react_silent_wav_v1.mp3 b/audio/mp3/tts/v2_msg02_react_silent_wav_v1.mp3 new file mode 100644 index 0000000..7ada1da Binary files /dev/null and b/audio/mp3/tts/v2_msg02_react_silent_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg02_react_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg02_react_tap_wav_v1.mp3 new file mode 100644 index 0000000..5e5340e Binary files /dev/null and b/audio/mp3/tts/v2_msg02_react_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg02_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg02_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..1fe42a0 Binary files /dev/null and b/audio/mp3/tts/v2_msg02_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg03_linxia_auto_wav_v1.mp3 b/audio/mp3/tts/v2_msg03_linxia_auto_wav_v1.mp3 new file mode 100644 index 0000000..c6dba14 Binary files /dev/null and b/audio/mp3/tts/v2_msg03_linxia_auto_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..21f8b6e Binary files /dev/null and b/audio/mp3/tts/v2_msg04_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..68ef303 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_linxia_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_linxia_tap_wav_v1.mp3 new file mode 100644 index 0000000..51cf1a1 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_linxia_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..5d7abd1 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_r3c_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_r3c_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..6fe4df5 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_r3c_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_r3c_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_r3c_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..4731530 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_r3c_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_r3c_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_r3c_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..79873c4 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_r3c_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_r3w_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_r3w_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..0c060f5 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_r3w_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_r3w_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_r3w_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..6e6a7b7 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_r3w_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_r3w_react_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_r3w_react_cold_wav_v1.mp3 new file mode 100644 index 0000000..5225052 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_r3w_react_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_r3w_react_silent_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_r3w_react_silent_wav_v1.mp3 new file mode 100644 index 0000000..4578d89 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_r3w_react_silent_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_r3w_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_r3w_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..781d3ba Binary files /dev/null and b/audio/mp3/tts/v2_msg04_r3w_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_react_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_react_cold_wav_v1.mp3 new file mode 100644 index 0000000..bae3d27 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_react_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..e6cf32e Binary files /dev/null and b/audio/mp3/tts/v2_msg04_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_react_silent_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_react_silent_wav_v1.mp3 new file mode 100644 index 0000000..6b16bc2 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_react_silent_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_react_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_react_tap_wav_v1.mp3 new file mode 100644 index 0000000..04579a2 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_react_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg04_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg04_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..af851c6 Binary files /dev/null and b/audio/mp3/tts/v2_msg04_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg05_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg05_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..f026039 Binary files /dev/null and b/audio/mp3/tts/v2_msg05_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg05_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg05_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..f8c12a3 Binary files /dev/null and b/audio/mp3/tts/v2_msg05_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg05_linxia_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg05_linxia_tap_wav_v1.mp3 new file mode 100644 index 0000000..b080932 Binary files /dev/null and b/audio/mp3/tts/v2_msg05_linxia_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg05_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg05_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..ebbca3a Binary files /dev/null and b/audio/mp3/tts/v2_msg05_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg05_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg05_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..9e5be07 Binary files /dev/null and b/audio/mp3/tts/v2_msg05_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg05_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg05_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..f0e09ef Binary files /dev/null and b/audio/mp3/tts/v2_msg05_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg06_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg06_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..fd02310 Binary files /dev/null and b/audio/mp3/tts/v2_msg06_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg06_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg06_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..e765ced Binary files /dev/null and b/audio/mp3/tts/v2_msg06_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg06_linxia_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg06_linxia_tap_wav_v1.mp3 new file mode 100644 index 0000000..f1b2f0c Binary files /dev/null and b/audio/mp3/tts/v2_msg06_linxia_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg06_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg06_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..5c23654 Binary files /dev/null and b/audio/mp3/tts/v2_msg06_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg06_react_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg06_react_cold_wav_v1.mp3 new file mode 100644 index 0000000..2090077 Binary files /dev/null and b/audio/mp3/tts/v2_msg06_react_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg06_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg06_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..f2ff86f Binary files /dev/null and b/audio/mp3/tts/v2_msg06_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg06_react_silent_wav_v1.mp3 b/audio/mp3/tts/v2_msg06_react_silent_wav_v1.mp3 new file mode 100644 index 0000000..64ccf85 Binary files /dev/null and b/audio/mp3/tts/v2_msg06_react_silent_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg06_react_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg06_react_tap_wav_v1.mp3 new file mode 100644 index 0000000..da49124 Binary files /dev/null and b/audio/mp3/tts/v2_msg06_react_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg06_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg06_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..8ab883b Binary files /dev/null and b/audio/mp3/tts/v2_msg06_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg07_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg07_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..4b116bf Binary files /dev/null and b/audio/mp3/tts/v2_msg07_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg07_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg07_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..b0223ff Binary files /dev/null and b/audio/mp3/tts/v2_msg07_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg07_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg07_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..ae7df93 Binary files /dev/null and b/audio/mp3/tts/v2_msg07_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg07_r3_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg07_r3_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..d4fad98 Binary files /dev/null and b/audio/mp3/tts/v2_msg07_r3_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg07_r3_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg07_r3_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..5258771 Binary files /dev/null and b/audio/mp3/tts/v2_msg07_r3_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg07_r3_react_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg07_r3_react_cold_wav_v1.mp3 new file mode 100644 index 0000000..2548a13 Binary files /dev/null and b/audio/mp3/tts/v2_msg07_r3_react_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg07_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg07_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..576916d Binary files /dev/null and b/audio/mp3/tts/v2_msg07_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg07_react_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg07_react_tap_wav_v1.mp3 new file mode 100644 index 0000000..f16245d Binary files /dev/null and b/audio/mp3/tts/v2_msg07_react_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg07_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg07_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..a45c4cb Binary files /dev/null and b/audio/mp3/tts/v2_msg07_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg09_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg09_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..ee85f9e Binary files /dev/null and b/audio/mp3/tts/v2_msg09_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg09_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg09_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..410315c Binary files /dev/null and b/audio/mp3/tts/v2_msg09_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg09_linxia_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg09_linxia_tap_wav_v1.mp3 new file mode 100644 index 0000000..f6c74e5 Binary files /dev/null and b/audio/mp3/tts/v2_msg09_linxia_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg09_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg09_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..c330598 Binary files /dev/null and b/audio/mp3/tts/v2_msg09_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg09_r3_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg09_r3_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..131bbc2 Binary files /dev/null and b/audio/mp3/tts/v2_msg09_r3_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg09_r3_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg09_r3_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..973428f Binary files /dev/null and b/audio/mp3/tts/v2_msg09_r3_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg09_r3_react_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg09_r3_react_cold_wav_v1.mp3 new file mode 100644 index 0000000..9872ee6 Binary files /dev/null and b/audio/mp3/tts/v2_msg09_r3_react_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg09_r3_react_silent_q_wav_v1.mp3 b/audio/mp3/tts/v2_msg09_r3_react_silent_q_wav_v1.mp3 new file mode 100644 index 0000000..572a7d4 Binary files /dev/null and b/audio/mp3/tts/v2_msg09_r3_react_silent_q_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg09_r3_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg09_r3_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..7fe4bd9 Binary files /dev/null and b/audio/mp3/tts/v2_msg09_r3_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg09_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg09_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..477b049 Binary files /dev/null and b/audio/mp3/tts/v2_msg09_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg09_react_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg09_react_tap_wav_v1.mp3 new file mode 100644 index 0000000..52c8199 Binary files /dev/null and b/audio/mp3/tts/v2_msg09_react_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg09_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg09_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..3fa4308 Binary files /dev/null and b/audio/mp3/tts/v2_msg09_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_call_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_call_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..733a0d6 Binary files /dev/null and b/audio/mp3/tts/v2_msg10_call_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_call_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_call_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..15123af Binary files /dev/null and b/audio/mp3/tts/v2_msg10_call_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_call_mom_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_call_mom_wav_v1.mp3 new file mode 100644 index 0000000..f12074a Binary files /dev/null and b/audio/mp3/tts/v2_msg10_call_mom_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_call_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_call_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..4f4addf Binary files /dev/null and b/audio/mp3/tts/v2_msg10_call_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_call_react_silent_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_call_react_silent_wav_v1.mp3 new file mode 100644 index 0000000..c1c81c0 Binary files /dev/null and b/audio/mp3/tts/v2_msg10_call_react_silent_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_call_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_call_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..d5a6546 Binary files /dev/null and b/audio/mp3/tts/v2_msg10_call_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..b4e106e Binary files /dev/null and b/audio/mp3/tts/v2_msg10_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..257a9a7 Binary files /dev/null and b/audio/mp3/tts/v2_msg10_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_linxia_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_linxia_tap_wav_v1.mp3 new file mode 100644 index 0000000..0f4fc88 Binary files /dev/null and b/audio/mp3/tts/v2_msg10_linxia_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..39d9b1e Binary files /dev/null and b/audio/mp3/tts/v2_msg10_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_react_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_react_cold_wav_v1.mp3 new file mode 100644 index 0000000..aafd6cb Binary files /dev/null and b/audio/mp3/tts/v2_msg10_react_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_react_silent_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_react_silent_wav_v1.mp3 new file mode 100644 index 0000000..b8f5030 Binary files /dev/null and b/audio/mp3/tts/v2_msg10_react_silent_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_react_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_react_tap_wav_v1.mp3 new file mode 100644 index 0000000..1aa85e5 Binary files /dev/null and b/audio/mp3/tts/v2_msg10_react_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..bfde6c3 Binary files /dev/null and b/audio/mp3/tts/v2_msg10_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_rw_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_rw_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..e11eb6f Binary files /dev/null and b/audio/mp3/tts/v2_msg10_rw_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_rw_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_rw_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..9eadf19 Binary files /dev/null and b/audio/mp3/tts/v2_msg10_rw_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_rw_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_rw_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..fe23c82 Binary files /dev/null and b/audio/mp3/tts/v2_msg10_rw_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg10_rw_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg10_rw_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..d2271c4 Binary files /dev/null and b/audio/mp3/tts/v2_msg10_rw_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg11_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg11_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..75ccaa7 Binary files /dev/null and b/audio/mp3/tts/v2_msg11_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg11_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg11_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..78aa77c Binary files /dev/null and b/audio/mp3/tts/v2_msg11_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg11_linxia_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg11_linxia_tap_wav_v1.mp3 new file mode 100644 index 0000000..13f5f7b Binary files /dev/null and b/audio/mp3/tts/v2_msg11_linxia_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg11_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg11_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..ea12bbc Binary files /dev/null and b/audio/mp3/tts/v2_msg11_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg11_react_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg11_react_cold_wav_v1.mp3 new file mode 100644 index 0000000..a5778c0 Binary files /dev/null and b/audio/mp3/tts/v2_msg11_react_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg11_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg11_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..4b212ce Binary files /dev/null and b/audio/mp3/tts/v2_msg11_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg11_react_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg11_react_tap_wav_v1.mp3 new file mode 100644 index 0000000..4c327a0 Binary files /dev/null and b/audio/mp3/tts/v2_msg11_react_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg11_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg11_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..ece8cf5 Binary files /dev/null and b/audio/mp3/tts/v2_msg11_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg12_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg12_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..18c10d6 Binary files /dev/null and b/audio/mp3/tts/v2_msg12_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg12_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg12_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..f29f1a2 Binary files /dev/null and b/audio/mp3/tts/v2_msg12_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg12_linxia_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg12_linxia_tap_wav_v1.mp3 new file mode 100644 index 0000000..5bea88e Binary files /dev/null and b/audio/mp3/tts/v2_msg12_linxia_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg12_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg12_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..0d16168 Binary files /dev/null and b/audio/mp3/tts/v2_msg12_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg12_react_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg12_react_cold_wav_v1.mp3 new file mode 100644 index 0000000..68d58da Binary files /dev/null and b/audio/mp3/tts/v2_msg12_react_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg12_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg12_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..342d673 Binary files /dev/null and b/audio/mp3/tts/v2_msg12_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg12_react_silent_wav_v1.mp3 b/audio/mp3/tts/v2_msg12_react_silent_wav_v1.mp3 new file mode 100644 index 0000000..6783135 Binary files /dev/null and b/audio/mp3/tts/v2_msg12_react_silent_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg12_react_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg12_react_tap_wav_v1.mp3 new file mode 100644 index 0000000..f087f81 Binary files /dev/null and b/audio/mp3/tts/v2_msg12_react_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg12_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg12_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..7afb473 Binary files /dev/null and b/audio/mp3/tts/v2_msg12_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..cf6e630 Binary files /dev/null and b/audio/mp3/tts/v2_msg13_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..2ec0a6b Binary files /dev/null and b/audio/mp3/tts/v2_msg13_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_linxia_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_linxia_tap_wav_v1.mp3 new file mode 100644 index 0000000..8d4b9c2 Binary files /dev/null and b/audio/mp3/tts/v2_msg13_linxia_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..e13e783 Binary files /dev/null and b/audio/mp3/tts/v2_msg13_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_r3_linxia_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_r3_linxia_cold_wav_v1.mp3 new file mode 100644 index 0000000..3e9ac35 Binary files /dev/null and b/audio/mp3/tts/v2_msg13_r3_linxia_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_r3_linxia_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_r3_linxia_curious_wav_v1.mp3 new file mode 100644 index 0000000..2538108 Binary files /dev/null and b/audio/mp3/tts/v2_msg13_r3_linxia_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_r3_linxia_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_r3_linxia_warm_wav_v1.mp3 new file mode 100644 index 0000000..e20cd6c Binary files /dev/null and b/audio/mp3/tts/v2_msg13_r3_linxia_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_r3_react_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_r3_react_cold_wav_v1.mp3 new file mode 100644 index 0000000..9a8398b Binary files /dev/null and b/audio/mp3/tts/v2_msg13_r3_react_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_r3_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_r3_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..87a22fd Binary files /dev/null and b/audio/mp3/tts/v2_msg13_r3_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_r3_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_r3_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..5078ff9 Binary files /dev/null and b/audio/mp3/tts/v2_msg13_r3_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_react_cold_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_react_cold_wav_v1.mp3 new file mode 100644 index 0000000..a960e6f Binary files /dev/null and b/audio/mp3/tts/v2_msg13_react_cold_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_react_curious_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_react_curious_wav_v1.mp3 new file mode 100644 index 0000000..59f450f Binary files /dev/null and b/audio/mp3/tts/v2_msg13_react_curious_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_react_silent_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_react_silent_wav_v1.mp3 new file mode 100644 index 0000000..3d119ad Binary files /dev/null and b/audio/mp3/tts/v2_msg13_react_silent_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_react_tap_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_react_tap_wav_v1.mp3 new file mode 100644 index 0000000..f5604cf Binary files /dev/null and b/audio/mp3/tts/v2_msg13_react_tap_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg13_react_warm_wav_v1.mp3 b/audio/mp3/tts/v2_msg13_react_warm_wav_v1.mp3 new file mode 100644 index 0000000..655cb65 Binary files /dev/null and b/audio/mp3/tts/v2_msg13_react_warm_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg14_linxia_auto_wav_v1.mp3 b/audio/mp3/tts/v2_msg14_linxia_auto_wav_v1.mp3 new file mode 100644 index 0000000..123ce83 Binary files /dev/null and b/audio/mp3/tts/v2_msg14_linxia_auto_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg14_mom_ask_wav_v1.mp3 b/audio/mp3/tts/v2_msg14_mom_ask_wav_v1.mp3 new file mode 100644 index 0000000..b36470c Binary files /dev/null and b/audio/mp3/tts/v2_msg14_mom_ask_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg14_react_linked_wav_v1.mp3 b/audio/mp3/tts/v2_msg14_react_linked_wav_v1.mp3 new file mode 100644 index 0000000..617ac38 Binary files /dev/null and b/audio/mp3/tts/v2_msg14_react_linked_wav_v1.mp3 differ diff --git a/audio/mp3/tts/v2_msg14_react_unlinked_wav_v1.mp3 b/audio/mp3/tts/v2_msg14_react_unlinked_wav_v1.mp3 new file mode 100644 index 0000000..52f5395 Binary files /dev/null and b/audio/mp3/tts/v2_msg14_react_unlinked_wav_v1.mp3 differ diff --git a/game/js/engine.js b/game/js/engine.js index 8dc6cc7..1230533 100644 --- a/game/js/engine.js +++ b/game/js/engine.js @@ -78,7 +78,7 @@ class GameEngine { await Snd.startAmbience(AMBIENCE_DEFAULT, { vol: 0.15 }); await sleep(1500); - Snd.startBGM(`${MUS}/lv11_bgm_m1_main_loop_v1.mp3`, { vol: 0.2 }); + Snd.startBGM(`${MUS}/lv11_bgm_m1_main_loop_v1.mp3`, { vol: 0.1 }); await sleep(1500); this._next(); @@ -208,15 +208,25 @@ class GameEngine { this.state = 'responding'; await this._playVoiceAsync([msg.autoReply]); - // 条件反应(MSG-14:根据 MOM_LINK 播放不同反应) + // 条件反应(MSG-14:先播前导语"真的没事?",再根据 MOM_LINK 播放不同反应) if (!this._skipRequested && msg.autoReact) { - const val = this.vars[msg.autoReact.condition]; - const reactUrl = val ? msg.autoReact['true'] : msg.autoReact['false']; - if (reactUrl) { - this.state = 'reacting'; + this.state = 'reacting'; + // 前导语(如"真的没事?") + if (msg.autoReactPreamble) { await sleep(500); if (!this._skipRequested) { - await this._playVoiceAsync([reactUrl]); + await this._playVoiceAsync([msg.autoReactPreamble]); + } + } + // 条件分支反应 + if (!this._skipRequested) { + const val = this.vars[msg.autoReact.condition]; + const reactUrl = val ? msg.autoReact['true'] : msg.autoReact['false']; + if (reactUrl) { + await sleep(400); + if (!this._skipRequested) { + await this._playVoiceAsync([reactUrl]); + } } } } @@ -253,7 +263,7 @@ class GameEngine { this.msgState[msg.id] = 'read'; this.vars.UNREAD = Math.max(0, this.vars.UNREAD - 1); - if (msg.bgm) Snd.startBGM(msg.bgm, { vol: 0.2 }); + if (msg.bgm) Snd.startBGM(msg.bgm, { vol: 0.1 }); // 系统通知(时光胶囊) if (msg.type === 'system_notification') { @@ -687,9 +697,10 @@ class GameEngine { async _endingE() { Haptic.message(); SFX_UI.notify(); + await sleep(1500); + // 周南秒回:"那我下次出差来北京,请你吃饭好不好。" + await this._playVoiceAsync([`${T}/v2_ending_e_zhounan_wav_v1.mp3`]); await sleep(2000); - Snd.playVoice([`${TDIR}/lv11_sys_goodnight_wav_v1.mp3`]); - await sleep(3000); SFX_UI.ending(); await sleep(1000); this._showCaption('好。'); diff --git a/game/js/story.js b/game/js/story.js index 814abaf..3be5aa6 100644 --- a/game/js/story.js +++ b/game/js/story.js @@ -1,4 +1,4 @@ -// story.js — 消息数据 v2(多轮对话 + 林夏语音 + NPC反应) +// story.js — 消息数据 v2(VoxCPM2 语音 + 多轮对话) // // 选择方向的情感含义(全局一致): // 单击(tap) → 简单回应 @@ -16,61 +16,64 @@ // profile: 画像分数变更 // followUp: 可选的下一轮对话 { choices: { ... } } -const A = '../audio/mp3'; -const TDIR = `${A}/tts`; -const LINXIA = `${A}/linxia`; -const REACT = `${A}/react`; -const MUS = `${A}/music`; -const SFX = `${A}/sfx`; -const AMB = `${A}/ambience`; +const A = '../audio/mp3'; +const T = `${A}/tts`; // VoxCPM2 生成的所有语音 +const MUS = `${A}/music`; +const SFX = `${A}/sfx`; +const AMB = `${A}/ambience`; + +// ── 快捷函数 ── +const npc = f => `${T}/${f}`; // NPC Round 1 台词 +const lx = f => `${T}/${f}`; // 林夏回应 +const rx = f => `${T}/${f}`; // NPC 反应 const MESSAGES = [ // ══════════════════════════════════════════ - // 01 · 妈妈 · 电话(教学:接/拒电话) + // 01 · 妈妈 · 电话 // ══════════════════════════════════════════ { id: 1, type: 'call', sender: 'mom', senderName: '妈妈', ringtone: `${MUS}/lv11_ring_mom_v1.mp3`, - audio: [`${TDIR}/lv11_2103_mom_call_01_wav_v1.mp3`], - autoReply: `${LINXIA}/msg01_auto.mp3`, + audio: [npc('lv11_2103_mom_call_01_wav_v1.mp3')], + autoReply: lx('v2_msg01_linxia_auto_wav_v1.mp3'), stateOnAnswer: { MOM_LINK: true }, profileOnMiss: { avoidance: 2 }, profileOnAnswer: { engagement: 1 }, }, // ══════════════════════════════════════════ - // 02 · 小美 · 微信语音 ×3(教学:选择系统) + // 02 · 小美 · 语音 ×3(教学关) // ══════════════════════════════════════════ { id: 2, type: 'wechat_voice', sender: 'xiaomei', senderName: '小美', audio: [ - `${TDIR}/lv11_2115_xiaomei_voice_01_wav_v1.mp3`, - `${TDIR}/lv11_2115_xiaomei_voice_02_wav_v1.mp3`, - `${TDIR}/lv11_2115_xiaomei_voice_03_wav_v1.mp3`, + npc('lv11_2115_xiaomei_voice_01_wav_v1.mp3'), + npc('lv11_2115_xiaomei_voice_02_wav_v1.mp3'), + npc('lv11_2115_xiaomei_voice_03_wav_v1.mp3'), ], choices: { right: { - linxia: `${LINXIA}/msg02_warm.mp3`, - npcReact: [`${REACT}/msg02_react_warm.mp3`], + linxia: lx('v2_msg02_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg02_react_warm_wav_v1.mp3')], profile: { engagement: 2 }, }, left: { - linxia: `${LINXIA}/msg02_cold.mp3`, - npcReact: [`${REACT}/msg02_react_cold.mp3`], + linxia: lx('v2_msg02_linxia_cold_wav_v1.mp3'), + npcReact: [rx('v2_msg02_react_cold_wav_v1.mp3')], profile: { avoidance: 1 }, }, up: { - linxia: `${LINXIA}/msg02_curious.mp3`, - npcReact: [`${REACT}/msg02_react_curious.mp3`], + linxia: lx('v2_msg02_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg02_react_curious_wav_v1.mp3')], profile: { engagement: 1 }, followUp: { choices: { right: { - linxia: `${LINXIA}/msg02_r3_curious.mp3`, - npcReact: [`${REACT}/msg02_r3_curious.mp3`], + linxia: lx('v2_msg02_r3_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg02_r3_react_wav_v1.mp3')], profile: { nostalgia: 1 }, }, }, @@ -78,90 +81,92 @@ const MESSAGES = [ }, down: { isSilence: true, - npcReact: [`${REACT}/msg02_react_silent.mp3`], + npcReact: [rx('v2_msg02_react_silent_wav_v1.mp3')], profile: { avoidance: 2 }, }, tap: { - linxia: `${LINXIA}/generic_hmm.mp3`, - npcReact: [`${REACT}/msg02_react_tap.mp3`], + linxia: lx('v2_msg02_linxia_tap_wav_v1.mp3'), + npcReact: [rx('v2_msg02_react_tap_wav_v1.mp3')], }, }, }, // ══════════════════════════════════════════ - // 03 · 外卖小哥 · 电话(日常感) + // 03 · 外卖小哥 · 电话 // ══════════════════════════════════════════ { id: 3, type: 'call', sender: 'delivery', senderName: '外卖小哥', ringtone: `${MUS}/lv11_ring_delivery_v1.mp3`, - audio: [`${TDIR}/lv11_2120_delivery_call_01_wav_v1.mp3`], - autoReply: `${LINXIA}/msg03_auto.mp3`, + audio: [npc('lv11_2120_delivery_call_01_wav_v1.mp3')], + autoReply: lx('v2_msg03_linxia_auto_wav_v1.mp3'), profileOnMiss: { avoidance: 1 }, profileOnAnswer: { engagement: 1 }, }, // ══════════════════════════════════════════ - // 04 · 阿哲 · 微信语音(前男友要东西) + // 04 · 阿哲 · 语音(前男友要东西) // ══════════════════════════════════════════ { id: 4, type: 'wechat_voice', sender: 'azhe', senderName: '阿哲', - audio: [`${TDIR}/lv11_2130_azhe_voice_01_wav_v1.mp3`], + audio: [npc('lv11_2130_azhe_voice_01_wav_v1.mp3')], choices: { right: { - linxia: `${LINXIA}/msg04_warm.mp3`, - npcReact: [`${REACT}/msg04_react_warm.mp3`], + linxia: lx('v2_msg04_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg04_react_warm_wav_v1.mp3')], profile: { engagement: 2 }, followUp: { + // 阿哲问"你呢?"后 choices: { right: { - linxia: `${LINXIA}/msg04_r3w_warm.mp3`, - npcReact: [`${REACT}/msg04_r3w_sorry.mp3`], + linxia: lx('v2_msg04_r3w_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg04_r3w_react_warm_wav_v1.mp3')], }, left: { - linxia: `${LINXIA}/msg04_r3w_cold.mp3`, - npcReact: [`${REACT}/msg04_r3w_good.mp3`], + linxia: lx('v2_msg04_r3w_linxia_cold_wav_v1.mp3'), + npcReact: [rx('v2_msg04_r3w_react_cold_wav_v1.mp3')], }, down: { isSilence: true, - npcReact: [`${REACT}/msg04_r3w_night.mp3`], + npcReact: [rx('v2_msg04_r3w_react_silent_wav_v1.mp3')], }, }, }, }, left: { - linxia: `${LINXIA}/msg04_cold.mp3`, - npcReact: [`${REACT}/msg04_react_cold.mp3`], + linxia: lx('v2_msg04_linxia_cold_wav_v1.mp3'), + npcReact: [rx('v2_msg04_react_cold_wav_v1.mp3')], state: { HE_BACK: false }, profile: { avoidance: 1 }, }, up: { - linxia: `${LINXIA}/msg04_curious.mp3`, - npcReact: [`${REACT}/msg04_react_curious.mp3`], + linxia: lx('v2_msg04_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg04_react_curious_wav_v1.mp3')], profile: { engagement: 2 }, followUp: { + // 阿哲说"不太方便"后 choices: { up: { - linxia: `${LINXIA}/msg04_r3c_curious.mp3`, - npcReact: [`${REACT}/msg04_r3c_nevermind.mp3`], + linxia: lx('v2_msg04_r3c_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg04_r3c_react_curious_wav_v1.mp3')], state: { HE_BACK: false }, }, right: { - linxia: `${LINXIA}/msg04_r3c_warm.mp3`, + linxia: lx('v2_msg04_r3c_linxia_warm_wav_v1.mp3'), }, }, }, }, down: { isSilence: true, - npcReact: [`${REACT}/msg04_react_silent.mp3`], + npcReact: [rx('v2_msg04_react_silent_wav_v1.mp3')], state: { HE_BACK: null }, profile: { avoidance: 2 }, }, tap: { - linxia: `${LINXIA}/generic_ok.mp3`, - npcReact: [`${REACT}/msg04_react_tap.mp3`], + linxia: lx('v2_msg04_linxia_tap_wav_v1.mp3'), + npcReact: [rx('v2_msg04_react_tap_wav_v1.mp3')], state: { HE_BACK: false }, profile: { engagement: 1 }, }, @@ -169,24 +174,24 @@ const MESSAGES = [ }, // ══════════════════════════════════════════ - // 05 · HR王姐 · 微信语音 + // 05 · HR王姐 · 语音 // ══════════════════════════════════════════ { id: 5, type: 'wechat_voice', sender: 'hr', senderName: 'HR王姐', - audio: [`${TDIR}/lv11_2145_hr_voice_01_wav_v1.mp3`], + audio: [npc('lv11_2145_hr_voice_01_wav_v1.mp3')], choices: { right: { - linxia: `${LINXIA}/msg05_warm.mp3`, - npcReact: [`${REACT}/msg05_react_warm.mp3`], + linxia: lx('v2_msg05_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg05_react_warm_wav_v1.mp3')], profile: { engagement: 1 }, }, left: { - linxia: `${LINXIA}/msg05_cold.mp3`, + linxia: lx('v2_msg05_linxia_cold_wav_v1.mp3'), }, up: { - linxia: `${LINXIA}/msg05_curious.mp3`, - npcReact: [`${REACT}/msg05_react_curious.mp3`], + linxia: lx('v2_msg05_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg05_react_curious_wav_v1.mp3')], profile: { engagement: 2 }, }, down: { @@ -194,84 +199,85 @@ const MESSAGES = [ profile: { avoidance: 1 }, }, tap: { - linxia: `${LINXIA}/generic_ok.mp3`, + linxia: lx('v2_msg05_linxia_tap_wav_v1.mp3'), }, }, }, // ══════════════════════════════════════════ - // 06 · 安安 · 微信语音(酒吧邀约) + // 06 · 安安 · 语音(酒吧邀约) // ══════════════════════════════════════════ { id: 6, type: 'wechat_voice', sender: 'anan', senderName: '安安', - audio: [`${TDIR}/lv11_2200_anan_voice_01_wav_v1.mp3`], + audio: [npc('lv11_2200_anan_voice_01_wav_v1.mp3')], choices: { right: { - linxia: `${LINXIA}/msg06_warm.mp3`, - npcReact: [`${REACT}/msg06_react_warm.mp3`], + linxia: lx('v2_msg06_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg06_react_warm_wav_v1.mp3')], profile: { engagement: 2 }, }, left: { - linxia: `${LINXIA}/msg06_cold.mp3`, - npcReact: [`${REACT}/msg06_react_cold.mp3`], + linxia: lx('v2_msg06_linxia_cold_wav_v1.mp3'), + npcReact: [rx('v2_msg06_react_cold_wav_v1.mp3')], }, up: { - linxia: `${LINXIA}/msg06_curious.mp3`, - npcReact: [`${REACT}/msg06_react_curious.mp3`], + linxia: lx('v2_msg06_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg06_react_curious_wav_v1.mp3')], profile: { engagement: 1 }, }, down: { isSilence: true, - npcReact: [`${REACT}/msg06_react_silent.mp3`], + npcReact: [rx('v2_msg06_react_silent_wav_v1.mp3')], profile: { avoidance: 1 }, }, tap: { - linxia: `${LINXIA}/generic_hmm.mp3`, - npcReact: [`${REACT}/msg06_react_tap.mp3`], + linxia: lx('v2_msg06_linxia_tap_wav_v1.mp3'), + npcReact: [rx('v2_msg06_react_tap_wav_v1.mp3')], }, }, }, // ══════════════════════════════════════════ - // 07 · 大学寝室群 · 5条语音 + // 07 · 寝室群 · 5条语音 // ══════════════════════════════════════════ { id: 7, type: 'wechat_voice', sender: 'dorm', senderName: '寝室群', audio: [ - `${TDIR}/lv11_2215_dorm_a_voice_01_wav_v1.mp3`, - `${TDIR}/lv11_2215_dorm_a_voice_02_wav_v1.mp3`, - `${TDIR}/lv11_2215_dorm_b_voice_01_wav_v1.mp3`, - `${TDIR}/lv11_2215_dorm_b_voice_02_wav_v1.mp3`, - `${TDIR}/lv11_2215_dorm_c_voice_01_wav_v1.mp3`, + npc('lv11_2215_dorm_a_voice_01_wav_v1.mp3'), + npc('lv11_2215_dorm_b_voice_01_wav_v1.mp3'), + npc('lv11_2215_dorm_c_voice_01_wav_v1.mp3'), + npc('lv11_2215_dorm_a_voice_02_wav_v1.mp3'), + npc('lv11_2215_dorm_b_voice_02_wav_v1.mp3'), ], choices: { right: { - linxia: `${LINXIA}/msg07_warm.mp3`, - npcReact: [`${REACT}/msg07_react_warm.mp3`], + linxia: lx('v2_msg07_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg07_react_warm_wav_v1.mp3')], state: { GROUP_REPLY: true }, profile: { engagement: 2 }, followUp: { + // 室友A问"你最近怎么样"后 choices: { right: { - linxia: `${LINXIA}/msg07_r3_warm.mp3`, + linxia: lx('v2_msg07_r3_linxia_warm_wav_v1.mp3'), profile: { engagement: 1 }, }, left: { - linxia: `${LINXIA}/msg07_r3_cold.mp3`, - npcReact: [`${REACT}/msg07_r3_cold.mp3`], + linxia: lx('v2_msg07_r3_linxia_cold_wav_v1.mp3'), + npcReact: [rx('v2_msg07_r3_react_cold_wav_v1.mp3')], }, }, }, }, left: { - linxia: `${LINXIA}/msg07_cold.mp3`, + linxia: lx('v2_msg07_linxia_cold_wav_v1.mp3'), profile: { avoidance: 1 }, }, up: { - linxia: `${LINXIA}/msg07_curious.mp3`, - npcReact: [`${REACT}/msg07_react_curious.mp3`], + linxia: lx('v2_msg07_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg07_react_curious_wav_v1.mp3')], state: { GROUP_REPLY: true }, profile: { engagement: 2 }, }, @@ -280,8 +286,8 @@ const MESSAGES = [ profile: { avoidance: 2 }, }, tap: { - linxia: `${LINXIA}/generic_hmm.mp3`, - npcReact: [`${REACT}/msg07_react_tap.mp3`], + linxia: lx('v2_linxia_generic_hmm_wav_v1.mp3'), + npcReact: [rx('v2_msg07_react_tap_wav_v1.mp3')], state: { GROUP_REPLY: true }, }, }, @@ -302,41 +308,46 @@ const MESSAGES = [ }, // ══════════════════════════════════════════ - // 09 · 小美(醉)· 4段语音(秘密揭露) + // 09 · 小美(醉)· 4段语音 // ══════════════════════════════════════════ { id: 9, type: 'wechat_voice', sender: 'xiaomei', senderName: '小美', audio: [ - `${TDIR}/lv11_2245_xiaomei_voice_01_seg1_wav_v1.mp3`, - `${TDIR}/lv11_2245_xiaomei_voice_01_seg2_wav_v1.mp3`, - `${TDIR}/lv11_2245_xiaomei_voice_01_seg3_wav_v1.mp3`, - `${TDIR}/lv11_2245_xiaomei_voice_01_seg4_wav_v1.mp3`, + npc('lv11_2245_xiaomei_voice_01_seg1_wav_v1.mp3'), + npc('lv11_2245_xiaomei_voice_01_seg2_wav_v1.mp3'), + npc('lv11_2245_xiaomei_voice_01_seg3_wav_v1.mp3'), + npc('lv11_2245_xiaomei_voice_01_seg4_wav_v1.mp3'), ], bgm: `${MUS}/lv11_bgm_m2_suspense_v1.mp3`, choices: { right: { - linxia: `${LINXIA}/msg09_warm.mp3`, - npcReact: [`${REACT}/msg09_react_warm.mp3`], + linxia: lx('v2_msg09_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg09_react_warm_wav_v1.mp3')], profile: { engagement: 2 }, }, left: { - linxia: `${LINXIA}/msg09_cold.mp3`, + linxia: lx('v2_msg09_linxia_cold_wav_v1.mp3'), profile: { avoidance: 1 }, }, up: { - linxia: `${LINXIA}/msg09_curious.mp3`, - npcReact: [`${REACT}/msg09_react_curious.mp3`], + linxia: lx('v2_msg09_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg09_react_curious_wav_v1.mp3')], profile: { engagement: 2 }, followUp: { + // 小美说"怕你恨我"后 choices: { right: { - linxia: `${LINXIA}/msg09_r3_warm.mp3`, - npcReact: [`${REACT}/msg09_r3_warm.mp3`], + linxia: lx('v2_msg09_r3_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg09_r3_react_warm_wav_v1.mp3')], }, left: { - linxia: `${LINXIA}/msg09_r3_cold.mp3`, - npcReact: [`${REACT}/msg09_r3_cold.mp3`], + linxia: lx('v2_msg09_r3_linxia_cold_wav_v1.mp3'), + npcReact: [rx('v2_msg09_r3_react_cold_wav_v1.mp3')], + }, + down: { + isSilence: true, + npcReact: [rx('v2_msg09_r3_react_silent_q_wav_v1.mp3')], }, }, }, @@ -346,8 +357,8 @@ const MESSAGES = [ profile: { avoidance: 2, nostalgia: 1 }, }, tap: { - linxia: `${LINXIA}/generic_hmm.mp3`, - npcReact: [`${REACT}/msg09_react_tap.mp3`], + linxia: lx('v2_msg09_linxia_tap_wav_v1.mp3'), + npcReact: [rx('v2_msg09_react_tap_wav_v1.mp3')], profile: { nostalgia: 1 }, }, }, @@ -362,59 +373,61 @@ const MESSAGES = [ text: '睡了吗?', choices: { right: { - linxia: `${LINXIA}/msg10_warm.mp3`, - npcReact: [`${REACT}/msg10_react_warm.mp3`], + linxia: lx('v2_msg10_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg10_react_warm_wav_v1.mp3')], state: { MOM_LINK: true }, profile: { engagement: 2 }, followUp: { + // 妈妈问"今天怎么样"后 choices: { right: { - linxia: `${LINXIA}/msg10_rw_warm.mp3`, - npcReact: [`${REACT}/msg10_rw_warm.mp3`], + linxia: lx('v2_msg10_rw_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg10_rw_react_warm_wav_v1.mp3')], }, up: { - linxia: `${LINXIA}/msg10_rw_curious.mp3`, - npcReact: [`${REACT}/msg10_rw_curious.mp3`], + linxia: lx('v2_msg10_rw_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg10_rw_react_curious_wav_v1.mp3')], }, }, }, }, left: { - linxia: `${LINXIA}/msg10_cold.mp3`, - npcReact: [`${REACT}/msg10_react_cold.mp3`], + linxia: lx('v2_msg10_linxia_cold_wav_v1.mp3'), + npcReact: [rx('v2_msg10_react_cold_wav_v1.mp3')], state: { MOM_LINK: true }, profile: { avoidance: 1 }, }, up: { - linxia: `${LINXIA}/msg10_curious.mp3`, - npcReact: [`${REACT}/msg10_react_curious.mp3`], + linxia: lx('v2_msg10_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg10_call_mom_wav_v1.mp3')], state: { MOM_LINK: true }, profile: { engagement: 3 }, followUp: { + // 妈妈打来电话说"怎么了夏夏?"后 choices: { right: { - linxia: `${LINXIA}/msg10_call_warm.mp3`, - npcReact: [`${REACT}/msg10_call_warm.mp3`], + linxia: lx('v2_msg10_call_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg10_call_react_warm_wav_v1.mp3')], }, up: { - linxia: `${LINXIA}/msg10_call_curious.mp3`, - npcReact: [`${REACT}/msg10_call_curious.mp3`], + linxia: lx('v2_msg10_call_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg10_call_react_curious_wav_v1.mp3')], }, down: { isSilence: true, - npcReact: [`${REACT}/msg10_call_silent.mp3`], + npcReact: [rx('v2_msg10_call_react_silent_wav_v1.mp3')], }, }, }, }, down: { isSilence: true, - npcReact: [`${REACT}/msg10_react_silent.mp3`], + npcReact: [rx('v2_msg10_react_silent_wav_v1.mp3')], profile: { avoidance: 2 }, }, tap: { - linxia: `${LINXIA}/generic_hmm.mp3`, - npcReact: [`${REACT}/msg10_react_tap.mp3`], + linxia: lx('v2_msg10_linxia_tap_wav_v1.mp3'), + npcReact: [rx('v2_msg10_react_tap_wav_v1.mp3')], state: { MOM_LINK: true }, }, }, @@ -427,33 +440,33 @@ const MESSAGES = [ id: 11, type: 'call', sender: 'azhe', senderName: '阿哲', ringtone: `${MUS}/lv11_ring_azhe_v1.mp3`, - audio: [`${TDIR}/lv11_2315_azhe_call_01_wav_v1.mp3`], + audio: [npc('lv11_2315_azhe_call_01_wav_v1.mp3')], profileOnMiss: { avoidance: 2 }, profileOnAnswer: { engagement: 1 }, choices: { right: { - linxia: `${LINXIA}/msg11_warm.mp3`, - npcReact: [`${REACT}/msg11_react_warm.mp3`], + linxia: lx('v2_msg11_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg11_react_warm_wav_v1.mp3')], state: { HE_BACK: true }, }, left: { - linxia: `${LINXIA}/msg11_cold.mp3`, - npcReact: [`${REACT}/msg11_react_cold.mp3`], + linxia: lx('v2_msg11_linxia_cold_wav_v1.mp3'), + npcReact: [rx('v2_msg11_react_cold_wav_v1.mp3')], state: { HE_BACK: false }, }, up: { - linxia: `${LINXIA}/msg11_curious.mp3`, - npcReact: [`${REACT}/msg11_react_curious.mp3`], + linxia: lx('v2_msg11_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg11_react_curious_wav_v1.mp3')], followUp: { choices: { right: { - linxia: `${LINXIA}/msg11_warm.mp3`, - npcReact: [`${REACT}/msg11_react_warm.mp3`], + linxia: lx('v2_msg11_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg11_react_warm_wav_v1.mp3')], state: { HE_BACK: true }, }, left: { - linxia: `${LINXIA}/msg11_cold.mp3`, - npcReact: [`${REACT}/msg11_react_cold.mp3`], + linxia: lx('v2_msg11_linxia_cold_wav_v1.mp3'), + npcReact: [rx('v2_msg11_react_cold_wav_v1.mp3')], state: { HE_BACK: false }, }, }, @@ -464,47 +477,47 @@ const MESSAGES = [ state: { HE_BACK: false }, }, tap: { - linxia: `${LINXIA}/msg11_tap.mp3`, - npcReact: [`${REACT}/msg11_react_tap.mp3`], + linxia: lx('v2_msg11_linxia_tap_wav_v1.mp3'), + npcReact: [rx('v2_msg11_react_tap_wav_v1.mp3')], state: { HE_BACK: false }, }, }, }, // ══════════════════════════════════════════ - // 12 · 周南 · 微信语音(老同学) + // 12 · 周南 · 语音(老同学) // ══════════════════════════════════════════ { id: 12, type: 'wechat_voice', sender: 'zhounan', senderName: '周南', - audio: [`${TDIR}/lv11_2330_zhounan_voice_01_wav_v1.mp3`], + audio: [npc('lv11_2330_zhounan_voice_01_wav_v1.mp3')], choices: { right: { - linxia: `${LINXIA}/msg12_warm.mp3`, - npcReact: [`${REACT}/msg12_react_warm.mp3`], + linxia: lx('v2_msg12_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg12_react_warm_wav_v1.mp3')], state: { ZHOUNAN_DEPTH: 1 }, profile: { engagement: 2, nostalgia: 1 }, }, left: { - linxia: `${LINXIA}/msg12_cold.mp3`, - npcReact: [`${REACT}/msg12_react_cold.mp3`], + linxia: lx('v2_msg12_linxia_cold_wav_v1.mp3'), + npcReact: [rx('v2_msg12_react_cold_wav_v1.mp3')], state: { ZHOUNAN_DEPTH: 1 }, profile: { nostalgia: 1 }, }, up: { - linxia: `${LINXIA}/msg12_curious.mp3`, - npcReact: [`${REACT}/msg12_react_curious.mp3`], + linxia: lx('v2_msg12_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg12_react_curious_wav_v1.mp3')], state: { ZHOUNAN_DEPTH: 1 }, profile: { nostalgia: 2 }, }, down: { isSilence: true, - npcReact: [`${REACT}/msg12_react_silent.mp3`], + npcReact: [rx('v2_msg12_react_silent_wav_v1.mp3')], profile: { nostalgia: 1 }, }, tap: { - linxia: `${LINXIA}/msg12_tap.mp3`, - npcReact: [`${REACT}/msg12_react_tap.mp3`], + linxia: lx('v2_msg12_linxia_tap_wav_v1.mp3'), + npcReact: [rx('v2_msg12_react_tap_wav_v1.mp3')], state: { ZHOUNAN_DEPTH: 1 }, }, }, @@ -517,75 +530,77 @@ const MESSAGES = [ id: 13, type: 'wechat_voice', sender: 'zhounan', senderName: '周南', audio: [ - `${TDIR}/lv11_2345_zhounan_voice_01_wav_v1.mp3`, - `${TDIR}/lv11_2345_zhounan_voice_02_wav_v1.mp3`, - `${TDIR}/lv11_2345_zhounan_voice_03_wav_v1.mp3`, - `${TDIR}/lv11_2345_zhounan_voice_04_wav_v1.mp3`, + npc('lv11_2345_zhounan_voice_01_wav_v1.mp3'), + npc('lv11_2345_zhounan_voice_02_wav_v1.mp3'), + npc('lv11_2345_zhounan_voice_03_wav_v1.mp3'), + npc('lv11_2345_zhounan_voice_04_wav_v1.mp3'), ], bgm: `${MUS}/lv11_bgm_m3_zhounan_v1.mp3`, choices: { right: { - linxia: `${LINXIA}/msg13_warm.mp3`, - npcReact: [`${REACT}/msg13_react_warm.mp3`], + linxia: lx('v2_msg13_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg13_react_warm_wav_v1.mp3')], state: { ZHOUNAN_DEPTH_ADD: 1 }, profile: { engagement: 2 }, followUp: { + // 周南问"你在北京过得好吗"后 choices: { right: { - linxia: `${LINXIA}/msg13_r3_warm.mp3`, - npcReact: [`${REACT}/msg13_r3_warm.mp3`], + linxia: lx('v2_msg13_r3_linxia_warm_wav_v1.mp3'), + npcReact: [rx('v2_msg13_r3_react_warm_wav_v1.mp3')], state: { ZHOUNAN_SHARE: true }, }, left: { - linxia: `${LINXIA}/msg13_r3_cold.mp3`, - npcReact: [`${REACT}/msg13_r3_cold.mp3`], + linxia: lx('v2_msg13_r3_linxia_cold_wav_v1.mp3'), + npcReact: [rx('v2_msg13_r3_react_cold_wav_v1.mp3')], }, up: { - linxia: `${LINXIA}/msg13_r3_curious.mp3`, - npcReact: [`${REACT}/msg13_r3_curious.mp3`], + linxia: lx('v2_msg13_r3_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg13_r3_react_curious_wav_v1.mp3')], }, }, }, }, left: { - linxia: `${LINXIA}/msg13_cold.mp3`, - npcReact: [`${REACT}/msg13_react_cold.mp3`], + linxia: lx('v2_msg13_linxia_cold_wav_v1.mp3'), + npcReact: [rx('v2_msg13_react_cold_wav_v1.mp3')], state: { ZHOUNAN_DEPTH_ADD: 1 }, profile: { nostalgia: 1 }, }, up: { - linxia: `${LINXIA}/msg13_curious.mp3`, - npcReact: [`${REACT}/msg13_react_curious.mp3`], + linxia: lx('v2_msg13_linxia_curious_wav_v1.mp3'), + npcReact: [rx('v2_msg13_react_curious_wav_v1.mp3')], state: { ZHOUNAN_DEPTH_ADD: 1, ZHOUNAN_SHARE: true }, profile: { nostalgia: 2, engagement: 1 }, }, down: { isSilence: true, - npcReact: [`${REACT}/msg13_react_silent.mp3`], + npcReact: [rx('v2_msg13_react_silent_wav_v1.mp3')], profile: { avoidance: 1 }, }, tap: { - linxia: `${LINXIA}/msg13_tap.mp3`, - npcReact: [`${REACT}/msg13_react_tap.mp3`], + linxia: lx('v2_msg13_linxia_tap_wav_v1.mp3'), + npcReact: [rx('v2_msg13_react_tap_wav_v1.mp3')], state: { ZHOUNAN_DEPTH_ADD: 1 }, }, }, }, // ══════════════════════════════════════════ - // 14 · 妈妈 · 第二次电话(自动回应 + 条件反应) + // 14 · 妈妈 · 第二次电话 // ══════════════════════════════════════════ { id: 14, type: 'call', sender: 'mom', senderName: '妈妈', ringtone: `${MUS}/lv11_ring_mom_v1.mp3`, - audio: [`${TDIR}/lv11_0015_mom_call_01_wav_v1.mp3`], - autoReply: `${LINXIA}/msg14_auto.mp3`, + audio: [npc('lv11_0015_mom_call_01_wav_v1.mp3')], + autoReply: lx('v2_msg14_linxia_auto_wav_v1.mp3'), autoReact: { condition: 'MOM_LINK', - true: `${REACT}/msg14_react_linked.mp3`, - false: `${REACT}/msg14_react_unlinked.mp3`, + true: rx('v2_msg14_react_linked_wav_v1.mp3'), + false: rx('v2_msg14_react_unlinked_wav_v1.mp3'), }, + autoReactPreamble: rx('v2_msg14_mom_ask_wav_v1.mp3'), stateOnAnswer: { MOM_LINK: true }, profileOnMiss: { avoidance: 2 }, profileOnAnswer: { engagement: 2 }, @@ -597,11 +612,11 @@ const MESSAGES = [ { id: 15, type: 'system_notification', sender: 'self', senderName: '时光胶囊', - systemAudio: `${TDIR}/lv11_sys_memo_01_wav_v1.mp3`, + systemAudio: npc('lv11_sys_memo_01_wav_v1.mp3'), audioByProfile: { - avoidance: `${TDIR}/lv11_0030_self3y_voice_02_wav_v1.mp3`, - engagement: `${TDIR}/lv11_0030_self3y_voice_01_wav_v1.mp3`, - nostalgia: `${TDIR}/lv11_0030_self3y_voice_03_wav_v1.mp3`, + avoidance: npc('lv11_0030_self3y_voice_02_wav_v1.mp3'), + engagement: npc('lv11_0030_self3y_voice_01_wav_v1.mp3'), + nostalgia: npc('lv11_0030_self3y_voice_03_wav_v1.mp3'), }, stateOnPlay: { SELF_RECORD: true }, profileOnPlay: { nostalgia: 2 }, @@ -609,20 +624,20 @@ const MESSAGES = [ }, // ══════════════════════════════════════════ - // 16 · 妈妈 · 长语音(结局前奏) + // 16 · 妈妈 · 长语音 ×8(结局前奏) // ══════════════════════════════════════════ { id: 16, type: 'wechat_voice', sender: 'mom', senderName: '妈妈', audio: [ - `${TDIR}/lv11_0200_mom_voice_01_seg1_wav_v1.mp3`, - `${TDIR}/lv11_0200_mom_voice_01_seg2_wav_v1.mp3`, - `${TDIR}/lv11_0200_mom_voice_01_seg3_wav_v1.mp3`, - `${TDIR}/lv11_0200_mom_voice_01_seg4_wav_v1.mp3`, - `${TDIR}/lv11_0200_mom_voice_01_seg5_wav_v1.mp3`, - `${TDIR}/lv11_0200_mom_voice_01_seg6_wav_v1.mp3`, - `${TDIR}/lv11_0200_mom_voice_01_seg7_wav_v1.mp3`, - `${TDIR}/lv11_0200_mom_voice_01_seg8_wav_v1.mp3`, + npc('lv11_0200_mom_voice_01_seg1_wav_v1.mp3'), + npc('lv11_0200_mom_voice_01_seg2_wav_v1.mp3'), + npc('lv11_0200_mom_voice_01_seg3_wav_v1.mp3'), + npc('lv11_0200_mom_voice_01_seg4_wav_v1.mp3'), + npc('lv11_0200_mom_voice_01_seg5_wav_v1.mp3'), + npc('lv11_0200_mom_voice_01_seg6_wav_v1.mp3'), + npc('lv11_0200_mom_voice_01_seg7_wav_v1.mp3'), + npc('lv11_0200_mom_voice_01_seg8_wav_v1.mp3'), ], bgm: `${MUS}/lv11_bgm_m4_ending_v1.mp3`, isEnding: true, diff --git a/voice/linxia/linxia_ref.wav b/voice/linxia/linxia_ref.wav new file mode 100644 index 0000000..f13e197 Binary files /dev/null and b/voice/linxia/linxia_ref.wav differ diff --git a/林夏.md b/林夏.md index 04a6a46..e8b9ffa 100644 --- a/林夏.md +++ b/林夏.md @@ -1,15 +1,16 @@ # 林夏 -> **类型**:情感 / 叙事 / 轻悬疑 · 中篇详细稿 +> **类型**:情感 / 叙事 / 轻悬疑 · 纯音频交互游戏 > **目标用户**:20–25 岁年轻女性(大学生 / 初入职场 / 一线城市独居) -> **时长**:约 60 分钟(剧内时间 21:00 – 02:00 共 5 小时压缩) +> **时长**:约 30–45 分钟 > **核心隐喻**:所有人都给你发语音,没有人发文字。今晚你必须一条一条听完,才能决定明天的自己长什么样。 +> **核心原则**:每次选择都必须产生玩家能听到的、不同的后果。 --- ## 0. 一句话概述 -24 岁的林夏,被分手、被裁员、被妈妈催婚,全发生在同一个周五。她把手机调成静音、躲进合租屋,关上灯。屏幕坏了——只剩耳机能听、手指能摸。今晚 16 个人会发来电话、语音、文字。她接谁、不接谁、回谁、不回谁,决定她明天还要不要从这张床上爬起来。 +24 岁的林夏,被分手、被裁员、被妈妈催婚,全发生在同一个周五。她把手机调成静音、躲进合租屋,关上灯。屏幕坏了——只剩耳机能听、手指能摸。今晚 16 个人会发来电话和语音。她接谁、不接谁、怎么回——每一个回应都会被对方听到,每一段对话都会走向不同的结局。 --- @@ -24,401 +25,761 @@ - 微信未读:99+。 - 屏幕:今天上午地铁挤碎了的那块屏,现在显示一片黑——但触控、震动、声音都还在。 -### 1.2 玩家不需要"玩"什么 -- 不解谜、不打怪、不收集线索。 -- 玩家的全部任务就一句话——**今晚是接,是不接;是回,是不回。** +### 1.2 林夏不是"听者"——她会说话 + +与传统音频叙事游戏不同,**林夏有自己的声音**。玩家的每次选择都会触发林夏的一句语音回应,对方会听到并做出反应。这构成了完整的对话: + +``` +NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回应做出反应 +``` + +林夏的声音设计:**疲惫但清醒、克制但有温度、年轻但不幼稚。** --- ## 2. 核心玩法 -### 2.1 三种"消息形态"对应三种触控(**纯触摸 · 零麦克风**) +### 2.1 六种手势——零视觉依赖 -| 消息形态 | 提示 | 接收交互 | 回应交互 | -|---|---|---|---| -| **来电** | 长震 + 角色专属铃声 | **单点屏幕**接听 / **左滑**拒接 / **不动**等它停 | 通话中:系统 TTS 在每个对话节点**依次念出 4 个回应选项**,玩家在听到目标时**朝对应方向滑**(左/右/上/下),无需记位置 | -| **微信语音** | 短震 + 自研提示音 | **单点**当场播放 / **不动**留作未读 | **单点** = 快速发"嗯" / **双点** = 发一句更暖的标准回应 / **长按 1 秒**进入"细回复"——系统 TTS 念出 4 个**针对当前消息**的候选回应,玩家朝对应方向滑选择 / **双指上滑** = 已读不回 / **不动** = 保持未读 | -| **微信文字** | 短震 + 文字提示音 | **单点**让 TTS 念出来 / **不动**留作未读 | **单点** = 回"嗯" / **长按 1 秒**进入"细回复"(同上) / **双指上滑** = 已读不回 / **不动** = 保持未读 | +| 手势 | 全局含义 | 情感色彩 | 触觉反馈 | 音效 | +|------|---------|---------|---------|------| +| **单击** | 接受 / 回应 / 继续 | 中性应答 | 轻触感 | 清脆短音 880Hz | +| **双击** | 跳过 / 忽略 | 不耐烦 / 赶时间 | 短促双震 | 下行滑音 587→392Hz | +| **右滑** | 温暖 / 靠近 | 接纳、关心 | 流畅长震 | 上行音 440→659Hz | +| **左滑** | 冷淡 / 推开 | 疏离、敷衍 | 干脆短震 | 下行音 523→349Hz | +| **上滑** | 好奇 / 追问 | 想了解更多 | 快速上弹震 | 快速上行 523→784Hz | +| **下滑** | 沉默 / 退缩 | 说不出口 | 极轻慢震 | 极轻低音 330Hz | -> 全部交互在 30 秒教学关里通过"小美第一通微信语音"完成:她会说"喂喂喂在不在,戳一下回我啊我等你呢"。 +> **不需要记位置**。每个方向有固定的情感含义,贯穿全游戏。右滑永远是温暖,左滑永远是冷淡。 -### 2.1.1 「细回复」机制详解(核心) - -每当玩家长按 1 秒,系统 TTS 用**对方此刻能听到的那种"内心独白旁白"语气**念出 4 个候选回复,例如阿哲发来"我的 AirPods 落你那了",长按后: +### 2.2 对话流程(多轮交互) ``` -(系统 TTS · 极轻 · 0.8x 语速) - 一…… "好,明天寄。" - 二…… "你怎么不自己来拿。" - 三…… "你那边……都好吗。" - 四…… (什么都不说) + ┌─────────────────────────────────────────────────┐ + │ [提示音 + 震动] │ + │ 消息到达 │ + │ → 单击播放 / 双击跳过 │ + └──────────┬──────────────────────────────────────┘ + ▼ + ┌─────────────────────────────────────────────────┐ + │ NPC 语音播放(Round 1) │ + │ "你被甩了我都听说了,明天陪你喝酒" │ + └──────────┬──────────────────────────────────────┘ + ▼ + ┌─────────────────────────────────────────────────┐ + │ [三音上行提示音 · 等待选择] │ + │ 玩家操作:右滑 / 左滑 / 上滑 / 下滑 / 单击 │ + └──────────┬──────────────────────────────────────┘ + ▼ + ┌─────────────────────────────────────────────────┐ + │ [打字音效 ~0.8s → 发送音效] │ + │ 林夏的语音播放 │ + │ 右滑 → "好啊,明天不见不散。" │ + │ 左滑 → "再说吧。" │ + │ 上滑 → "你怎么知道的啊?" │ + │ 下滑 → [一秒沉默] │ + │ 单击 → "嗯。" │ + └──────────┬──────────────────────────────────────┘ + ▼ + ┌─────────────────────────────────────────────────┐ + │ NPC 反应播放(Round 2)—— 因选择而异 │ + │ │ + │ 右滑: "行!那我叫上安安!老地方见!" │ + │ 左滑: "……好吧,你休息。" │ + │ 上滑: "还不是阿哲那个大嘴巴告诉我的……" │ + │ 下滑: "??你还活着吗哈哈。算了不打扰你了" │ + │ 单击: "嗯是什么意思啊哈哈!行吧你休息" │ + └──────────┬──────────────────────────────────────┘ + ▼ + ┌─────────────────────────────────────────────────┐ + │ [可选] Round 3: 继续对话(仅部分路径触发) │ + │ NPC 追问 → 再次选择 → 林夏回应 → 对话结束 │ + └──────────┬──────────────────────────────────────┘ + ▼ + ┌─────────────────────────────────────────────────┐ + │ → 等待 → 下一条消息到达 │ + └─────────────────────────────────────────────────┘ ``` -玩家在听到目标时朝对应方向滑(左 1 / 右 2 / 上 3 / 下 4)。不需要记位置——**只需要听节奏**。 +### 2.3 关键设计 -这套机制保证: -- **零视觉依赖**:所有选项靠语音呈现。 -- **零麦克风依赖**:玩家从不需要说话。 -- **节奏可控**:4 个选项约 6 秒念完,没听清可以再次长按重听。 - -### 2.2 全黑屏的"通讯录辅助" -- **屏幕中央三指长按 2 秒** → 进入"未读列表",TTS 依次念出:"妈妈 3 条未读、小美 1 条已读、阿哲 1 条未播放……" -- 玩家在听到目标条目时,点击屏幕**任意位置**进入。 -- **不需要记位置**——靠节奏与语音引导。 - -### 2.3 时间是流动的 -- 5 小时压缩为 60 分钟现实时间。 -- 每个时间点会自动响起特定来电/语音。 -- **漏接 / 不听是合法的**——但漏掉的内容不会重发,会变成"未读"留在收件箱里。 -- 全程结束时游戏会**统计你的"今晚未读数"**——这是结局判定的关键变量。 +| 方面 | 说明 | +|------|------| +| **林夏的声音** | 每次选择后都有语音回应(或有感知的沉默) | +| **NPC 反应** | 根据选择播放完全不同的反应语音 | +| **选择的感受** | 五个方向各自导向不同的音频内容 | +| **对话长度** | 温暖/追问路径更长、信息更多;冷淡/沉默路径更短 | +| **独家信息** | 追问路径可获得其他路径听不到的剧情细节 | +| **玩家驱动** | 没有时间限制,消息不会自动推进,完全由玩家操作控制节奏 | +| **双击跳过** | 在林夏回应或 NPC 反应播放中,双击可跳过当前语音,加速流程 | ### 2.4 关键机制:「正在输入…」 -- 当某个角色在剧情中"正在打字"时,耳机里会有微弱的**键盘敲击声**(左声道 / 右声道随角色),但消息可能最终**没发出**——他打了一半删了。 -- 这是这个游戏最痛的细节之一。 + +当林夏回应前,耳机里会播放微弱的**键盘敲击声** → 发送音效 → 然后才播放林夏的语音。这 ~1 秒的"打字等待"让选择感觉真实——你不是在"选菜单",你是在"发语音"。 --- -## 3. 来电 / 消息全列表(16 条) +## 3. 消息全列表(16 条) -| # | 时间 | 来源 | 形式 | 关键内容 | 设计意图 | -|---|---|---|---|---|---| -| 01 | 21:03 | 妈妈 | 电话 | "今晚煮了你爱吃的排骨,给你拍张照啊。下周五回来一趟,你高中物理老师的儿子,挺好的。" | 开局先给一个**温柔但不能接**的电话——你妈不知道你今天发生了什么 | -| 02 | 21:15 | 闺蜜小美 | 微信语音 | 三连:①"在吗" ②"你被甩了我都听说了" ③"明天晚上喝酒,海底捞旁边那家,不许放鸽子" | 教学关,引导玩家学会**单点/双点/长按细回复**三种回应方式 | -| 03 | 21:20 | 外卖小哥 | 电话 | "你好你的麻辣烫到了,放门口可以吗?" | 一通**必须接**的电话,作为玩家"原来还能接"的钩子 | -| 04 | 21:30 | 前任阿哲 | 微信语音 | 一条 8 秒:"我的 AirPods 落你那了,方便寄一下吗" | **第一刀**——分手 6 小时,他先想到的是耳机 | -| 05 | 21:45 | HR 王姐 | 微信语音 + 邮件 | "夏夏啊,离职流程你下周三之前办完哈,公积金那边记得跟进。辛苦啦,外面有合适的工作我帮你留意。" | 苦中带甜——HR 不是反派,她其实尽力了 | -| 06 | 22:00 | 室友安安 | 微信语音 | 喧闹背景音里:"夏夏!我在 helens!要不要过来!我请客!" | 给一个**可以逃离今晚**的选项,看玩家选不选 | -| 07 | 22:15 | 大学寝室群(4人群) | 连续 5 条语音 | 室友 A 升了职,B/C 在恭喜,A 突然 @ 你:"夏夏你怎么不说话呀" | 群消息无法"回复某一条",**只能整体响应或沉默** | -| 08 | 22:30 | 未知号码 | 电话 | 接通后只有呼吸声 + 远处的人声背景,6 秒后挂断 | **轻悬疑钩子**——这是 Mystery 线起点 | -| 09 | 22:45 | 闺蜜小美 | 微信语音 | 醉了,超长 1 分 20 秒:从安慰变成抱怨自己的男朋友,到末尾突然说"其实……阿哲他大三那年来追过我" | **第二刀**——你以为闺蜜是同盟,但她也有秘密 | -| 10 | 23:00 | 妈妈 | 微信文字 | "睡了吗?" | 极简 3 个字。点开听 TTS 念出来的瞬间,最痛 | -| 11 | 23:15 | 前任阿哲 | 电话 | 直接打来:"我能过去拿吗?就 5 分钟" | 一个**真正考验玩家**的电话——接 or 拒 是分支锚点 | -| 12 | 23:30 | 未知号码(已加微信) | 微信语音 | "林夏,是我,周南。你应该不记得我了吧,初三坐你后桌那个" | 悬疑揭晓——是初中同桌 | -| 13 | 23:45 | 周南 | 连续 4 条微信语音 | 他讲了这十年:高考没考好、复读、二本、回老家、今年来北京出差。最后一条:"今天我生日。本来就想跟你说一声,对不起这么晚。" | **苦中的那一缕甜**——一个你早就忘了的人,记得你 | -| 14 | 00:15 | 妈妈 | 电话 | "夏夏,你不接电话我有点担心。妈妈知道你工作忙,你早点睡。" | 第二次妈妈来电,是分支判定点 | -| 15 | 00:30 | 自己 | 系统通知 | iPhone "时光胶囊"提醒:3 年前的今晚你给自己录过一段备忘录。"是否播放?" | 自我对话——3 年前的林夏说了什么,由游戏根据玩家本场的选择动态生成 | -| 16 | 02:00 | 妈妈 | 微信语音 | 4 分 17 秒长语音。她讲了她 24 岁那年——也曾被分手、也曾失业、也曾一个人在 1995 年的广州。结尾:"你今天接了好多电话吧。妈妈不打扰你了。" | **结尾** | +| # | 来源 | 形式 | 关键内容 | 有无选择 | +|---|------|------|---------|---------| +| 01 | 妈妈 | 电话 | "今晚煮了你爱吃的排骨" | 接听 → 无选择(自动回应) | +| 02 | 闺蜜小美 | 语音 ×3 | "你被甩了我都听说了,明天陪你喝酒" | 5 方向 + 追问 Round 3 | +| 03 | 外卖小哥 | 电话 | "你的麻辣烫到了" | 接听 → 无选择(自动回应) | +| 04 | 前任阿哲 | 语音 | "我的 AirPods 落你那了" | 5 方向 + 温暖/追问各有 Round 3 | +| 05 | HR 王姐 | 语音 | "离职流程周三前办完就行" | 5 方向 | +| 06 | 室友安安 | 语音 | "我在 Helens!快来!" | 5 方向 | +| 07 | 寝室群 | 语音 ×5 | 室友 A 升职,@林夏 | 5 方向 + 温暖 Round 3 | +| 08 | 未知号码 | 电话 | 呼吸声 6 秒后挂断 | 无选择(悬疑钩子) | +| 09 | 小美(醉)| 语音 ×4 | "阿哲大三追过我" | 5 方向 + 追问 Round 3 | +| 10 | 妈妈 | 文字 | "睡了吗?" | 5 方向 + 温暖/追问各有 Round 3 | +| 11 | 阿哲 | 电话 | "我能过去拿吗?就 5 分钟" | 5 方向(关键分支锚点) | +| 12 | 周南 | 语音 | "初三坐你后桌那个" | 5 方向 | +| 13 | 周南 | 语音 ×4 | "今天我生日" | 5 方向 + 温暖 Round 3 | +| 14 | 妈妈 | 电话 | "你不接电话我有点担心" | 接听 → 自动回应,条件分支 | +| 15 | 自己 | 系统通知 | 3 年前录的备忘录 | 单击播放 / 双击跳过 | +| 16 | 妈妈 | 长语音 ×8 | 妈妈 24 岁那年的故事 | 无选择(结局前奏) | --- -## 4. 分支结构 +## 4. 16 条消息详细设计 -### 4.1 关键判定变量(5 个 + 1 个隐性画像) +### MSG-01 妈妈 · 电话 + +**类型**:来电(铃声 + 长震动) +**Round 1**(接听后):妈妈说"今晚煮了你爱吃的排骨,你什么时候回来呀。" +**接听后自动**:林夏轻轻说"嗯……好。"(无选择) +**不接** → 直接跳到下一条。 + +**状态**:接听 → MOM_LINK = true + +--- + +### MSG-02 小美 · 语音 ×3(教学关) + +**Round 1**:小美连发三条语音: +- "林夏!你还活着吗!" +- "你被甩了我都听说了——别难过啊" +- "明天陪你喝酒,去不去" + +**选择 + Round 2**: + +| 方向 | 林夏说 | 小美反应 | profile | +|------|--------|---------|---------| +| 右滑 | "好啊,明天不见不散。" | "行!那我叫上安安!老地方见!别放鸽子啊!" | engagement+2 | +| 左滑 | "再说吧。" | "……好吧。你要是改主意了随时说。" | avoidance+1 | +| 上滑 | "你怎么知道的啊?" | "还不是阿哲自己发朋友圈!你不知道吗?他发了又秒删……" | engagement+1,**独家信息** | +| 下滑 | [沉默 2s] | "??你还活着吗哈哈。好了不烦你了,有事找我。" | avoidance+2 | +| 单击 | "嗯。" | "嗯是什么意思啊哈哈!行吧你休息。" | — | + +**追问 Round 3**(上滑后,小美说完"他发了又秒删"): +- 右滑 → 林夏:"他……发了什么?" → 小美:"就发了一张你们以前的照片,配了一句'值得'。两分钟就删了" → **独家信息** +- 其他 → 对话结束 + +--- + +### MSG-03 外卖小哥 · 电话 + +**类型**:来电 +**Round 1**:外卖小哥:"你好,你的麻辣烫到了,在楼下。" +**接听后自动**:林夏说"好,我下来拿。"(无选择) +**不接** → 漏接。 + +--- + +### MSG-04 阿哲 · 语音 + +**Round 1**:阿哲说"那个……我的 AirPods 好像落你那了,方便帮我寄一下吗?" + +| 方向 | 林夏说 | 阿哲反应 | 效果 | +|------|--------|---------|------| +| 右滑 | "你那边……还好吗。" | "我……挺好的。" [停顿] "你呢?" | engagement+2 | +| 左滑 | "知道了。明天寄。" | "嗯……谢了。" | HE_BACK=false, avoidance+1 | +| 上滑 | "你怎么不自己来拿。" | "我……不太方便过去。" [欲言又止] | engagement+2 | +| 下滑 | [沉默 3s] | [等 5 秒] "那……我晚点再说吧。" → 阿哲撤回消息 | HE_BACK=null, avoidance+2 | +| 单击 | "好。" | "谢谢。" | HE_BACK=false, engagement+1 | + +**右滑 Round 3**(阿哲问"你呢?"后): +- 右滑 → 林夏:"不太好。" → 阿哲:"……对不起。" [沉默] +- 左滑 → 林夏:"挺好的。" → 阿哲:"那就好。" +- 下滑 → [沉默] → 阿哲:"……那,晚安。" + +**上滑 Round 3**(阿哲说"不太方便"后): +- 上滑 → 林夏:"为什么。" → 阿哲:"我……" [长停顿] "没什么。你寄吧。" → HE_BACK=false +- 右滑 → 林夏:"算了,不问了。" → 对话结束 + +--- + +### MSG-05 HR 王姐 · 语音 + +**Round 1**:王姐说"小夏,离职流程周三前办完就行。别有压力。" + +| 方向 | 林夏说 | 王姐反应 | profile | +|------|--------|---------|---------| +| 右滑 | "谢谢王姐。" | "别客气。你还年轻,出去看看也好。这行业你知道的。" | engagement+1 | +| 左滑 | "好的,知道了。" | [无反应,对话结束] | — | +| 上滑 | "王姐……有什么建议吗?" | "你想听真话?别在这行耗了。你有本事,去试试别的。" → **独家** | engagement+2 | +| 下滑 | [沉默] | [无反应,对话结束] | avoidance+1 | +| 单击 | "好。" | [无反应,对话结束] | — | + +--- + +### MSG-06 安安 · 语音 + +**Round 1**:安安在酒吧里大喊:"我在 Helens!快来!今天 happy hour!"(背景很吵) + +| 方向 | 林夏说 | 安安反应 | profile | +|------|--------|---------|---------| +| 右滑 | "在哪儿啊?我看看吧。" | "对面那个!我给你留了位置!赶紧赶紧!" | engagement+2 | +| 左滑 | "不了,你们玩好。" | "好吧!那我给你寄一杯!" [大笑] | — | +| 上滑 | "你喝了多少了?" | "才两杯!我清醒得很!……好吧三杯。快来!" | engagement+1 | +| 下滑 | [沉默] | "喂?信号不好吗?好吧以后再约!" | avoidance+1 | +| 单击 | "嗯。" | "嗯是来还是不来啊!算了回头聊!" | — | + +--- + +### MSG-07 寝室群 · 语音 ×5 + +**Round 1**:群消息连续播放: +- 室友 A:"我升主管了!!!" +- 室友 A:"请你们吃饭!!" +- 室友 B:"我的天!恭喜!!" +- 室友 B:"必须喝一个!" +- 室友 C:"@林夏 你也来吧" + +| 方向 | 林夏说 | 群里反应 | 效果 | +|------|--------|---------|------| +| 右滑 | "恭喜恭喜!太厉害了!" | 室友 A:"哈哈谢谢!我请客!你最近怎么样啊?" | GROUP_REPLY, engagement+2 | +| 左滑 | "恭喜。" | [群里继续聊别的,没人注意到你的冷淡] | avoidance+1 | +| 上滑 | "怎么升的啊?讲讲!" | 室友 A:"说来话长!我们老板走了我就顶上了哈哈" → **独家** | GROUP_REPLY, engagement+2 | +| 下滑 | [沉默,不回群] | [群里继续热闹,但渐渐没人 @ 你了] | avoidance+2 | +| 单击 | [发一个表情] | 室友 B:"林夏发表情了哈哈" | GROUP_REPLY | + +**右滑 Round 3**(室友 A 问"你最近怎么样"后): +- 右滑 → 林夏:"挺好的~你请客我去!" → 室友们欢呼 +- 左滑 → 林夏:"还行。" → 室友 B:"你是不是有事啊?没事可以跟我们说" +- 下滑 → [沉默] → 对话自然结束 + +--- + +### MSG-08 未知号码 · 电话(悬疑) + +**类型**:来电 +**接听后**:只有呼吸声,6 秒后自动挂断。然后心跳音效。 +**无选择**——悬疑钩子。 +**不接** → 未接来电。 + +--- + +### MSG-09 小美(醉)· 语音 ×4 + +**BGM 切换为悬疑风格** + +**Round 1**(四段醉酒语音): +- "林夏……我跟你说个事" +- "阿哲那个人……大三的时候追过我" +- "但我没答应他……因为我知道你喜欢他" +- "我到现在都没跟你说……对不起" + +| 方向 | 林夏说 | 小美反应 | profile | +|------|--------|---------|---------| +| 右滑 | "小美……你没事吧。" | "我没事……我就是喝多了想告诉你……你别怪我。" [哭] | engagement+2 | +| 左滑 | "我知道了。" | [沉默 3 秒] → 小美撤回了刚才的语音 | avoidance+1 | +| 上滑 | "你……为什么现在才说。" | "因为你今天……我怕你以后恨我……" | engagement+2 | +| 下滑 | [沉默] | [10 秒后] 小美撤回了刚才的语音 | avoidance+2, nostalgia+1 | +| 单击 | "嗯。" | "你不生气吗?" [等 3 秒] "算了……当我没说" | nostalgia+1 | + +**上滑 Round 3**(小美说"怕你恨我"后): +- 右滑 → 林夏:"我不恨你。" → 小美 [大哭]:"谢谢你……谢谢你林夏" +- 左滑 → 林夏:"我得想想。" → 小美:"好……对不起" → 对话结束 +- 下滑 → [沉默] → 小美:"你还在吗?" → [长沉默] → 小美撤回 + +--- + +### MSG-10 妈妈 · 文字消息 + +**Round 1**:消息到达提示音。TTS 朗读三个字——"睡了吗?" + +| 方向 | 林夏说 | 妈妈反应 | 效果 | +|------|--------|---------|------| +| 右滑 | "没有,怎么了?" | 妈妈发语音:"没事,就是想起来问一下。今天怎么样?" | MOM_LINK, engagement+2 | +| 左滑 | "嗯,要睡了。" | 妈妈发文字:"好,早点睡。" | MOM_LINK, avoidance+1 | +| 上滑 | "妈……你能打电话给我吗?" | [电话铃响] 妈妈打来了!接听后:"怎么了夏夏?" | MOM_LINK, engagement+3 | +| 下滑 | [不回复] | [30 秒后] 妈妈发文字:"看到了回一下。" | avoidance+2 | +| 单击 | "嗯。" | 妈妈发文字:"嗯就是睡了?晚安。" | MOM_LINK | + +**上滑路径**(妈妈打来电话后,说"怎么了夏夏?你声音不对。"): +- 右滑 → 林夏:"没什么……就是有点累。" → 妈妈:"累了就回家歇几天。妈煮了排骨。" +- 上滑 → 林夏:"妈,我辞职了。" → 妈妈 [沉默 2 秒]:"辞了就辞了。回来再说。" +- 下滑 → [沉默,只有呼吸] → 妈妈:"不想说就不说。妈在这呢。" + +**右滑路径**(妈妈问"今天怎么样"后): +- 右滑 → 林夏:"还好。" → 妈妈:"那就好。别太晚了。" +- 上滑 → 林夏:"不太好。" → 妈妈:"怎么了?" → [可继续展开] + +--- + +### MSG-11 阿哲 · 电话(关键分支) + +**类型**:来电 +**Round 1**(接听后):阿哲说"那个……AirPods 的事,我能过去拿吗?就 5 分钟。" + +| 方向 | 林夏说 | 阿哲反应 | 效果 | +|------|--------|---------|------| +| 右滑 | "可以。但只有 5 分钟。" | "好,我马上过来。谢谢。" → [挂断] | HE_BACK=true | +| 左滑 | "不方便。我明天寄给你。" | "好吧……那谢了。" → [挂断] | HE_BACK=false | +| 上滑 | "为什么非要今天来?" | "我……明天要出差。" [停顿] "就 5 分钟。" | 继续选择 | +| 下滑 | [沉默 → 挂断音] | [电话断了] | HE_BACK=false | +| 单击 | "不了。" | "好。" → [挂断] | HE_BACK=false | + +**如果 HE_BACK=true** → 消息 12 之前插入门铃声 + 敲门声场景。 + +--- + +### MSG-12 周南 · 语音 + +**Round 1**:周南说"林夏,是我,周南。初三坐你后桌那个。好久没联系了,今天突然想到你。" + +| 方向 | 林夏说 | 周南反应 | 效果 | +|------|--------|---------|------| +| 右滑 | "周南!你还记得我啊!" | "当然记得。你是我们班最安静的那个。" [轻笑] | ZHOUNAN_DEPTH+1, engagement+2 | +| 左滑 | "嗯,你好。" | "嗯……好久不见。你现在在哪呢?" | ZHOUNAN_DEPTH+1, nostalgia+1 | +| 上滑 | "你怎么有我微信的?" | "你忘了吗?毕业那天你加的我。不过你可能早忘了哈哈。" | ZHOUNAN_DEPTH+1, nostalgia+2 | +| 下滑 | [沉默] | "你是不是不记得我了?没关系的。就是突然想说声嗨。" | nostalgia+1 | +| 单击 | "嗯……好久不见。" | "是啊,好久不见。" | ZHOUNAN_DEPTH+1 | + +--- + +### MSG-13 周南 · 长语音 ×4 + +**BGM 切换为周南主题(温暖吉他)** + +**Round 1**(四段语音): +- "高考那年我没考好,去复读了" +- "后来上了个二本,学了个不喜欢的专业" +- "毕业回了老家,在我爸店里帮忙" +- "今年刚来北京出差……今天是我生日" + +| 方向 | 林夏说 | 周南反应 | 效果 | +|------|--------|---------|------| +| 右滑 | "一个人在北京,还好吗?" | "还行吧。习惯了。你呢?你在北京过得好吗?" | ZHOUNAN_DEPTH+1, engagement+2 | +| 左滑 | "你讲了好多。" | "是吗?不好意思,话太多了。" [轻笑] | ZHOUNAN_DEPTH+1, nostalgia+1 | +| 上滑 | "……生日快乐。" | [沉默 2 秒] "谢谢你。你是今天第一个跟我说的。" | ZHOUNAN_DEPTH+1, ZHOUNAN_SHARE, nostalgia+2 | +| 下滑 | [沉默] | "不好意思突然说这么多……那,晚安。" | avoidance+1 | +| 单击 | "嗯,我都听到了。" | "嗯。谢谢你听我说。" | ZHOUNAN_DEPTH+1 | + +**右滑 Round 3**(周南问"你在北京过得好吗"后): +- 右滑 → 林夏:"不太好。今天也是我的坏日子。" → 周南:"那我们算是难兄难妹了。" [轻笑] → **ZHOUNAN_SHARE** +- 左滑 → 林夏:"还行吧。" → 周南:"那就好。" +- 上滑 → 林夏:"你为什么突然联系我?" → 周南:"因为……今天生日嘛,想起以前的事。想起你。" + +--- + +### MSG-14 妈妈 · 第二次电话 + +**类型**:来电 +**Round 1**:妈妈说"夏夏,你不接电话我有点担心。你还好吗?" + +接听后 → 林夏自动说"嗯,我没事。" +妈妈:"真的没事?" +- 如果 MOM_LINK=true:"好。那早点睡。妈爱你。" +- 如果 MOM_LINK=false:"你今天怎么都不理妈妈?出什么事了吗?" → 可选择回应 + +**状态**:接听 → MOM_LINK=true + +--- + +### MSG-15 时光胶囊 · 系统通知 + +**特殊消息**:系统提示音 + "3 年前的今晚,你给自己录过一段备忘录。" +- 单击播放 → 根据玩家画像播放不同版本(avoidance / engagement / nostalgia) → SELF_RECORD=true +- 双击跳过 → avoidance+2 + +*无选择交互。这条消息是独白时刻。* + +--- + +### MSG-16 妈妈 · 长语音 ×8(结局前奏) + +**条件**:MOM_LINK=true 时播放 + +妈妈讲她 24 岁那年的故事: +1. "夏夏。妈妈知道你今天电话不接。妈妈不催你,就是想跟你说一件事。" +2. "你 24 了对吧。妈妈 24 那年——你应该没听我说过——" +3. "那时候妈妈在广州,给一个香港老板做秘书,1995 年。那年我也被分手过。也被开除过。" +4. "那年我哭得特别难看。后来呢?后来就没什么后来了。我回了老家,认识了你爸,生了你。" +5. "我想跟你说,那年夏天我没死,是因为我外婆每天晚上都给我打一个电话。" +6. "她不说什么,就问我今天吃饭了没。" +7. "我现在打给你,你不接也没事。妈妈给你发个语音,你什么时候想听就听。你不用回我。" +8. "你今天接了好多电话吧。妈妈不打扰你了。晚安。" + +*无选择交互。让玩家安静听完。播放结束 → 触发结局判定。* + +--- + +## 5. 分支判定变量 | 变量 | 触发条件 | 影响 | -|---|---|---| -| **MOM_LINK** | 是否接妈妈两次电话中的至少一次 / 是否回复"睡了吗?" | 决定第 16 条妈妈的长语音是否播放 | -| **HE_BACK** | 是否答应阿哲过来拿耳机 | 决定 23:30 后是否插入一段额外门铃声场景 | -| **GROUP_REPLY** | 是否在大学群里"细回复"过 | 决定一周后玩家会不会收到大学室友的"补救"消息(彩蛋) | -| **ZHOUNAN_DEPTH** | 给周南的回应深度:单点 < 双点 < 细回复,且看选了哪个分支 | 决定结局字幕的最后一行,决定 self-3y 版本基调 | -| **SELF_RECORD** | 是否听完 3 年前自己的备忘录 | 决定结局是"林夏睡着了"还是"林夏起来去了厨房" | -| **PROFILE**(隐性) | 全场玩家行为聚合而成的"今晚画像"标签:`avoidance` / `engagement` / `nostalgia` 三选一 | 决定 self-3y 选择哪一个预录版本播放 | +|------|---------|------| +| **MOM_LINK** | 是否接妈妈两次电话中的至少一次 / 是否回复"睡了吗?" | 决定 MSG-16 是否播放 | +| **HE_BACK** | 是否答应阿哲过来拿耳机(MSG-11 右滑) | 决定 MSG-12 前是否插入门铃场景 | +| **GROUP_REPLY** | 是否在寝室群里回复过(MSG-07 非沉默) | 决定彩蛋消息 | +| **ZHOUNAN_DEPTH** | 给周南的回应深度(累计) | 决定结局字幕 + self-3y 版本 | +| **ZHOUNAN_SHARE** | 是否在周南对话中选择"分享自己"(MSG-13 上滑/右滑 R3 右滑) | 决定隐藏结局 | +| **SELF_RECORD** | 是否听完 3 年前备忘录(MSG-15) | 决定结局类型 | +| **PROFILE** | 全场行为聚合画像:avoidance / engagement / nostalgia | 决定 self-3y 版本 + 环境音 | -#### PROFILE 计分细则(程序内部维护,玩家不可见) +### PROFILE 计分细则 | 行为 | avoidance | engagement | nostalgia | -|---|---|---|---| -| 漏接电话(铃响完不动) | +2 | 0 | 0 | -| 单点回"嗯" | +1 | 0 | 0 | -| 双指上滑"已读不回" | +2 | 0 | 0 | -| 长按 1 秒"细回复" | 0 | +2 | 0 | -| 细回复中选"主动追问"方向 | 0 | +2 | 0 | -| 细回复中选"分享自己"方向 | 0 | +1 | +1 | -| 反复重听同一条消息 | 0 | 0 | +2 | -| 听完小美醉语音、周南长讲述未做任何回应 | +1 | 0 | +1 | +|------|-----------|------------|-----------| +| 漏接电话(不动) | +2 | 0 | 0 | +| 双击跳过消息 | +2 | 0 | 0 | +| 单击回"嗯" | +1 | 0 | 0 | +| 左滑冷淡回应 | +1 | 0 | 0 | +| 下滑沉默 | +2 | 0 | +1 | +| 右滑温暖回应 | 0 | +2 | 0 | +| 上滑追问 | 0 | +2 | 0 | +| Round 3 中选择"分享自己" | 0 | +1 | +1 | -最终取分数最高的那一项作为 PROFILE 标签。 +最终取分数最高项作为 PROFILE 标签。 -### 4.2 结局组合(4 种主调 + 隐藏) +--- -#### 结局 A · "明天就明天吧" -**触发**:MOM_LINK = true,SELF_RECORD = true,至少回了周南或群里 1 条 -- 02:00 妈妈语音播完。 -- 林夏从床上起身,系统播放她走到厨房、烧水、坐在小板凳上的环境声。 -- 黑屏中第一次出现一行字幕:"明天再说。" -- 烧水壶滴的一声完成 → 游戏结束。 +## 6. 结局系统(5 种) -#### 结局 B · "AirPods 我寄给你" -**触发**:HE_BACK = false(拒了阿哲),同时回过周南至少 1 条 -- 林夏在 01:30 给阿哲发了一条文字"我寄给你,地址发我"。 +### 结局 A · "明天就明天吧" +**触发**:MOM_LINK=true + SELF_RECORD=true + 至少回了周南或群里 1 条 +- 妈妈语音播完。林夏起身,环境声:走到厨房、烧水、坐在小板凳上。 +- 字幕:"明天再说。" +- 烧水壶滴的一声 → 游戏结束。 + +### 结局 B · "AirPods 我寄给你" +**触发**:HE_BACK=false + 回过周南至少 1 条 +- 林夏给阿哲发了文字"我寄给你,地址发我"。 - 字幕:"寄出。" -- 没有更多剧情,也没有更多情绪。这是一个"清醒地走"的结局。 +- 一个"清醒地走"的结局。 -#### 结局 C · "全部已读" -**触发**:未读数 = 0(每一条都听了,但回了 ≤ 2 条) -- 02:30 屏幕黑着,TTS 念出:"今晚共 16 条新消息,已读 16,未回复 14。" -- 一段长沉默。 -- 字幕:"今晚就这样。" +### 结局 C · "全部已读" +**触发**:未读数=0 且回复≤2 条 +- TTS:"今晚共 16 条新消息,已读 16,未回复 14。" +- 长沉默。字幕:"今晚就这样。" -#### 结局 D · "明天再开机" -**触发**:未读数 ≥ 8(你大半都没听) -- 01:00 系统提示电量 1%。 -- 屏幕"亮"了一下(其实是触感震动 + 一段非常短的白噪),然后彻底黑了。 -- 第二天清晨的鸟叫开始播放。 -- 字幕:"早上好。" +### 结局 D · "明天再开机" +**触发**:未读数≥8 +- 系统提示电量 1%。触感震动 + 白噪 → 彻底黑。 +- 第二天清晨鸟叫。字幕:"早上好。" -#### 结局 E · 隐藏 · "周南,我也记得你" -**触发**:ZHOUNAN_DEPTH ≥ 2 次细回复 且 至少一次选择"分享自己"方向 且 PROFILE = `engagement` 或 `nostalgia` +### 结局 E · 隐藏 · "周南,我也记得你" +**触发**:ZHOUNAN_DEPTH≥2 + ZHOUNAN_SHARE + PROFILE=engagement 或 nostalgia - 周南秒回:"那我下次出差来北京,请你吃饭好不好。" -- 系统播放一段**林夏的轻笑**——这段笑声**预录**于 self-3y 同一录音棚(同一克隆音色),代表"3 年前那个还能笑出来的林夏,今天又笑了一次"。 +- 系统播放林夏的轻笑——这是全游戏唯一一次林夏主动笑出声。 - 字幕:"好。" -- 这是全片唯一一个**林夏发出声音**的结局——前面所有戏她都是"听者"。 --- -## 5. 关键场景脚本示例(节选) +## 7. 音频设计 -### 5.1 第 04 条 · 21:30 · 阿哲的微信语音 +### 7.1 音频资产分类 -> **铺垫**:21:25 玩家一直坐在床上没动。耳机里只有冰箱嗡嗡声、远处地铁过站声。 +| 类别 | 说明 | 数量 | 制作方案 | +|------|------|------|---------| +| **林夏语音** | 主角对话回应(通用 + 专属) | ~55 条, ~100s | VoxCPM2 声音克隆 | +| **NPC Round 1 语音** | 各角色主要台词 | ~30 条, ~300s | VoxCPM2 声音克隆 | +| **NPC Round 2/3 反应** | 对话分支反应 | ~54 条, ~205s | VoxCPM2 声音克隆 | +| **self-3y 三版** | 时光胶囊备忘录 ×3 | 3 条, ~150s | VoxCPM2(linxia 音色) | +| **UI 短音效** | 提示音、选择反馈、打字/发送 | ~12 种 | Web Audio 合成(无需文件) | +| **BGM** | 4 段主题音乐 | 4 段 | Suno / Udio 生成 | +| **环境音** | 卧室夜噪、酒吧、街声 | 4-5 段 | Freesound (CC0/CC-BY) | +| **角色铃声** | 来电铃声 | 3-4 段 | 简单合成或 Suno 短旋律 | -**[震动 · 短促 · 微信"咚"]** +### 7.2 TTS 方案:VoxCPM2 本地声音克隆 -**TTS(系统女声 · 平静)**:阿哲,发来 1 条语音。8 秒。 +所有角色语音使用 **VoxCPM2**(本地部署,无需外部 API),通过声音克隆实现一致的角色音色。 -> 玩家选择 —— -> A. 单点:播放 -> B. 不动:留为未读 +**服务启动**: +```bash +bash /home/xsl/AutoVideo/start-voxcpm.sh +# uvicorn 服务: http://127.0.0.1:8000 +``` -**A · 播放:** -**[阿哲 · TTS · 男声 · 略带歉意但很疏离]**: -"嗯……夏夏。我那个 AirPods 充电盒,好像落你抽屉里了。你方便的时候帮我寄一下吗,工位还要用。地址我等下发你。" +**API 接口**: +- `POST /v1/voices` — 注册参考音频(wav_base64)→ 返回 voice_id +- `POST /v1/speech` — 合成语音(text + voice_id)→ 返回 WAV +- 输出格式:48kHz 16-bit PCM WAV +- 参数:cfg_value(默认 2.0)、inference_timesteps(默认 10) -**[3 秒沉默]** +**声音克隆流程**: +``` +参考音频 (voice/{角色}/*.wav) + → 转换为 16kHz mono WAV + → POST /v1/voices 注册 → voice_id + → POST /v1/speech 合成台词 → WAV + → ffmpeg atempo 调速 → 最终音频 +``` -**TTS(系统)**:是否回复? +### 7.3 角色音色配置 -> 玩家选择 —— -> 1. **单点** → 系统发出"嗯。"(最快、最敷衍) -> 2. **双点** → 系统发出"好,明天寄给你。"(一句标准回执) -> 3. **长按 1 秒**进入细回复,TTS 依次念出: -> - 一…… "好,明天寄。" -> - 二…… "你怎么不自己来拿。" -> - 三…… "你那边……都好吗。" -> - 四…… (什么都不说) -> -> 玩家在听到目标时朝对应方向滑(左 1 / 右 2 / 上 3 / 下 4)。 -> 4. **双指上滑**:已读不回 -> 5. **不动**:保持未读 +| 角色 | 参考音频目录 | 语速 | 音色描述 | +|------|-------------|------|---------| +| **林夏** | `voice/linxia/` | 1.00 | 24 岁女生,疲惫但清醒,克制但有温度 | +| 妈妈 | `voice/mom/` | 0.92 | 50 上下,温柔不黏腻,西部口音轻微 | +| 阿哲 | `voice/azhe/` | 1.05 | 27 岁,城市男生,理性偏冷 | +| 小美 | `voice/xiaomei/` | 1.10 | 24 岁,活泼,语速快 | +| 小美(醉) | `voice/xiaomei/` | 0.95 | 同音色,降速模拟醉态 | +| 周南 | `voice/zhounan/` | 0.95 | 26 岁,温和略拘谨 | +| HR 王姐 | `voice/hr/` | 1.00 | 38 岁,职场化,疲惫的善意 | +| 安安 | `voice/anan/` | 1.15 | 23 岁,活泼,比小美更嗲 | +| 室友 A | `voice/dorm_a/` | 1.10 | 年轻女声,兴奋 | +| 室友 B | `voice/dorm_b/` | 1.00 | 年轻女声,随和 | +| 室友 C | `voice/dorm_c/` | 0.95 | 年轻女声,温柔 | +| 外卖小哥 | `voice/delivery/` | 1.20 | 30 岁,麻利 | +| self-3y | `voice/linxia/` | 1.15 | 3 年前的林夏,更明朗(同一音色,不同语速) | -**B · 不动:** -**[10 秒后,耳机左声道极轻的键盘敲击声 · 持续 8 秒 · 然后停]** -**TTS(系统女声)**:阿哲撤回了一条消息。 +### 7.4 批量合成脚本 + +```bash +# 启动 VoxCPM2 服务 +bash /home/xsl/AutoVideo/start-voxcpm.sh + +# 使用 WAV 参考音色,批量合成所有台词 +python audio/batch_tts_voxcpm.py --source voice + +# 只合成特定角色 +python audio/batch_tts_voxcpm.py --source voice --role linxia + +# 干跑查看任务列表 +python audio/batch_tts_voxcpm.py --source voice --dry-run +``` + +### 7.5 UI 音效(Web Audio 合成,无需文件) + +所有 UI 音效通过 `SFX_UI` 对象在浏览器端实时合成: + +| 音效 | 用途 | 实现方式 | +|------|------|---------| +| `tap` | 单击确认 | 880Hz, 45ms | +| `swipe` | 滑动确认 | 660Hz, 60ms | +| `error` | 无效操作 | 200Hz square, 100ms | +| `notify` | 新消息到达 | 523Hz + 659Hz 双音 | +| `choiceReady` | 进入选择 | 440→523→659 三音上行 | +| `choiceWarm` | 右滑温暖 | 440→659Hz 上行 | +| `choiceCold` | 左滑冷淡 | 523→349Hz 下行 | +| `choiceCurious` | 上滑追问 | 523→784Hz 快速上行 | +| `choiceSilent` | 下滑沉默 | 330Hz 极轻 | +| `typing` | 打字中 | 6 次随机频率短脉冲 | +| `send` | 发送 | 400→900Hz 上扫 | +| `ending` | 结局 | 三音渐弱 | + +### 7.6 BGM 与环境音 + +| 曲目 | 用途 | 描述 | +|------|------|------| +| M1 | 全程低氛围 Loop | lo-fi midnight bedroom, soft piano, light vinyl noise | +| M2 | MSG-08/09 悬疑 | subtle ambient suspense, low drone | +| M3 | MSG-13 周南段落 | gentle indie folk, fingerpicked guitar | +| M4 | MSG-16 结尾 | warm cinematic piano, single melody line | + +环境音根据 PROFILE 累计分数动态调整: +- engagement 倾向:柔和雨声、远处隐约的音乐 +- avoidance 倾向:安静、时钟滴答、远处车声 +- nostalgia 倾向:梦幻混响、远处风铃 --- -### 5.2 第 09 条 · 22:45 · 小美的醉语音(节选) +## 8. 技术实现 -**[小美 · TTS · 女声 · 醉态语调 · 背景酒吧嘈杂]**: -"夏夏夏夏夏。你睡了吗?我跟你说,我跟你说啊……今天我喝多了…… -你别嫌我烦哈,我跟你说…… -其实啊……阿哲他,大三那年,他来追过我。 -我没答应他。我那时候就觉得他这人,呵,差点意思。 -你跟他在一起的时候,我没说,因为说了也没意义嘛对吧。 -现在他甩了你,我才敢说。 -夏夏,你听到了吗? -你别恨我啊。 -…… -喂? -(长 5 秒沉默) -(突然小声)算了。" +### 8.1 技术栈 -**[消息结束 · 8 秒后系统提示]** -**TTS**:小美撤回了刚才的语音。但你已经听过了。 +纯前端 Web 应用(单 HTML + JS),无需后端服务器: +- **手势识别**:`gestures.js` — 6 种手势(单击、双击、四方向滑动) +- **音频管理**:`audio.js` — Web Audio API 合成 + HTML5 Audio 播放 +- **触觉反馈**:`haptics.js` — Vibration API +- **剧情数据**:`story.js` — 16 条消息的完整对话树 +- **游戏引擎**:`engine.js` — 状态机 + 异步对话流程 +- **行为画像**:`profile.js` — PROFILE 计分 + 状态变量 ---- +### 8.2 状态机 -### 5.3 第 16 条 · 02:00 · 妈妈的长语音(结尾,节选) +``` +idle → notification/ringing → playing → choosing → responding → reacting → [choosing | idle] → ending +``` -**[妈妈 · TTS · 中年女声 · 温柔但克制 · 4 分 17 秒]** +| 状态 | 说明 | 可用手势 | +|------|------|---------| +| idle | 等待消息 | — | +| notification | 消息到达提示中 | 单击(播放)、双击(跳过) | +| ringing | 来电铃声中 | 单击(接听)、左滑(拒接) | +| playing | NPC 语音播放中 | 双击(跳过语音) | +| choosing | 等待玩家选择 | 右滑、左滑、上滑、下滑、单击 | +| responding | 林夏语音播放中 | 双击(跳过) | +| reacting | NPC 反应播放中 | 双击(跳过) | +| ending | 结局播放中 | — | -> **TTS 配置说明**:本段建议使用克隆 + 高情绪化模型,必要时分句录制后做轻拼接,以承载这段戏的全部重量。 +### 8.3 异步对话流程 -"夏夏。 -妈妈知道你今天电话不接。妈妈不催你,就是想跟你说一件事。 -你 24 了对吧。妈妈 24 那年——你应该没听我说过—— -那时候妈妈在广州,给一个香港老板做秘书,1995 年。 -那年我也被分手过。也被开除过。一模一样的春天。 -那年我哭得特别难看。 -后来呢?后来就没什么后来了。 -我回了老家,认识了你爸,生了你。 -我跟你说这个不是想跟你说"什么都会过去"—— -我想跟你说,那年夏天我没死,是因为我外婆每天晚上都给我打一个电话。 -她不说什么,就问我"今天吃饭了没"。 -我现在打给你,你不接也没事。 -妈妈给你发个语音,你什么时候想听就听。 -你不用回我。 -你今天接了好多电话吧。 -妈妈不打扰你了。 -晚安。" +```javascript +async _handleChoice(dir) { + const choice = currentMsg.choices[dir]; -**[消息结束 · 长沉默 · 远处一辆夜班公交驶过 · 楼下野猫叫了一声]** + // 1. 情感音效 + SFX_UI[emotionSfx[dir]](); ---- + // 2. 打字 → 发送 + state = 'responding'; + SFX_UI.typing(); + await sleep(800); + SFX_UI.send(); + await sleep(300); -## 6. 音频设计(含 AI 生成方案) + // 3. 林夏的回应 + if (choice.linxia) { + await playVoiceAsync([choice.linxia]); + } else { + await sleep(1500); // 沉默 + } -### 6.1 音频资产分类与制作方案 + // 4. NPC 反应 + if (choice.npcReact) { + state = 'reacting'; + await playVoiceAsync(choice.npcReact); + } -| 类别 | 数量 | 推荐工具 | 备注 | -|---|---|---|---| -| **A. 角色语音(TTS)** | 9 个角色 / 约 25 分钟净时长 | **MiniMax Speech 02 / 字节豆包 Voice / ElevenLabs v3** | 中文优先 MiniMax 与豆包,英文/特殊情绪可备 ElevenLabs | -| **B. 关键长语音** | 妈妈 4 分 17 秒 + 小美 1 分 20 秒 + 周南 4 段 | **MiniMax 克隆 + 分段录制 + 轻拼接** | 这两段决定全片成败,预算优先放这里 | -| **C. self-3y 三版预录 + self-laugh** | 3 年前自己 ×3 版 (~50s/版) + 隐藏笑声 ~3s | MiniMax 同基底音色,emotion 区分;笑声需真人录或克隆生成 | 替代原"玩家麦克风录入"方案,保证零权限 | -| **D. 主题音乐** | 4 段(开场低氛围、悬疑钩子、温暖、结尾) | **Suno V4 / Udio v2 / Stable Audio 2.5** | 每段 60–120 秒,做 Loop 版与结尾版两个变体 | -| **E. 短音效(UI 类)** | 微信咚、震动、铃声、消息提示 | **ElevenLabs Sound Effects** 或微信公开音源 | 注意微信原生提示音版权——建议用近似不雷同的合成版本 | -| **F. 短音效(拟音类)** | 烧水壶、键盘、外卖敲门、撤回 swoosh | **ElevenLabs Sound Effects** | 一句 prompt 如 "kettle whistle, close mic, kitchen ambience",效果已经够用 | -| **G. 环境长底噪** | 合租屋夜噪、楼下街声、地铁远声、酒吧嘈杂 | **不推荐 AI**——用 [Freesound](https://freesound.org)(CC0/CC-BY)或订阅 [Epidemic Sound](https://www.epidemicsound.com) | AI 生成长 ambience 现阶段质量不稳定,会"漂",长音轨建议用真实采样 | -| **H. 角色铃声** | 9 个不同角色铃声 | **Suno** 短旋律生成 | 每个角色独立铃声让玩家"听铃辨人" | + // 5. 后续对话(Round 3) + if (choice.followUp) { + state = 'playing'; + await playVoiceAsync(choice.followUp.audio); + if (choice.followUp.choices) { + enterChoosing(choice.followUp.choices); + return; + } + } -### 6.2 TTS 角色配音清单(含建议音色与情绪 prompt) + // 6. 推进到下一条 + proceedToNext(); +} +``` -| 角色 | 推荐 TTS | 音色描述 | 情绪基调 | -|---|---|---|---| -| 妈妈 | MiniMax `Chinese (Mandarin)_Warm_Ladylike_Aunt` 或克隆 | 50 上下,温柔不黏腻,西部口音轻微 | 平稳、克制、第 16 条要"故作平静" | -| 阿哲 | 豆包 `灿烂青年男声` / MiniMax `Patient_Man` | 27 岁,城市男生,理性偏冷 | 全程"礼貌但已抽离" | -| 小美 | MiniMax `Lively_Girl` | 24 岁,活泼,偶尔语速过快 | 醉酒段需开到高情绪强度 | -| 周南 | MiniMax `Gentleman` 偏年轻调 | 26 岁,二三线城市男生,温和略拘谨 | 紧张但克制的喜悦 | -| HR 王姐 | 豆包 `知性女声` | 38 岁,职场化,疲惫的善意 | 公事公办里夹一点点歉意 | -| 室友安安 | MiniMax `Cute_Girl` | 23 岁,比小美更"嗲"一点 | 自顾自的快乐 | -| 室友 A/B/C(群) | MiniMax 不同年轻女声 × 3 | 区分度优先 | 中性日常 | -| 外卖小哥 | 豆包 `北方青年男声` | 30 岁,普通话+少许河南腔 | 麻利,工作语气 | -| 系统 TTS | 豆包 `通用女声-柔` | 中性,类似 iOS Siri 但更轻 | 全程平静无情绪 | +### 8.4 跳过机制 -### 6.3 音乐设计 +在 `responding` 或 `reacting` 状态中双击: +- 设置 `_skipRequested = true` +- 中断当前语音播放 +- 异步函数在每个步骤检查 skip 标志,快速跳到下一阶段 -| 曲目 | 用途 | Suno Prompt 草案 | 时长 | -|---|---|---|---| -| **M1 · 21:00 主题** | 开场 + 全程低氛围 Loop | `lo-fi midnight bedroom, soft piano, light vinyl noise, rain outside, melancholy but warm, no drums, 70bpm` | 90s loop | -| **M2 · 悬疑过门** | 22:30 未知号码 | `subtle ambient suspense, low drone, single hanging piano note, sparse, no melody, 60bpm` | 30s | -| **M3 · 周南段落** | 23:45 周南讲十年 | `gentle indie folk, fingerpicked guitar, soft male hum no lyrics, hopeful nostalgic, 75bpm` | 120s | -| **M4 · 结尾** | 02:00 妈妈语音后 + 字幕 | `warm cinematic piano, single melody line, very slow, like dawn, 50bpm` | 90s | +### 8.5 隐私承诺 -### 6.4 音效是否能 AI 生成 · 判定表 - -| 音效类型 | AI 是否够用 | 推荐方案 | -|---|---|---| -| **短促 UI 音**(提示、震动、点击) | ✅ 完全够 | ElevenLabs SFX 一句 prompt 搞定 | -| **拟音类**(敲门、烧水、撤回 swoosh、键盘、玻璃放下) | ✅ 够用 | ElevenLabs SFX,需多生成几条选最好 | -| **角色化音效**(具体某品牌的提示音、微信原音) | ⚠️ 不能用真的,但可以做"近似不雷同" | AI 合成 + 人工调音,避免侵权 | -| **环境长底噪 30 秒以上** | ⚠️ 质量不稳,长曲会"漂" | 用 Freesound 免费 / Epidemic Sound 订阅 | -| **真实城市夜景采样**(远地铁、夜班车、野猫) | ❌ AI 还不行,"假" | 必须用真采样素材库 | - -### 6.5 总音频预算估算(独立团队) - -| 项 | 工具 | 月费 / 一次性 | 备注 | -|---|---|---|---| -| MiniMax TTS | API | ¥200–500(按字数计) | 主力 | -| 豆包 TTS | API | ¥100–300 | 备用 + 系统女声 | -| ElevenLabs(含 SFX) | Creator $22/月 | ≈ ¥160 | 一个月内完工 | -| Suno Pro | $10/月 | ≈ ¥75 | 4 段曲足够 | -| Epidemic Sound | $15/月(个人) | ≈ ¥110 | 环境采样必备 | -| **合计** | | **≈ ¥600–1100** | 一个月内全套出片 | - -> 与传统配音 + 原创配乐方案(保守 5–8 万)相比,AI 方案把音频预算压到 1% 以内。这是这个题材"能做出来"的关键前提。 - ---- - -## 7. 技术实现要点 - -### 7.1 全黑屏的可行性 -- iOS:可以通过 `UIScreen.main.brightness = 0` 强制最低亮度,但不能完全黑——**建议用全屏 0,0,0 黑色 View 覆盖**。 -- Android:同理,用 `Window.attributes.screenBrightness = 0.01` + 黑色 View。 -- 实际"全黑"是 OLED 屏幕的福利——OLED 显示纯黑时像素不发光,体感真的全黑。**LCD 屏会有背光**——这点要在游戏开始前提示玩家。 - -### 7.2 触控手势识别 -- 用原生 GestureDetector(Android)/ UIGestureRecognizer(iOS)。 -- 区分 10 种基础手势:单点 / 双点 / 长按 1 s / 长按 5 s(强制挂断)/ 左滑 / 右滑 / 上滑 / 下滑 / 双指上滑 / 三指长按。 -- 每种手势必须有**强反馈**(Haptic + 极短提示音),让玩家"摸黑也知道自己点对了"。 -- 手势冲突处理: - - 单点 vs 双点:单点延迟判定 250ms。 - - 长按 1s vs 长按 5s:到达 1s 触发"细回复入口"提示音,玩家可松手进入;继续长按到 5s 触发"强制挂断"。 - - 滑动方向阈值:30 px 起算,40° 内归类为该方向。 - -### 7.3 玩家"行为画像"识别(替代原"麦克风情绪识别") - -> 本作**全程不要求任何麦克风、摄像头、位置、通讯录权限**。 -> 玩家的"情绪状态"完全通过**触摸行为模式**做轻量统计推导。 - -- 每个玩家行为打入 §4.1 PROFILE 计分表。 -- 3 个维度记录在本地 JSON(与 §4.1 PROFILE 计分表对应): - - `avoidance`(回避度) - - `engagement`(投入度) - - `nostalgia`(怀旧倾向) -- 这些数据**只在本地**存储,从不上传任何服务器。 -- 用于: - 1. 决定 self-3y 播放哪个预录版本 - 2. 决定结局走向 - 3. 决定字幕的最后一行措辞 - -### 7.4 真实时间挂钩 -- 游戏内 21:00–02:00 不强制对应现实时间,但**夜间启动有特殊彩蛋**:现实里 22 点之后启动,游戏开场会多一句 TTS:"你也还醒着啊。" - -### 7.5 隐私承诺(重要卖点) - -- 本作**不申请任何敏感权限**: - - ❌ 不要麦克风 - - ❌ 不要摄像头 - - ❌ 不要位置 - - ❌ 不要通讯录 / 短信 / 通知历史 - - ❌ 不要联网(全程离线可玩) +本作**不申请任何敏感权限**: +- ❌ 不要麦克风、摄像头、位置、通讯录 +- ❌ 不需要联网(全程离线可玩) - ✅ 仅使用:触觉震动 + 触控输入 + 系统音量 -- 这是这款游戏的承诺,也是上架商店时**最有力的宣传点**之一:「一款不会动你任何东西的游戏」。 +- 玩家行为数据只在本地存储,从不上传。 --- -## 8. 商业化 / 包装思路 +## 9. 音频资产清单 -### 8.1 定位 -- **付费单机叙事小品**,定价 12–18 元(学生友好)。 -- 不做内购、不做广告。 -- 主打"晚上戴上耳机的 60 分钟"。 +### 林夏语音 -### 8.2 渠道 -- iOS App Store / Google Play 主推。 -- TapTap / B 站游戏首发——用户画像最重叠。 -- 抖音/小红书素人传播——"那个全黑屏的游戏让我哭了"是天然短视频选题。 +| 类别 | 数量 | 平均时长 | 总时长 | +|------|------|---------|-------| +| 通用短句(嗯、好、知道了等) | ~10 条 | 1s | ~10s | +| 消息专属台词 | ~45 条 | 2s | ~90s | +| **合计** | **~55 条** | | **~100s** | -### 8.3 长线 -- 同 IP 可拓展"林夏"宇宙:明年她 25 岁 / 她妈妈的故事 / 她闺蜜的故事,做成系列。 -- 真实音乐 / 品牌联名空间大(耳机品牌、深夜餐饮、心理咨询平台)。 +### NPC 语音(Round 1 主台词 + Round 2/3 反应) -### 8.4 关键卖点提炼(用于 store 文案) -- "屏幕坏了,但她的一夜还没结束。" -- "16 条消息,5 个结局,60 分钟。" -- "戴上耳机。把灯关了。" -- "她不是你,但今晚你要替她活一遍。" +| 角色 | Round 1 | Round 2/3 | 总条数 | 总时长 | +|------|---------|-----------|-------|-------| +| 妈妈 | 11 段 | 10 条 | ~21 条 | ~340s | +| 小美 | 7 段 | 10 条 | ~17 条 | ~120s | +| 阿哲 | 2 段 | 8 条 | ~10 条 | ~50s | +| 周南 | 5 段 | 10 条 | ~15 条 | ~80s | +| HR 王姐 | 1 段 | 3 条 | ~4 条 | ~20s | +| 安安 | 1 段 | 5 条 | ~6 条 | ~20s | +| 寝室群 | 5 段 | 8 条 | ~13 条 | ~50s | +| 外卖小哥 | 1 段 | 0 | ~1 条 | ~5s | +| self-3y | 3 版 | 0 | ~3 条 | ~150s | +| **合计** | | | **~90 条** | **~835s** | + +### 总计 + +- 林夏 ~55 条,~100 秒 +- NPC ~90 条,~835 秒 +- **总语音约 145 条,约 15.5 分钟** +- UI 音效:12 种,全部 Web Audio 合成 +- BGM:4 段 +- 环境音:4-5 段 --- -## 9. 风险与对策 +## 10. 项目文件结构 + +``` +blind/ +├── game/ +│ ├── index.html # 入口 +│ └── js/ +│ ├── main.js # 启动 + 事件绑定 +│ ├── gestures.js # 6 种手势识别 +│ ├── haptics.js # 触觉反馈 +│ ├── audio.js # 音频管理 + UI 合成音效 +│ ├── story.js # 16 条消息剧情数据 +│ ├── engine.js # 游戏引擎(状态机 + 对话流程) +│ ├── profile.js # 行为画像 + 状态变量 +│ └── utils.js # 工具函数(sleep, dbg 等) +├── audio/ +│ ├── mp3/ +│ │ ├── linxia/ # 林夏语音(当前 edge-tts 版本) +│ │ └── react/ # NPC 反应语音(当前 edge-tts 版本) +│ ├── 00_raw/tts/ # VoxCPM2 生成原始 WAV +│ └── batch_tts_voxcpm.py # VoxCPM2 批量合成脚本 +├── voice/ # VoxCPM2 参考音色(WAV) +│ ├── linxia/ # 林夏 +│ ├── mom/ # 妈妈 +│ ├── azhe/ # 阿哲 +│ ├── xiaomei/ # 小美 +│ ├── zhounan/ # 周南 +│ ├── hr/ # HR 王姐 +│ ├── anan/ # 安安 +│ ├── dorm_a/ # 室友 A +│ ├── dorm_b/ # 室友 B +│ ├── dorm_c/ # 室友 C +│ └── delivery/ # 外卖小哥 +├── gdd/ +│ └── redesign_v2.md # v2 重构设计文档 +└── 林夏.md # 本文档(总设计纲要) +``` + +--- + +## 11. 风险与对策 | 风险 | 对策 | -|---|---| -| **题材敏感**——分手 + 失业 + 催婚的叠加可能让部分玩家代入过深而不适 | 开场加心理预警卡 + 提供"5 分钟试玩版"让玩家自评是否适合继续 | -| **TTS 长情绪戏(妈妈 4 分钟)质感不够** | 这一段必须用克隆 + 分句录制 + 后期混音;预留预算的 30% 给这段戏 | -| **微信交互形态被认为"擦边"** | 法务先过——所有 UI 元素自研、提示音原创合成、避免任何 logo/微信字样 | -| **女性玩家的真实代入感** | 编剧组必须有 24 岁左右的女性参与第一稿;不能由 35+ 男性主笔写林夏 | -| **60 分钟无目标导致弃游** | 22:30 未知号码(悬疑钩子)必须强存在感——铃声不一样、震动模式不一样,让玩家"必须想知道是谁" | -| **「细回复」长按 1 s 学习曲线** | 教学关(小美第一通微信语音)必须**强引导**:单点先教学,双点再教学,长按最后教学;每一步玩家做对都给"叮"的正向反馈 | -| **滑动方向误判导致选错回复** | 提供"听完 4 个选项再次长按可重听"的兜底;选错后 2 秒内双指捏合 = 撤回选择 | -| **OLED / LCD 屏幕差异导致"全黑"体验不一致** | 游戏开场触摸式问答:"你现在能看到屏幕吗?看不到请单点/还能看到一点请双点",分支引导玩家盖一块布或把亮度手动调到最低 | +|------|------| +| VoxCPM2 长情绪戏(妈妈 4 分钟)质感不够 | 分段录制 + 后期拼接,预留调参时间 | +| 声音克隆音色不够区分 | 每角色选择差异大的参考音频 + 调速区分 | +| 60+ 分支语音管理复杂 | batch_tts_voxcpm.py 统一管理,命名规范 | +| 手势误判导致选错 | 滑动阈值 30px + 40°范围;双击间隔 250ms 内 | +| 玩家不理解手势含义 | MSG-02(小美教学关)强引导,每步正向反馈 | +| 女性玩家代入感 | 编剧组需有 24 岁左右女性参与 | +| 题材敏感 | 开场加心理预警 + 提供试玩版 | --- -## 10. 制作里程碑(独立团队 4 人 · 6 个月) +## 12. 制作流程 -| 阶段 | 时长 | 关键交付 | -|---|---|---| -| **T1 · 剧本定稿** | 4 周 | 16 条全脚本 + 5 结局完整对白 + 全部「细回复」4 选项词条 + 5 主角人设 | -| **T2 · 音频生成** | 6 周 | 全部 TTS 配音 + self-3y 三个预录版本 + 4 段 BGM + SFX 库 + 环境采样整理 | -| **T3 · 程序原型** | 4 周 | 黑屏触控 + 8 种手势识别 + 消息系统 + PROFILE 行为画像统计 | -| **T4 · 整合调试** | 6 周 | 16 通关卡逐一接入、分支判定、结局拼接 | -| **T5 · 内测 + 心理咨询顾问审稿** | 3 周 | 50 名年轻女性测试 + 1 位心理咨询师审定 | -| **T6 · 发布 + 营销** | 3 周 | App Store + TapTap 上架 + 小红书种草 | +| 阶段 | 关键交付 | +|------|---------| +| **剧本定稿** | 16 条全脚本 + 5 结局完整对白 + 所有分支台词 | +| **参考音色准备** | voice/ 下 12 个角色参考音频确认 | +| **VoxCPM2 批量合成** | ~145 条语音 + 语速调整 + 质检 | +| **BGM + 环境音** | 4 段 BGM + 环境采样 | +| **程序集成** | story.js 对接最终音频路径 + 全流程调试 | +| **测试** | 全 5 结局通关测试 + 分支覆盖 | --- -## 附录 · 一句话给主创团队 +## 附录 · 一句话给主创 -> "这不是一个让女孩们更难过的游戏。这是一个让她们在难过的时候,知道**有人也曾这样难过、并且活下来**的游戏。" +> "这不是一个让女孩们更难过的游戏。这是一个让她们在难过的时候,知道有人也曾这样难过、并且活下来的游戏。"