背景

场景

设想一个场景,我们在会议中,现场录制了下来了会议音频。随后整理会议纪要时,需要重新人工听一遍,然后写成文字。

但是这个重复枯燥的事情能否由自动完成呢?

通过录音文件识别功能,即可以实现。

方案

之前使用过讯飞听见来完成这个功能。我们在上面上传一段语音,即可以自动识别成为文字。

但是讯飞听见只有 2 小时免费转写时长。在讯飞的免费时长使用完后,开始调研了一下别的产品。

阿里云录音文件识别

阿里云的录音文件识别功能,也可以完成相应的事情。

目前只支持 API 方式的调用,但是对于我们工程师来说,这都不是困难。API 调用可能还更方便一些。

注册阿里云账号

注册阿里云账号后,可以得到 AccessKey ID 和 AccessKey Secret。

之后就可以开通试用了。

pom 依赖

官方文档中直接拿过来

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
    <version>3.5.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.49</version>
</dependency>

Java 调用

依赖从官方文档中拿过来。

import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;

public class FileTransJavaDemo {
    /**
     * 地域 ID 常量内容,请勿改变
     */
    public static final String REGIONID = "cn-shanghai";
    public static final String ENDPOINTNAME = "cn-shanghai";
    public static final String PRODUCT = "nls-filetrans";
    public static final String DOMAIN = "filetrans.cn-shanghai.aliyuncs.com";
    public static final String API_VERSION = "2018-08-17";
    public static final String POST_REQUEST_ACTION = "SubmitTask";
    public static final String GET_REQUEST_ACTION = "GetTaskResult";
    /**
     * 参数设置 Key 常量内容,请勿改变
     */
    public static final String KEY_APP_KEY = "app_key";
    public static final String KEY_FILE_LINK = "file_link";
    public static final String KEY_TASK = "Task";
    public static final String KEY_TASK_ID = "TaskId";
    public static final String KEY_STATUS_TEXT = "StatusText";

    public static void main(String args[]) throws Exception {
        if (args.length < 3) {
            System.err.println("FileTransJavaDemo need params: <AccessKey Id> <AccessKey Secret> <app-key>");
        }
        final String accessKeyId = args[0];
        final String accessKeySecret = args[1];
        final String appKey = args[2];
        /**
         * 阿里云鉴权 client
         */
        IAcsClient client;
        // 设置 endpoint
        DefaultProfile.addEndpoint(ENDPOINTNAME, REGIONID, PRODUCT, DOMAIN);
        // 创建 DefaultAcsClient 实例并初始化
        DefaultProfile profile = DefaultProfile.getProfile(REGIONID, accessKeyId, accessKeySecret);
        client = new DefaultAcsClient(profile);
        /**
         * 创建 CommonRequest 设置请求参数
         */
        CommonRequest postRequest = new CommonRequest();
        // 设置域名
        postRequest.setDomain(DOMAIN);
        // 设置 API 的版本号,格式为 YYYY-MM-DD
        postRequest.setVersion(API_VERSION);
        // 设置 action
        postRequest.setAction(POST_REQUEST_ACTION);
        // 设置产品名称
        postRequest.setProduct(PRODUCT);
        /**
         * 设置录音文件识别请求参数,以 JSON 字符串的格式设置到请求的 Body 中
         */
        JSONObject taskObject = new JSONObject();
        // 设置 app_key
        taskObject.put(KEY_APP_KEY, appKey);
        // 设置音频文件访问链接
        taskObject.put(KEY_FILE_LINK, "http://aliyun-nls.oss.aliyuncs.com/asr/fileASR/examples/nls-sample.wav");
        String task = taskObject.toJSONString();
        // 设置以上 JOSN 字符串为 Body 参数
        postRequest.putBodyParameter(KEY_TASK, task);
        // 设置为 POST 方式的请求
        postRequest.setMethod(MethodType.POST);
        /**
         * 提交录音文件识别请求
         */
        // 获取录音文件识别请求任务的 ID,以供识别结果查询使用
        String taskId = "";
        CommonResponse postResponse = client.getCommonResponse(postRequest);
        if (postResponse.getHttpStatus() == 200) {
            JSONObject result = JSONObject.parseObject(postResponse.getData());
            String statusText = result.getString(KEY_STATUS_TEXT);
            if (statusText.equals("SUCCESS")) {
                System.out.println("录音文件识别请求成功响应:" + result.toJSONString());
                taskId = result.getString(KEY_TASK_ID);
            } else {
                System.err.println("录音文件识别请求失败:" + postResponse.getData());
                return;
            }
        } else {
            System.err.println("录音文件识别请求失败,Http 错误码:" + postResponse.getHttpStatus());
            System.err.println("录音文件识别请求失败响应:" + postResponse.getData());
            return;
        }
        /**
         * 创建 CommonRequest 设置任务 ID
         */
        CommonRequest getRequest = new CommonRequest();
        // 设置域名
        getRequest.setDomain(DOMAIN);
        // 设置 API 版本
        getRequest.setVersion(API_VERSION);
        // 设置 action
        getRequest.setAction(GET_REQUEST_ACTION);
        // 设置产品名称
        getRequest.setProduct(PRODUCT);
        // 设置任务 ID 为查询参数
        getRequest.putQueryParameter(KEY_TASK_ID, taskId);
        // 设置为 GET 方式的请求
        getRequest.setMethod(MethodType.GET);
        /**
         * 提交录音文件识别结果查询请求
         * 以轮询的方式进行识别结果的查询,直到服务端返回的状态描述为“SUCCESS”、“SUCCESS_WITH_NO_VALID_FRAGMENT”,或者为错误描述,则结束轮询。
         */
        String statusText = "";
        while (true) {
            CommonResponse getResponse = client.getCommonResponse(getRequest);
            if (getResponse.getHttpStatus() != 200) {
                System.err.println("识别结果查询请求失败,Http 错误码:" + getResponse.getHttpStatus());
                System.err.println("识别结果查询请求失败:" + getResponse.getData());
                break;
            }
            JSONObject result = JSONObject.parseObject(getResponse.getData());
            System.out.println("识别查询结果:" + result.toJSONString());
            statusText = result.getString(KEY_STATUS_TEXT);
            if (statusText.equals("RUNNING") || statusText.equals("QUEUEING")) {
                // 继续轮询
                Thread.sleep(3000);
            } else {
                break;
            }
        }
        if (statusText.equals("SUCCESS") || statusText.equals("SUCCESS_WITH_NO_VALID_FRAGMENT")) {
            System.out.println("录音文件识别成功!");
        } else {
            System.err.println("录音文件识别失败!");
        }
    }
}

