前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >玩转AI新声态 | 我将王者荣耀的ASR语音识别,接入到了腾讯元器小程序...

玩转AI新声态 | 我将王者荣耀的ASR语音识别,接入到了腾讯元器小程序...

原创
作者头像
叫我阿柒啊
修改2024-06-30 12:16:38
330
修改2024-06-30 12:16:38
举报

前言

刚结束了腾讯云BI的体验活动,在文章提到了SaSS、PaSS的概念,腾讯云BI是一个SaSS,而今天要写的腾讯云语音识别就是一个PaSS,平台即服务,用户只需要调用接口就能实现语音识别的功能,而语音识别所需要的算法、计算资源都是PaSS来分配。

曾惊叹王者荣耀的语音转文字,轻声细语的说一句cpdd,它真的就能转换成字母‘CPDD’。

今天才发现,这居然是腾讯云语音识别提供的服务。所以,今天就带着一个极大的兴趣,来体验一下腾讯云语音识别服务。

腾讯云语音识别

登录腾讯云语音识别的首页,可以看到一些服务的简介和套餐优惠。

简介

腾讯云语音识别(Automatic Speech Recognition,ASR)是将语音转成文字的 PaaS 产品,能够为企业提供极具性价比的语音识别服务。被微信、王者荣耀、腾讯视频等大量内部业务使用,外部亦服务于呼叫中心录音转写、会议实时转写、语音输入法、数字人、互动直播、课堂内容分析等多个业务场景,产品具备丰富的行业落地经验。

活动期间腾讯云语音提供了新用户专享资源包,其中包括一句话识别调用、实时语音识别、录音文件识别、语音流异步识别,可以尽情体验语音识别的各种功能。

我也是在新用户专享资源包的基础上完成此次的开发,如果想要更多的体验,可以选择其他的套餐。

优势

  1. 海量数据积累:累了数十万小时的语音标注数据,拥有丰富多样的语料库
  2. 基于多种序列神经网络结构,在通用以及垂直领域有业内领先的识别精度
  3. 支持多平台设备,提供 REST API 和 SDK,支持智能硬件、移动应用、网站、桌面客户端和物联网等多种设备终端
  4. 支持语种丰富:现阶段已经支持中文普通话、英文、粤语、韩语、日语、泰语等15种语种和上海话、四川话、武汉话、贵阳话、昆明话等23种方言的语音识别,后续将持续开放其他语种和方言的识别能力
  5. 经过微信、腾讯视频、王者荣耀等内部业务充分验证,日服务亿级用户,性能稳定

功能

腾讯云语音识别一共有五个服务:录音文件识别、实时语音识别、录音文件识别极速版、一句话识别和语音流异步识别

关于体验语音转文字的场景,我构思了好久,最终还是觉得即时通讯是语音转文字绝佳的体验场景,加上之前想要开发一个ChatGPT的微信小程序, 所以决定将语音转文字服务集成到ChatGPT中去,无需通过输入文字就能和ChatGPT交流。

主要是用一句话识别接口来完成语音识别。在一句话识别API中,可以识别URL指向的语音文件base64格式的语音数据。我们使用base64来进行语音数据交互,来实现语音识别。

分析asr提供的API调用demo,我们在Java中构造请求及其参数。

签名方法 v3

我们发现整个请求的参数分为两个部分:公共参数接口调用参数。公共参数放在了request的header部分,我们通过阅读API文档,前面的X-TC开头的参数用作标识不同服务,而负责服务鉴权的Authorization需要调用签名方法v3来生成。

签名方法v3文档中,实现了多种语言的签名方法。新建TencentCloudAPITC3类,将Java版本的v3复制进去。

接着开始重构TencentCloudAPITC3,在签名方法v3中,需要SecretIdSecretKey,以及host、version等参数。原代码中是从环境变量中获取,这里使用Value注解绑定application.properties中的配置。

接着就是将源代码中的main方法,重构成generateAPITC3方法,并将一些变量放到形参中调用。

最后返回一个包含请求header所有参数的Map

但是上面这种鉴权的方式不仅麻烦,而且是很麻烦。用Java、python、rest client搞了一个下午、报了一下午的错误,我直接放弃,直接使用腾讯官方的SDK来调用,所以说撤回上面的签名方法v3的实现,直接使用SDK。

ASR SDK

使用maven引入依赖:

