欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

30分鐘快速實(shí)現(xiàn)小程序語(yǔ)音識(shí)別功能

前言

10年積累的網(wǎng)站設(shè)計(jì)制作、成都做網(wǎng)站經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶(hù)對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶(hù)得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站制作后付款的網(wǎng)站建設(shè)流程,更有烏魯木齊免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。

為了參加某個(gè)作秀活動(dòng),研究了一波如何結(jié)合小程序、科大訊飛實(shí)現(xiàn)語(yǔ)音錄入、識(shí)別的實(shí)現(xiàn)??拼笥嶏w開(kāi)發(fā)文檔中只給出 Python 的 demo,并沒(méi)有給出 node.js 的 sdk,但問(wèn)題不大。本文將從小程序相關(guān)代碼到最后對(duì)接科大訊飛 api 過(guò)程,一步步介紹,半個(gè)小時(shí),搭建完成小程序語(yǔ)音識(shí)別功能!不能再多了!

當(dāng)然,前提是最好掌握有一點(diǎn)點(diǎn)小程序、node.js 甚至是音頻相關(guān)的知識(shí)。下面話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧

架構(gòu)先行

架構(gòu)比較簡(jiǎn)單,大伙兒可以先看下圖。除了小程序,需要提供 3 個(gè)服務(wù),文件上傳、音頻編碼及對(duì)接科大訊飛的服務(wù)。
node.js 對(duì)接科大訊飛的 api,npm 上已經(jīng)有同學(xué)提供了 sdk,有興趣的同學(xué)可以去搜索了解一下,筆者這里是直接調(diào)用了科大訊飛的 api 接口。

擼起袖子加油干

1、創(chuàng)建小程序

鵝廠的小程序文檔非常詳細(xì),在這里筆者就不對(duì)如何創(chuàng)建一個(gè)小程序的步驟進(jìn)行詳細(xì)闡述了。有需要的同學(xué)可以查看鵝廠的小程序開(kāi)發(fā)文檔。

1.1 相關(guān)代碼

我們摘取小程序里面,語(yǔ)音錄入和語(yǔ)音上傳部分的代碼。

// 根據(jù)wx提供的api創(chuàng)建錄音管理對(duì)象
const recorderManager = wx.getRecorderManager();

// 監(jiān)聽(tīng)語(yǔ)音識(shí)別結(jié)束后的行為
recorderManager.onStop(recorderResponse => {
 // tempFilePath 是錄制的音頻文件
 const { tempFilePath } = recorderResponse;

 // 上傳音頻文件,完成語(yǔ)音識(shí)別翻譯
 wx.uploadFile({
 url: 'http://127.0.0.1:7001/voice', // 該服務(wù)在后面搭建。另外,小程序發(fā)布時(shí)要求后臺(tái)服務(wù)提供https服務(wù)!這里的地址僅為開(kāi)發(fā)環(huán)境配置。
 filePath: tempFilePath,
 name: 'file',
 complete: res => {
  console.log(res); // 我們期待res,就是翻譯后的內(nèi)容
 }
 });
});

// 開(kāi)始錄音,觸發(fā)條件可以是按鈕或其他,由你自己決定
recorderManager.start({
 duration: 5000 // 最長(zhǎng)錄制時(shí)間
 // 其他參數(shù)可以默認(rèn),更多參數(shù)可以查看https://developers.weixin.qq.com/miniprogram/dev/api/media/recorder/RecorderManager.start.html
});

2、搭建文件服務(wù)器

步驟 1 代碼中提到了一個(gè) url 地址大家應(yīng)該都還記得。

http://127.0.0.1:7001/voice

小程序本身還并沒(méi)有提供語(yǔ)音識(shí)別的功能,所以在這里我們需要借助于“后端”服務(wù)的能力,完成我們語(yǔ)音識(shí)別翻譯的功能。

2.1 egg.js 服務(wù)初始化

我們使用 egg.js 的 cli 快速初始化一個(gè)工程,當(dāng)然你也可以使用 express、koa、kraken 等等框架,框架的選型在此不是重點(diǎn)我們就不做展開(kāi)闡述了。對(duì) egg.js 不熟悉的同學(xué)可以查看egg.js 的官網(wǎng)。

npm i egg-init -g
egg-init voice-server --type=simple
cd voice-server
npm i

安裝完成后,執(zhí)行以下代碼

npm run dev

隨后訪問(wèn)瀏覽器http://127.0.0.1:7001應(yīng)該可以看到一個(gè)Hi, egg 的頁(yè)面。至此我們的服務(wù)初始化完成。

2.2 文件上傳接口

a) 修改 egg.js 的文件上傳配置

打開(kāi) config/config.default.js,添加以下兩項(xiàng)配置

module.exports = appInfo => {
 ...
 config.multipart = {
 fileSize: '2gb', // 限制文件大小
 whitelist: [ '.aac', '.m4a', '.mp3' ], // 支持上傳的文件后綴名
 };

 config.security = {
 csrf: {
  enable: false // 關(guān)閉csrf
 }
 };
 ...
}

b) 添加 VoiceController

