diff --git a/README.md b/README.md index ca9edb7a2d6773697a5e1e48f647cdfe30c4d2ce..beb088013aae05a8fad2460c686c53292259539c 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ pinned: false | # | 里程碑 | 状态 | |---|---|---| | 1 | 写 README:计划 / 大纲 / 步骤 / 技术方案 | ✅ | -| 2 | 原创旁白脚本 + 分镜(12 幕) | ⏳ | -| 3 | 生成 AI 配音(edge-tts 中文男声)+ 自动测时长、生成时间轴 | ⏳ | +| 2 | 原创旁白脚本 + 分镜(12 幕) | ✅ | +| 3 | 生成 AI 配音(edge-tts 中文男声)+ 自动测时长、生成时间轴 | ✅(57 句,总长约 4分47秒) | | 4 | 动画引擎:Canvas 角色(猴子/村民/商人/助手)、镜头、转场、字幕 | ⏳ | | 5 | 12 幕场景动画实现(横屏 16:9 + 竖屏 9:16 双布局) | ⏳ | | 6 | 音频:程序化配乐(Web Audio)+ 音效 + 旁白混音(OfflineAudioContext) | ⏳ | diff --git a/monkey-fable/audio/vo/check-00.mp3 b/monkey-fable/audio/vo/check-00.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ac369c271241d50538a0a5729b0c0a304d3954f9 Binary files /dev/null and b/monkey-fable/audio/vo/check-00.mp3 differ diff --git a/monkey-fable/audio/vo/check-01.mp3 b/monkey-fable/audio/vo/check-01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..5353939d58560678cd2969065a7d981c92ef1936 Binary files /dev/null and b/monkey-fable/audio/vo/check-01.mp3 differ diff --git a/monkey-fable/audio/vo/check-02.mp3 b/monkey-fable/audio/vo/check-02.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..037fce61c9cea49cf40122164af655e2b9b285b1 Binary files /dev/null and b/monkey-fable/audio/vo/check-02.mp3 differ diff --git a/monkey-fable/audio/vo/check-03.mp3 b/monkey-fable/audio/vo/check-03.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b5b033451e0fe2134c4f756d5d410529827fb2d5 Binary files /dev/null and b/monkey-fable/audio/vo/check-03.mp3 differ diff --git a/monkey-fable/audio/vo/fifty-00.mp3 b/monkey-fable/audio/vo/fifty-00.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..efa0c442b8bb635696e504309db724806054b7e0 Binary files /dev/null and b/monkey-fable/audio/vo/fifty-00.mp3 differ diff --git a/monkey-fable/audio/vo/fifty-01.mp3 b/monkey-fable/audio/vo/fifty-01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..aea65ea9cf5981d5aa9370575a5cd1328cdce2f2 Binary files /dev/null and b/monkey-fable/audio/vo/fifty-01.mp3 differ diff --git a/monkey-fable/audio/vo/fifty-02.mp3 b/monkey-fable/audio/vo/fifty-02.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..941dcc4b5fae6854d522c98fad04a1863340f3e2 Binary files /dev/null and b/monkey-fable/audio/vo/fifty-02.mp3 differ diff --git a/monkey-fable/audio/vo/fifty-03.mp3 b/monkey-fable/audio/vo/fifty-03.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..930429bd9fcd34862f6b754c8a37734c7d01967d Binary files /dev/null and b/monkey-fable/audio/vo/fifty-03.mp3 differ diff --git a/monkey-fable/audio/vo/fifty-04.mp3 b/monkey-fable/audio/vo/fifty-04.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..bd6b7c61da6ee509c92204e3acb17edbaace77cd Binary files /dev/null and b/monkey-fable/audio/vo/fifty-04.mp3 differ diff --git a/monkey-fable/audio/vo/gone-00.mp3 b/monkey-fable/audio/vo/gone-00.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..933fb8d3d8a1c78649cd1add6b45cb0d8de26a25 Binary files /dev/null and b/monkey-fable/audio/vo/gone-00.mp3 differ diff --git a/monkey-fable/audio/vo/gone-01.mp3 b/monkey-fable/audio/vo/gone-01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f8cf0c0fc1ac57eaab5293b6c04ef1a50eeb95e7 Binary files /dev/null and b/monkey-fable/audio/vo/gone-01.mp3 differ diff --git a/monkey-fable/audio/vo/gone-02.mp3 b/monkey-fable/audio/vo/gone-02.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..d5a67dd792fef4766eb0991792e9bce2433e099f Binary files /dev/null and b/monkey-fable/audio/vo/gone-02.mp3 differ diff --git a/monkey-fable/audio/vo/gone-03.mp3 b/monkey-fable/audio/vo/gone-03.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..230aa45abb2bfc9de4f29f492b0695aa04b88519 Binary files /dev/null and b/monkey-fable/audio/vo/gone-03.mp3 differ diff --git a/monkey-fable/audio/vo/helper-00.mp3 b/monkey-fable/audio/vo/helper-00.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f4e34722fc7554f035088ff5615002c046538844 Binary files /dev/null and b/monkey-fable/audio/vo/helper-00.mp3 differ diff --git a/monkey-fable/audio/vo/helper-01.mp3 b/monkey-fable/audio/vo/helper-01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..d452aad564009e360df28b67989f140bfa8ee464 Binary files /dev/null and b/monkey-fable/audio/vo/helper-01.mp3 differ diff --git a/monkey-fable/audio/vo/helper-02.mp3 b/monkey-fable/audio/vo/helper-02.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..8c2cd57f7e3cd90c418c33b7a3676db4323a0920 Binary files /dev/null and b/monkey-fable/audio/vo/helper-02.mp3 differ diff --git a/monkey-fable/audio/vo/helper-03.mp3 b/monkey-fable/audio/vo/helper-03.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..02809ed3c80c57c3fc66e14e83dc4fe29b4b8f73 Binary files /dev/null and b/monkey-fable/audio/vo/helper-03.mp3 differ diff --git a/monkey-fable/audio/vo/helper-04.mp3 b/monkey-fable/audio/vo/helper-04.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..9cc414af4e7863ec9b2890a809a9ebd1b497ce95 Binary files /dev/null and b/monkey-fable/audio/vo/helper-04.mp3 differ diff --git a/monkey-fable/audio/vo/helper-05.mp3 b/monkey-fable/audio/vo/helper-05.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..9411da146277c95234d9b95e107753eb6ac5ccd1 Binary files /dev/null and b/monkey-fable/audio/vo/helper-05.mp3 differ diff --git a/monkey-fable/audio/vo/hook-00.mp3 b/monkey-fable/audio/vo/hook-00.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..42f61c91c19901356b11db63269426863e30c9a3 Binary files /dev/null and b/monkey-fable/audio/vo/hook-00.mp3 differ diff --git a/monkey-fable/audio/vo/hook-01.mp3 b/monkey-fable/audio/vo/hook-01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ef649b6f9beaad5578cef3cc71ef622a8796357a Binary files /dev/null and b/monkey-fable/audio/vo/hook-01.mp3 differ diff --git a/monkey-fable/audio/vo/hook-02.mp3 b/monkey-fable/audio/vo/hook-02.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2f7dde058ec14f54863c3ec43e46c73a06cb0486 Binary files /dev/null and b/monkey-fable/audio/vo/hook-02.mp3 differ diff --git a/monkey-fable/audio/vo/hook-03.mp3 b/monkey-fable/audio/vo/hook-03.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..dbda3b61e576dffde1b27c52805494e280768405 Binary files /dev/null and b/monkey-fable/audio/vo/hook-03.mp3 differ diff --git a/monkey-fable/audio/vo/hook-04.mp3 b/monkey-fable/audio/vo/hook-04.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0afc578ed1a50e41794201221ecda9773ddc9a2f Binary files /dev/null and b/monkey-fable/audio/vo/hook-04.mp3 differ diff --git a/monkey-fable/audio/vo/ledger-00.mp3 b/monkey-fable/audio/vo/ledger-00.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..28daef7524bd0bffb20ee042288bc62c83b57101 Binary files /dev/null and b/monkey-fable/audio/vo/ledger-00.mp3 differ diff --git a/monkey-fable/audio/vo/ledger-01.mp3 b/monkey-fable/audio/vo/ledger-01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..7ae5eb2316ad8968ccf68f2f5534d1ed9937c24f Binary files /dev/null and b/monkey-fable/audio/vo/ledger-01.mp3 differ diff --git a/monkey-fable/audio/vo/ledger-02.mp3 b/monkey-fable/audio/vo/ledger-02.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b8fc38c59af88f120c78e5650372a5834f340674 Binary files /dev/null and b/monkey-fable/audio/vo/ledger-02.mp3 differ diff --git a/monkey-fable/audio/vo/ledger-03.mp3 b/monkey-fable/audio/vo/ledger-03.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..8eb5434fa05a47b28486c24eeaa5a4186319f710 Binary files /dev/null and b/monkey-fable/audio/vo/ledger-03.mp3 differ diff --git a/monkey-fable/audio/vo/ledger-04.mp3 b/monkey-fable/audio/vo/ledger-04.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..c55e830e7f162d9a5feb8fc15f938f157e67c5d4 Binary files /dev/null and b/monkey-fable/audio/vo/ledger-04.mp3 differ diff --git a/monkey-fable/audio/vo/outro-00.mp3 b/monkey-fable/audio/vo/outro-00.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..51b06c744cd80d9ec55b5962ef758694e05e2a91 Binary files /dev/null and b/monkey-fable/audio/vo/outro-00.mp3 differ diff --git a/monkey-fable/audio/vo/outro-01.mp3 b/monkey-fable/audio/vo/outro-01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..8f570a31e399dd2b01e484e06d0c6b001573dc1e Binary files /dev/null and b/monkey-fable/audio/vo/outro-01.mp3 differ diff --git a/monkey-fable/audio/vo/outro-02.mp3 b/monkey-fable/audio/vo/outro-02.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..90d958fbea779b092215ffb2ffeeadc002d25530 Binary files /dev/null and b/monkey-fable/audio/vo/outro-02.mp3 differ diff --git a/monkey-fable/audio/vo/real-00.mp3 b/monkey-fable/audio/vo/real-00.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0d95ee3661198693ae12c64d92166bf37a8a7d04 Binary files /dev/null and b/monkey-fable/audio/vo/real-00.mp3 differ diff --git a/monkey-fable/audio/vo/real-01.mp3 b/monkey-fable/audio/vo/real-01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..61b1b5bc253ff6baacae3b3e0182bdd44811a04b Binary files /dev/null and b/monkey-fable/audio/vo/real-01.mp3 differ diff --git a/monkey-fable/audio/vo/real-02.mp3 b/monkey-fable/audio/vo/real-02.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..d6049272406bbe675bf363845a7fbe1eee55bfd5 Binary files /dev/null and b/monkey-fable/audio/vo/real-02.mp3 differ diff --git a/monkey-fable/audio/vo/real-03.mp3 b/monkey-fable/audio/vo/real-03.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b1abf40eb616a3da47458fa55130741c23d00688 Binary files /dev/null and b/monkey-fable/audio/vo/real-03.mp3 differ diff --git a/monkey-fable/audio/vo/real-04.mp3 b/monkey-fable/audio/vo/real-04.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..baaab7f5850f25894775ad383c458293ca857f16 Binary files /dev/null and b/monkey-fable/audio/vo/real-04.mp3 differ diff --git a/monkey-fable/audio/vo/ten-00.mp3 b/monkey-fable/audio/vo/ten-00.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..d4a12edca1ec899a0d1258661d4bc9a38980994e Binary files /dev/null and b/monkey-fable/audio/vo/ten-00.mp3 differ diff --git a/monkey-fable/audio/vo/ten-01.mp3 b/monkey-fable/audio/vo/ten-01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2c477b3a91b8033751e6c89327a309f02e9a7f81 Binary files /dev/null and b/monkey-fable/audio/vo/ten-01.mp3 differ diff --git a/monkey-fable/audio/vo/ten-02.mp3 b/monkey-fable/audio/vo/ten-02.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..4bbd3b2d18721f1905ca71fcaf6fc6520e2f9838 Binary files /dev/null and b/monkey-fable/audio/vo/ten-02.mp3 differ diff --git a/monkey-fable/audio/vo/ten-03.mp3 b/monkey-fable/audio/vo/ten-03.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..774574b565cb62e834747378f04432a6c0033ae1 Binary files /dev/null and b/monkey-fable/audio/vo/ten-03.mp3 differ diff --git a/monkey-fable/audio/vo/ten-04.mp3 b/monkey-fable/audio/vo/ten-04.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..26dead4b9648171fc7a952528aa46e5539f8f5c3 Binary files /dev/null and b/monkey-fable/audio/vo/ten-04.mp3 differ diff --git a/monkey-fable/audio/vo/ten-05.mp3 b/monkey-fable/audio/vo/ten-05.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..5fda75f403e84717107450c9e36386aee62414b5 Binary files /dev/null and b/monkey-fable/audio/vo/ten-05.mp3 differ diff --git a/monkey-fable/audio/vo/twenty-00.mp3 b/monkey-fable/audio/vo/twenty-00.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b44a5259d3390dc0e1098179e0331976d44fe49d Binary files /dev/null and b/monkey-fable/audio/vo/twenty-00.mp3 differ diff --git a/monkey-fable/audio/vo/twenty-01.mp3 b/monkey-fable/audio/vo/twenty-01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..936911f2132377f5b046f77ffb3a7660414c781c Binary files /dev/null and b/monkey-fable/audio/vo/twenty-01.mp3 differ diff --git a/monkey-fable/audio/vo/twenty-02.mp3 b/monkey-fable/audio/vo/twenty-02.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2edc1b2e3b306f9033c792696e1be11891501973 Binary files /dev/null and b/monkey-fable/audio/vo/twenty-02.mp3 differ diff --git a/monkey-fable/audio/vo/twenty-03.mp3 b/monkey-fable/audio/vo/twenty-03.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..75be0876f15df137306837e1112c86b62551bde4 Binary files /dev/null and b/monkey-fable/audio/vo/twenty-03.mp3 differ diff --git a/monkey-fable/audio/vo/village-00.mp3 b/monkey-fable/audio/vo/village-00.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0b980d230a260ef2631b08788acce8c090b26bbe Binary files /dev/null and b/monkey-fable/audio/vo/village-00.mp3 differ diff --git a/monkey-fable/audio/vo/village-01.mp3 b/monkey-fable/audio/vo/village-01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..810d5beab921f37087220f61d86cf400d6a719d4 Binary files /dev/null and b/monkey-fable/audio/vo/village-01.mp3 differ diff --git a/monkey-fable/audio/vo/village-02.mp3 b/monkey-fable/audio/vo/village-02.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..c2dfa97ca6c79d1c0d55ee405edf881fd0c04d8f Binary files /dev/null and b/monkey-fable/audio/vo/village-02.mp3 differ diff --git a/monkey-fable/audio/vo/village-03.mp3 b/monkey-fable/audio/vo/village-03.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..4a2ccc8322a665bc05ea69ea07c8b7a61f86ce48 Binary files /dev/null and b/monkey-fable/audio/vo/village-03.mp3 differ diff --git a/monkey-fable/audio/vo/why-00.mp3 b/monkey-fable/audio/vo/why-00.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2a175db367fd60a5c5882c52e62b4aec094d9174 Binary files /dev/null and b/monkey-fable/audio/vo/why-00.mp3 differ diff --git a/monkey-fable/audio/vo/why-01.mp3 b/monkey-fable/audio/vo/why-01.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..14ae4bb17c1ad81c2878fdde4189d16e191b0fdc Binary files /dev/null and b/monkey-fable/audio/vo/why-01.mp3 differ diff --git a/monkey-fable/audio/vo/why-02.mp3 b/monkey-fable/audio/vo/why-02.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..95e039bfa280d7eb5cb8dd7669dc9bd9d8f7c35d Binary files /dev/null and b/monkey-fable/audio/vo/why-02.mp3 differ diff --git a/monkey-fable/audio/vo/why-03.mp3 b/monkey-fable/audio/vo/why-03.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..c5265057c295aa04d88a8883ea0961cddd478c34 Binary files /dev/null and b/monkey-fable/audio/vo/why-03.mp3 differ diff --git a/monkey-fable/audio/vo/why-04.mp3 b/monkey-fable/audio/vo/why-04.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..ec8596e8b5113bbc37747a928a6278308a924d8e Binary files /dev/null and b/monkey-fable/audio/vo/why-04.mp3 differ diff --git a/monkey-fable/audio/vo/why-05.mp3 b/monkey-fable/audio/vo/why-05.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..53a599cf0de06df88ad5b2a3d326debce79ebf86 Binary files /dev/null and b/monkey-fable/audio/vo/why-05.mp3 differ diff --git a/monkey-fable/js/timeline.js b/monkey-fable/js/timeline.js new file mode 100644 index 0000000000000000000000000000000000000000..14bcd3b21ccb06e6744699ef6801f9dc8c996de5 --- /dev/null +++ b/monkey-fable/js/timeline.js @@ -0,0 +1,536 @@ +// 自动生成:tools/build_vo.py —— 请勿手改 +window.TIMELINE = { + "total": 286.68, + "scenes": [ + { + "id": "hook", + "title": "开场", + "start": 0.0, + "end": 24.624 + }, + { + "id": "village", + "title": "猴子村", + "start": 24.624, + "end": 41.514 + }, + { + "id": "ten", + "title": "十块一只", + "start": 41.514, + "end": 68.106 + }, + { + "id": "twenty", + "title": "涨到二十", + "start": 68.106, + "end": 86.344 + }, + { + "id": "fifty", + "title": "喊到五十", + "start": 86.344, + "end": 107.256 + }, + { + "id": "helper", + "title": "助手的好意", + "start": 107.256, + "end": 138.84 + }, + { + "id": "gone", + "title": "人去笼空", + "start": 138.84, + "end": 155.68 + }, + { + "id": "ledger", + "title": "算一笔账", + "start": 155.68, + "end": 177.844 + }, + { + "id": "why", + "title": "为什么会上当", + "start": 177.844, + "end": 219.928 + }, + { + "id": "real", + "title": "现实中的猴子", + "start": 219.928, + "end": 245.79 + }, + { + "id": "check", + "title": "三个自检问题", + "start": 245.79, + "end": 269.028 + }, + { + "id": "outro", + "title": "结尾", + "start": 269.028, + "end": 286.68 + } + ], + "lines": [ + { + "scene": "hook", + "i": 0, + "text": "假如有人站在村口喊:猴子,十块钱一只,有多少收多少。", + "file": "audio/vo/hook-00.mp3", + "start": 0.5, + "dur": 6.048 + }, + { + "scene": "hook", + "i": 1, + "text": "你会不会扛起网兜,冲上后山?", + "file": "audio/vo/hook-01.mp3", + "start": 6.898, + "dur": 3.24 + }, + { + "scene": "hook", + "i": 2, + "text": "先别急着回答。", + "file": "audio/vo/hook-02.mp3", + "start": 10.638, + "dur": 2.088 + }, + { + "scene": "hook", + "i": 3, + "text": "今天讲一个经济学里很有名的寓言,叫《收猴子的故事》。", + "file": "audio/vo/hook-03.mp3", + "start": 13.026, + "dur": 5.064 + }, + { + "scene": "hook", + "i": 4, + "text": "听完你就明白,为什么总有人,在最高点接了最后一棒。", + "file": "audio/vo/hook-04.mp3", + "start": 18.44, + "dur": 5.184 + }, + { + "scene": "village", + "i": 0, + "text": "很久以前,有一个小山村。", + "file": "audio/vo/village-00.mp3", + "start": 25.124, + "dur": 2.928 + }, + { + "scene": "village", + "i": 1, + "text": "村子后面的山上,住着成百上千只猴子。", + "file": "audio/vo/village-01.mp3", + "start": 28.352, + "dur": 3.912 + }, + { + "scene": "village", + "i": 2, + "text": "在村民眼里,猴子一点用都没有。", + "file": "audio/vo/village-02.mp3", + "start": 32.564, + "dur": 3.48 + }, + { + "scene": "village", + "i": 3, + "text": "不能吃,不能干活,还天天下山偷玉米。", + "file": "audio/vo/village-03.mp3", + "start": 36.294, + "dur": 4.32 + }, + { + "scene": "ten", + "i": 0, + "text": "直到有一天,村里来了一个穿西装的商人。", + "file": "audio/vo/ten-00.mp3", + "start": 42.014, + "dur": 3.768 + }, + { + "scene": "ten", + "i": 1, + "text": "他在村口贴了一张告示:收猴子,十块钱一只!", + "file": "audio/vo/ten-01.mp3", + "start": 46.082, + "dur": 4.848 + }, + { + "scene": "ten", + "i": 2, + "text": "村民们一看,这不是白捡钱吗?", + "file": "audio/vo/ten-02.mp3", + "start": 51.33, + "dur": 3.216 + }, + { + "scene": "ten", + "i": 3, + "text": "于是全村出动,男女老少都上山抓猴子。", + "file": "audio/vo/ten-03.mp3", + "start": 54.846, + "dur": 4.296 + }, + { + "scene": "ten", + "i": 4, + "text": "商人说话算话,一手交猴,一手交钱。", + "file": "audio/vo/ten-04.mp3", + "start": 59.442, + "dur": 4.248 + }, + { + "scene": "ten", + "i": 5, + "text": "没几天,他就收了六百只猴子。", + "file": "audio/vo/ten-05.mp3", + "start": 63.99, + "dur": 3.216 + }, + { + "scene": "twenty", + "i": 0, + "text": "可山上的猴子越来越少,越来越难抓。", + "file": "audio/vo/twenty-00.mp3", + "start": 68.606, + "dur": 3.648 + }, + { + "scene": "twenty", + "i": 1, + "text": "村民们抓累了,热情也慢慢退了。", + "file": "audio/vo/twenty-01.mp3", + "start": 72.554, + "dur": 3.48 + }, + { + "scene": "twenty", + "i": 2, + "text": "这时商人又宣布:价格翻倍,二十块一只!", + "file": "audio/vo/twenty-02.mp3", + "start": 76.334, + "dur": 4.848 + }, + { + "scene": "twenty", + "i": 3, + "text": "村民们再次冲上山,又抓来了三百只。", + "file": "audio/vo/twenty-03.mp3", + "start": 81.532, + "dur": 3.912 + }, + { + "scene": "fifty", + "i": 0, + "text": "现在,山上几乎找不到猴子了。", + "file": "audio/vo/fifty-00.mp3", + "start": 86.844, + "dur": 3.312 + }, + { + "scene": "fifty", + "i": 1, + "text": "商人却把价格直接喊到:五十块一只!", + "file": "audio/vo/fifty-01.mp3", + "start": 90.456, + "dur": 3.936 + }, + { + "scene": "fifty", + "i": 2, + "text": "五十块啊!村民们眼睛都红了。", + "file": "audio/vo/fifty-02.mp3", + "start": 94.742, + "dur": 3.528 + }, + { + "scene": "fifty", + "i": 3, + "text": "可商人说:我要回城里办点事,过几天就回来。", + "file": "audio/vo/fifty-03.mp3", + "start": 98.57, + "dur": 4.344 + }, + { + "scene": "fifty", + "i": 4, + "text": "这几天的生意,先交给我的助手。", + "file": "audio/vo/fifty-04.mp3", + "start": 103.164, + "dur": 3.192 + }, + { + "scene": "helper", + "i": 0, + "text": "商人前脚刚走,助手就把村民们叫到了笼子前。", + "file": "audio/vo/helper-00.mp3", + "start": 107.756, + "dur": 4.704 + }, + { + "scene": "helper", + "i": 1, + "text": "他压低声音说:笼子里这九百只猴子,我三十五块一只卖给你们。", + "file": "audio/vo/helper-01.mp3", + "start": 112.76, + "dur": 6.0 + }, + { + "scene": "helper", + "i": 2, + "text": "等老板回来,你们再五十块卖给他。", + "file": "audio/vo/helper-02.mp3", + "start": 119.01, + "dur": 3.24 + }, + { + "scene": "helper", + "i": 3, + "text": "村民们一算:一只净赚十五块,稳赚不赔啊!", + "file": "audio/vo/helper-03.mp3", + "start": 122.6, + "dur": 4.608 + }, + { + "scene": "helper", + "i": 4, + "text": "有存款的掏存款,没钱的找亲戚借,还有人把房子都抵押了。", + "file": "audio/vo/helper-04.mp3", + "start": 127.508, + "dur": 5.712 + }, + { + "scene": "helper", + "i": 5, + "text": "九百只猴子,一只不剩,全被抢光。", + "file": "audio/vo/helper-05.mp3", + "start": 133.52, + "dur": 4.32 + }, + { + "scene": "gone", + "i": 0, + "text": "第二天一早,村民们拎着猴子来到村口。", + "file": "audio/vo/gone-00.mp3", + "start": 139.34, + "dur": 3.696 + }, + { + "scene": "gone", + "i": 1, + "text": "助手不见了。", + "file": "audio/vo/gone-01.mp3", + "start": 143.636, + "dur": 1.848 + }, + { + "scene": "gone", + "i": 2, + "text": "商人,也再没有回来。", + "file": "audio/vo/gone-02.mp3", + "start": 146.384, + "dur": 2.784 + }, + { + "scene": "gone", + "i": 3, + "text": "村子里,只剩下满山遍野的猴子,和一屁股的债。", + "file": "audio/vo/gone-03.mp3", + "start": 149.968, + "dur": 4.512 + }, + { + "scene": "ledger", + "i": 0, + "text": "我们来算一笔账。", + "file": "audio/vo/ledger-00.mp3", + "start": 156.18, + "dur": 1.896 + }, + { + "scene": "ledger", + "i": 1, + "text": "商人十块收了六百只,二十块收了三百只,一共花了一万二。", + "file": "audio/vo/ledger-01.mp3", + "start": 158.376, + "dur": 5.4 + }, + { + "scene": "ledger", + "i": 2, + "text": "助手三十五块卖回去九百只,收了三万一千五。", + "file": "audio/vo/ledger-02.mp3", + "start": 164.076, + "dur": 4.896 + }, + { + "scene": "ledger", + "i": 3, + "text": "一进一出,这伙人净赚一万九千五。", + "file": "audio/vo/ledger-03.mp3", + "start": 169.272, + "dur": 3.864 + }, + { + "scene": "ledger", + "i": 4, + "text": "而这笔钱,全部来自村民的口袋。", + "file": "audio/vo/ledger-04.mp3", + "start": 173.436, + "dur": 3.408 + }, + { + "scene": "why", + "i": 0, + "text": "为什么一群精明的村民,会集体上当?", + "file": "audio/vo/why-00.mp3", + "start": 178.344, + "dur": 3.576 + }, + { + "scene": "why", + "i": 1, + "text": "第一,价格脱离了价值。猴子本身没用,它值钱,只是因为有人在收。", + "file": "audio/vo/why-01.mp3", + "start": 182.32, + "dur": 6.84 + }, + { + "scene": "why", + "i": 2, + "text": "第二,稀缺是被制造出来的。商人先把猴子收走,再把价格抬高。", + "file": "audio/vo/why-02.mp3", + "start": 189.51, + "dur": 6.264 + }, + { + "scene": "why", + "i": 3, + "text": "第三,博傻理论。我买它,不是因为它值这个价,而是相信会有个更傻的人出更高的价。", + "file": "audio/vo/why-03.mp3", + "start": 196.124, + "dur": 8.52 + }, + { + "scene": "why", + "i": 4, + "text": "第四,信息不对称。村民不知道,商人和助手根本就是一伙的。", + "file": "audio/vo/why-04.mp3", + "start": 204.994, + "dur": 6.456 + }, + { + "scene": "why", + "i": 5, + "text": "第五,加杠杆。借钱买猴子,亏掉的就不只是本金,而是整个家。", + "file": "audio/vo/why-05.mp3", + "start": 211.8, + "dur": 7.128 + }, + { + "scene": "real", + "i": 0, + "text": "这个故事,在现实里一次又一次重演。", + "file": "audio/vo/real-00.mp3", + "start": 220.428, + "dur": 4.008 + }, + { + "scene": "real", + "i": 1, + "text": "十七世纪的荷兰,一个郁金香球茎,能换一栋房子。", + "file": "audio/vo/real-01.mp3", + "start": 224.736, + "dur": 4.728 + }, + { + "scene": "real", + "i": 2, + "text": "后来,有人炒兰花,炒普洱茶,炒球鞋。", + "file": "audio/vo/real-02.mp3", + "start": 229.764, + "dur": 4.56 + }, + { + "scene": "real", + "i": 3, + "text": "今天的币圈,那些没有任何用途、只靠喊单拉盘的空气币,也是同一个剧本。", + "file": "audio/vo/real-03.mp3", + "start": 234.624, + "dur": 6.264 + }, + { + "scene": "real", + "i": 4, + "text": "道具一直在换,剧本从来没变过。", + "file": "audio/vo/real-04.mp3", + "start": 241.238, + "dur": 3.552 + }, + { + "scene": "check", + "i": 0, + "text": "所以下次,当你想买一个正在疯涨的东西,先问自己三个问题。", + "file": "audio/vo/check-00.mp3", + "start": 246.29, + "dur": 5.184 + }, + { + "scene": "check", + "i": 1, + "text": "第一,如果没人接盘,它本身还有用吗?", + "file": "audio/vo/check-01.mp3", + "start": 251.824, + "dur": 4.128 + }, + { + "scene": "check", + "i": 2, + "text": "第二,它涨价,是因为真实需求,还是因为下一个人愿意出更高的价?", + "file": "audio/vo/check-02.mp3", + "start": 256.302, + "dur": 6.264 + }, + { + "scene": "check", + "i": 3, + "text": "第三,卖给我的人,既然这么看好,为什么不自己留着?", + "file": "audio/vo/check-03.mp3", + "start": 262.916, + "dur": 5.112 + }, + { + "scene": "outro", + "i": 0, + "text": "记住:价格可以被喊出来,价值不会。", + "file": "audio/vo/outro-00.mp3", + "start": 269.528, + "dur": 4.008 + }, + { + "scene": "outro", + "i": 1, + "text": "下次再有人跟你说稳赚不赔,先低头看看,你手里拎着的,是不是一笼猴子。", + "file": "audio/vo/outro-01.mp3", + "start": 273.936, + "dur": 7.416 + }, + { + "scene": "outro", + "i": 2, + "text": "我们下期见!", + "file": "audio/vo/outro-02.mp3", + "start": 281.852, + "dur": 1.728 + } + ] +}; diff --git a/monkey-fable/tools/build_vo.py b/monkey-fable/tools/build_vo.py new file mode 100644 index 0000000000000000000000000000000000000000..dfb488a21a954f10b2dafac0d25bc3390aa44791 --- /dev/null +++ b/monkey-fable/tools/build_vo.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +"""生成旁白配音 + 时间轴。 +用法: python3 tools/build_vo.py +依赖: pip install edge-tts mutagen +输出: audio/vo/-.mp3 以及 js/timeline.js +""" +import asyncio, json, os, sys +import edge_tts +from mutagen.mp3 import MP3 + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +VOICE = "zh-CN-YunxiNeural" +RATE = "+6%" + +# (scene id, 场景标题, [ (旁白, 句后停顿秒) ... ]) +SCRIPT = [ + ("hook", "开场", [ + ("假如有人站在村口喊:猴子,十块钱一只,有多少收多少。", .35), + ("你会不会扛起网兜,冲上后山?", .5), + ("先别急着回答。", .3), + ("今天讲一个经济学里很有名的寓言,叫《收猴子的故事》。", .35), + ("听完你就明白,为什么总有人,在最高点接了最后一棒。", .6), + ]), + ("village", "猴子村", [ + ("很久以前,有一个小山村。", .3), + ("村子后面的山上,住着成百上千只猴子。", .3), + ("在村民眼里,猴子一点用都没有。", .25), + ("不能吃,不能干活,还天天下山偷玉米。", .5), + ]), + ("ten", "十块一只", [ + ("直到有一天,村里来了一个穿西装的商人。", .3), + ("他在村口贴了一张告示:收猴子,十块钱一只!", .4), + ("村民们一看,这不是白捡钱吗?", .3), + ("于是全村出动,男女老少都上山抓猴子。", .3), + ("商人说话算话,一手交猴,一手交钱。", .3), + ("没几天,他就收了六百只猴子。", .5), + ]), + ("twenty", "涨到二十", [ + ("可山上的猴子越来越少,越来越难抓。", .3), + ("村民们抓累了,热情也慢慢退了。", .3), + ("这时商人又宣布:价格翻倍,二十块一只!", .35), + ("村民们再次冲上山,又抓来了三百只。", .5), + ]), + ("fifty", "喊到五十", [ + ("现在,山上几乎找不到猴子了。", .3), + ("商人却把价格直接喊到:五十块一只!", .35), + ("五十块啊!村民们眼睛都红了。", .3), + ("可商人说:我要回城里办点事,过几天就回来。", .25), + ("这几天的生意,先交给我的助手。", .5), + ]), + ("helper", "助手的好意", [ + ("商人前脚刚走,助手就把村民们叫到了笼子前。", .3), + ("他压低声音说:笼子里这九百只猴子,我三十五块一只卖给你们。", .25), + ("等老板回来,你们再五十块卖给他。", .35), + ("村民们一算:一只净赚十五块,稳赚不赔啊!", .3), + ("有存款的掏存款,没钱的找亲戚借,还有人把房子都抵押了。", .3), + ("九百只猴子,一只不剩,全被抢光。", .6), + ]), + ("gone", "人去笼空", [ + ("第二天一早,村民们拎着猴子来到村口。", .6), + ("助手不见了。", .9), + ("商人,也再没有回来。", .8), + ("村子里,只剩下满山遍野的猴子,和一屁股的债。", .8), + ]), + ("ledger", "算一笔账", [ + ("我们来算一笔账。", .3), + ("商人十块收了六百只,二十块收了三百只,一共花了一万二。", .3), + ("助手三十五块卖回去九百只,收了三万一千五。", .3), + ("一进一出,这伙人净赚一万九千五。", .3), + ("而这笔钱,全部来自村民的口袋。", .6), + ]), + ("why", "为什么会上当", [ + ("为什么一群精明的村民,会集体上当?", .4), + ("第一,价格脱离了价值。猴子本身没用,它值钱,只是因为有人在收。", .35), + ("第二,稀缺是被制造出来的。商人先把猴子收走,再把价格抬高。", .35), + ("第三,博傻理论。我买它,不是因为它值这个价,而是相信会有个更傻的人出更高的价。", .35), + ("第四,信息不对称。村民不知道,商人和助手根本就是一伙的。", .35), + ("第五,加杠杆。借钱买猴子,亏掉的就不只是本金,而是整个家。", .6), + ]), + ("real", "现实中的猴子", [ + ("这个故事,在现实里一次又一次重演。", .3), + ("十七世纪的荷兰,一个郁金香球茎,能换一栋房子。", .3), + ("后来,有人炒兰花,炒普洱茶,炒球鞋。", .3), + ("今天的币圈,那些没有任何用途、只靠喊单拉盘的空气币,也是同一个剧本。", .35), + ("道具一直在换,剧本从来没变过。", .6), + ]), + ("check", "三个自检问题", [ + ("所以下次,当你想买一个正在疯涨的东西,先问自己三个问题。", .35), + ("第一,如果没人接盘,它本身还有用吗?", .35), + ("第二,它涨价,是因为真实需求,还是因为下一个人愿意出更高的价?", .35), + ("第三,卖给我的人,既然这么看好,为什么不自己留着?", .6), + ]), + ("outro", "结尾", [ + ("记住:价格可以被喊出来,价值不会。", .4), + ("下次再有人跟你说稳赚不赔,先低头看看,你手里拎着的,是不是一笼猴子。", .5), + ("我们下期见!", 1.2), + ]), +] + +SCENE_LEAD = 0.5 # 每幕开始到第一句的留白 +SCENE_TAIL = 0.4 # 每幕最后一句后额外留白 + +async def tts(text, path): + for attempt in range(4): + try: + c = edge_tts.Communicate(text, VOICE, rate=RATE) + await c.save(path) + return + except Exception as e: + print("retry", path, e); await asyncio.sleep(2) + raise RuntimeError(path) + +async def main(): + os.makedirs(os.path.join(ROOT, "audio/vo"), exist_ok=True) + jobs = [] + for sid, _, lines in SCRIPT: + for i, (text, _) in enumerate(lines): + p = os.path.join(ROOT, f"audio/vo/{sid}-{i:02d}.mp3") + if not os.path.exists(p) or "--force" in sys.argv: + jobs.append(tts(text, p)) + sem = asyncio.Semaphore(6) + async def run(j): + async with sem: await j + await asyncio.gather(*[run(j) for j in jobs]) + + t = 0.0 + scenes, lines_out = [], [] + for sid, title, lines in SCRIPT: + s0 = t + t += SCENE_LEAD + for i, (text, pause) in enumerate(lines): + f = f"audio/vo/{sid}-{i:02d}.mp3" + d = MP3(os.path.join(ROOT, f)).info.length + lines_out.append({"scene": sid, "i": i, "text": text, "file": f, + "start": round(t, 3), "dur": round(d, 3)}) + t += d + pause + t += SCENE_TAIL + scenes.append({"id": sid, "title": title, "start": round(s0, 3), "end": round(t, 3)}) + total = round(t + 1.5, 3) + scenes[-1]["end"] = total + js = "// 自动生成:tools/build_vo.py —— 请勿手改\nwindow.TIMELINE = " + json.dumps( + {"total": total, "scenes": scenes, "lines": lines_out}, ensure_ascii=False, indent=1) + ";\n" + open(os.path.join(ROOT, "js/timeline.js"), "w", encoding="utf-8").write(js) + print("total seconds:", total) + for s in scenes: print(s) + +asyncio.run(main())