代码语言:xml
复制
<dependency>
    <groupId>com.tencentcloudapi</groupId>
    <artifactId>tencentcloud-sdk-java</artifactId>
    <version>3.1.830</version>
</dependency>

新建一个类,借助tencentcloudapi的SDK能力,实现asrSentenceRecognitionRequest() 方法来完成asr的请求,其中对于url还是Data识别,都做了一个二选一的判断处理。

代码语言:java
复制
public SentenceRecognitionResponse asrSentenceRecognitionRequest(String url, String data, String voiceFormat, String engSerViceType) throws TencentCloudSDKException {
    Credential cred = new Credential(SECRET_ID, SECRET_KEY);
    AsrClient asrClient = new AsrClient(cred, "ap-shanghai");
    SentenceRecognitionRequest request = new SentenceRecognitionRequest();
    if (url != null && url.length() !=0) {
        request.setUrl(url);
        request.setSourceType(0L);
    } else if (data != null) {
        request.setData(data);
        request.setSourceType(1L);
    }
    request.setVoiceFormat(voiceFormat);
    request.setEngSerViceType(engSerViceType);
    SentenceRecognitionResponse sentenceRecognitionResponse = asrClient.SentenceRecognition(request);
    return sentenceRecognitionResponse;
}

Credential管理密钥,AsrClient来封装asr所有的接口,一句话识别接口请求对应的是SentenceRecognitionRequest类,在此类中通过setter传入请求参数。

发起请求之后,会返回SentenceRecognitionResponse的对象,包含的一句话识别接口返回Json的字段,这样我们就不用再定义实体类去转换json了。

从注释以及API文档给出的样例中,Result识别结果字段是是我们需要的。同样,对比前面自己实现签名方法v3,你会发现什么timestamp、action都无需自己声明定义,整个请求对于用户来说是透明化的。

controller

接着就是controller的实现,对于controller,首先定义入参类AsrWordRequest,一共四个字段,分别是:EngSerViceType、VoiceFormat、Data、url。

配图

EngSerViceType、VoiceFormat分别代表着翻译语言和语音文件格式,Data和url二者选一传入,Data是base64的语音文件,url表示服务器上的语音文件资源。

然后我们就开发controller,我们只需要使用请求参数调用asrSentenceRecognitionRequest方法,完成asr一句话语音接口的请求,然后获取返回值。

代码语言:java
复制
@RequestMapping(value = "/sentenceRecognition", method = RequestMethod.POST)
@CrossOrigin(origins = "*", maxAge = 3600)
public Map<String, Object> sentenceRecognition(@RequestBody AsrWordRequest params) {
    Map<String, Object> result = new HashMap<>();
    SentenceRecognitionResponse sentenceRecognitionResponse;
    try {
        sentenceRecognitionResponse = tencentAPIAsrCredential.asrSentenceRecognitionRequest(params.getUrl(), params.getData(), params.getVoiceFormat(), params.getEngSerViceType());
    } catch (TencentCloudSDKException e) {
        result.put("code", 1);
        result.put("message", e.getMessage());
        return result;
    }
    result.put("code", 0);
    result.put("message", sentenceRecognitionResponse.getResult());
    return result;
}

在定义controller类时,使用 @RestController 注解让接口返回json格式的数据。这里我一共定义了两个字段:code和message。conde为0,message为正常请求asr返回的翻译结果数据,code为1,message是asr返回的异常信息。

接口测试

在测试时,我语音识别的Data表示的base64的语音文件,但是从网上下载的又有问题。但是我灵机一动,腾讯云产品除了有ASR语音识别,还有TTS语音合成。于是我就领取了一个免费的语音合成资源包

然后在API Explorer中输入TEXT“你好,阿柒!”,调用基础语音合成接口,将文本转换成wav语音文件。

接口响应结果返回的Audio就是base64的语音文件,我使用Rest Client进行接口测试,直接将Audio内容直接复制到Data参数上。