打開(kāi) app/controller 文件夾,新建文件 voice.js。編寫(xiě) VoiceController 使其繼承于 egg.js 的 Controller。具體代碼如下:

const Controller = require('egg').Controller;
const fs = require('fs');
const path = require('path');
const pump = require('mz-modules/pump');
const uuidv1 = require('uuid/v1'); // 依賴(lài)于uuid庫(kù),用于生成唯一文件名,使用npm i uuid安裝即可

// 音頻文件上傳后存儲(chǔ)的路徑
const targetPath = path.resolve(__dirname, '..', '..', 'uploads');

class VoiceController extends Controller {
 constructor(params) {
 super(params);
 if (!fs.existsSync(targetPath)) {
  fs.mkdirSync(targetPath);
 }
 }

 async translate() {
 const parts = this.ctx.multipart({ autoFields: true });
 let stream;
 const voicePath = path.join(targetPath, uuidv1());
 while (!isEmpty((stream = await parts()))) {
  await pump(stream, fs.createWriteStream(voicePath));
 }
 // 到這里就完成了文件上傳。如果你不需要文件落地,也可以在后續(xù)的操作中,直接使用stream操作文件流

 ...
 // 音頻編碼
 // 科大訊飛語(yǔ)音識(shí)別
 ...
 }
}

c) 最后一步,新增路由規(guī)則

寫(xiě)完 controller 之后,我們依據(jù) egg.js 的規(guī)則,在 router.js 里面新增一個(gè)路由。

module.exports = app => {
 const { router, controller } = app;
 router.get('/', controller.home.index);
 router.get('/voice', controller.voice.translate);
};

OK,至此你可以測(cè)試一下從小程序錄音,錄音完成后上傳到后臺(tái)文件服務(wù)器的完整流程。如果沒(méi)問(wèn)題,那恭喜你你已經(jīng)完成了 80%的工作了!

3、音頻編碼服務(wù)

在上文中,小程序錄音的方法 recorderManager.start 的時(shí)候我們提及到了“更多參數(shù)”。其中有一個(gè)參數(shù)是 format,支持 aac 和 mp3 兩種(默認(rèn)是 aac)。然后我們查閱了科大訊飛的 api 文檔,音頻編碼支持“未壓縮的 pcm 或 wav 格式”。

什么 aac、pcm、wav?emmm.. OK,我們只是前端,既然格式不對(duì)等,那只需要完成 aac -> pcm 轉(zhuǎn)化即可,ffmpeg 立即浮現(xiàn)在筆者的腦海里。一番搜索,命令大概是這樣子的:

ffmpeg -i uploads/a3f588d0-edf8-11e8-b6f5-2929aef1b7f8.aac -f s16le -ar 8000 -ac 2 -y decoded.pcm

# -i 后面帶的是源文件
# -f s16le 指的是編碼格式
# -ar 8000 編碼碼率
# -ac 2 通道

接下來(lái)我們使用 node.js 來(lái)實(shí)現(xiàn)上述命令。

3.1 引入相關(guān)依賴(lài)包

npm i ffmpeg-static
npm i fluent-ffmpeg

3.2 創(chuàng)建一個(gè)編碼服務(wù)

在 app/service 文件夾中,創(chuàng)建 ffmpeg.js 文件。新建 FFmpegService 繼承于 egg.js 的 Service

const { Service } = require('egg');
const ffmpeg = require('fluent-ffmpeg');
const ffmpegStatic = require('ffmpeg-static');
const path = require('path');
const fs = require('fs');

ffmpeg.setFfmpegPath(ffmpegStatic.path);

class FFmpegService extends Service {
 async aac2pcm(voicePath) {
  const command = ffmpeg(voicePath);

  // 方便測(cè)試,我們將轉(zhuǎn)碼后文件落地到磁盤(pán)
  const targetDir = path.join(path.dirname(voicePath), 'pcm');
  if (!fs.existsSync(targetDir)) {
   fs.mkdirSync(targetDir);
  }

  const target = path.join(targetDir, path.basename(voicePath)) + '.pcm';
  return new Promise((resolve, reject) => {
   command
    .audioCodec('pcm_s16le')
    .audioChannels(2)
    .audioBitrate(8000)
    .output(target)
    .on('error', error => {
     reject(error);
    })
    .on('end', () => {
     resolve(target);
    })
    .run();
  });
 }
}

module.exports = FFmpegService;

3.3 調(diào)用 ffmpegService,獲得 pcm 文件

回到 app/controller/voice.js 文件中,我們?cè)谖募蟼魍瓿珊螅{(diào)用 ffmpegService 提供的 aac2pcm 方法,獲取到 pcm 文件的路徑。

// app/controller/voice.js
...
async translate() {
 ...
 ...
 const pcmPath = await this.ctx.service.ffmpeg.aac2pcm(voicePath);
 ...
}
...

4、對(duì)接科大訊飛 API

首先,需要到科大訊飛開(kāi)放平臺(tái)注冊(cè)并新增應(yīng)用、開(kāi)通應(yīng)用的語(yǔ)音聽(tīng)寫(xiě)服務(wù)。

我們?cè)賹?xiě)一個(gè)服務(wù),在 app/service 文件夾下創(chuàng)建 xfyun.js 文件,實(shí)現(xiàn) XFYunService 繼承于 egg.js 的 Service。

