微信小程序录音+百度语音接口+情绪识别接口简单案例

框架介绍

前端

使用微信小程序进行录音,并将录音内容上传至后端,根据后端返回的结果判断录音内容所表达的情绪

后端

基于Python的flask框架搭建一个简易的后端,用于保存上传录音的音频,并对音频进行转码后提交至百度语音识别接口获取识别的文字内容,再将识别的文字内容提交至百度对话情绪识别接口获取所带情绪的系数比,根据系数比判断所表达的情绪

所需环境

语言环境

Python 3.0+(并安装相关模块:pip install flaskpip install baidu-aip

工具

微信开发者工具(开发微信小程序)
下载链接:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
ffmpeg(对录音音频进行转码)
下载链接:https://ffmpeg.zeranoe.com/builds/

微信小程序开发

全局配置文件
app.json
{
  "pages": [
    "pages/index/index",
    "pages/show/show",
    "pages/logs/logs"
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#0094ff",
    "navigationBarTitleText": "录音界面",
    "navigationBarTextStyle": "black"
  },
  "tabBar": {
    "color": "#000",
    "selectedColor": "#f23030",
    "backgroundColor": "#0094ff",
    "borderStyle": "white",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "初始页面"
      },
      {
        "pagePath": "pages/show/show",
        "text": "查看结果"
      }
    ]
  },
  "sitemapLocation": "sitemap.json"
}
录音及请求界面
界面设计代码(pages/index/index.wxml)
<view class="main">
  <button bindtap='play'>播放录音</button>
  <button bindtap='submit'>上传</button>
  
  <button bindtouchstart="clickDown" bind:touchend="clickUp"  touchcancel="clickUp" bindtouchmove="clickMove" class="record_btn">
    <span wx:if="{{recording}}">{{cancel_record?'松开取消':'松开发送'}}</span>
    <span wx:else>按下录音</span>
  </button>
</view>
录音及上传功能代码(pages/index/index.js)
const app = getApp();
const recorder = wx.getRecorderManager();
const player = wx.createInnerAudioContext();
const file = wx.getFileSystemManager();
var that;
var file_path;

Page({
  onLoad: function (options) {
    that = this;

    //先定好停止录音后要干嘛
    recorder.onStop(function suc(e) {
      console.log(e.tempFilePath);
      file_path = e.tempFilePath;
      //保存录音文件的临时路径
      that.setData({
        filePath: e.tempFilePath,
      })
      wx.setStorageSync('filePath', e.tempFilePath);
      wx.showLoading({
        title: '文件读取中...'
      })
      wx.hideLoading();
    })
  },
  submit() {
    wx.uploadFile({
      // 将音频上传
      url: 'http://127.0.0.1:5000/voice',
      filePath: file_path,
      name: 'file',
      header: {
        'content-type': 'multipart/form-data'
      },
      success: function (res) {
        console.log(res.data);
        app.globalData.result = res.data;
        // 将返回的数据放入全局里
      },
      fail: function (res) {
      }
    });

  },
  //手指按下
  clickDown(e) {
    console.log('start');
    that.setData({
      recording: true,
      start_y: e.touches[0].clientY,
      cancel_record: false,
    })

    //开始录音
    recorder.start({
      // duration: 60000,//最大时长
      // sampleRate: that.data.rate,//采样率
      // numberOfChannels: 1,//录音通道数
      // encodeBitRate: 16000,//编码码率,有效值见下表格
      format: 'silk',//音频格式
      // frameSize: 2000,//指定大小 kb
    })
  },
  //手指移动
  clickMove(e) {
    if (e.touches[0].clientY - that.data.start_y <= -50) {
      that.setData({
        cancel_record: true,
      })
    } else {
      that.setData({
        cancel_record: false,
      })
    }
    return false;
  },
  //手指松开
  clickUp(e) {
    if (that.data.cancel_record) {
      wx.showModal({
        title: '提示',
        content: '您选择了取消发送,确定吗?',
        confirmText: '继续发送',
        cancelText: '取消重录',
        success: res => {
          if (res.confirm) {
            wx.showToast({
              title: '发送成功',
            })
          } else {
            wx.showToast({
              title: '您选择了取消',
            })
          }
          that.setData({
            recording: false
          })
        }
      })
    } else {
      wx.showToast({
        title: '发送成功',
      })
      that.setData({
        recording: false
      })
    }
    recorder.stop();
    return false;
  },
  //播放
  play() {
    player.src = that.data.filePath;
    player.play();
  },
})

注:
开发时注意关闭合法域名等的校验,如图:

数据展示界面
界面设计代码(pages/show/show.wxml)
<text>你说的话是:</text>
<view> {{result.result.text}}</view>
<text>识别的情绪是:</text>
<view> {{result.result.emotion}}</view>
载入数据代码(pages/show/show.js)
var app = getApp();
Page({
  onLoad: function (options) {
    console.log(app.globalData);
    var that = this;
    
    if (app.globalData.result){
      that.setData({
        result: JSON.parse(app.globalData.result),
        // 将数据转json格式供读取
      });
    }else{

    }    
  },
  onShow() {
    this.onLoad();
    // 每次打开页面时重新载入
  },
})

后端代码(.py)

from flask import Flask, request
from aip import AipSpeech, AipNlp
import os
import json

app = Flask(__name__)

@app.route('/voice', methods=['GET','POST'])
def main():
    '''后端请求处理接口,将小程序上传的音频保存、转码,并发送到语音识别接口识别文字后,再调用对话情绪识别接口'''
    result = {"status": 0}
    try:
        f = request.files['file']
        f.save("voice/xxx.silk")
        # 保存文件至本地路径,需要提前在py文件的路径下创建个voice文件夹
        os.system("ffmpeg -y -i voice/xxx.silk -acodec pcm_s16le -f s16le -ac 1 -ar 16000 voice/a.pcm")
        # 文件转码
        text = get_result()
        # 调用语音识别接口获取识别文字
        emotion = get_emotion(text)
        # 调用对话情绪识别接口获取判断结果
        result["status"] = 1
        result["result"] = emotion
    except Exception as e:
        print(repr(e))
    finally:
        return json.dumps(result)

def get_result(path='voice/a.pcm'):
    '''调用百度语音识别接口,返回识别后的文字结果'''
    # 百度语音识别接口的ID、KEY等信息
    APP_ID = ''
    API_KEY = ''
    SECRET_KEY = ''
    client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
    # 读取转码后的文件
    def get_file_content(filePath):
        with open(filePath, 'rb') as fp:
            return fp.read()
    result = client.asr(get_file_content(path), 'pcm', 16000, {
        'dev_pid': 1536,
    })
    print(result)
    return result["result"][0]

def get_emotion(text=""):
    '''调用百度对话情绪识别接口,返回判断的情绪结果'''
    # 百度自然语言处理接口的ID、KEY等信息
    APP_ID = ''
    API_KEY = ''
    SECRET_KEY = ''
    emotion_result = {
        "text": text
        }
    client = AipNlp(APP_ID, API_KEY, SECRET_KEY)
    options = {
        "scene": "talk"
    }
    try:
        result = client.emotion(text, options)
        for each in result["items"]:
            if each["label"] == "optimistic":
                opti = each["prob"]
            elif each["label"] == "pessimistic":
                pess = each["prob"]

        if opti >= pess:
            emotion_result["emotion"] = "高兴"
        else:
            emotion_result["emotion"] = "悲伤"
    except Exception as e:
        print(repr(e))
    finally:
        return emotion_result

if __name__ == '__main__':
    app.run(debug=True)

使用测试

使用步骤

先运行Python后端文件,然后在微信小程序当中点击录音->上传,当在控制台看到返回的结果时,进入查看结果页面查看

结果图示
使用测试1

使用测试2(感觉好真实...)

注意点

百度语音识别接口音频转码问题

对于调用百度语音识别接口,并非直接上传音频即可正确识别,在上传前需要进行音频的转码,否则往往结果会返回错误码3301,并提示你音频质量不行,其中转码基于ffmpeg工具,相关命令可参考官方提供的文档:https://cloud.baidu.com/doc/SPEECH/ASR-Tool.html#.E8.BD.AC.E6.8D.A2.E5.91.BD.E4.BB.A4.E7.A4.BA.E4.BE.8B
当然鉴于微信小程序的录音格式还很特殊,建议直接生成.silk文件,然后根据如下命令直接生成转码后的.pcm文件供接口识别:

ffmpeg -y -i xxx.silk(源文件) -acodec pcm_s16le -f s16le -ac 1 -ar 16000 xxx.pcm(输出文件)
微信小程序录音格式问题

由于微信小程序的录音格式比较特殊(貌似是silk格式),因此当后端将录音保存到本地时,无法直接打开文件读取,当然上传到百度语音识别接口也同样无法识别,为此如果想要在本地播放该音频,可以下载软件:silk v3(建议下载完整版),将文件转格式后即可播放,软件地址:https://kn007.net/topics/batch-convert-silk-v3-audio-files-to-mp3-in-windows/

使用步骤

1.在软件下载完成后,将压缩包内文件都拷贝到一个文件夹下,打开文件silk2mp3.exe如图:


2.导入对应的silk文件,并选择:解码->开始转换即可,此时生成的解码文件就可以播放了

其他

ffmpeg参考

https://cloud.tencent.com/developer/article/1119403

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354