代码语言:shell
复制
POST http://localhost:8080/asr/sentenceRecognition
Accept: */*
Content-Type: application/json; charset=utf-8

{
    "VoiceFormat": "wav",
    "Data": "Audio base64语音数据",
    "EngSerViceType": "16k_zh",
    "url": ""
}

运行返回结果:

使用TTS生成语音文件,成功被ASR一句话识别接口识别。

在上面测试示例中,如果不传入EngSerViceType,接口返回的code为1,message就是ASR返回的异常信息。

小程序接入

在,腾讯元器接入小程序的文章中

在右下角预留了麦克风,我们先从麦克风的一些功能入手。我想做的效果就是:长按麦克风录音的时候,麦克风变化的效果,松开手就录音结束。

代码语言:html
复制
<view class="input">
    <uni-icons type="chat" size="30"></uni-icons>
    <view class="chat-input uni-column">
        <input class="uni-input" v-model="inputMessage" maxlength="200" placeholder="你有什么想知道的?"
            @confirm="sendMessages" />
    </view>
    <uni-icons class='mic' :class="isRecording ? 'recording-icon' : ''" type="mic" size="30"
        @touchstart="startRecording"  @touchend="stopRecording" @touchmove="handleTouchMove"></uni-icons>
</view>

麦克风录音效果

上面的代码中,主要是对mic这个uni-icons做了一些变动。当isRecording为true时,使用三目运算绑定了一个recording-icon的class,在css中定义recording-icon实现麦克风的录音效果。

首先是定义了isRecording变量,用来标识是否录音,以此来实现录音时的一些效果。

代码语言:javascript
复制
const isRecording = ref(false);

然后就是当isRecording时,实现mic图标的动画效果。

代码语言:css
复制
.recording-icon {
    animation: pulse 1s infinite;
    z-index: 100;
}

@keyframes pulse {
    0% {
        opacity: 1;
    }
    50% {
        opacity: 0.5;
    }
    100% {
        opacity: 1;
    }
}

使用 @keyframes定义了一个pulse动画,实际上就是利用opacity属性改变mic图标的透明度。

开始录音

麦克风图标绑定了touchstart触摸事件,当按住麦克风的时候开始录音。具体逻辑在startRecording实现。

在uni-app中,结束和开始录音由RecorderManager对象控制,所以需要通过uni.getRecorderManager()获取。

代码语言:JavaScript
复制
let recorderManager = uni.getRecorderManager();
const startRecording = () => {
    isRecording.value = true;
    recorderManager.start({
        format: 'wav',
    });
};

这里将isRecording修改为true,表示我要开始录音了,然后使用start的options,将录音文件的格式设置为wav。同是要说的是,后面的很多效果和事件,都会在startRecording中和开始录音一起触发。

结束录音

麦克风图标绑定了touchend结束触摸事件,当松开麦克风的时候停止录音,具体逻辑在stopRecording实现。

代码语言:JavaScript
复制
const stopRecording = () => {
    if (isRecording.value) {
        recorderManager.stop();
    }
    isRecording.value = false;
};

调用stop即可结束录音,并将isRecording修改为false。最终麦克风的动态效果结合开始、结束录音的实现如下:

录音遮罩

当我长按麦克风的时候,我希望弹出一个灰色的遮罩层,里面有一个录音频率的条形框,松开录音的时候遮罩层就会消失。

代码语言:html
复制
<view class="overlay" v-if="isRecording">
    <view class="canvas">
        <canvas canvas-id="waveform"></canvas>
    </view>
</view>

overlay的view表示遮罩层,canvas用来渲染录音频率。这里要说的是,overlay是在footer中实现的。所以overlay要作为template的子节点,也就是要和输入框、mic图标所在的view同级,这样header、main、footer、aside以及overlay都是body的子节点。

这样在使用position: absolute时,才能相当于父元素body进行定位,这时将overlay的width和height设置为100%,遮罩层就会渲染整个页面。

代码语言:css
复制
.overlay {
    width: 100%;
    height: 100%;
    margin-bottom: 0px;
    background: rgba(0, 0, 0, 0.5);
    position: absolute;
}

.canvas {
    width: 100%;
    height: 50%;
    position: absolute;
    top: 20vh;
}

canvas {
    position: absolute;
    top: 55%;
    left: 30%;
    width: 40vw;
    height: 8vh;
    background-image: linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%);
    border-radius: 20rpx;
    margin: auto;
    z-index: 9999;
}

这里使用css定义了canvas的样式,最终效果图如下:

gif有色差,遮罩层在实际上是灰色透明的。

语音识别、对话渲染

然后就是实现onStop录音结束回调,实现后面的功能和逻辑。整体代码如下:

代码语言:JavaScript
复制
const fs = uni.getFileSystemManager()

recorderManager.onStop((res) => {
    console.log('recorder stop', res);
    // 第一部分
    isRecording.value = false;
    audioFilePath = res.tempFilePath; 
    uni.showToast({
        icon: 'none',
        title: '录音结束'
    });
    
    // 第二部分
    fs.readFile({
        filePath: audioFilePath,
        success(res) {
            console.log(res.data)
            const base64Data = uni.arrayBufferToBase64(res.data);
            console.log(base64Data)
            // 第三部分
            uni.request({
                url: 'http://192.168.50.14:8080/asr/sentenceRecognition',
                method: 'POST',
                header: {
                    'Accept': '*/*',
                    'Content-Type': 'application/json; charset=utf-8'
                },
                data: {
                    VoiceFormat: 'wav',
                    Data: base64Data,
                    EngSerViceType: '16k_zh',
                    url: ''
                },
                success(res) {
                    console.log('请求成功:', res.data);
                    // 第四部分
                    chat.constructMessage(res.data.message, 1)
                    requestYQ();
                },
                fail(err) {
                    console.error('请求失败:', err);
                }
            });
        },
        fail(err) {
            console.error('读取WAV文件失败:', err);
        }
    });
})

