tts_demo2.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /*================================================================
  2. Copyright (c) 2020 Quectel Wireless Solution, Co., Ltd. All Rights Reserved.
  3. Quectel Wireless Solution Proprietary and Confidential.
  4. =================================================================*/
  5. /************************************************************************************************************
  6. 此demo将tts的api,与audio的播放函数一起进行封装,封装后的ql_tts_play,ql_tts_init,ql_tts_deinit与我司
  7. ASR系列的TTS播放函数类似,直接调用ql_tts_init+ql_tts_play即可以开始播放,播放完成后,调用ql_tts_deinit
  8. 函数释放TTS资源。用户可参考tts_demo.c中的方法,也可使用本demo中的方法进行TTS播放
  9. 注意:由于audio的播放器只有一个,因此此demo与tts_demo.c同一时间只能有一个运行
  10. 用户如需将TTS库放到内核,则在target.config中将 CONFIG_QUEC_PROJECT_FEATURE_TTS_IN_KERNEL 置为 y,
  11. 并修改分区,将内核分区增大250k左右,其余的内容不变
  12. *************************************************************************************************************/
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include <stdlib.h>
  16. #include "tts_demo.h"
  17. #include "ql_api_osi.h"
  18. #include "ql_api_tts.h"
  19. #include "ql_log.h"
  20. #include "ql_osi_def.h"
  21. #include "ql_audio.h"
  22. #include "ql_fs.h"
  23. #include "ql_api_dev.h"
  24. /*
  25. 1. 不同的TTS资源文件,对应的播放效果不同。其中中文资源不能用来播英文单词,单词会以字母的方式播出; 英文资源也不能用来播中文。默认使用
  26. 中文16k TTS资源
  27. 2. 若使用16k中文TTS资源,且TTS资源文件预置到内置flash中,则不需要修改json脚本(脚本默认已选择预置16k中文资源,且预置到内置flash),只需要调用
  28. ql_tts_engine_init函数即可完成初始化,不需要关注以下描述
  29. 3. 所有的资源文件均在components\ql-config\download\prepack下,其中:
  30. 英文16k资源文件名为: "quectel_tts_resource_english_16k.bin"
  31. 中文8k资源文件为:"quectel_tts_resource_chinese_8k.bin"
  32. 中文16k资源文件为:"quectel_pcm_resource.bin"
  33. 4. 预置文件时,请将json脚本中的"file"固定为"/qsfs/quectel_pcm_resource.bin"(预置资源文件到内置flash), 或
  34. "/ext/qsfs/quectel_pcm_resource.bin"(预置到外置6线spi flash中), 并修改"local_file"来选择上传哪个资源文件,如下述示例.
  35. 若不使用中文16k资源,则需要使用"ql_tts_engine_init_ex"函数,将配置结构体中的"resource"变量设置为需要使用的资源;
  36. 若将资源文件预置到外置6线spi flash,需要将"position"变量设置为 POSIT_EFS
  37. 当TTS资源文件预置在内置Flash时,针对需要FOTA升级的情况,新版本SDK中默认将该文件进行拆分为多个子文件进行预置!
  38. 外置存储时可以不用拆分。
  39. 5. 使用英文16k TTS资源播放时,需要1.45M的RAM空间,因此要注意RAM空间是否充足; 选择中文16k TTS资源文件时,需要620k的RAM空间; 选择中文
  40. 8k资源时,需要570kRAM空间
  41. 预置文件示例:
  42. 1. 预置16k中文TTS资源文件到内部flash(默认):
  43. "files": [
  44. {
  45. "file": "/qsfs/quectel_pcm_resource.bin",
  46. "local_file": "quectel_pcm_resource.bin"
  47. }
  48. ]
  49. 2. 预置16k英文TTS资源文件到内部flash(以"/qsfs/quectel_pcm_resource.bin"为文件系统路径)
  50. "files": [
  51. {
  52. "file": "/qsfs/quectel_pcm_resource.bin",
  53. "local_file": "quectel_tts_resource_english_16k.bin"
  54. }
  55. ]
  56. 3. 预置8K中文TTS资源文件到内部flash(以"/qsfs/quectel_pcm_resource.bin"为文件系统路径)
  57. "files": [
  58. {
  59. "file": "/qsfs/quectel_pcm_resource.bin",
  60. "local_file": "quectel_tts_resource_chinese_8k.bin"
  61. }
  62. ]
  63. 4. (1)预置16k英文TTS资源到外置6线spi flash(以/ext/qsfs/quectel_pcm_resource.bin"为文件系统路径)
  64. "files": [
  65. {
  66. "file": "/ext/qsfs/quectel_pcm_resource.bin",
  67. "local_file": "quectel_tts_resource_english_16k.bin"
  68. }
  69. ]
  70. (2)需要把boot_fdl_dnld.c文件的bool fdlDnldStart(fdlEngine_t *fdl, unsigned devtype),
  71. 6线flash部分的#if 0打开为1(CONFIG_QUEC_PROJECT_FEATURE_SPI6_EXT_NOR_SFFS部分);
  72. (3)在target.config中,CONFIG_QUEC_PROJECT_FEATURE_SPI6_EXT_NOR_SFFS打开,CONFIG_QUEC_PROJECT_FEATURE_SPI4_EXT_NOR关闭
  73. 5. (1)预置16k中文TTS资源文件到外置4线flash
  74. "files": [
  75. {
  76. "file": "/ext4n/qsfs/quectel_pcm_resource.bin",
  77. "local_file": "quectel_pcm_resource.bin"
  78. }
  79. ]
  80. (2)需要把boot_fdl_dnld.c文件的bool fdlDnldStart(fdlEngine_t *fdl, unsigned devtype),
  81. 4线flash部分的#if 0打开为1(CONFIG_QUEC_PROJECT_FEATURE_SPI4_EXT_NOR_SFFS部分);
  82. (3)在target.config中,CONFIG_QUEC_PROJECT_FEATURE_SPI4_EXT_NOR_SFFS打开,CONFIG_QUEC_PROJECT_FEATURE_SPI6_EXT_NOR关闭
  83. */
  84. #define QL_TTS_LANGUAGE_USE_ENGLISH 0
  85. /*0:tts库在内置flash,1:tts库在六线flash,2:tts库在四线flash*/
  86. #define QL_TTS_LOCATION 0
  87. #define QL_TTS_LOG_LEVEL QL_LOG_LEVEL_INFO
  88. #define QL_TTS_LOG(msg, ...) QL_LOG(QL_TTS_LOG_LEVEL, "ql_app_tts", msg, ##__VA_ARGS__)
  89. #define QL_TTS_LOG_PUSH(msg, ...) QL_LOG_PUSH("ql_app_tts", msg, ##__VA_ARGS__)
  90. #if !defined(tts_demo_no_err)
  91. #define tts_demo_no_err(x, action, str) \
  92. do \
  93. { \
  94. if(x != 0) \
  95. { \
  96. QL_TTS_LOG(str); \
  97. {action;} \
  98. } \
  99. } while( 1==0 )
  100. #endif
  101. typedef struct
  102. {
  103. ql_tts_encoding_e encode;
  104. char *str;
  105. uint len;
  106. }tts_demo_play_info_t;
  107. /*===========================================================================
  108. * Variate
  109. ===========================================================================*/
  110. PCM_HANDLE_T ql_player = NULL;
  111. PCM_HANDLE_T ql_recorder = NULL;
  112. ql_task_t ql_tts_demo_task2 = NULL;
  113. ql_queue_t ql_tts_demo_queue = NULL;
  114. /*===========================================================================
  115. * Functions
  116. ===========================================================================*/
  117. int userCallback(void *param, int param1, int param2, int param3, int data_len, const void *pcm_data)
  118. {
  119. int err;
  120. err = ql_pcm_write(ql_player, (void *)pcm_data, data_len);
  121. if(err <= 0)
  122. {
  123. QL_TTS_LOG("write data to PCM player failed");
  124. return -1;
  125. }
  126. return 0;
  127. }
  128. void ql_tts_thread_demo_2(void *param)
  129. {
  130. int err = 0;
  131. tts_demo_play_info_t info = {0};
  132. ql_set_audio_path_earphone();
  133. ql_aud_set_volume(QL_AUDIO_PLAY_TYPE_LOCAL, AUDIOHAL_SPK_VOL_11);
  134. poc_demo_test();
  135. while(1)
  136. {
  137. err = ql_rtos_queue_wait(ql_tts_demo_queue, (uint8 *)&info, sizeof(tts_demo_play_info_t), QL_WAIT_FOREVER);
  138. tts_demo_no_err(err, continue, "invalid queue");
  139. if(!info.str || !info.len){
  140. QL_TTS_LOG("invalid tts string");
  141. continue;
  142. }
  143. ql_pcm_poc_init_ex();
  144. err = ql_tts_init(userCallback);
  145. tts_demo_no_err(err, goto exit, "tts init failed");
  146. #if !QL_TTS_LANGUAGE_USE_ENGLISH //使用英文TTS库不需要设置编码
  147. err = ql_tts_set_config_param(QL_TTS_CONFIG_ENCODING, info.encode);
  148. tts_demo_no_err(err, goto exit, "config tts failed");
  149. #endif
  150. //设置使用数字播报
  151. //err = ql_tts_set_config_param(QL_TTS_CONFIG_DIGITS,TTS_DIGIT_NUMBER);
  152. //tts_demo_no_err(err, goto exit, "config tts failed");
  153. err = ql_tts_start(info.str, info.len);
  154. tts_demo_no_err(err, goto exit, "tts start failed");
  155. while(ql_pcm_buffer_used(ql_player)) //in poc mode, player will not stop if not ql_aud_stop_poc_mode called
  156. {
  157. ql_rtos_task_sleep_ms(20); //wait the write buffer empty
  158. }
  159. exit:
  160. if(info.str){
  161. free(info.str);
  162. }
  163. QL_TTS_LOG("tts done");
  164. ql_pcm_poc_deinit_ex();
  165. ql_tts_deinit();
  166. }
  167. }
  168. int ql_tts_init(pUserCallback mCallback)
  169. {
  170. tts_param_t tts_param = {0};
  171. if(!mCallback){
  172. return QL_TTS_INVALID_PARAM;
  173. }
  174. if(!ql_tts_is_running())
  175. {
  176. #if !QL_TTS_LANGUAGE_USE_ENGLISH
  177. tts_param.resource = TTS_RESOURCE_16K_CN; //使用中文16k资源
  178. #else
  179. tts_param.resource = TTS_RESOURCE_16K_EN; //使用英文16k资源
  180. #endif
  181. #if QL_TTS_LOCATION==1 //使用的tts库在外部6线flash
  182. tts_param.position = POSIT_EFS;
  183. #elif QL_TTS_LOCATION==2 //使用的tts库在外部4线flash
  184. tts_param.position = POSIT_EXNSFFS;
  185. #else //默认使用的tts库在内置flash
  186. tts_param.position = POSIT_INTERNAL_FS;
  187. #endif
  188. //int err = ql_tts_engine_init(userCallback); //若使用默认的中文16k资源,且资源文件预置到内置flash, 则直接调用ql_tts_engine_init即可
  189. int err = ql_tts_engine_init_ex(userCallback, &tts_param);
  190. tts_demo_no_err(err, return err, "tts session begain failed");
  191. }
  192. else
  193. {
  194. QL_TTS_LOG("tts is running");
  195. return QL_TTS_DEVICE_BUSY;
  196. }
  197. return QL_TTS_SUCCESS;
  198. }
  199. int ql_tts_deinit(void)
  200. {
  201. int err = 0;
  202. err = ql_tts_end();
  203. tts_demo_no_err(err, return err, "tts end failed");
  204. return err;
  205. }
  206. int ql_tts_play(ql_tts_encoding_e encoding, const char* string, uint len)
  207. {
  208. tts_demo_play_info_t param = {0};
  209. int err = 0;
  210. if(encoding < QL_TTS_GBK || encoding > QL_TTS_UCS2 || !string || !len)
  211. {
  212. QL_TTS_LOG("invalid param");
  213. return QL_TTS_INVALID_PARAM;
  214. }
  215. param.encode = encoding;
  216. param.len = len;
  217. param.str = calloc(1, len);
  218. if(!param.str)
  219. {
  220. QL_TTS_LOG("tts no memory");
  221. return QL_TTS_NO_MEMORY;
  222. }
  223. memcpy(param.str, string, len);
  224. err = ql_rtos_queue_release(ql_tts_demo_queue, sizeof(tts_demo_play_info_t), (uint8 *)&param, 0);
  225. if(err)
  226. {
  227. free(param.str);
  228. }
  229. return err;
  230. }
  231. int ql_tts_play_english(const char* string, uint len)
  232. {
  233. tts_demo_play_info_t param = {0};
  234. int err = 0;
  235. if(!string || !len)
  236. {
  237. QL_TTS_LOG("invalid param");
  238. return QL_TTS_INVALID_PARAM;
  239. }
  240. param.len = len;
  241. param.str = calloc(1, len);
  242. if(!param.str)
  243. {
  244. QL_TTS_LOG("tts no memory");
  245. return QL_TTS_NO_MEMORY;
  246. }
  247. memcpy(param.str, string, len);
  248. err = ql_rtos_queue_release(ql_tts_demo_queue, sizeof(tts_demo_play_info_t), (uint8 *)&param, 0);
  249. if(err)
  250. {
  251. free(param.str);
  252. }
  253. return err;
  254. }
  255. void ql_tts_demo2_init(void)
  256. {
  257. uint8_t err = QL_OSI_SUCCESS;
  258. err = ql_rtos_queue_create(&ql_tts_demo_queue, sizeof(tts_demo_play_info_t), 10);
  259. if (err != QL_OSI_SUCCESS)
  260. {
  261. QL_TTS_LOG("TTS queue created failed");
  262. return;
  263. }
  264. err = ql_rtos_task_create(&ql_tts_demo_task2, QL_TTS_TASK_STACK, QL_TTS_TASK_PRIO-1, "ql_tts_task", ql_tts_thread_demo_2, NULL, 1);
  265. if (err != QL_OSI_SUCCESS)
  266. {
  267. ql_rtos_queue_delete(ql_tts_demo_queue);
  268. ql_tts_demo_queue = NULL;
  269. QL_TTS_LOG("TTS demo task2 created failed");
  270. return;
  271. }
  272. #if !QL_TTS_LANGUAGE_USE_ENGLISH //播放中文TTS
  273. char *str1 = "支付宝收款 12345元";
  274. char *str2 = "您已超速, 请减速";
  275. char *str3 = "条形码为: 2 2 1 9 8 3 3 6 4 5 2 3 8 8"; //空格代表以号码的方式播报
  276. uint16 ucs_str[8] = {0x6B22, 0x8FCE, 0x4F7F, 0x7528, 0x79FB, 0x8FDC, 0x6A21, 0x5757}; //欢迎使用移远模块
  277. ql_tts_play(QL_TTS_UTF8, str1, strlen(str1));
  278. ql_tts_play(QL_TTS_UTF8, str2, strlen(str2));
  279. ql_tts_play(QL_TTS_UTF8, str3, strlen(str3));
  280. ql_tts_play(QL_TTS_UCS2, (const char *)ucs_str, sizeof(ucs_str));
  281. #else //播放英文TTS
  282. char *str_eng = "The price of the shirt is $50, and the price of the computer is $1200";
  283. ql_tts_play_english(str_eng, strlen(str_eng));
  284. #endif
  285. }
  286. void poc_demo_test(void)
  287. {
  288. void *data = NULL;
  289. int size, total_size = 0, cnt=0, write_cnt=0;
  290. ql_pcm_poc_init_ex();
  291. QL_TTS_LOG("##poc start");
  292. ql_pcm_record_init_ex();
  293. data = malloc(100*1024);
  294. if(data == NULL)
  295. {
  296. goto exit;
  297. }
  298. QL_TTS_LOG("start read");
  299. //start record
  300. while(total_size < 80*1024)
  301. {
  302. size = ql_pcm_record_ex(data+total_size, 640);
  303. if(size <= 0)
  304. {
  305. break;
  306. }
  307. total_size += size;
  308. }
  309. ql_pcm_record_deinit_ex();
  310. QL_TTS_LOG("exit record");
  311. if(total_size <= 0)
  312. {
  313. QL_TTS_LOG("read pcm failed");
  314. goto exit;
  315. }
  316. QL_TTS_LOG("size is %d", total_size);
  317. while(write_cnt < total_size)
  318. {
  319. if(total_size - write_cnt > PACKET_WRITE_MAX_SIZE) //单次最多可写 PACKET_WRITE_MAX_SIZE 字节
  320. {
  321. cnt = ql_pcm_play_ex(data+write_cnt, PACKET_WRITE_MAX_SIZE);
  322. }
  323. else
  324. {
  325. cnt = ql_pcm_play_ex(data+write_cnt, total_size - write_cnt);
  326. }
  327. if(cnt <= 0)
  328. {
  329. QL_TTS_LOG("write failed");
  330. goto exit;
  331. }
  332. write_cnt += cnt;
  333. }
  334. while(ql_pcm_buffer_used(ql_player)) //in poc mode, player will not stop if not ql_aud_stop_poc_mode called
  335. {
  336. ql_rtos_task_sleep_ms(20); //wait the write buffer empty
  337. }
  338. ql_pcm_play_stop_ex();
  339. exit:
  340. if(data != NULL)
  341. {
  342. free(data);
  343. data = NULL;
  344. }
  345. ql_pcm_poc_deinit_ex();
  346. }
  347. /*************************************************** Audio API 封装 ***************************************************************************/
  348. void ql_pcm_poc_init_ex(void)
  349. {
  350. QL_PCM_CONFIG_T pcm_config = {1, 16000, 0};
  351. if(ql_recorder == NULL)
  352. {
  353. ql_recorder = ql_aud_pcm_open(&pcm_config, QL_AUDIO_FORMAT_PCM, QL_PCM_BLOCK_FLAG|QL_PCM_READ_FLAG, QL_PCM_POC);
  354. if(ql_recorder == NULL)
  355. {
  356. return;
  357. }
  358. }
  359. if(ql_player == NULL)
  360. {
  361. ql_player = ql_aud_pcm_open(&pcm_config, QL_AUDIO_FORMAT_PCM, QL_PCM_BLOCK_FLAG|QL_PCM_WRITE_FLAG, QL_PCM_POC);
  362. if(ql_player == NULL)
  363. {
  364. return;
  365. }
  366. }
  367. ql_aud_start_poc_mode(QL_POC_TYPE_HALF_DUPLEX);
  368. ql_aud_poc_switch(QL_POC_MODE_PLAY);
  369. }
  370. int ql_pcm_play_ex(uint8_t *data, uint32_t count)
  371. {
  372. return ql_pcm_write(ql_player, data, count);
  373. }
  374. void ql_pcm_play_stop_ex(void)
  375. {
  376. ql_pcm_buffer_reset(ql_player);
  377. }
  378. void ql_pcm_record_init_ex(void)
  379. {
  380. ql_aud_poc_switch(QL_POC_MODE_REC);
  381. }
  382. void ql_pcm_record_deinit_ex(void)
  383. {
  384. ql_aud_poc_switch(QL_POC_MODE_PLAY);
  385. }
  386. int ql_pcm_record_ex(void *data, uint32_t count)
  387. {
  388. int cnt_read;
  389. if(ql_recorder)
  390. {
  391. cnt_read = ql_pcm_read(ql_recorder, data, count);
  392. }
  393. else
  394. {
  395. cnt_read = -1;
  396. }
  397. return cnt_read;
  398. }
  399. void ql_pcm_poc_deinit_ex(void)
  400. {
  401. ql_aud_stop_poc_mode();
  402. if(ql_player != NULL)
  403. {
  404. ql_pcm_close(ql_player);
  405. ql_player = NULL;
  406. }
  407. if(ql_recorder != NULL)
  408. {
  409. ql_pcm_close(ql_recorder);
  410. ql_recorder = NULL;
  411. }
  412. }