index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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("WechatSI")
  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. /**
  50. * 按住按钮开始语音识别
  51. */
  52. streamRecord: function(e) {
  53. // console.log("streamrecord" ,e)
  54. let detail = e.detail || {}
  55. let buttonItem = detail.buttonItem || {}
  56. manager.start({
  57. lang: buttonItem.lang,
  58. })
  59. this.setData({
  60. recordStatus: 0,
  61. recording: true,
  62. currentTranslate: {
  63. // 当前语音输入内容
  64. create: util.recordTime(new Date()),
  65. text: '正在聆听中',
  66. lfrom: buttonItem.lang,
  67. lto: buttonItem.lto,
  68. },
  69. })
  70. this.scrollToNew();
  71. },
  72. /**
  73. * 松开按钮结束语音识别
  74. */
  75. streamRecordEnd: function(e) {
  76. // console.log("streamRecordEnd" ,e)
  77. let detail = e.detail || {} // 自定义组件触发事件时提供的detail对象
  78. let buttonItem = detail.buttonItem || {}
  79. // 防止重复触发stop函数
  80. if(!this.data.recording || this.data.recordStatus != 0) {
  81. console.warn("has finished!")
  82. return
  83. }
  84. manager.stop()
  85. this.setData({
  86. bottomButtonDisabled: true,
  87. })
  88. },
  89. /**
  90. * 翻译
  91. */
  92. translateText: function(item, index) {
  93. let lfrom = item.lfrom || 'zh_CN'
  94. let lto = item.lto || 'en_US'
  95. plugin.translate({
  96. lfrom: lfrom,
  97. lto: lto,
  98. content: item.text,
  99. tts: true,
  100. success: (resTrans)=>{
  101. let passRetcode = [
  102. 0, // 翻译合成成功
  103. -10006, // 翻译成功,合成失败
  104. -10007, // 翻译成功,传入了不支持的语音合成语言
  105. -10008, // 翻译成功,语音合成达到频率限制
  106. ]
  107. if(passRetcode.indexOf(resTrans.retcode) >= 0 ) {
  108. let tmpDialogList = this.data.dialogList.slice(0)
  109. if(!isNaN(index)) {
  110. let tmpTranslate = Object.assign({}, item, {
  111. autoPlay: true, // 自动播放背景音乐
  112. translateText: resTrans.result,
  113. translateVoicePath: resTrans.filename || "",
  114. translateVoiceExpiredTime: resTrans.expired_time || 0
  115. })
  116. tmpDialogList[index] = tmpTranslate
  117. this.setData({
  118. dialogList: tmpDialogList,
  119. bottomButtonDisabled: false,
  120. recording: false,
  121. })
  122. this.scrollToNew();
  123. } else {
  124. console.error("index error", resTrans, item)
  125. }
  126. } else {
  127. console.warn("翻译失败", resTrans, item)
  128. }
  129. },
  130. fail: function(resTrans) {
  131. console.error("调用失败",resTrans, item)
  132. this.setData({
  133. bottomButtonDisabled: false,
  134. recording: false,
  135. })
  136. },
  137. complete: resTrans => {
  138. this.setData({
  139. recordStatus: 1,
  140. })
  141. wx.hideLoading()
  142. }
  143. })
  144. },
  145. /**
  146. * 修改文本信息之后触发翻译操作
  147. */
  148. translateTextAction: function(e) {
  149. // console.log("translateTextAction" ,e)
  150. let detail = e.detail // 自定义组件触发事件时提供的detail对象
  151. let item = detail.item
  152. let index = detail.index
  153. this.translateText(item, index)
  154. },
  155. /**
  156. * 语音文件过期,重新合成语音文件
  157. */
  158. expiredAction: function(e) {
  159. let detail = e.detail || {} // 自定义组件触发事件时提供的detail对象
  160. let item = detail.item || {}
  161. let index = detail.index
  162. if(isNaN(index) || index < 0) {
  163. return
  164. }
  165. let lto = item.lto || 'en_US'
  166. plugin.textToSpeech({
  167. lang: lto,
  168. content: item.translateText,
  169. success: resTrans => {
  170. if(resTrans.retcode == 0) {
  171. let tmpDialogList = this.data.dialogList.slice(0)
  172. let tmpTranslate = Object.assign({}, item, {
  173. autoPlay: true, // 自动播放背景音乐
  174. translateVoicePath: resTrans.filename,
  175. translateVoiceExpiredTime: resTrans.expired_time || 0
  176. })
  177. tmpDialogList[index] = tmpTranslate
  178. this.setData({
  179. dialogList: tmpDialogList,
  180. })
  181. } else {
  182. console.warn("语音合成失败", resTrans, item)
  183. }
  184. },
  185. fail: function(resTrans) {
  186. console.warn("语音合成失败", resTrans, item)
  187. }
  188. })
  189. },
  190. /**
  191. * 初始化为空时的卡片
  192. */
  193. initCard: function () {
  194. let initTranslateNew = Object.assign({}, this.data.initTranslate, {
  195. create: util.recordTime(new Date()),
  196. })
  197. this.setData({
  198. initTranslate: initTranslateNew
  199. })
  200. },
  201. /**
  202. * 删除卡片
  203. */
  204. deleteItem: function(e) {
  205. // console.log("deleteItem" ,e)
  206. let detail = e.detail
  207. let item = detail.item
  208. let tmpDialogList = this.data.dialogList.slice(0)
  209. let arrIndex = detail.index
  210. tmpDialogList.splice(arrIndex, 1)
  211. // 不使用setTImeout可能会触发 Error: Expect END descriptor with depth 0 but get another
  212. setTimeout( ()=>{
  213. this.setData({
  214. dialogList: tmpDialogList
  215. })
  216. if(tmpDialogList.length == 0) {
  217. this.initCard()
  218. }
  219. }, 0)
  220. },
  221. /**
  222. * 识别内容为空时的反馈
  223. */
  224. showRecordEmptyTip: function() {
  225. this.setData({
  226. recording: false,
  227. bottomButtonDisabled: false,
  228. })
  229. wx.showToast({
  230. title: this.data.tips_language.recognize_nothing,
  231. icon: 'success',
  232. image: '/image/no_voice.png',
  233. duration: 1000,
  234. success: function (res) {
  235. },
  236. fail: function (res) {
  237. console.log(res);
  238. }
  239. });
  240. },
  241. /**
  242. * 初始化语音识别回调
  243. * 绑定语音播放开始事件
  244. */
  245. initRecord: function() {
  246. //有新的识别内容返回,则会调用此事件
  247. manager.onRecognize = (res) => {
  248. let currentData = Object.assign({}, this.data.currentTranslate, {
  249. text: res.result,
  250. })
  251. this.setData({
  252. currentTranslate: currentData,
  253. })
  254. this.scrollToNew();
  255. }
  256. // 识别结束事件
  257. manager.onStop = (res) => {
  258. let text = res.result
  259. if(text == '') {
  260. this.showRecordEmptyTip()
  261. return
  262. }
  263. let lastId = this.data.lastId + 1
  264. let currentData = Object.assign({}, this.data.currentTranslate, {
  265. text: res.result,
  266. translateText: '正在翻译中',
  267. id: lastId,
  268. voicePath: res.tempFilePath
  269. })
  270. this.setData({
  271. currentTranslate: currentData,
  272. recordStatus: 1,
  273. lastId: lastId,
  274. })
  275. this.scrollToNew();
  276. this.translateText(currentData, this.data.dialogList.length)
  277. }
  278. // 识别错误事件
  279. manager.onError = (res) => {
  280. this.setData({
  281. recording: false,
  282. bottomButtonDisabled: false,
  283. })
  284. }
  285. // 语音播放开始事件
  286. wx.onBackgroundAudioPlay(res=>{
  287. const backgroundAudioManager = wx.getBackgroundAudioManager()
  288. let src = backgroundAudioManager.src
  289. this.setData({
  290. currentTranslateVoice: src
  291. })
  292. })
  293. },
  294. /**
  295. * 设置语音识别历史记录
  296. */
  297. setHistory: function() {
  298. try {
  299. let dialogList = this.data.dialogList
  300. dialogList.forEach(item => {
  301. item.autoPlay = false
  302. })
  303. wx.setStorageSync('history',dialogList)
  304. } catch (e) {
  305. console.error("setStorageSync setHistory failed")
  306. }
  307. },
  308. /**
  309. * 得到历史记录
  310. */
  311. getHistory: function() {
  312. try {
  313. let history = wx.getStorageSync('history')
  314. if (history) {
  315. let len = history.length;
  316. let lastId = this.data.lastId
  317. if(len > 0) {
  318. lastId = history[len-1].id || -1;
  319. }
  320. this.setData({
  321. dialogList: history,
  322. toView: this.data.toView,
  323. lastId: lastId,
  324. })
  325. }
  326. } catch (e) {
  327. // Do something when catch error
  328. this.setData({
  329. dialogList: []
  330. })
  331. }
  332. },
  333. /**
  334. * 重新滚动到底部
  335. */
  336. scrollToNew: function() {
  337. this.setData({
  338. toView: this.data.toView
  339. })
  340. },
  341. onShow: function() {
  342. this.scrollToNew();
  343. this.initCard()
  344. if(this.data.recordStatus == 2) {
  345. wx.showLoading({
  346. // title: '',
  347. mask: true,
  348. })
  349. }
  350. },
  351. onLoad: function () {
  352. this.getHistory()
  353. this.initRecord()
  354. this.setData({toView: this.data.toView})
  355. app.getRecordAuth()
  356. },
  357. onHide: function() {
  358. this.setHistory()
  359. },
  360. })