科大讯飞语音听写接口使用的封装

科大讯飞开放平台——语音听写接口的使用这篇文章里介绍了科大讯飞语音听写接口的简单使用方法,但是在实际开发中发现仅仅那样做在使用起来还是不方便,于是想到把语音听写接口的调用、Json数据解析、听写结果的处理等操作进行封装,使用异步回调的方式进行调用,这样在使用到语音听写的地方只需创建一个监听接口并重写语音识别返回结果的处理方法即可。梳理了一下步骤如下:

(一)前期准备工作

略,见:科大讯飞开放平台——语音听写接口的使用

注:将获取到的AppId存到一个常量类里,后面便于管理:

public class GlobalConfig {
     // 科大讯飞语音SDK AppID
     public static final String IFLY_VOICE_SDK_APP_ID = "570657ad";
}

(二)封装工具类

创建用GSON解析Json数据的通用工具类GsonUtil,用于为解析语音听写服务器返回的Json格式数据做准备:

import java.util.ArrayList;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;

/*
 * 封装的GSON解析工具类,提供泛型参数方法
 * 
 */
public class GsonUtil {
    // 将单条Json数据解析成相应的映射对象
    public static <T> T parseJsonWithGson(String jsonData, Class<T> type) {
        Gson gson = new Gson();
        T result = gson.fromJson(jsonData, type);
        return result;
    }

    // 将Json数组解析成相应的映射对象列表
    public static <T> List<T> parseJsonArrayWithGson(String jsonData,
            Class<T> type) {
        Gson gson = new Gson();
        List<T> result = new ArrayList<T>();

        // 下面这句因为泛型在编译期类型会被擦除,从而导致如下错误:
        // java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap
        // cannot be cast to DictationResult
        // List<T> result = gson.fromJson(jsonData, new TypeToken<List<T>>() {
        // }.getType());

        // 正确写法
        JsonArray array = new JsonParser().parse(jsonData).getAsJsonArray();
        for (final JsonElement elem : array) {
            result.add(new Gson().fromJson(elem, type));
        }

        return result;
    }
}

(三)创建语音听写结果解析工具类

创建专门解析语音听写结果Json数据的工具类DictationJsonParseUtil,用于解析科大讯飞语音听写服务器返回的Json数据:

import java.util.List;

/**
 * 用于解析科大讯飞语音听写服务器返回的Json数据
 *
 * 语音识别结果Json数据格式:
 * {"sn":1,"ls":true,"bg":0,"ed":0,"ws":[{"bg":0,"cw":[{"w":"今天","sc":0}]},
 * {"bg":0,"cw":[{"w":"的","sc":0}]},{"bg":0,"cw":[{"w":"天气","sc":0}]},
 * {"bg":0,"cw":[{"w":"怎么样","sc":0}]},{"bg":0,"cw":[{"w":"。","sc":0}]}]}
 */
public class DictationJsonParseUtil {

    // 解析服务器返回的语音听写结果Json格式数据的静态方法,返回值为语音的字符串
    public static String parseJsonData(String jsonDataStr) {
        String speechStr = "";
        List<DictationResult> resultList = GsonUtil.parseJsonArrayWithGson(
                jsonDataStr, DictationResult.class);

        for (int i = 0; i < resultList.size() - 1; i++) { // 这里减1是因为最后有一组作为结尾的标点符号数据,要舍去
            speechStr += resultList.get(i).toString();
        }

        return speechStr;
    }
}

// 语音听写结果类
class DictationResult {
    private String sn;
    private String ls;
    private String bg;
    private String ed;

    private List<Words> ws;

    public static class Words {
        private String bg;
        private List<Cw> cw;

        public static class Cw {
            private String w;
            private String sc;

            public String getW() {
                return w;
            }

            public void setW(String w) {
                this.w = w;
            }

            public String getSc() {
                return sc;
            }

            public void setSc(String sc) {
                this.sc = sc;
            }

            @Override
            public String toString() {
                return w;
            }
        }

        public String getBg() {
            return bg;
        }

        public void setBg(String bg) {
            this.bg = bg;
        }

        public List<Cw> getCw() {
            return cw;
        }

        public void setCw(List<Cw> cw) {
            this.cw = cw;
        }

        @Override
        public String toString() {
            String result = "";
            for (Cw cwTmp : cw) {
                result += cwTmp.toString();
            }
            return result;
        }
    }

    public String getSn() {
        return sn;
    }

    public void setSn(String sn) {
        this.sn = sn;
    }

    public String getLs() {
        return ls;
    }

    public void setLs(String ls) {
        this.ls = ls;
    }

    public String getBg() {
        return bg;
    }

    public void setBg(String bg) {
        this.bg = bg;
    }

    public String getEd() {
        return ed;
    }

    public void setEd(String ed) {
        this.ed = ed;
    }

    public List<Words> getWs() {
        return ws;
    }

    public void setWs(List<Words> ws) {
        this.ws = ws;
    }

    @Override
    public String toString() {
        String result = "";
        for (Words wsTmp : ws) {
            result += wsTmp.toString();
        }
        return result;
    }
}

