index.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <template>
  2. <view>
  3. <view class="VoiceModel" v-show="VoiceModel">
  4. <view class="container" @touchstart="startRecord" @touchend="endRecord">
  5. <text class="zhcx-iconfont zhcx-icon-yuyin"></text>
  6. <view class="status">{{status}}</view>
  7. </view>
  8. </view>
  9. </view>
  10. </template>
  11. <script>
  12. import {notEmpty} from '@/libs';
  13. import config from '@/config';
  14. const speechConfig=config.baidu.speech;
  15. const speechAppId=speechConfig.appId;
  16. const speechApiKey=speechConfig.apiKey;
  17. const speechSecretKey=speechConfig.secretKey;
  18. const recorderManager = uni.getRecorderManager();
  19. const innerAudioContext = uni.createInnerAudioContext();
  20. innerAudioContext.autoplay = true;
  21. export default {
  22. name:'speech-baidu',
  23. data() {
  24. return {
  25. text: 'uni-app',
  26. voicePath: '',
  27. fileSize:0,
  28. word:"",
  29. VoiceModel:false,
  30. status:"长按开始录音"
  31. }
  32. },
  33. created() {
  34. this.$nextTick(()=>{
  35. recorderManager.onStop(res=>{
  36. console.log('recorder stop' + JSON.stringify(res));
  37. uni.showToast({
  38. title:"录音结束!"
  39. })
  40. this.status="录音结束";
  41. this.voicePath = res.tempFilePath;
  42. this.fileSize=res.fileSize;
  43. this.startYuyin();
  44. });
  45. })
  46. },
  47. methods: {
  48. startRecordHanlle(){
  49. this.VoiceModel=true;
  50. },
  51. startRecord() {
  52. this.status="开始录音";
  53. recorderManager.start();
  54. setTimeout(()=>{
  55. this.endRecord()
  56. },60000)
  57. },
  58. endRecord() {
  59. console.log('录音结束')
  60. // #ifdef H5
  61. return;
  62. // #endif
  63. this.status="录音结束";
  64. recorderManager.stop();
  65. setTimeout(()=>{
  66. this.VoiceModel=false;
  67. },600)
  68. },
  69. playVoice() {
  70. console.log('播放录音');
  71. if (this.voicePath) {
  72. innerAudioContext.src = this.voicePath;
  73. innerAudioContext.play();
  74. }
  75. },
  76. async startYuyin() {
  77. console.log('识别中……')
  78. uni.showLoading({
  79. title: '识别中……'
  80. });
  81. let adioFileData=await this.Audio2dataURL(this.voicePath);
  82. let {access_token}=await this.getToken();
  83. let result=await this.ConversionWords({
  84. format: 'm4a', //语音文件的格式,pcm/wav/amr/m4a。不区分大小写。推荐pcm文件
  85. rate: 16000, // 采样率,16000,固定值 此处文档参数16000,达不到这种高保真音频,故 使用8000
  86. // dev_pid: 1537,//普通话
  87. channel: 1, //声道数,仅支持单声道,请填写固定值 1
  88. cuid: 'zhcxcuid', //用户唯一标识,用来区分用户,计算UV值。建议填写能区分用户的机器 MAC 地址或 IMEI 码,长度为60字符以内。
  89. token:access_token,
  90. speech:adioFileData, //本地语音文件的的二进制语音数据 ,需要进行base64 编码。与len参数连一起使用。
  91. len: this.fileSize //本地语音文件的的字节数,单位字节 init
  92. })
  93. if(result.statusCode===200){
  94. let word=result.data.result[0];
  95. if(notEmpty(word)){
  96. this.$emit('success',result)
  97. }else{
  98. uni.showToast({
  99. icon:"none",
  100. title:"未识别到文字!"
  101. })
  102. this.status="未识别到文字";
  103. }
  104. }else{
  105. uni.showToast({
  106. icon:"none",
  107. title:"语音识别失败"
  108. })
  109. this.status="语音识别失败";
  110. }
  111. },
  112. getToken(){
  113. return new Promise((resolve,reject)=>{
  114. //获取token
  115. uni.request({
  116. url: 'https://openapi.baidu.com/oauth/2.0/token', //仅为示例,并非真实接口地址。
  117. data: {
  118. grant_type: 'client_credentials',
  119. client_id: speechApiKey,
  120. client_secret:speechSecretKey,
  121. },
  122. header: {
  123. 'content-type': 'application/json;charset=utf-8' //自定义请求头信息
  124. },
  125. success: (res) => {
  126. resolve(res.data)
  127. },
  128. fail(err) {
  129. uni.showToast({
  130. icon:"none",
  131. title:"语音识别失败"
  132. })
  133. this.status="语音识别失败";
  134. uni.hideLoading();
  135. reject(err)
  136. }
  137. });
  138. })
  139. },
  140. ConversionWords(postData) {
  141. //调用语音识别接口
  142. return new Promise((resolve,reject)=>{
  143. uni.request({
  144. url: 'https://vop.baidu.com/server_api', //仅为示例,并非真实接口地址。
  145. data: postData,
  146. header: {
  147. "Content-Type":"application/json" //自定义请求头信息
  148. },
  149. method: 'POST',
  150. success: (res) => {
  151. resolve(res)
  152. },
  153. fail(err) {
  154. uni.showToast({
  155. icon:"none",
  156. title:"语音识别失败"
  157. })
  158. this.status="语音识别失败";
  159. reject(err)
  160. },
  161. complete() {
  162. uni.hideLoading();
  163. }
  164. })
  165. })
  166. },
  167. Audio2dataURL(path) {
  168. return new Promise((resolve,reject)=>{
  169. const FMS = uni.getFileSystemManager();
  170. FMS.readFile({
  171. filePath:this.voicePath,
  172. encoding: 'base64',
  173. success(response) {
  174. resolve(response.data)
  175. },
  176. fail(err){
  177. uni.showToast({
  178. icon:"none",
  179. title:"语音识别失败"
  180. })
  181. this.status="语音识别失败";
  182. uni.hideLoading();
  183. reject(err)
  184. }
  185. })
  186. // plus.io.resolveLocalFileSystemURL(path, function(entry) {
  187. // entry.file(function(file) {
  188. // var reader = new plus.io.FileReader();
  189. // _this.adioSize = file.size;
  190. // reader.onloadend = function(e) {
  191. // console.log(e.target.result);
  192. // console.log(e.target.result.split(",")[1]);
  193. // resolve({
  194. // voice:e.target.result.split(",")[1]
  195. // })
  196. // };
  197. // reader.readAsDataURL(file);
  198. // }, function(e) {
  199. // reject(e.message)
  200. // console.log("读写出现异常: " + e.message);
  201. // })
  202. // })
  203. })
  204. }
  205. }
  206. }
  207. </script>
  208. <style lang="scss" scoped>
  209. .word-cont{
  210. height: 200upx;
  211. overflow-y: auto;
  212. border-bottom:1px solid #ccc;
  213. }
  214. .startRecordHanlle{
  215. width:280upx;
  216. margin: 100upx auto;
  217. background-color: #0081ff;
  218. color: #fff;
  219. .zhcx-icon-yuyin{
  220. font-size: 46upx;
  221. padding-right: 10upx;
  222. }
  223. }
  224. .VoiceModel{
  225. width: 100%;
  226. height: 100%;
  227. position: fixed;
  228. left: 0;
  229. top: 0;
  230. background-color: rgba(0,0,0,0.6);
  231. z-index: 999;
  232. .container{
  233. width: 240upx;
  234. height: 320upx;
  235. border-radius: 10upx;
  236. background-color:#fff;
  237. box-shadow:0 0 2upx rgba(0,0,0,0.6);
  238. position: fixed;
  239. left: 50%;
  240. top:280upx;
  241. transform: translateX(-50%);
  242. z-index: 999;
  243. text-align: center;
  244. display: flex;
  245. justify-content: space-between;
  246. align-items: center;
  247. flex-direction: column;
  248. .zhcx-icon-yuyin{
  249. color: #17b18f;
  250. font-size: 200upx;
  251. }
  252. .status{
  253. color:#17b18f;
  254. padding-bottom: 10upx;
  255. }
  256. }
  257. }
  258. </style>