4.1 引入相關(guān)依賴(lài)

npm i axios // 網(wǎng)絡(luò)請(qǐng)求庫(kù)
npm i md5 // 科大訊飛接口中需要md5計(jì)算
npm i form-urlencoded // 接口中需要對(duì)部分內(nèi)容進(jìn)行urlencoded

4.2 XFYunService 實(shí)現(xiàn)

const { Service } = require('egg');
const fs = require('fs');
const formUrlencoded = require('form-urlencoded').default;
const axios = require('axios');
const md5 = require('md5');
const API_KEY = 'xxxx'; // 在科大訊飛控制臺(tái)上可以查到服務(wù)的APIKey
const API_ID = 'xxxxx'; // 同樣可以在控制臺(tái)查到

class XFYunService extends Service {
 async voiceTranslate(voicePath) {
  // 繼上文,暴力的讀取文件
  let data = fs.readFileSync(voicePath);
  // 將內(nèi)容進(jìn)行base64編碼
  data = new Buffer(data).toString('base64');
  // 進(jìn)行url encode
  data = formUrlencoded({ audio: data });
  const params = {
   engine_type: 'sms16k',
   aue: 'raw'
  };
  const x_CurTime = Math.floor(new Date().getTime() / 1000) + '',
   x_Param = new Buffer(JSON.stringify(params)).toString('base64');
  return axios({
   url: 'http://api.xfyun.cn/v1/service/v1/iat',
   method: 'POST',
   data,
   headers: {
    'X-Appid': API_ID,
    'X-CurTime': x_CurTime,
    'X-Param': x_Param,
    'X-CheckSum': md5(API_KEY + x_CurTime + x_Param)
   }
  }).then(res => {
   // 查詢(xún)成功后,返回response的data
   return res.data || {};
  });
 }
}

module.exports = XFYunService;

4.3 調(diào)用 XFYunService,完成語(yǔ)音識(shí)別

再次回到 app/controller/voice.js 文件中,我們?cè)?ffmpeg 轉(zhuǎn)碼完成后,調(diào)用 XFYunService 提供的 voiceTranslate 方法,完成語(yǔ)音識(shí)別。

// app/controller/voice.js
...
async translate() {
 ...
 ...
 const result = await this.ctx.service.xfyun.voiceTranslate(pcmPath);
 this.ctx.body = result;
 if (+result.code !== 0) {
  this.ctx.status = 500;
 }
}
...

至此我們完成語(yǔ)音識(shí)別的代碼編寫(xiě)。主要流程其實(shí)很簡(jiǎn)單,通過(guò)小程序錄入語(yǔ)音文件,上傳到文件服務(wù)器之后,通過(guò) ffmpeg 獲取到 pcm 文件, 最后再轉(zhuǎn)發(fā)到科大訊飛的 api 接口進(jìn)行識(shí)別。

以上,如有錯(cuò)漏,歡迎指正!

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)創(chuàng)新互聯(lián)的支持。

當(dāng)前標(biāo)題:30分鐘快速實(shí)現(xiàn)小程序語(yǔ)音識(shí)別功能
分享網(wǎng)址:http://aaarwkj.com/article18/jjhjdp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供營(yíng)銷(xiāo)型網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站排名網(wǎng)站策劃、小程序開(kāi)發(fā)微信公眾號(hào)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都app開(kāi)發(fā)公司
特级特色生活片免费看| 亚洲成av人片又粗又长| 日日狠狠久久偷偷综合色| 99久久伊人精品综合观看| 欧美高清成人一区二区三区| 国产白浆一区二区视频| 亚洲婷婷综合久久一区二区| 日韩av熟女人妻一区二| 久久综合午夜福利视频| 亚洲av偷拍一区二区三区不卡| 亚洲综合久久精品少妇av| 欧美在线免费黄片视频| 女同同性av观看免费| 在线免费观看国产不卡| 国产精品一区二区久久毛片| 香蕉久草官网视频观看| 亚洲av天堂天天天堂色| 人妻鲁丝一区二区三区| 2018在线不卡爱视频| 久久精品一区欧美成人| 国产亚洲成人精品久久| 国产亚洲欧美日韩各类| 一区二区三区特黄色片| 无遮挡无掩盖的免费网站| 欧美日韩精品成人大片| 亚洲中文字幕视频在看| 国产一区999精品在线| 国产在线精彩视频自拍| 午夜国产激情福利网站| 国产精品国产三级国产av野外| 亚洲精品日韩一区二区| 国产高清大片一级黄色| 国产美女冒白浆视频免费| 青青草av一区二区三区| av 一区二区三区av| 亚洲品质自拍在线观看| 91国产自拍在线视频| 欧美日本国产高清不卡| 日韩 高清 一区二区| 国产精品亚洲在钱视频| 亚洲av激情码国产一区|