├── Content ├── Monika │ ├── Monika_ske.json │ ├── Monika_tex.json │ └── Monika_tex.png ├── choice.png ├── choice_dark.png ├── clothes │ ├── c_miku │ │ ├── c_miku_ske.json │ │ ├── c_miku_tex.json │ │ └── c_miku_tex.png │ └── c_monika │ │ ├── c_monika_ske.json │ │ ├── c_monika_tex.json │ │ └── c_monika_tex.png ├── fonts │ ├── Marker Felt.ttf │ ├── arial.ttf │ └── black.ttf ├── models │ └── single_speaker_tiny.bin ├── settings.png ├── spaceroom.png └── textbox.png ├── README.md └── Source ├── AppDelegate.cpp ├── AppDelegate.h ├── FastLLM.cpp ├── FastLLM.h ├── GameScene.cpp ├── GameScene.h ├── SummerTTS.cpp └── SummerTTS.h /Content/Monika/Monika_ske.json: -------------------------------------------------------------------------------- 1 | {"armature":[{"aabb":{"width":1280,"y":-720,"height":720,"x":-640},"skin":[{"name":"","slot":[{"display":[{"uvs":[0.83828,0.42382,0.84719,0.41174,0.87109,0.4,0.90414,0.39528,0.93391,0.40236,0.94688,0.41326,0.94688,0.43354,0.93305,0.4509,0.91773,0.45889,0.88602,0.45861,0.86039,0.44535],"type":"mesh","vertices":[216.5,-54.85,222.2,-63.55,237.5,-72,258.65,-75.4,277.7,-70.3,286,-62.45,286,-47.85,277.15,-35.35,267.35,-29.6,247.05,-29.8,230.65,-39.35],"transform":{"y":-360,"x":-320},"triangles":[4,7,6,4,6,5,3,9,8,3,8,7,3,7,4,2,10,9,2,9,3,1,10,2,1,0,10],"path":"eye_left","width":640,"height":720,"name":"eye_left","edges":[0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,0],"userEdges":[]}],"name":"eye_left"},{"display":[{"uvs":[0.03484,0.42521,0.03609,0.40479,0.04859,0.39326,0.08406,0.38701,0.12195,0.38958,0.14492,0.39958,0.15078,0.40958,0.13242,0.43778,0.1032,0.44854,0.07109,0.44965,0.05195,0.44],"type":"mesh","vertices":[-297.7,-53.85,-296.9,-68.55,-288.9,-76.85,-266.2,-81.35,-241.95,-79.5,-227.25,-72.3,-223.5,-65.1,-235.25,-44.8,-253.95,-37.05,-274.5,-36.25,-286.75,-43.2],"transform":{"y":-360,"x":320},"triangles":[5,7,6,4,7,5,3,8,4,4,8,7,3,9,8,10,9,3,2,10,3,2,1,10,1,0,10],"path":"eye_right","width":640,"height":720,"name":"eye_right","edges":[0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,0],"userEdges":[]}],"name":"eye_right"},{"display":[{"uvs":[0.47594,0.53833,0.48137,0.5291,0.51953,0.52826,0.52516,0.53715,0.52402,0.54632,0.51207,0.5616,0.49941,0.56604,0.48793,0.56042,0.47641,0.54833],"type":"mesh","vertices":[-30.8,27.6,-23.85,20.95,25,20.35,32.2,26.75,30.75,33.35,15.45,44.35,-0.75,47.55,-15.45,43.5,-30.2,34.8],"transform":{"y":-360},"triangles":[2,4,3,2,5,4,6,5,2,1,6,2,1,7,6,0,8,1,1,8,7],"path":"mouth_normal","width":1280,"height":720,"name":"mouth_normal","edges":[0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,0],"userEdges":[]},{"uvs":[0.48121,0.55639,0.48492,0.5416,0.49211,0.52993,0.50004,0.52812,0.5098,0.52903,0.5166,0.53972,0.52043,0.5541,0.51648,0.56736,0.5084,0.57986,0.49492,0.58035,0.4866,0.57028],"type":"mesh","vertices":[-24.05,40.6,-19.3,29.95,-10.1,21.55,0.05,20.25,12.55,20.9,21.25,28.6,26.15,38.95,21.1,48.5,10.75,57.5,-6.5,57.85,-17.15,50.6],"transform":{"y":-360},"triangles":[5,7,6,3,9,8,3,8,7,3,7,5,4,3,5,1,10,2,10,9,3,2,10,3,1,0,10],"path":"mouth_talking","width":1280,"height":720,"name":"mouth_talking","edges":[0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,0],"userEdges":[]}],"name":"mouth"},{"display":[{"uvs":[0,0,1,0,0,1,1,1],"type":"mesh","vertices":[-640,-360,640,-360,-640,360,640,360],"transform":{"y":-360},"triangles":[1,0,3,0,2,3],"path":"body","width":1280,"height":720,"name":"body","edges":[0,1,1,3,3,2,2,0],"userEdges":[]}],"name":"body"}]}],"type":"Armature","frameRate":24,"bone":[{"name":"root","transform":{}},{"transform":{},"name":"eye_right","parent":"root"},{"transform":{},"name":"eye_left","parent":"root"},{"length":3,"transform":{},"parent":"root","name":"mouth"}],"slot":[{"name":"body","parent":"root","color":{}},{"z":1,"name":"mouth","parent":"mouth","color":{}},{"z":2,"name":"eye_right","parent":"eye_right","color":{}},{"z":3,"name":"eye_left","parent":"eye_left","color":{}}],"defaultActions":[{"gotoAndPlay":"steady"}],"animation":[{"bone":[{"frame":[{"duration":72,"transform":{}}],"name":"root"},{"frame":[{"duration":72,"transform":{}}],"name":"eye_left"},{"frame":[{"duration":72,"transform":{}}],"name":"mouth"},{"frame":[{"duration":72,"transform":{}}],"name":"eye_right"}],"ffd":[{"frame":[{"tweenEasing":0,"offset":2,"duration":34,"vertices":[0.1,0.15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{"tweenEasing":0,"offset":2,"duration":2,"vertices":[0.1,0.15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{"tweenEasing":0,"offset":0,"duration":2,"vertices":[1.2,7.35,6,14.05,5.05,18.75,3.3,20.45,-2,14.05,-2.25,10.1,0,0,-0.1,-9.75,-0.25,-14.8,3.85,-14.8,3.15,-4.35]},{"tweenEasing":0,"offset":2,"duration":34,"vertices":[0.1,0.15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{"tweenEasing":null,"offset":2,"duration":0,"vertices":[0.1,0.15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],"skin":"","scale":1,"slot":"eye_left","offset":0,"name":"eye_left"},{"frame":[{"tweenEasing":0,"offset":12,"duration":34,"vertices":[0.25,0.05,0,0,0,0,0,0,0,0]},{"tweenEasing":0,"offset":12,"duration":2,"vertices":[0.25,0.05,0,0,0,0,0,0,0,0]},{"tweenEasing":0,"offset":2,"duration":2,"vertices":[4.5,13.6,4.95,19.95,2.45,23.25,0.15,23.25,-5.2,17.05,0.25,11.4,-1.05,-4.25,0.9,-10.9,2.25,-12.5,0.15,-6.45]},{"tweenEasing":0,"offset":12,"duration":34,"vertices":[0.25,0.05,0,0,0,0,0,0,0,0]},{"tweenEasing":null,"offset":12,"duration":0,"vertices":[0.25,0.05,0,0,0,0,0,0,0,0]}],"skin":"","scale":1,"slot":"eye_right","offset":0,"name":"eye_right"}],"duration":72,"playTimes":0,"frame":[],"slot":[],"name":"steady","ik":[]},{"bone":[{"frame":[{"duration":24,"transform":{}}],"name":"root"},{"frame":[{"duration":24,"transform":{}}],"name":"eye_left"},{"frame":[{"duration":24,"transform":{}}],"name":"mouth"},{"frame":[{"duration":24,"transform":{}}],"name":"eye_right"}],"ffd":[{"frame":[{"tweenEasing":0,"offset":12,"duration":8,"vertices":[0.2,0,0,0,0,0,0,0,0,0]},{"tweenEasing":0,"offset":0,"duration":8,"vertices":[4.15,1.3,4.95,2.05,0.9,2.5,0,0,-1.9,1.6,-5.9,1.7,-4.15,0.7,-5.75,1.5,1.35,-0.1,-1,0.4,4.4,0.35]},{"tweenEasing":0,"offset":0,"duration":8,"vertices":[-0.18,1.25,2.37,8.27,4.35,17.7,3.25,19.25,1.55,17.65,0.55,10.95,0.38,0.85,-2.32,-4.95,-0.87,-12.65,2.8,-12.85,3.2,-5.87]},{"tweenEasing":null,"offset":12,"duration":0,"vertices":[0.2,0,0,0,0,0,0,0,0,0]}],"skin":"","scale":1,"slot":"mouth","offset":0,"name":"mouth_talking"}],"duration":24,"playTimes":0,"frame":[],"slot":[{"frame":[{"displayIndex":1,"tweenEasing":null,"duration":24}],"name":"mouth"}],"name":"talk","ik":[]}],"name":"Monika","ik":[]}],"frameRate":24,"name":"Monika","version":"4.5","isGlobal":0} -------------------------------------------------------------------------------- /Content/Monika/Monika_tex.json: -------------------------------------------------------------------------------- 1 | {"width":2048,"imagePath":"Monika_tex.png","name":"Monika","SubTexture":[{"width":1280,"y":1,"height":720,"name":"body","x":1},{"width":1280,"y":723,"height":720,"name":"mouth_talking","x":1},{"width":1280,"y":1445,"height":720,"name":"mouth_normal","x":1},{"width":640,"y":1,"height":720,"name":"eye_right","x":1283},{"width":640,"y":723,"height":720,"name":"eye_left","x":1283}],"height":4096} -------------------------------------------------------------------------------- /Content/Monika/Monika_tex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shafvfshkga/Chat-Monika-Chinese-cpp/c817f7680386e3c0b639fae52218f970b588a11f/Content/Monika/Monika_tex.png -------------------------------------------------------------------------------- /Content/choice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shafvfshkga/Chat-Monika-Chinese-cpp/c817f7680386e3c0b639fae52218f970b588a11f/Content/choice.png -------------------------------------------------------------------------------- /Content/choice_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shafvfshkga/Chat-Monika-Chinese-cpp/c817f7680386e3c0b639fae52218f970b588a11f/Content/choice_dark.png -------------------------------------------------------------------------------- /Content/clothes/c_miku/c_miku_ske.json: -------------------------------------------------------------------------------- 1 | {"armature":[{"ik":[],"aabb":{"width":1280,"y":-720,"height":720,"x":-640},"skin":[{"name":"","slot":[{"display":[{"width":1280,"type":"mesh","vertices":[-640,-360,640,-360,-640,360,640,360],"transform":{"y":-360},"uvs":[0,0,1,0,0,1,1,1],"triangles":[1,0,3,0,2,3],"path":"body","height":720,"name":"body","edges":[0,1,1,3,3,2,2,0],"userEdges":[]}],"name":"body"}]}],"type":"Armature","name":"c_miku","bone":[{"name":"root","transform":{}}],"slot":[{"name":"body","parent":"root","color":{}}],"animation":[{"bone":[{"frame":[{"duration":0,"transform":{}}],"name":"root"}],"ik":[],"ffd":[],"duration":0,"playTimes":0,"frame":[],"slot":[],"name":"talk"}],"frameRate":24,"defaultActions":[{"gotoAndPlay":"talk"}]}],"frameRate":24,"name":"c_miku","version":"4.5","isGlobal":0} -------------------------------------------------------------------------------- /Content/clothes/c_miku/c_miku_tex.json: -------------------------------------------------------------------------------- 1 | {"width":2048,"imagePath":"c_miku_tex.png","name":"c_miku","SubTexture":[{"width":1280,"y":1,"height":720,"name":"body","x":1}],"height":1024} -------------------------------------------------------------------------------- /Content/clothes/c_miku/c_miku_tex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shafvfshkga/Chat-Monika-Chinese-cpp/c817f7680386e3c0b639fae52218f970b588a11f/Content/clothes/c_miku/c_miku_tex.png -------------------------------------------------------------------------------- /Content/clothes/c_monika/c_monika_ske.json: -------------------------------------------------------------------------------- 1 | {"armature":[{"defaultActions":[{"gotoAndPlay":"steady"}],"slot":[{"name":"body","parent":"root","color":{}}],"skin":[{"name":"","slot":[{"display":[{"width":1280,"edges":[0,1,1,3,3,2,2,0],"type":"mesh","vertices":[-640,-360,640,-360,-640,360,640,360],"transform":{"y":-360},"triangles":[1,0,3,0,2,3],"path":"body","height":720,"name":"body","uvs":[0,0,1,0,0,1,1,1],"userEdges":[]}],"name":"body"}]}],"type":"Armature","frameRate":24,"bone":[{"name":"root","transform":{}}],"aabb":{"width":1280,"y":-720,"height":720,"x":-640},"ik":[],"animation":[{"bone":[{"frame":[{"duration":0,"transform":{}}],"name":"root"}],"ffd":[],"duration":0,"playTimes":0,"frame":[],"slot":[],"ik":[],"name":"steady"},{"bone":[{"frame":[{"duration":0,"transform":{}}],"name":"root"}],"ffd":[],"duration":0,"playTimes":0,"frame":[],"slot":[],"ik":[],"name":"talk"}],"name":"c_monika"}],"frameRate":24,"name":"c_monika","version":"4.5","isGlobal":0} -------------------------------------------------------------------------------- /Content/clothes/c_monika/c_monika_tex.json: -------------------------------------------------------------------------------- 1 | {"width":2048,"imagePath":"c_monika_tex.png","name":"c_monika","SubTexture":[{"width":1280,"y":1,"height":720,"name":"body","x":1}],"height":1024} -------------------------------------------------------------------------------- /Content/clothes/c_monika/c_monika_tex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shafvfshkga/Chat-Monika-Chinese-cpp/c817f7680386e3c0b639fae52218f970b588a11f/Content/clothes/c_monika/c_monika_tex.png -------------------------------------------------------------------------------- /Content/fonts/Marker Felt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shafvfshkga/Chat-Monika-Chinese-cpp/c817f7680386e3c0b639fae52218f970b588a11f/Content/fonts/Marker Felt.ttf -------------------------------------------------------------------------------- /Content/fonts/arial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shafvfshkga/Chat-Monika-Chinese-cpp/c817f7680386e3c0b639fae52218f970b588a11f/Content/fonts/arial.ttf -------------------------------------------------------------------------------- /Content/fonts/black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shafvfshkga/Chat-Monika-Chinese-cpp/c817f7680386e3c0b639fae52218f970b588a11f/Content/fonts/black.ttf -------------------------------------------------------------------------------- /Content/models/single_speaker_tiny.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shafvfshkga/Chat-Monika-Chinese-cpp/c817f7680386e3c0b639fae52218f970b588a11f/Content/models/single_speaker_tiny.bin -------------------------------------------------------------------------------- /Content/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shafvfshkga/Chat-Monika-Chinese-cpp/c817f7680386e3c0b639fae52218f970b588a11f/Content/settings.png -------------------------------------------------------------------------------- /Content/spaceroom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shafvfshkga/Chat-Monika-Chinese-cpp/c817f7680386e3c0b639fae52218f970b588a11f/Content/spaceroom.png -------------------------------------------------------------------------------- /Content/textbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shafvfshkga/Chat-Monika-Chinese-cpp/c817f7680386e3c0b639fae52218f970b588a11f/Content/textbox.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chat-Monika-Chinese-cpp 2 | 引用 3 | ---- 4 | 图像界面使用axmol制作 5 | https://github.com/axmolengine/axmol 6 | 语言大模型取自 7 | https://github.com/ztxz16/fastllm 8 | 中文语音取自 9 | https://github.com/huakunyang/SummerTTS 10 | 图像资源取自 11 | https://github.com/Monika-After-Story/MonikaModDev 12 | 使用方法 13 | ---- 14 | 下载Release.zip 解压 15 | https://pan.baidu.com/share/init?surl=5ZjhxmA9viSrVYhN935lBg 提取码:ah5w (fastllm作者提供) 16 | 下载chatglm-6b-int4.bin 放在FastLLM-Monika.exe同一目录下 17 | 运行FastLLM-Monika.exe 18 | -------------------------------------------------------------------------------- /Source/AppDelegate.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. 3 | Copyright (c) 2021 Bytedance Inc. 4 | 5 | https://axmolengine.github.io/ 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | ****************************************************************************/ 25 | 26 | #include "AppDelegate.h" 27 | #include "GameScene.h" 28 | 29 | #define USE_AUDIO_ENGINE 1 30 | 31 | #if USE_AUDIO_ENGINE 32 | # include "audio/AudioEngine.h" 33 | #endif 34 | 35 | USING_NS_AX; 36 | 37 | static ax::Size designResolutionSize = ax::Size(1280, 720); 38 | static ax::Size smallResolutionSize = ax::Size(480, 320); 39 | static ax::Size mediumResolutionSize = ax::Size(1024, 768); 40 | static ax::Size largeResolutionSize = ax::Size(2048, 1536); 41 | 42 | AppDelegate::AppDelegate() {} 43 | 44 | AppDelegate::~AppDelegate() {} 45 | 46 | // if you want a different context, modify the value of glContextAttrs 47 | // it will affect all platforms 48 | void AppDelegate::initGLContextAttrs() 49 | { 50 | // set OpenGL context attributes: red,green,blue,alpha,depth,stencil,multisamplesCount 51 | GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8, 0}; 52 | 53 | GLView::setGLContextAttrs(glContextAttrs); 54 | } 55 | 56 | // if you want to use the package manager to install more packages, 57 | // don't modify or remove this function 58 | static int register_all_packages() 59 | { 60 | return 0; // flag for packages manager 61 | } 62 | 63 | bool AppDelegate::applicationDidFinishLaunching() 64 | { 65 | // initialize director 66 | auto director = Director::getInstance(); 67 | auto glView = director->getOpenGLView(); 68 | if (!glView) 69 | { 70 | #if (AX_TARGET_PLATFORM == AX_PLATFORM_WIN32) || (AX_TARGET_PLATFORM == AX_PLATFORM_MAC) || \ 71 | (AX_TARGET_PLATFORM == AX_PLATFORM_LINUX) 72 | glView = GLViewImpl::createWithRect( 73 | "FastLLM-Monika", ax::Rect(0, 0, designResolutionSize.width, designResolutionSize.height)); 74 | #else 75 | glView = GLViewImpl::create("FastLLM-Monika"); 76 | #endif 77 | director->setOpenGLView(glView); 78 | } 79 | 80 | // set FPS. the default value is 1.0/60 if you don't call this 81 | director->setAnimationInterval(1.0f / 60); 82 | 83 | // Set the design resolution 84 | glView->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, 85 | ResolutionPolicy::SHOW_ALL); 86 | auto frameSize = glView->getFrameSize(); 87 | // if the frame's height is larger than the height of medium size. 88 | if (frameSize.height > mediumResolutionSize.height) 89 | { 90 | director->setContentScaleFactor(MIN(largeResolutionSize.height / designResolutionSize.height, 91 | largeResolutionSize.width / designResolutionSize.width)); 92 | } 93 | // if the frame's height is larger than the height of small size. 94 | else if (frameSize.height > smallResolutionSize.height) 95 | { 96 | director->setContentScaleFactor(MIN(mediumResolutionSize.height / designResolutionSize.height, 97 | mediumResolutionSize.width / designResolutionSize.width)); 98 | } 99 | // if the frame's height is smaller than the height of medium size. 100 | else 101 | { 102 | director->setContentScaleFactor(MIN(smallResolutionSize.height / designResolutionSize.height, 103 | smallResolutionSize.width / designResolutionSize.width)); 104 | } 105 | 106 | register_all_packages(); 107 | 108 | // create a scene. it's an autorelease object 109 | auto scene = utils::createInstance(); 110 | 111 | // run 112 | director->runWithScene(scene); 113 | 114 | return true; 115 | } 116 | 117 | // This function will be called when the app is inactive. Note, when receiving a phone call it is invoked. 118 | void AppDelegate::applicationDidEnterBackground() 119 | { 120 | Director::getInstance()->stopAnimation(); 121 | 122 | #if USE_AUDIO_ENGINE 123 | AudioEngine::pauseAll(); 124 | #endif 125 | } 126 | 127 | // this function will be called when the app is active again 128 | void AppDelegate::applicationWillEnterForeground() 129 | { 130 | Director::getInstance()->startAnimation(); 131 | 132 | #if USE_AUDIO_ENGINE 133 | AudioEngine::resumeAll(); 134 | #endif 135 | } 136 | -------------------------------------------------------------------------------- /Source/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. 3 | Copyright (c) 2021 Bytedance Inc. 4 | 5 | https://axmolengine.github.io/ 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | ****************************************************************************/ 25 | 26 | #ifndef _APP_DELEGATE_H_ 27 | #define _APP_DELEGATE_H_ 28 | 29 | #include "axmol.h" 30 | 31 | /** 32 | @brief The axmol Application. 33 | 34 | Private inheritance here hides part of interface from Director. 35 | */ 36 | class AppDelegate : private ax::Application 37 | { 38 | public: 39 | AppDelegate(); 40 | virtual ~AppDelegate(); 41 | 42 | void initGLContextAttrs() override; 43 | 44 | /** 45 | @brief Implement Director and Scene init code here. 46 | @return true Initialize success, app continue. 47 | @return false Initialize failed, app terminate. 48 | */ 49 | bool applicationDidFinishLaunching() override; 50 | 51 | /** 52 | @brief Called when the application moves to the background 53 | @param the pointer of the application 54 | */ 55 | void applicationDidEnterBackground() override; 56 | 57 | /** 58 | @brief Called when the application reenters the foreground 59 | @param the pointer of the application 60 | */ 61 | void applicationWillEnterForeground() override; 62 | }; 63 | 64 | #endif // _APP_DELEGATE_H_ 65 | -------------------------------------------------------------------------------- /Source/FastLLM.cpp: -------------------------------------------------------------------------------- 1 | #include "FastLLM.h" 2 | #include "httplib.h" 3 | #include "textconvertor.h" 4 | 5 | USING_NS_AX; 6 | 7 | FastLLM* FastLLM::_fastLLM = nullptr; 8 | 9 | FastLLM* FastLLM::getInstance() 10 | { 11 | if (_fastLLM == nullptr) 12 | { 13 | _fastLLM = new FastLLM(); 14 | if (_fastLLM && _fastLLM->init()) 15 | { 16 | _fastLLM->autorelease(); 17 | _fastLLM->retain(); 18 | } 19 | else 20 | { 21 | AX_SAFE_DELETE(_fastLLM); 22 | _fastLLM = nullptr; 23 | } 24 | } 25 | 26 | return _fastLLM; 27 | } 28 | 29 | bool FastLLM::init() 30 | { 31 | fastllm::SetThreads(8); 32 | chatGLM.LoadFromFile("chatglm-6b-int4.bin"); 33 | 34 | return true; 35 | } 36 | 37 | std::string FastLLM::getResponse(std::string input) 38 | { 39 | std::stringstream ss; 40 | std::string ret = chatGLM.Response(input, &ss); 41 | 42 | return ret; 43 | } -------------------------------------------------------------------------------- /Source/FastLLM.h: -------------------------------------------------------------------------------- 1 | #ifndef __FASTLLM_H__ 2 | #define __FASTLLM_H__ 3 | 4 | #include "axmol.h" 5 | #include "chatglm.h" 6 | 7 | USING_NS_AX; 8 | 9 | class FastLLM :public Node 10 | { 11 | public: 12 | static FastLLM* getInstance(); 13 | virtual bool init(); 14 | 15 | std::string getResponse(std::string input); 16 | private: 17 | static FastLLM* _fastLLM; 18 | fastllm::ChatGLMModel chatGLM; 19 | }; 20 | 21 | #endif -------------------------------------------------------------------------------- /Source/GameScene.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. 3 | Copyright (c) 2021 Bytedance Inc. 4 | 5 | https://axmolengine.github.io/ 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | ****************************************************************************/ 25 | 26 | #include "GameScene.h" 27 | #include "SummerTTS.h" 28 | #include "FastLLM.h" 29 | #include "textconvertor.h" 30 | #include "audio/AudioEngine.h" 31 | 32 | USING_NS_AX; 33 | 34 | dragonBones::CCFactory* ChatMonika::factory = dragonBones::CCFactory::getFactory(); 35 | 36 | #include "io.h" 37 | void ChatMonika::getFilesName(std::string path, std::vector& files, std::string suffix) 38 | { 39 | long long hFile = 0; 40 | struct _finddata_t fileinfo; 41 | std::string p; 42 | if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) 43 | { 44 | do 45 | { 46 | std::string str = fileinfo.name; 47 | if (str.find(suffix) != -1) 48 | { 49 | std::string fileName = fileinfo.name; 50 | files.push_back(fileName); 51 | } 52 | 53 | } while (_findnext(hFile, &fileinfo) == 0); 54 | _findclose(hFile); 55 | } 56 | } 57 | 58 | int ChatMonika::getIndex(std::string filePath) 59 | { 60 | files.clear(); 61 | getFilesName(filePath, files, ".wav"); 62 | 63 | int max = 0; 64 | int index = 0; 65 | for (int i = 0; i < files.size(); i++) 66 | { 67 | auto str = files.at(i); 68 | int start_pos = str.find_last_of('e') + 1; 69 | int end_pos = str.find('.'); 70 | int val = atoi(str.substr(start_pos, end_pos - start_pos).c_str()); 71 | if (val > max) 72 | { 73 | max = val; 74 | index = i; 75 | } 76 | 77 | } 78 | 79 | return index; 80 | } 81 | 82 | void ChatMonika::stringSplit(std::string str, std::vector& vector) 83 | { 84 | std::string pat = "。"; 85 | bool isRemovePat = true; 86 | int patSplitPos = pat.size(); 87 | std::vector bufStr; 88 | while (true) 89 | { 90 | int index = str.find(pat); 91 | int iSplitPos = 0;//分割点 92 | int iEraserPos = 0;//分割点 93 | if (isRemovePat || index == -1)//如果去掉分割字符串 94 | { 95 | iSplitPos = index; 96 | iEraserPos = index + pat.size(); 97 | } 98 | else 99 | { 100 | iSplitPos = index + patSplitPos; 101 | iEraserPos = index + patSplitPos; 102 | } 103 | std::string subStr = str.substr(0, iSplitPos); 104 | if (!subStr.empty()) vector.push_back(subStr); 105 | str.erase(0, iEraserPos); 106 | if (index == -1) break; 107 | } 108 | } 109 | 110 | // on "init" you need to initialize your instance 111 | bool ChatMonika::init() 112 | { 113 | ////////////////////////////// 114 | // 1. super init first 115 | if (!Scene::init()) 116 | { 117 | return false; 118 | } 119 | 120 | visibleSize = _director->getVisibleSize(); 121 | 122 | //语音路径 123 | voicePath = FileUtils::getInstance()->getDefaultResourceRootPath() + "voice/out.wav"; 124 | 125 | //加载背景 126 | bg = Sprite::create("spaceroom.png"); 127 | bg->setPosition(visibleSize / 2); 128 | this->addChild(bg); 129 | 130 | UILayer[0] = Layer::create(); 131 | UILayer[1] = Layer::create(); 132 | 133 | //加载莫妮卡 134 | factory->loadDragonBonesData("Monika/Monika_ske.json"); 135 | factory->loadTextureAtlasData("Monika/Monika_tex.json"); 136 | 137 | factory->loadDragonBonesData("clothes/c_miku/c_miku_ske.json"); 138 | factory->loadTextureAtlasData("clothes/c_miku/c_miku_tex.json"); 139 | factory->loadDragonBonesData("clothes/c_monika/c_monika_ske.json"); 140 | factory->loadTextureAtlasData("clothes/c_monika/c_monika_tex.json"); 141 | 142 | chara = new dragonBones::CCArmatureDisplay(); 143 | chara = factory->buildArmatureDisplay("Monika"); 144 | chara->getAnimation()->play("steady"); 145 | chara->setAnchorPoint(Vec2(0.5, 0)); 146 | chara->setPosition(Vec2(visibleSize.width / 2, 0)); 147 | this->addChild(chara); 148 | 149 | uiState = ChatMonika::UIState::UI_1; 150 | settings = ui::Button::create("settings.png"); 151 | settings->setScale(visibleSize.height * 0.12f / settings->getContentSize().height); 152 | settings->setPosition(Vec2(visibleSize.width - visibleSize.height * 0.08f, visibleSize.height - visibleSize.height * 0.08f)); 153 | this->addChild(settings, 1); 154 | settings->addTouchEventListener([&](Ref* sender, ui::Widget::TouchEventType type) 155 | { 156 | if (type == ui::Widget::TouchEventType::ENDED) 157 | { 158 | switch (uiState) 159 | { 160 | case ChatMonika::UIState::UI_1: 161 | uiState = ChatMonika::UIState::UI_2; 162 | UILayer[1]->setPositionY(0); 163 | UILayer[0]->setPositionY(-visibleSize.height); 164 | break; 165 | case ChatMonika::UIState::UI_2: 166 | uiState = ChatMonika::UIState::UI_1; 167 | UILayer[0]->setPositionY(0); 168 | UILayer[1]->setPositionY(-visibleSize.height); 169 | break; 170 | } 171 | } 172 | }); 173 | 174 | initUILayer1(); 175 | initUILayer2(); 176 | 177 | this->addChild(UILayer[0], 1); 178 | this->addChild(UILayer[1], 1); 179 | return true; 180 | } 181 | 182 | void ChatMonika::initUILayer1() 183 | { 184 | //加载文本框 185 | textbox = Sprite::create("textbox.png"); 186 | textbox->setAnchorPoint(Vec2(0.5, 0)); 187 | textbox->setPosition(Vec2(visibleSize.width / 2, visibleSize.height * 0.1f)); 188 | UILayer[0]->addChild(textbox); 189 | textbox->setVisible(false); 190 | 191 | //加载输入框 192 | auto size = textbox->getContentSize(); 193 | editbox = ui::EditBox::create(Size(size.width, size.height / 2), ui::Scale9Sprite::create("choice.png")); 194 | editbox->setAnchorPoint(Vec2(0.5, 0)); 195 | editbox->setPosition(textbox->getPosition()); 196 | editbox->setFontSize(visibleSize.height * 0.04f); 197 | const char* tip = "本项目github开源,禁止商用。\n输入文字后,点击[对话]。"; 198 | editbox->setText(GBKToUTF8(tip).c_str()); 199 | UILayer[0]->addChild(editbox); 200 | 201 | //加载文字 202 | text = Label::createWithTTF("", "fonts/black.ttf", visibleSize.height * 0.04f); 203 | text->setAnchorPoint(Vec2(0, 1)); 204 | text->setPosition(Vec2(visibleSize.width / 2 - size.width * 0.48f, visibleSize.height * 0.1f + size.height * 0.94f)); 205 | text->setDimensions(size.width * 0.96f, size.height * 0.88f); 206 | UILayer[0]->addChild(text, 1); 207 | 208 | //确认按钮 209 | button[0] = ui::Button::create("choice.png", "choice_dark.png"); 210 | button[0]->setTitleFontName("fonts/black.ttf"); 211 | button[0]->setTitleFontSize(0.5f * button[0]->getContentSize().height); 212 | const char* button_text1 = "对话"; 213 | button[0]->setTitleText(GBKToUTF8(button_text1)); 214 | 215 | button[0]->setPosition(Vec2(visibleSize.width * 0.36f, visibleSize.height * 0.05f)); 216 | UILayer[0]->addChild(button[0]); 217 | button[0]->addTouchEventListener([&](Ref* sender, ui::Widget::TouchEventType type) 218 | { 219 | if (type == ui::Widget::TouchEventType::ENDED && editbox->isEnabled()) 220 | { 221 | auto str = editbox->getText(); 222 | editbox->setVisible(false); 223 | editbox->setEnabled(false); 224 | auto answer = FastLLM::getInstance()->getResponse(str); 225 | 226 | stringSplit(UTF8ToGBK(answer), talkList); 227 | answer = GBKToUTF8(talkList.at(0)); 228 | 229 | bool isDone = SummerTTS::getInstance()->generateVoice(answer); 230 | if (isDone) 231 | { 232 | textbox->setVisible(true); 233 | 234 | AudioEngine::uncacheAll(); 235 | int audioID = AudioEngine::play2d(voicePath); 236 | AudioEngine::setFinishCallback(audioID, AX_CALLBACK_0(ChatMonika::voiceCallBack, this)); 237 | chara->getAnimation()->play("talk"); 238 | text->setString(answer.c_str()); 239 | } 240 | } 241 | }); 242 | 243 | //继续按钮 244 | button[1] = ui::Button::create("choice.png", "choice_dark.png"); 245 | button[1]->setTitleFontName("fonts/black.ttf"); 246 | button[1]->setTitleFontSize(0.5f * button[1]->getContentSize().height); 247 | const char* button_text2 = "继续"; 248 | button[1]->setTitleText(GBKToUTF8(button_text2)); 249 | 250 | button[1]->setPosition(Vec2(visibleSize.width * 0.64f, visibleSize.height * 0.05f)); 251 | UILayer[0]->addChild(button[1]); 252 | button[1]->addTouchEventListener([&](Ref* sender, ui::Widget::TouchEventType type) 253 | { 254 | if (type == ui::Widget::TouchEventType::ENDED) 255 | { 256 | if (textbox->isVisible() && (talkTimes < talkList.size() - 1)) 257 | { 258 | talkTimes += 1; 259 | auto answer = GBKToUTF8(talkList.at(talkTimes)); 260 | 261 | bool isDone = SummerTTS::getInstance()->generateVoice(answer); 262 | if (isDone) 263 | { 264 | AudioEngine::uncacheAll(); 265 | int audioID = AudioEngine::play2d(voicePath); 266 | AudioEngine::setFinishCallback(audioID, AX_CALLBACK_0(ChatMonika::voiceCallBack, this)); 267 | chara->getAnimation()->play("talk"); 268 | text->setString(answer.c_str()); 269 | } 270 | } 271 | else if (talkTimes == talkList.size() - 1) 272 | { 273 | talkTimes = 0; 274 | talkList.clear(); 275 | text->setString(""); 276 | textbox->setVisible(false); 277 | editbox->setVisible(true); 278 | editbox->setEnabled(true); 279 | } 280 | } 281 | }); 282 | } 283 | 284 | void ChatMonika::initUILayer2() 285 | { 286 | UILayer[1]->setPositionY(-visibleSize.height); 287 | auto vw = visibleSize.width; 288 | auto vh = visibleSize.height; 289 | 290 | auto dataMask = DrawNode::create(); 291 | dataMask->drawSolidRect(Vec2(vw / 2 - vh * 0.4f, vh * 0.04f), Vec2(vw / 2 + vh * 0.4f, vh * 0.96f), Color4F::BLACK); 292 | dataMask->setOpacity(100); 293 | UILayer[1]->addChild(dataMask); 294 | 295 | auto dataList = Menu::create(); 296 | auto fileUtils = FileUtils::getInstance(); 297 | 298 | auto item1 = MenuItemToggle::createWithCallback(CC_CALLBACK_1(ChatMonika::settingsCallBack, this), 299 | MenuItemLabel::create(Label::createWithTTF(GBKToUTF8("[模样]经典套装"), "fonts/black.ttf", vh * 0.08f)), 300 | MenuItemLabel::create(Label::createWithTTF(GBKToUTF8("[模样]初音未来"), "fonts/black.ttf", vh * 0.08f)), NULL); 301 | auto item2 = MenuItemLabel::create(Label::createWithTTF(GBKToUTF8("退出"), "fonts/black.ttf", vh * 0.08f), CC_CALLBACK_1(ChatMonika::settingsCallBack, this)); 302 | item1->setTag(0); 303 | item2->setTag(1); 304 | dataList->addChild(item1); 305 | dataList->addChild(item2); 306 | dataList->setPosition(Vec2(vw / 2, vh / 2)); 307 | dataList->alignItemsVertically(); 308 | UILayer[1]->addChild(dataList); 309 | } 310 | 311 | void ChatMonika::voiceCallBack() 312 | { 313 | chara->getAnimation()->play("steady"); 314 | } 315 | 316 | void ChatMonika::settingsCallBack(Ref* sender) 317 | { 318 | auto item = (MenuItemLabel*)sender; 319 | auto itemTag = item->getTag(); 320 | switch (itemTag) 321 | { 322 | case 0: 323 | { 324 | std::string suitName = ""; 325 | if (clothes == 0) 326 | { 327 | clothes = 1; 328 | suitName = "c_miku"; 329 | } 330 | else 331 | { 332 | clothes = 0; 333 | suitName = "c_monika"; 334 | } 335 | const auto armatureData = factory->getArmatureData(suitName); 336 | if (armatureData != nullptr) 337 | factory->replaceSkin(chara->getArmature(), armatureData->defaultSkin); 338 | break; 339 | } 340 | case 1: 341 | Director::getInstance()->end(); 342 | break; 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /Source/GameScene.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. 3 | Copyright (c) 2021 Bytedance Inc. 4 | 5 | https://axmolengine.github.io/ 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | ****************************************************************************/ 25 | 26 | #ifndef __GAME_SCENE_H__ 27 | #define __GAME_SCENE_H__ 28 | 29 | #include "axmol.h" 30 | #include "ui/CocosGUI.h" 31 | #include "DragonBones/CCArmatureDisplay.h" 32 | #include "DragonBones/CCFactory.h" 33 | 34 | USING_NS_AX; 35 | 36 | class ChatMonika : public Scene 37 | { 38 | public: 39 | bool init() override; 40 | //void update(float delta) override; 41 | 42 | static dragonBones::CCFactory* factory; 43 | dragonBones::CCArmatureDisplay* chara; 44 | 45 | Vec2 visibleSize; 46 | void initUILayer1(); 47 | void initUILayer2(); 48 | 49 | int clothes = 0; 50 | enum UIState 51 | { 52 | UI_1, 53 | UI_2, 54 | }uiState; 55 | ui::Button* settings; 56 | private: 57 | Sprite* bg; 58 | Sprite* textbox; 59 | ui::Button* button[2]; 60 | ui::EditBox* editbox; 61 | Label* text; 62 | Layer* UILayer[2]; 63 | 64 | int talkTimes = 0; 65 | std::vector talkList; 66 | std::vector files; 67 | int getIndex(std::string filePath); 68 | void getFilesName(std::string path, std::vector& files, std::string); 69 | void stringSplit(std::string str, std::vector& vector); 70 | 71 | std::string voicePath; 72 | void voiceCallBack(); 73 | void settingsCallBack(Ref* sender); 74 | }; 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /Source/SummerTTS.cpp: -------------------------------------------------------------------------------- 1 | #include "SummerTTS.h" 2 | #include 3 | 4 | USING_NS_AX; 5 | 6 | SummerTTS* SummerTTS::_summerTTS = nullptr; 7 | 8 | SummerTTS* SummerTTS::getInstance() 9 | { 10 | if (_summerTTS == nullptr) 11 | { 12 | _summerTTS = new SummerTTS(); 13 | if (_summerTTS && _summerTTS->init()) 14 | { 15 | _summerTTS->autorelease(); 16 | _summerTTS->retain(); 17 | } 18 | else 19 | { 20 | AX_SAFE_DELETE(_summerTTS); 21 | _summerTTS = nullptr; 22 | } 23 | } 24 | 25 | return _summerTTS; 26 | } 27 | 28 | bool SummerTTS::init() 29 | { 30 | float* dataW = NULL; 31 | std::string filePath = FileUtils::getInstance()->getDefaultResourceRootPath() + "models/single_speaker_tiny.bin"; 32 | int32_t modelSize = ttsLoadModel((char*)filePath.c_str(), &dataW); 33 | 34 | synthesizer = new SynthesizerTrn(dataW); 35 | 36 | tts_free_data(dataW); 37 | return true; 38 | } 39 | 40 | void SummerTTS::convertAudioToWavBuf( 41 | char* toBuf, 42 | char* fromBuf, 43 | int totalAudioLen) 44 | { 45 | char* header = toBuf; 46 | int byteRate = 16 * 16000 * 1 / 8; 47 | int totalDataLen = totalAudioLen + 36; 48 | int channels = 1; 49 | int longSampleRate = 16000; 50 | 51 | header[0] = 'R'; // RIFF/WAVE header 52 | header[1] = 'I'; 53 | header[2] = 'F'; 54 | header[3] = 'F'; 55 | header[4] = (char)(totalDataLen & 0xff); 56 | header[5] = (char)((totalDataLen >> 8) & 0xff); 57 | header[6] = (char)((totalDataLen >> 16) & 0xff); 58 | header[7] = (char)((totalDataLen >> 24) & 0xff); 59 | header[8] = 'W'; 60 | header[9] = 'A'; 61 | header[10] = 'V'; 62 | header[11] = 'E'; 63 | header[12] = 'f'; // 'fmt ' chunk 64 | header[13] = 'm'; 65 | header[14] = 't'; 66 | header[15] = ' '; 67 | header[16] = 16; // 4 bytes: size of 'fmt ' chunk 68 | header[17] = 0; 69 | header[18] = 0; 70 | header[19] = 0; 71 | header[20] = 1; // format = 1 72 | header[21] = 0; 73 | header[22] = (char)channels; 74 | header[23] = 0; 75 | header[24] = (char)(longSampleRate & 0xff); 76 | header[25] = (char)((longSampleRate >> 8) & 0xff); 77 | header[26] = (char)((longSampleRate >> 16) & 0xff); 78 | header[27] = (char)((longSampleRate >> 24) & 0xff); 79 | header[28] = (char)(byteRate & 0xff); 80 | header[29] = (char)((byteRate >> 8) & 0xff); 81 | header[30] = (char)((byteRate >> 16) & 0xff); 82 | header[31] = (char)((byteRate >> 24) & 0xff); 83 | header[32] = (char)(1 * 16 / 8); // block align 84 | header[33] = 0; 85 | header[34] = 16; // bits per sample 86 | header[35] = 0; 87 | header[36] = 'd'; 88 | header[37] = 'a'; 89 | header[38] = 't'; 90 | header[39] = 'a'; 91 | header[40] = (char)(totalAudioLen & 0xff); 92 | header[41] = (char)((totalAudioLen >> 8) & 0xff); 93 | header[42] = (char)((totalAudioLen >> 16) & 0xff); 94 | header[43] = (char)((totalAudioLen >> 24) & 0xff); 95 | 96 | memcpy(toBuf + 44, fromBuf, totalAudioLen); 97 | 98 | } 99 | 100 | bool SummerTTS::generateVoice(std::string line) 101 | { 102 | if (hanz2piny.isStartWithBom(line)) 103 | { 104 | line = std::string(line.cbegin() + 3, line.cend()); 105 | } 106 | 107 | int32_t retLen = 0; 108 | int16_t* wavData = synthesizer->infer(line, 0, 1.1, retLen); 109 | 110 | char* dataForFile = new char[retLen * sizeof(int16_t) + 44]; 111 | convertAudioToWavBuf(dataForFile, (char*)wavData, retLen * sizeof(int16_t)); 112 | 113 | FILE* fpOut = fopen("Content/voice/out.wav", "wb"); 114 | fwrite(dataForFile, retLen * sizeof(int16_t) + 44, 1, fpOut); 115 | fclose(fpOut); 116 | tts_free_data(wavData); 117 | 118 | return true; 119 | } 120 | -------------------------------------------------------------------------------- /Source/SummerTTS.h: -------------------------------------------------------------------------------- 1 | #ifndef __SUMMERTTS_H__ 2 | #define __SUMMERTTS_H__ 3 | 4 | #include "axmol.h" 5 | #include "SynthesizerTrn.h" 6 | #include "utils.h" 7 | #include "Hanz2Piny.h" 8 | #include "hanzi2phoneid.h" 9 | 10 | USING_NS_AX; 11 | 12 | class SummerTTS :public Node 13 | { 14 | public: 15 | static SummerTTS* getInstance(); 16 | virtual bool init(); 17 | 18 | void convertAudioToWavBuf( 19 | char* toBuf, 20 | char* fromBuf, 21 | int totalAudioLen); 22 | bool generateVoice(std::string input); 23 | private: 24 | static SummerTTS* _summerTTS; 25 | Hanz2Piny hanz2piny; 26 | 27 | SynthesizerTrn* synthesizer; 28 | }; 29 | 30 | #endif --------------------------------------------------------------------------------