index.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. /*
  2. Tencent is pleased to support the open source community by making Face-2-Face Translator available.
  3. Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
  4. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
  5. http://opensource.org/licenses/MIT
  6. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
  7. */
  8. const app = getApp()
  9. const util = require('../../utils/util.js')
  10. const plugin = requirePlugin("WechatAI")
  11. import { language } from '../../utils/conf.js'
  12. // 获取**全局唯一**的语音识别管理器**recordRecoManager**
  13. const manager = plugin.getRecordRecognitionManager()
  14. Page({
  15. data: {
  16. dialogList: [
  17. // {
  18. // // 当前语音输入内容
  19. // create: '04/27 15:37',
  20. // lfrom: 'zh_CN',
  21. // lto: 'en_US',
  22. // text: '这是测试这是测试这是测试这是测试',
  23. // translateText: 'this is test.this is test.this is test.this is test.',
  24. // voicePath: '',
  25. // translateVoicePath: '',
  26. // autoPlay: false, // 自动播放背景音乐
  27. // id: 0,
  28. // },
  29. ],
  30. scroll_top: 10000, // 竖向滚动条位置
  31. bottomButtonDisabled: false, // 底部按钮disabled
  32. tips_language: language[0], // 目前只有中文
  33. initTranslate: {
  34. // 为空时的卡片内容
  35. create: '04/27 15:37',
  36. text: '等待说话',
  37. },
  38. currentTranslate: {
  39. // 当前语音输入内容
  40. create: '04/27 15:37',
  41. text: '等待说话',
  42. },
  43. recording: false, // 正在录音
  44. recordStatus: 0, // 状态: 0 - 录音中 1- 翻译中 2 - 翻译完成/二次翻译
  45. toView: 'fake', // 滚动位置
  46. lastId: -1, // dialogList 最后一个item的 id
  47. currentTranslateVoice: '', // 当前播放语音路径
  48. },
  49. longpressEvent: function(e) {
  50. console.log("长按", e)
  51. },
  52. /**
  53. * 按住按钮开始语音识别
  54. */
  55. streamRecord: function(e) {
  56. console.log("streamrecord" ,e)
  57. let detail = e.detail
  58. let buttonItem = detail.buttonItem
  59. manager.start({
  60. lang: buttonItem.lang,
  61. })
  62. this.setData({
  63. recordStatus: 0,
  64. recording: true,
  65. currentTranslate: {
  66. // 当前语音输入内容
  67. create: util.recordTime(new Date()),
  68. text: '正在聆听中',
  69. lfrom: buttonItem.lang,
  70. lto: buttonItem.lto,
  71. },
  72. })
  73. this.scrollToNew();
  74. wx.reportAnalytics('record_and_translate_event', {
  75. lfrom: buttonItem.lang,
  76. lto: buttonItem.lto,
  77. });
  78. },
  79. /**
  80. * 松开按钮结束语音识别
  81. */
  82. streamRecordEnd: function(e) {
  83. console.log("streamRecordEnd" ,e)
  84. let detail = e.detail // 自定义组件触发事件时提供的detail对象
  85. let buttonItem = detail.buttonItem
  86. // 防止重复触发stop函数
  87. if(!this.data.recording || this.data.recordStatus != 0) {
  88. console.warn("has finished!")
  89. return
  90. }
  91. manager.stop()
  92. this.setData({
  93. bottomButtonDisabled: true,
  94. })
  95. },
  96. /**
  97. * 翻译
  98. */
  99. translateText: function(item, index) {
  100. let lfrom = item.lfrom || 'zh_CN'
  101. let lto = item.lto || 'en_US'
  102. plugin.translate({
  103. lfrom: lfrom,
  104. lto: lto,
  105. content: item.text,
  106. tts: true,
  107. success: (resTrans)=>{
  108. let passRetcode = [
  109. 0, // 成功
  110. -10006, // 翻译成功,合成失败
  111. ]
  112. if(passRetcode.indexOf(resTrans.retcode) >= 0 ) {
  113. let tmpDialogList = this.data.dialogList.slice(0)
  114. if(!isNaN(index)) {
  115. let tmpTranslate = Object.assign({}, item, {
  116. autoPlay: true, // 自动播放背景音乐
  117. translateText: resTrans.result,
  118. translateVoicePath: resTrans.filename || "",
  119. translateVoiceExpiredTime: resTrans.expired_time || 0
  120. })
  121. tmpDialogList[index] = tmpTranslate
  122. this.setData({
  123. dialogList: tmpDialogList,
  124. bottomButtonDisabled: false,
  125. recording: false,
  126. })
  127. this.scrollToNew();
  128. } else {
  129. console.error("index error", resTrans, item)
  130. }
  131. } else {
  132. console.warn("翻译失败", resTrans, item)
  133. }
  134. },
  135. fail: function(resTrans) {
  136. console.log("调用失败",resTrans, item)
  137. },
  138. complete: resTrans => {
  139. this.setData({
  140. recordStatus: 1,
  141. })
  142. wx.hideLoading()
  143. }
  144. })
  145. },
  146. /**
  147. * 修改文本信息之后触发翻译操作
  148. */
  149. translateTextAction: function(e) {
  150. // console.log("translateTextAction" ,e)
  151. let detail = e.detail // 自定义组件触发事件时提供的detail对象
  152. let item = detail.item
  153. let index = detail.index
  154. this.translateText(item, index)
  155. },
  156. /**
  157. * 语音文件过期,重新合成语音文件
  158. */
  159. expiredAction: function(e) {
  160. let detail = e.detail // 自定义组件触发事件时提供的detail对象
  161. let item = detail.item
  162. let index = detail.index
  163. let lto = item.lto || 'en_US'
  164. plugin.textToSpeech({
  165. lang: lto,
  166. content: item.translateText,
  167. success: resTrans => {
  168. if(resTrans.retcode == 0) {
  169. let tmpDialogList = this.data.dialogList.slice(0)
  170. let tmpTranslate = Object.assign({}, item, {
  171. autoPlay: true, // 自动播放背景音乐
  172. translateVoicePath: resTrans.filename,
  173. translateVoiceExpiredTime: resTrans.expired_time || 0
  174. })
  175. tmpDialogList[index] = tmpTranslate
  176. this.setData({
  177. dialogList: tmpDialogList,
  178. })
  179. } else {
  180. console.warn("语音合成失败", resTrans, item)
  181. }
  182. },
  183. fail: function(resTrans) {
  184. console.warn("语音合成失败", resTrans, item)
  185. }
  186. })
  187. },
  188. /**
  189. * 删除卡片
  190. */
  191. deleteItem: function(e) {
  192. // console.log("deleteItem" ,e)
  193. let detail = e.detail
  194. let item = detail.item
  195. let tmpDialogList = this.data.dialogList.slice(0)
  196. let arrIndex = detail.index
  197. tmpDialogList.splice(arrIndex, 1)
  198. // 不使用setTImeout可能会触发 Error: Expect END descriptor with depth 0 but get another
  199. setTimeout( ()=>{
  200. this.setData({
  201. dialogList: tmpDialogList
  202. })
  203. }, 0)
  204. },
  205. /**
  206. * 识别内容为空时的反馈
  207. */
  208. showRecordEmptyTip: function() {
  209. this.setData({
  210. recording: false,
  211. bottomButtonDisabled: false,
  212. })
  213. wx.showToast({
  214. title: this.data.tips_language.recognize_nothing,
  215. icon: 'success',
  216. duration: 1000,
  217. success: function (res) {
  218. },
  219. fail: function (res) {
  220. console.log(res);
  221. }
  222. });
  223. },
  224. /**
  225. * 初始化语音识别回调
  226. * 绑定语音播放开始事件
  227. */
  228. initRecord: function() {
  229. //有新的识别内容返回,则会调用此事件
  230. manager.onRecognize = (res) => {
  231. let currentData = Object.assign({}, this.data.currentTranslate, {
  232. text: res.result,
  233. })
  234. this.setData({
  235. currentTranslate: currentData,
  236. })
  237. this.scrollToNew();
  238. }
  239. // 识别结束事件
  240. manager.onStop = (res) => {
  241. let text = res.result
  242. if(text == '') {
  243. this.showRecordEmptyTip()
  244. return
  245. }
  246. let lastId = this.data.lastId + 1
  247. let currentData = Object.assign({}, this.data.currentTranslate, {
  248. text: res.result,
  249. translateText: '正在翻译中',
  250. id: lastId,
  251. voicePath: res.tempFilePath
  252. })
  253. this.setData({
  254. currentTranslate: currentData,
  255. recordStatus: 1,
  256. lastId: lastId,
  257. })
  258. this.scrollToNew();
  259. this.translateText(currentData, this.data.dialogList.length)
  260. }
  261. // 识别错误事件
  262. manager.onError = (res) => {
  263. this.setData({
  264. recording: false,
  265. bottomButtonDisabled: false,
  266. })
  267. }
  268. // 语音播放开始事件
  269. wx.onBackgroundAudioPlay(res=>{
  270. const backgroundAudioManager = wx.getBackgroundAudioManager()
  271. let src = backgroundAudioManager.src
  272. this.setData({
  273. currentTranslateVoice: src
  274. })
  275. })
  276. },
  277. /**
  278. * 设置语音识别历史记录
  279. */
  280. setHistory: function() {
  281. try {
  282. let dialogList = this.data.dialogList
  283. dialogList.forEach(item => {
  284. item.autoPlay = false
  285. })
  286. wx.setStorageSync('history',dialogList)
  287. } catch (e) {
  288. console.error("setStorageSync setHistory failed")
  289. }
  290. },
  291. /**
  292. * 得到历史记录
  293. */
  294. getHistory: function() {
  295. try {
  296. let history = wx.getStorageSync('history')
  297. if (history) {
  298. let len = history.length;
  299. let lastId = this.data.lastId
  300. if(len > 0) {
  301. lastId = history[len-1].id || -1;
  302. }
  303. this.setData({
  304. dialogList: history,
  305. toView: this.data.toView,
  306. lastId: lastId,
  307. })
  308. }
  309. } catch (e) {
  310. // Do something when catch error
  311. this.setData({
  312. dialogList: []
  313. })
  314. }
  315. },
  316. /**
  317. * 重新滚动到底部
  318. */
  319. scrollToNew: function() {
  320. this.setData({
  321. toView: this.data.toView
  322. })
  323. },
  324. onShow: function() {
  325. this.scrollToNew();
  326. if(this.data.recordStatus == 2) {
  327. wx.showLoading({
  328. // title: '',
  329. mask: true,
  330. })
  331. }
  332. },
  333. onLoad: function () {
  334. this.getHistory()
  335. this.initRecord()
  336. this.setData({toView: this.data.toView})
  337. app.getRecordAuth()
  338. },
  339. onHide: function() {
  340. this.setHistory()
  341. },
  342. })