实际调用

录音文件识别请求成功响应:

{
    "TaskId": "64d8d379cd4c11e8b422f1057c7axxxx",
    "RequestId": "EBBFC8E8-16BC-4220-84D3-C2DB1B7Dxxxx",
    "StatusText": "SUCCESS",
    "StatusCode": 21050000
}

识别查询结果:

{
    "TaskId": "64d8d379cd4c11e8b422f1057c7axxxx",
    "RequestId": "A378A43D-97AD-4314-BD52-D3865CD1xxxx",
    "StatusText": "RUNNING",
    "BizDuration": 0,
    "StatusCode": 21050001
}

识别查询结果:

{
    "TaskId": "64d8d379cd4c11e8b422f1057c7axxxx",
    "RequestId": "ECB8FB0E-2503-45CD-BF17-FF76BB98xxxx",
    "StatusText": "SUCCESS",
    "BizDuration": 3574,
    "SolveTime": 1539258996543,
    "StatusCode": 21050000,
    "Result": {
        "Sentences": [{
            "EndTime": 3574,
            "SilenceDuration": 0,
            "BeginTime": 470,
            "Text": "明天是周一天气挺好的",
            "ChannelId": 0,
            "SpeechRate": 193,
            "EmotionValue": 6.0
        }, {
            "EndTime": 3574,
            "SilenceDuration": 0,
            "BeginTime": 470,
            "Text": "明天是周一天气挺好的",
            "ChannelId": 1,
            "SpeechRate": 193,
            "EmotionValue": 6.0
        }]
    }
}

录音文件识别成功!

注意,结果中的两句话,并不是识别重复了,而是两个不同的 ChannelId 的结果是一样的而已。

参考

  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品: PipeSoloSymWide 等,欢迎大家加入,贡献开源。

    3000 引用 • 3702 回帖 • 661 关注
  • speech
    3 引用
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    2276 引用 • 6673 回帖 • 1224 关注
感谢    赞同    分享    收藏    关注    反对    举报    ...