(四)创建DictationListener监听类

创建语音听写结果监听接口DictationListener,提供一个onDictationListener(String dictationResultStr)方法,调用方可以重写该方法,对语音听写结果进行其他操作。

/**
 * 科大讯飞语音解析结果返回监听接口
 *
 */
public interface DictationListener {
    public abstract void onDictationListener(String dictationResultStr);
}

(五)创建DictationUtil类

创建DictationUtil类,其showDictationDialog方法会弹出语音听写Dialog窗口,并对语音返回结果进行解析得到最终结果,最后用onDictationListener接口来将最终结果传送给调用方。

import android.content.Context;

import com.easydo.constant.GlobalConfig;
import com.iflytek.cloud.RecognizerResult;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechRecognizer;
import com.iflytek.cloud.SpeechUtility;
import com.iflytek.cloud.ui.RecognizerDialog;
import com.iflytek.cloud.ui.RecognizerDialogListener;

/**
 * 语音听写工具类,用于弹出语音听写Dialog进行听写
 *
 */

public class DictationUtil {
    private static final String DICTATION_APPID = GlobalConfig.IFLY_VOICE_SDK_APP_ID;

    private static SpeechRecognizer mIat;
    private static RecognizerDialog iatDialog;
    private static String dictationResultStr;
    private static String finalResult;

    public static void showDictationDialog(final Context context,
            final DictationListener listener) {
        // 初始化语音配置
        initConfig(context);

        // 开始听写
        iatDialog.setListener(new RecognizerDialogListener() {

            @Override
            public void onResult(RecognizerResult results, boolean isLast) {
                if (!isLast) {
                    dictationResultStr += results.getResultString() + ",";
                } else {
                    dictationResultStr += results.getResultString() + "]";

                    finalResult = DictationJsonParseUtil
                            .parseJsonData(dictationResultStr);

                    listener.onDictationListener(finalResult);
                }

            }

            @Override
            public void onError(SpeechError error) {
                error.getPlainDescription(true);
            }
        });

        // 开始听写
        iatDialog.show();
    }

    private static void initConfig(Context context) {
        dictationResultStr = "[";
        finalResult = "";

        // 语音配置对象初始化
        SpeechUtility.createUtility(context, SpeechConstant.APPID + "="
                + DICTATION_APPID);

        // 1.创建SpeechRecognizer对象,第2个参数:本地听写时传InitListener
        mIat = SpeechRecognizer.createRecognizer(context, null);
        // 交互动画
        iatDialog = new RecognizerDialog(context, null);

        // 2.设置听写参数,详见《科大讯飞MSC API手册(Android)》SpeechConstant类
        mIat.setParameter(SpeechConstant.DOMAIN, "iat"); // domain:域名
        mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
        mIat.setParameter(SpeechConstant.ACCENT, "mandarin"); // mandarin:普通话
    }
}

(六)最终调用

最后就是在需要语音听写的地方进行调用了,调用起来很简单,只需调用DictationUtil类的静态方法showDictationDialog,第一个参数传入Context,第二个参数创建DictationListener匿名内部类,重写其onDictationListener方法,在该方法中对语音听写最终结果进行处理即可(比如为EditText设置文本等)。

import com.easydo.util.DictationListener;
import com.easydo.util.DictationUtil;
import com.jiayongji.easydo.R;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.ImageButton;

public class CreateScheduleActivity extends BaseActivity implements
        OnClickListener {

    // 日程内容et
    private EditText createScheduleContentEt;
    // 日程内容语音听写ib
    private ImageButton createScheduleContentDictationIb;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_create_schedule);

        createScheduleContentEt = (EditText) findViewById(R.id.create_schedule_content_et);
        createScheduleContentDictationIb = (ImageButton) findViewById(R.id.create_schedule_content_dictation_ib);

        createScheduleContentDictationIb.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.create_schedule_content_dictation_ib:
            DictationUtil.showDictationDialog(this, new DictationListener() {

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

推荐阅读更多精彩内容

  • 最近,想用一下科大讯飞的语音,查看了使用文档,有点笨,还是没有学会,后来参考了其他人写的一些博客,终于搞清楚...
    ZDominic阅读 5,342评论 0 3
  • 自己一直对于男朋友的穿着感到不理解,于是乎抱着这样的想法,开始关注男生相关的东西。还是想自己的男朋友穿的更帅更好看...
    方方小公主阅读 1,873评论 2 1
  • 最简单的优化建议: 1.PC平台的话保持场景中显示的顶点数少于200K~3M,移动设备的话少于10W,一切取决于你...
    好怕怕阅读 5,887评论 1 12
  • 昨天发生一件闹心的事,集团公司下来一个检查组,对我所在的生产车间进行全面检查。按照规定,各生产队应当单独设置器材仓...
    一梦舒心阅读 479评论 0 0
  • jQuery - 获得内容和属性 jQuery 拥有可操作 HTML 元素和属性的强大方法。 jQuery DOM...
    hx永恒之恋阅读 327评论 0 6