整段代码我分为四个部分:

  1. 从onStop回调函数中可以获取录音的信息,例如时长、文件大小以及文件路径。
  1. 读取语音文件转换成base64
  2. 将base64格式语音文件发起语音识别ASR识别
  3. 最后将识别的结果,调用元器接口,并将结果渲染到对话框中
代码语言:javascript
复制
const requestYQ = function () {
    uni.request({
        url: "https://yuanqi.tencent.com/openapi/v1/agent/chat/completions",
        method: 'POST',
        header: {
            'X-Source': 'openapi',
            'Content-Type': 'application/json',
            'Authorization': 'Bearer pJhqvfJ1GhjzdMW',
        },

        data: {
            "assistant_id": "v0c4Fkv",
            "user_id": "rodney",
            "stream": false,
            "messages": chat.state.messages
        },
        success: (res) => {
            const message = res.data['choices'][0]['message']['content']
            chat.constructMessage(message, 2)
            console.log(res.data);
        }
    })
}

chat.constructMessage是使用pinia封装的状态共享变量,具体实现可以参考腾讯元器:为了荒天帝,自学从零开发了一个微信小程序...

效果测试

基本功能都已经实现,美中不足的是预想中,录音时根据音频频率波浪线效果没有做出来,后面需要研究一下canvas和uni-app的接口。

语音转文字

元器回复

分享一个比较有意思的,就是在测试的过程中,ASR接口返回了一个错误信息,元器最后用荒天帝的口吻给了回复。

迭代优化

这里是直接将语音转换的文字,渲染到对话框中,实际上是可以直接将录音文件渲染到对话框中,实现这个功能的话我需要重构两个部分:

  1. 重构消息结构体,区分是文字还是语音
  2. 实现语音框组件
  3. 重构对话框渲染部分,通过标识判断渲染文字框还是语音框

至于录音的播放,也是需要将语音框中的语音文件filepath重新设置,绑定到语音对话框中,点击播放事件可以绑定下面的代码。

代码语言:javascript
复制
const playVoice = function () {
    console.log('播放录音');
    if (audioFilePath.length > 0) {
        innerAudioContext.src = audioFilePath;
        innerAudioContext.play();
    }
}

结语

腾讯云语音识别ASR和语音合成TTS,在准确性和实时性上的确具有优势。在整个微信小程序的开发中,只对用户方使用了ASR语音识别,有机会的话还是会将元器的回答,接入到语音合成TTS,实现ASR和TTS的完美联动。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 腾讯云语音识别
    • 简介
      • 优势
        • 功能
        • 签名方法 v3
          • ASR SDK
            • controller
              • 接口测试
              • 小程序接入
                • 麦克风录音效果
                  • 开始录音
                    • 结束录音
                      • 录音遮罩
                        • 语音识别、对话渲染
                        • 效果测试
                          • 语音转文字
                            • 元器回复
                            • 迭代优化
                            • 结语
                            相关产品与服务
                            语音识别
                            腾讯云语音识别(Automatic Speech Recognition,ASR)是将语音转化成文字的PaaS产品,为企业提供精准而极具性价比的识别服务。被微信、王者荣耀、腾讯视频等大量业务使用,适用于录音质检、会议实时转写、语音输入法等多个场景。
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档