Android调用科大讯飞语音转写 API以及解析踩坑之旅

news2024/10/1 3:30:48

需求

需要对本地音频文件,调用科大讯飞的api进行转文字,本来呢,以为很简单,结果坑不少。
语音转写 API 文档

坑1:解析

下载demo,代码也挺简单,放到idea中,替换一下key,直接运行,但是看到返回,傻眼了
在这里插入图片描述

{“code”:“000000”,“descInfo”:“success”,“content”:{“orderInfo”:{“orderId”:“111”,“failType”:11,“status”:-1,“originalDuration”:200,“realDuration”:20173,“expireTime”:1727867828000},“orderResult”:“{“lattice”:[{“json_1best”:”{\“st\”:{\“sc\”:\“0.00\”,\“pa\”:\“0\”,\“rt\”:[{\“ws\”:[{\“cw\”:[{\“w\”:\“观察\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:1,\“we\”:24},{\“cw\”:[{\“w\”:\“右侧\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:25,\“we\”:56},{\“cw\”:[{\“w\”:\“柱\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:57,\“we\”:68},{\“cw\”:[{\“w\”:\“体\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:69,\“we\”:80},{\“cw\”:[{\“w\”:\“,\”,\“wp\”:\“p\”,\“wc\”:\“0.0000\”}],\“wb\”:80,\“we\”:80},{\“cw\”:[{\“w\”:\“判断\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:81,\“we\”:116},{\“cw\”:[{\“w\”:\“Mike\”,\“wp\”:\“n\”,\“sm\”:\“replace_list\”,\“wc\”:\“0.0000\”}],\“wb\”:117,\“we\”:144},{\“cw\”:[{\“w\”:\“是否\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:145,\“we\”:168},{\“cw\”:[{\“w\”:\“正常\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:169,\“we\”:204},{\“cw\”:[{\“w\”:\“。\”,\“wp\”:\“p\”,\“wc\”:\“0.0000\”}],\“wb\”:204,\“we\”:204}]}],\“bg\”:\“730\”,\“rl\”:\“0\”,\“ed\”:\“2930\”}}“},{“json_1best”:”{\“st\”:{\“sc\”:\“0.00\”,\“pa\”:\“0\”,\“rt\”:[{\“ws\”:[{\“cw\”:[{\“w\”:\“123456789\”,\“og\”:\“一\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:1,\“we\”:88},{\“cw\”:[{\“w\”:\" 10\“,\“og\”:\” 十\“,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:89,\“we\”:100},{\“cw\”:[{\“w\”:\” 11\“,\“og\”:\” 十一\“,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:101,\“we\”:116},{\“cw\”:[{\“w\”:\“小时\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:117,\“we\”:136},{\“cw\”:[{\“w\”:\”,\“,\“wp\”:\“p\”,\“wc\”:\“0.0000\”}],\“wb\”:136,\“we\”:136},{\“cw\”:[{\“w\”:\“吧\”,\“wp\”:\“s\”,\“wc\”:\“0.0000\”}],\“wb\”:137,\“we\”:144},{\“cw\”:[{\“w\”:\“谢谢\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:145,\“we\”:264},{\“cw\”:[{\“w\”:\”。\“,\“wp\”:\“p\”,\“wc\”:\“0.0000\”}],\“wb\”:264,\“we\”:264}]}],\“bg\”:\“3660\”,\“rl\”:\“0\”,\“ed\”:\“6460\”}}”},{“json_1best”:“{\“st\”:{\“sc\”:\“0.00\”,\“pa\”:\“0\”,\“rt\”:[{\“ws\”:[{\“cw\”:[{\“w\”:\“嗯\”,\“wp\”:\“s\”,\“wc\”:\“0.0000\”}],\“wb\”:1,\“we\”:32},{\“cw\”:[{\“w\”:\“快\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:33,\“we\”:52},{\“cw\”:[{\“w\”:\”。\“,\“wp\”:\“p\”,\“wc\”:\“0.0000\”}],\“wb\”:52,\“we\”:52}]}],\“bg\”:\“7910\”,\“rl\”:\“0\”,\“ed\”:\“8630\”}}”},{“json_1best”:“{\“st\”:{\“sc\”:\“0.00\”,\“pa\”:\“0\”,\“rt\”:[{\“ws\”:[{\“cw\”:[{\“w\”:\“不要\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:1,\“we\”:16},{\“cw\”:[{\“w\”:\“发生\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:17,\“we\”:40},{\“cw\”:[{\“w\”:\“问题\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:41,\“we\”:64},{\“cw\”:[{\“w\”:\“了\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:65,\“we\”:76},{\“cw\”:[{\“w\”:\“就\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:77,\“we\”:84},{\“cw\”:[{\“w\”:\“行\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:85,\“we\”:108},{\“cw\”:[{\“w\”:\”。\“,\“wp\”:\“p\”,\“wc\”:\“0.0000\”}],\“wb\”:108,\“we\”:108}]}],\“bg\”:\“15140\”,\“rl\”:\“0\”,\“ed\”:\“16290\”}}”},{“json_1best”:“{\“st\”:{\“sc\”:\“0.00\”,\“pa\”:\“0\”,\“rt\”:[{\“ws\”:[{\“cw\”:[{\“w\”:\“问题\”,\“wp\”:\“n\”,\“wc\”:\“0.0000\”}],\“wb\”:1,\“we\”:36},{\“cw\”:[{\“w\”:\”。\“,\“wp\”:\“p\”,\“wc\”:\“0.0000\”}],\“wb\”:36,\“we\”:36},{\“cw\”:[{\“w\”:\”\“,\“wp\”:\“g\”,\“wc\”:\“0.0000\”}],\“wb\”:36,\“we\”:36}]}],\“bg\”:\“17560\”,\“rl\”:\“0\”,\“ed\”:\“17970\”}}”}],“lattice2”:[{“lid”:“0”,“end”:“2930”,“begin”:“730”,“json_1best”:{“st”:{“sc”:“0.00”,“pa”:“0”,“rt”:[{“nb”:“1”,“nc”:“1.0”,“ws”:[{“cw”:[{“w”:“观察”,“wp”:“n”,“wc”:“0.0000”}],“wb”:1,“we”:24},{“cw”:[{“w”:“右侧”,“wp”:“n”,“wc”:“0.0000”}],“wb”:25,“we”:56},{“cw”:[{“w”:“柱”,“wp”:“n”,“wc”:“0.0000”}],“wb”:57,“we”:68},{“cw”:[{“w”:“体”,“wp”:“n”,“wc”:“0.0000”}],“wb”:69,“we”:80},{“cw”:[{“w”:“,”,“wp”:“p”,“wc”:“0.0000”}],“wb”:80,“we”:80},{“cw”:[{“w”:“判断”,“wp”:“n”,“wc”:“0.0000”}],“wb”:81,“we”:116},{“cw”:[{“w”:“Mike”,“wp”:“n”,“sm”:“replace_list”,“wc”:“0.0000”}],“wb”:117,“we”:144},{“cw”:[{“w”:“是否”,“wp”:“n”,“wc”:“0.0000”}],“wb”:145,“we”:168},{“cw”:[{“w”:“正常”,“wp”:“n”,“wc”:“0.0000”}],“wb”:169,“we”:204},{“cw”:[{“w”:“。”,“wp”:“p”,“wc”:“0.0000”}],“wb”:204,“we”:204}]}],“pt”:“replace_list”,“bg”:“730”,“si”:“0”,“rl”:“0”,“ed”:“2930”}},“spk”:“段落-0”},{“lid”:“0”,“end”:“6460”,“begin”:“3660”,“json_1best”:{“st”:{“sc”:“0.00”,“pa”:“0”,“rt”:[{“nb”:“1”,“nc”:“1.0”,“ws”:[{“cw”:[{“w”:“123456789”,“og”:“一”,“wp”:“n”,“wc”:“0.0000”}],“wb”:1,“we”:88},{“cw”:[{“w”:" 10",“og”:" 十",“wp”:“n”,“wc”:“0.0000”}],“wb”:89,“we”:100},{“cw”:[{“w”:" 11",“og”:" 十一",“wp”:“n”,“wc”:“0.0000”}],“wb”:101,“we”:116},{“cw”:[{“w”:“小时”,“wp”:“n”,“wc”:“0.0000”}],“wb”:117,“we”:136},{“cw”:[{“w”:“吧”,“wp”:“n”,“wc”:“0.0000”}],“wb”:137,“we”:144},{“cw”:[{“w”:“,”,“wp”:“p”,“wc”:“0.0000”}],“wb”:144,“we”:144},{“cw”:[{“w”:“谢谢”,“wp”:“n”,“wc”:“0.0000”}],“wb”:145,“we”:264},{“cw”:[{“w”:“。”,“wp”:“p”,“wc”:“0.0000”}],“wb”:264,“we”:264}]}],“pt”:“reserved”,“bg”:“3660”,“si”:“1”,“rl”:“0”,“ed”:“6460”}},“spk”:“段落-0”},{“lid”:“0”,“end”:“8630”,“begin”:“7910”,“json_1best”:{“st”:{“sc”:“0.00”,“pa”:“0”,“rt”:[{“nb”:“1”,“nc”:“1.0”,“ws”:[{“cw”:[{“w”:“嗯”,“wp”:“n”,“wc”:“0.0000”}],“wb”:1,“we”:32},{“cw”:[{“w”:“快”,“wp”:“n”,“wc”:“0.0000”}],“wb”:33,“we”:52},{“cw”:[{“w”:“。”,“wp”:“p”,“wc”:“0.0000”}],“wb”:52,“we”:52}]}],“pt”:“reserved”,“bg”:“7910”,“si”:“2”,“rl”:“0”,“ed”:“8630”}},“spk”:“段落-0”},{“lid”:“0”,“end”:“16290”,“begin”:“15140”,“json_1best”:{“st”:{“sc”:“0.00”,“pa”:“0”,“rt”:[{“nb”:“1”,“nc”:“1.0”,“ws”:[{“cw”:[{“w”:“不要”,“wp”:“n”,“wc”:“0.0000”}],“wb”:1,“we”:16},{“cw”:[{“w”:“发生”,“wp”:“n”,“wc”:“0.0000”}],“wb”:17,“we”:40},{“cw”:[{“w”:“问题”,“wp”:“n”,“wc”:“0.0000”}],“wb”:41,“we”:64},{“cw”:[{“w”:“了”,“wp”:“n”,“wc”:“0.0000”}],“wb”:65,“we”:76},{“cw”:[{“w”:“就”,“wp”:“n”,“wc”:“0.0000”}],“wb”:77,“we”:84},{“cw”:[{“w”:“行”,“wp”:“n”,“wc”:“0.0000”}],“wb”:85,“we”:108},{“cw”:[{“w”:“。”,“wp”:“p”,“wc”:“0.0000”}],“wb”:108,“we”:108}]}],“pt”:“reserved”,“bg”:“15140”,“si”:“3”,“rl”:“0”,“ed”:“16290”}},“spk”:“段落-0”},{“lid”:“0”,“end”:“17970”,“begin”:“17560”,“json_1best”:{“st”:{“sc”:“0.00”,“pa”:“0”,“rt”:[{“nb”:“1”,“nc”:“1.0”,“ws”:[{“cw”:[{“w”:“问题”,“wp”:“n”,“wc”:“0.0000”}],“wb”:1,“we”:36},{“cw”:[{“w”:“。”,“wp”:“p”,“wc”:“0.0000”}],“wb”:36,“we”:36},{“cw”:[{“w”:“”,“wp”:“g”,“wc”:“0.0000”}],“wb”:36,“we”:36}]}],“pt”:“reserved”,“bg”:“17560”,“si”:“4”,“rl”:“0”,“ed”:“17970”}},“spk”:“段落-0”}]}",“taskEstimateTime”:0}}

不知道为什么要这样返回,在线找的一些json转JavaBean的工具都解析不出来,最后没办法,只能自己剥洋葱一样,一点一点解析,拿到需要的结果(我不知道有没有更好的办法,我只能用这个笨办法了)

public class Ifasrdemo {
    private static final String HOST = "https://raasr.xfyun.cn";
    private static String AUDIO_FILE_PATH;
    private static final String appid = "";
    private static final String keySecret = "";

    private static final Gson gson = new Gson();

    static {
        try {
            AUDIO_FILE_PATH = Ifasrdemo.class.getResource("/").toURI().getPath() + "/audio/pcmqile.pcm";
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) throws Exception {
        String result = upload();
        String jsonStr = StringEscapeUtils.unescapeJavaScript(result);
        String orderId = String.valueOf(JSONUtil.getByPath(JSONUtil.parse(jsonStr), "content.orderId"));
        getResult(orderId);
    }

    private static String upload() throws SignatureException, FileNotFoundException {
        HashMap<String, Object> map = new HashMap<>(16);
        File audio = new File(AUDIO_FILE_PATH);
        String fileName = audio.getName();
        long fileSize = audio.length();
        map.put("appId", appid);
        map.put("fileSize", fileSize);
        map.put("fileName", fileName);
        map.put("duration", "200");
        LfasrSignature lfasrSignature = new LfasrSignature(appid, keySecret);
        map.put("signa", lfasrSignature.getSigna());
        map.put("ts", lfasrSignature.getTs());

        String paramString = HttpUtil.parseMapToPathParam(map);
        System.out.println("upload paramString:" + paramString);

        String url = HOST + "/v2/api/upload" + "?" + paramString;
        System.out.println("upload_url:" + url);
        String response = HttpUtil.iflyrecUpload(url, new FileInputStream(audio));

        System.out.println("upload response:" + response);
        return response;
    }

    private static String getResult(String orderId) throws SignatureException, InterruptedException, IOException {
        HashMap<String, Object> map = new HashMap<>(16);
        map.put("orderId", orderId);
        LfasrSignature lfasrSignature = new LfasrSignature(appid, keySecret);
        map.put("signa", lfasrSignature.getSigna());
        map.put("ts", lfasrSignature.getTs());
        map.put("appId", appid);
        map.put("resultType", "transfer,predict");
        String paramString = HttpUtil.parseMapToPathParam(map);
        String url = HOST + "/v2/api/getResult" + "?" + paramString;
        System.out.println("\nget_result_url:" + url);
        StringBuilder resultText=new StringBuilder();
        while (true) {
            String response = HttpUtil.iflyrecGet(url);
            Root jsonParse = gson.fromJson(response, Root.class);
            if(jsonParse.content.orderResult!=null&&!jsonParse.content.orderResult.equals("")){
                JSONObject jsonObject = new JSONObject(jsonParse.content.orderResult);
                JSONArray jsonArray= (JSONArray) jsonObject.get("lattice");
                for (Object o : jsonArray) {
                    JSONObject json1best= new JSONObject(o);
                    STRoot stRoot = gson.fromJson((String) json1best.get("json_1best"), STRoot.class);
                    List<Rt> rt = stRoot.getSt().getRt();
                    for (Rt rt1 : rt) {
                        for (Ws w : rt1.getWs()) {
                            for (Cw cw : w.getCw()) {
                                resultText.append(cw.getW());
                            }
                        }
                    }
                }

                System.out.println("resultText:"+resultText.toString());

            }
            if (jsonParse.content.orderInfo.status == 4 || jsonParse.content.orderInfo.status == -1) {
                System.out.println("订单完成:" + response);
                write(response);
                return response;
            } else {
                System.out.println("进行中...,状态为:" + jsonParse.content.orderInfo.status);
                //建议使用回调的方式查询结果,查询接口有请求频率限制
                Thread.sleep(7000);
            }
        }
    }


    public static void write(String resp) throws IOException {
        //将写入转化为流的形式
        BufferedWriter bw = new BufferedWriter(new FileWriter("src\\main\\resources\\output\\test.txt"));
        String ss = resp;
        bw.write(ss);
        //关闭流
        bw.close();
        System.out.println("写入txt成功");
    }

//    class JsonParse {
//        Content content;
//    }
//
//    class Content {
//        OrderInfo orderInfo;
//    }
//
//    class OrderInfo {
//        Integer status;
//    }

    public class OrderInfo
    {
        private String orderId;

        private int failType;

        private int status;

        private int originalDuration;

        private int realDuration;

        private long expireTime;

        public void setOrderId(String orderId){
            this.orderId = orderId;
        }
        public String getOrderId(){
            return this.orderId;
        }
        public void setFailType(int failType){
            this.failType = failType;
        }
        public int getFailType(){
            return this.failType;
        }
        public void setStatus(int status){
            this.status = status;
        }
        public int getStatus(){
            return this.status;
        }
        public void setOriginalDuration(int originalDuration){
            this.originalDuration = originalDuration;
        }
        public int getOriginalDuration(){
            return this.originalDuration;
        }
        public void setRealDuration(int realDuration){
            this.realDuration = realDuration;
        }
        public int getRealDuration(){
            return this.realDuration;
        }
        public void setExpireTime(long expireTime){
            this.expireTime = expireTime;
        }
        public long getExpireTime(){
            return this.expireTime;
        }
    }


    public class Content
    {
        private OrderInfo orderInfo;

        private String orderResult;

        private int taskEstimateTime;

        public void setOrderInfo(OrderInfo orderInfo){
            this.orderInfo = orderInfo;
        }
        public OrderInfo getOrderInfo(){
            return this.orderInfo;
        }
        public void setOrderResult(String orderResult){
            this.orderResult = orderResult;
        }
        public String getOrderResult(){
            return this.orderResult;
        }
        public void setTaskEstimateTime(int taskEstimateTime){
            this.taskEstimateTime = taskEstimateTime;
        }
        public int getTaskEstimateTime(){
            return this.taskEstimateTime;
        }
    }


    public class Root
    {
        private String code;

        private String descInfo;

        private Content content;

        public void setCode(String code){
            this.code = code;
        }
        public String getCode(){
            return this.code;
        }
        public void setDescInfo(String descInfo){
            this.descInfo = descInfo;
        }
        public String getDescInfo(){
            return this.descInfo;
        }
        public void setContent(Content content){
            this.content = content;
        }
        public Content getContent(){
            return this.content;
        }
    }
    public class Cw
    {
        private String w;

        private String wp;

        private String wc;

        public void setW(String w){
            this.w = w;
        }
        public String getW(){
            return this.w;
        }
        public void setWp(String wp){
            this.wp = wp;
        }
        public String getWp(){
            return this.wp;
        }
        public void setWc(String wc){
            this.wc = wc;
        }
        public String getWc(){
            return this.wc;
        }
    }


    public class Ws
    {
        private List<Cw> cw;

        private int wb;

        private int we;

        public void setCw(List<Cw> cw){
            this.cw = cw;
        }
        public List<Cw> getCw(){
            return this.cw;
        }
        public void setWb(int wb){
            this.wb = wb;
        }
        public int getWb(){
            return this.wb;
        }
        public void setWe(int we){
            this.we = we;
        }
        public int getWe(){
            return this.we;
        }
    }


    public class Rt
    {
        private List<Ws> ws;

        public void setWs(List<Ws> ws){
            this.ws = ws;
        }
        public List<Ws> getWs(){
            return this.ws;
        }
    }


    public class St
    {
        private String sc;

        private String pa;

        private List<Rt> rt;

        private String bg;

        private String rl;

        private String ed;

        public void setSc(String sc){
            this.sc = sc;
        }
        public String getSc(){
            return this.sc;
        }
        public void setPa(String pa){
            this.pa = pa;
        }
        public String getPa(){
            return this.pa;
        }
        public void setRt(List<Rt> rt){
            this.rt = rt;
        }
        public List<Rt> getRt(){
            return this.rt;
        }
        public void setBg(String bg){
            this.bg = bg;
        }
        public String getBg(){
            return this.bg;
        }
        public void setRl(String rl){
            this.rl = rl;
        }
        public String getRl(){
            return this.rl;
        }
        public void setEd(String ed){
            this.ed = ed;
        }
        public String getEd(){
            return this.ed;
        }
    }


    public class STRoot
    {
        private St st;

        public void setSt(St st){
            this.st = st;
        }
        public St getSt(){
            return this.st;
        }
    }

}

这是后台代码,可以定一个方法作为回调,但是呢,又没有提供其他平台的sdk,所以android的怎么定义回调?总不能去后台绕一圈吧,所以只能走

Thread.sleep(7000);

这个就是很被动了。

坑2:android包冲突

本来觉得都是java代码,直接拿到android工程里就能用,在maven库里找到对应依赖的gradle版本,结果一运行,傻眼了,报错

java.lang.NoSuchFieldError: No static field INSTANCE of type Lorg/apache/http/conn/ssl/AllowAllHostnameVerifier; in class Lorg/apache/http/conn/ssl/AllowAllHostnameVerifier; or its superclasses (declaration of ‘org.apache.http.conn.ssl.AllowAllHostnameVerifier’ appears in /system/framework/framework.jar!classes3.dex)

原因:经查看是 android_sdk中有一个 AllowAllHostnameVerifier 类 没有INSTANCE字段

而httpclient-4.5.3.jar 中有 AllowAllHostnameVerifier 类 有INSTANCE字段,而代码运行优先使用sdk中的AllowAllHostnameVerifier类了,所以报No static field INSTANCE;

就这个问题,在网上找了好几篇博客,按照他们的方法,都没有解决,发现一篇有用的博客,就是引入了cz.msebera.android:httpclient这个库代替HttpClient,然后把对应的org.apache.http的引用换成cz.msebera.android.httpclient,完美解决
cz.msebera.android:httpclient 最新版本
No static field INSTANCE of type Lorg/apache/http/conn/ssl/AllowAllHostnameVerifier;

坑3:用okhttp调用上传接口失败

既然用apache的网络引用有问题,那就换成android上的okhttp吧,但是这个upload接口比较奇怪,我用postman测试,
在这里插入图片描述
上传pcm文件,只能通过body的binary方式,其他的我试过,都不行。
试了好几种okhttp上传文件的方法,都行不通

public class OkhttpUtil {
    private static final String HOST = "https://raasr.xfyun.cn";
    private String AUDIO_FILE_PATH;
    private static final String appid = "111";
    private static final String keySecret = "111";
    private static final Gson gson = new Gson();
    private Activity activity;
    private static final String UTF8 = "UTF-8";
    public OkhttpUtil(String AUDIO_FILE_PATH, Activity activity) {
        this.AUDIO_FILE_PATH = AUDIO_FILE_PATH;
        this.activity=activity;
    }

    public void upload() throws SignatureException {


        HashMap<String, Object> map = new HashMap<>(16);
        File audio = new File(AUDIO_FILE_PATH);
        String fileName = audio.getName();
        long fileSize = audio.length();
        map.put("appId", appid);
        map.put("fileSize", fileSize);
        map.put("fileName", fileName);
        map.put("duration", "200");
        LfasrSignature lfasrSignature = new LfasrSignature(appid, keySecret);
        map.put("signa", lfasrSignature.getSigna());
        map.put("ts", lfasrSignature.getTs());
        String paramString = parseMapToPathParam(map);

        byte[] fileContent = new byte[(int) audio.length()];

        try (FileInputStream fileInputStream = new FileInputStream(audio)) {
            // 读取文件内容到byte数组
            fileInputStream.read(fileContent);
        } catch (IOException e) {
            e.printStackTrace();
        }

        OkHttpClient client = new OkHttpClient();
        // 构造 MultipartBody 对象来包装要上传的文件数据
        MediaType mediaType = MediaType.parse("application/octet-stream");
        RequestBody requestBody = RequestBody.create(mediaType,fileContent);
        //RequestBody requestFile = RequestBody.create(mediaType, audio);


        // 构建请求
        Request request = new Request.Builder()
                .url("https://raasr.xfyun.cn/v2/api/upload?duration=200&signa=ycOd3kgAunVw%2FahO7V0ht9ChKc8%3D&fileName=C4%3A16%3AAB%3AEF%3A10%3AD8--20240929_110929.pcm&fileSize=53200&appId=f7355f0b&ts=1727669949")
                .addHeader("Content-Type","application/octet-stream")
                .post(requestBody)
                .build();

//
//                RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), audio);
//                RequestBody multipartBody = null;
//
//                    multipartBody = new MultipartBody.Builder()
//                            .setType(MultipartBody.FORM)
//                            .addFormDataPart("file", fileName,fileBody)//文件主体
//                            .build();
//
//
//                Request request = new Request.Builder()
//                        .post(multipartBody)
//
//                        .url("https://raasr.xfyun.cn/v2/api/upload"+"?"+paramString)
//                        .build();

                client.newCall(request).enqueue(new Callback() {
                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        if(response.isSuccessful()){//回调的方法执行在子线程。
                            String result = response.body().string();
                            Logger.show("result",result);
                            }else{

                            }

                    }
                    @Override
                    public void onFailure(Call call, IOException e) {

                        e.printStackTrace();
                    }
                });


    }

    public static String parseMapToPathParam(Map<String, Object> param) {
        StringBuilder sb = new StringBuilder();
        try {
            Set<Map.Entry<String, Object>> entryset = param.entrySet();
            boolean isFirst = true;
            for (Map.Entry<String, Object> entry : entryset) {
                if (!isFirst) {
                    sb.append("&");
                } else {
                    isFirst = false;
                }
                sb.append(URLEncoder.encode(entry.getKey(), UTF8));
                sb.append("=");
                sb.append(URLEncoder.encode(entry.getValue().toString(), UTF8));
            }
        } catch (UnsupportedEncodingException e) {
            Logger.show("HttpUtil","HttpUtil parseMapToPathParam Exception!");
        }

        return sb.toString();
    }
}

坑4:Android进程切换

Java代码不涉及到控件和网络的切换,但是Android会涉及到,直接用java代码会报错:

android.os.NetworkOnMainThreadException

所以只能切换到子线程,但是得到结果以后,又要给控件赋值,又得切回到主线程

public class XunfeiRequest {
    private static final String HOST = "https://raasr.xfyun.cn";
    private String AUDIO_FILE_PATH;
    private static final String appid = "f7355f0b";
    private static final String keySecret = "777e2077d71f092bca0c4b5eb74a9a62";
    private static final Gson gson = new Gson();

    public XunfeiRequest(String AUDIO_FILE_PATH) throws Exception {
        this.AUDIO_FILE_PATH = AUDIO_FILE_PATH;
        new Thread(new Runnable() {
            @Override
            public void run() {
                String result = null;
                try {
                    result = upload();
                    String jsonStr = StringEscapeUtils.unescapeJavaScript(result);
                    String orderId = String.valueOf(JSONUtil.getByPath(JSONUtil.parse(jsonStr), "content.orderId"));
                    getResult(orderId);
                } catch (SignatureException e) {
                    throw new RuntimeException(e);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }

    private String upload() throws SignatureException, FileNotFoundException {
        HashMap<String, Object> map = new HashMap<>(16);
        File audio = new File(AUDIO_FILE_PATH);
        String fileName = audio.getName();
        long fileSize = audio.length();
        map.put("appId", appid);
        map.put("fileSize", fileSize);
        map.put("fileName", fileName);
        map.put("duration", "200");
        LfasrSignature lfasrSignature = new LfasrSignature(appid, keySecret);
        map.put("signa", lfasrSignature.getSigna());
        map.put("ts", lfasrSignature.getTs());

        String paramString = HttpUtil.parseMapToPathParam(map);
        System.out.println("upload paramString:" + paramString);

        String url = HOST + "/v2/api/upload" + "?" + paramString;
        System.out.println("upload_url:" + url);
        String response = HttpUtil.iflyrecUpload(url, new FileInputStream(audio));

        System.out.println("upload response:" + response);
        return response;
    }

    private void getResult(String orderId) throws SignatureException, InterruptedException, IOException {
        HashMap<String, Object> map = new HashMap<>(16);
        map.put("orderId", orderId);
        LfasrSignature lfasrSignature = new LfasrSignature(appid, keySecret);
        map.put("signa", lfasrSignature.getSigna());
        map.put("ts", lfasrSignature.getTs());
        map.put("appId", appid);
        map.put("resultType", "transfer,predict");
        String paramString = HttpUtil.parseMapToPathParam(map);
        String url = HOST + "/v2/api/getResult" + "?" + paramString;
        System.out.println("\nget_result_url:" + url);
        StringBuilder resultText = new StringBuilder();
        while (true) {
            String response = HttpUtil.iflyrecGet(url);
            Root jsonParse = gson.fromJson(response, Root.class);

            if (jsonParse.content.orderInfo.status == 4 || jsonParse.content.orderInfo.status == -1) {
                System.out.println("订单完成:" + response);
                if (jsonParse.content.orderResult != null && !jsonParse.content.orderResult.equals("")) {
                    JSONObject jsonObject = new JSONObject(jsonParse.content.orderResult);
                    JSONArray jsonArray = (JSONArray) jsonObject.get("lattice");
                    for (Object o : jsonArray) {
                        JSONObject json1best = new JSONObject(o);
                        STRoot stRoot = gson.fromJson((String) json1best.get("json_1best"), STRoot.class);
                        List<Rt> rt = stRoot.getSt().getRt();
                        for (Rt rt1 : rt) {
                            for (Ws w : rt1.getWs()) {
                                for (Cw cw : w.getCw()) {
                                    resultText.append(cw.getW());
                                }
                            }
                        }
                    }

                    System.out.println("resultText:" + resultText.toString());

                }

                final Handler mainHandler = new Handler(Looper.getMainLooper());
                // 在子线程中更新数据并刷新RecyclerView
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // 这里进行数据的更新操作
                        // updateYourData();

                        // 使用Handler将刷新操作切换到UI线程
                        mainHandler.post(new Runnable() {
                            @Override
                            public void run() {

                                if (iXunfeiRequest != null) {
                                    iXunfeiRequest.getXunfeiResult(resultText.toString(),jsonParse.content.orderInfo.failType);
                                }
                            }
                        });
                    }
                }).start();
                break;

            } else {
                System.out.println("进行中...,状态为:" + jsonParse.content.orderInfo.status);
                //建议使用回调的方式查询结果,查询接口有请求频率限制
                Thread.sleep(5000);
            }
        }
    }

    IXunfeiRequest iXunfeiRequest;

    public void setiXunfeiRequest(IXunfeiRequest iXunfeiRequest) {
        this.iXunfeiRequest = iXunfeiRequest;
    }

   public interface IXunfeiRequest {
        void getXunfeiResult(String result,int failType);
    }


    public class OrderInfo {
        private String orderId;

        private int failType;

        private int status;

        private int originalDuration;

        private int realDuration;

        private long expireTime;

        public void setOrderId(String orderId) {
            this.orderId = orderId;
        }

        public String getOrderId() {
            return this.orderId;
        }

        public void setFailType(int failType) {
            this.failType = failType;
        }

        public int getFailType() {
            return this.failType;
        }

        public void setStatus(int status) {
            this.status = status;
        }

        public int getStatus() {
            return this.status;
        }

        public void setOriginalDuration(int originalDuration) {
            this.originalDuration = originalDuration;
        }

        public int getOriginalDuration() {
            return this.originalDuration;
        }

        public void setRealDuration(int realDuration) {
            this.realDuration = realDuration;
        }

        public int getRealDuration() {
            return this.realDuration;
        }

        public void setExpireTime(long expireTime) {
            this.expireTime = expireTime;
        }

        public long getExpireTime() {
            return this.expireTime;
        }
    }


    public class Content {
        private OrderInfo orderInfo;

        private String orderResult;

        private int taskEstimateTime;

        public void setOrderInfo(OrderInfo orderInfo) {
            this.orderInfo = orderInfo;
        }

        public OrderInfo getOrderInfo() {
            return this.orderInfo;
        }

        public void setOrderResult(String orderResult) {
            this.orderResult = orderResult;
        }

        public String getOrderResult() {
            return this.orderResult;
        }

        public void setTaskEstimateTime(int taskEstimateTime) {
            this.taskEstimateTime = taskEstimateTime;
        }

        public int getTaskEstimateTime() {
            return this.taskEstimateTime;
        }
    }


    public class Root {
        private String code;

        private String descInfo;

        private Content content;

        public void setCode(String code) {
            this.code = code;
        }

        public String getCode() {
            return this.code;
        }

        public void setDescInfo(String descInfo) {
            this.descInfo = descInfo;
        }

        public String getDescInfo() {
            return this.descInfo;
        }

        public void setContent(Content content) {
            this.content = content;
        }

        public Content getContent() {
            return this.content;
        }
    }

    public class Cw {
        private String w;

        private String wp;

        private String wc;

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

        public String getW() {
            return this.w;
        }

        public void setWp(String wp) {
            this.wp = wp;
        }

        public String getWp() {
            return this.wp;
        }

        public void setWc(String wc) {
            this.wc = wc;
        }

        public String getWc() {
            return this.wc;
        }
    }


    public class Ws {
        private List<Cw> cw;

        private int wb;

        private int we;

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

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

        public void setWb(int wb) {
            this.wb = wb;
        }

        public int getWb() {
            return this.wb;
        }

        public void setWe(int we) {
            this.we = we;
        }

        public int getWe() {
            return this.we;
        }
    }


    public class Rt {
        private List<Ws> ws;

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

        public List<Ws> getWs() {
            return this.ws;
        }
    }


    public class St {
        private String sc;

        private String pa;

        private List<Rt> rt;

        private String bg;

        private String rl;

        private String ed;

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

        public String getSc() {
            return this.sc;
        }

        public void setPa(String pa) {
            this.pa = pa;
        }

        public String getPa() {
            return this.pa;
        }

        public void setRt(List<Rt> rt) {
            this.rt = rt;
        }

        public List<Rt> getRt() {
            return this.rt;
        }

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

        public String getBg() {
            return this.bg;
        }

        public void setRl(String rl) {
            this.rl = rl;
        }

        public String getRl() {
            return this.rl;
        }

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

        public String getEd() {
            return this.ed;
        }
    }


    public class STRoot {
        private St st;

        public void setSt(St st) {
            this.st = st;
        }

        public St getSt() {
            return this.st;
        }
    }

}

用到回调的地方

 adapter.setiChangeWord(filePath -> {
            setMessage(getString(R.string.voice_transcription_in_progress));

            XunfeiRequest  xunfeiRequest= null;
            try {
                String filePathUpload=FileUtil.getSDPath(App.getInstance(),filePath);
                xunfeiRequest = new XunfeiRequest(filePathUpload);
                xunfeiRequest.setiXunfeiRequest(new XunfeiRequest.IXunfeiRequest() {
                    @Override
                    public void getXunfeiResult(String result,int failType) {
                        dismissProgressDialog();
                        String msg="失败";
                        String tip=getRsString(R.string.error_tip);
                        if(!"".equals(result)){
                            msg=result;
                            tip=getRsString(R.string.hint);
                        }else if(failType==1){
                            msg="音频上传失败";
                        }else if(failType==2){
                            msg="音频转码失败";
                        }else if(failType==3){
                            msg="音频识别失败";
                        }else if(failType==4){
                            msg="音频时长超限";
                        }else if(failType==5){
                            msg="音频校验失败";
                        }else if(failType==6){
                            msg="失败,是静音文件";
                        }else if(failType==7){
                            msg="翻译失败";
                        }

                        new XPopup.Builder(VoiceRecordingActivity.this).asConfirm(tip, msg,
                                () -> {

                                }).show();

                    }
                });
            } catch (Exception e) {
               e.printStackTrace();
                dismissProgressDialog();
                ToastUtils.show("转码失败");
            }

        });

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2181377.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Springboot+Vue的网上书店(含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 在这个…

用户体验测试——21条UX设计原则

根据心理学的研究和发展,对应理论不断被完善到用户体验设计领域,以下21条UX设计原则即为心理学在用户体验设计中的应用,在设计和测试中应用这些原则,可以提高用户体验质量特性,如下所示: 21条UX设计原则 有效性 特斯勒定律(最小复杂度定律) 这个定律也被称为“复杂性…

数据转换新利器,开启企业高效之路

Maria是 S 公司的采购专员&#xff0c;最近需要进行公司设备的采购&#xff0c;这可让她犯了难&#xff1a;公司多个部门提交采购申请&#xff0c;但每个部门都有着不同的需求。一时间她就收到了大量的申请单&#xff0c;却难以高效整合处理&#xff08;比如哪些申请可以合并采…

环境变量

见一见环境变量&#xff1a; 查看环境变量的命令 命令行&#xff1a; echo &PATH echo &HOME env ps ajx ps aux ps -f -o pid code.c中获取环境变量 main&#xff08;char* env[]&#xff09; char* getenv&#xff08;env_name&#xff09; 本地变量 shell…

App模拟下载场景的demo

摘要 目的&#xff1a;提供一个稳定的下载场景&#xff0c;可以手动触发和定时触发下载&#xff0c;每次下载相同大小文件&#xff0c;研究下载场景的功耗影响 原理&#xff1a;把电脑当做服务器&#xff0c;手机测试App固定下载电脑存放的某个XXXMB的大文件&#xff0c;基于…

如何使用ssm实现校园体育赛事管理系统的设计与实现+vue

TOC ssm713校园体育赛事管理系统的设计与实现vue 绪论 课题背景 身处网络时代&#xff0c;随着网络系统体系发展的不断成熟和完善&#xff0c;人们的生活也随之发生了很大的变化。目前&#xff0c;人们在追求较高物质生活的同时&#xff0c;也在想着如何使自身的精神内涵得…

PE节表中是否存在misc.VirtualSize 比SizeofRawData还要大的情况

确实是存在的,这是win10自带记事本,可以看到 确实是大.所以在申请imagebuffer的时候,还是需要比较大小.但是在还原的时候.只考虑sizeofRawData即可>

【Android 14源码分析】WMS-窗口显示-第一步:addWindow

忽然有一天&#xff0c;我想要做一件事&#xff1a;去代码中去验证那些曾经被“灌输”的理论。                                                                                  – 服装…

项目管理专业资质认证ICB 3中关于项目经理素质的标准

项目管理专业资质认证ICB 3中关于项目经理素质的标准&#xff0c;的确很全面&#xff0c;下面摘录之&#xff1a;

三款专业的英文文献翻译工具,翻译论文不在话下

阅读英文论文文献时免不了要借用一些翻译软件来帮助理解&#xff0c;但因为论文文献的特殊性&#xff0c;普通的翻译软件不能很好的翻译一些专业名词和术语&#xff0c;所以这里给大家分享三款可以胜任文献翻译的专业翻译工具&#xff0c;可以快速准确的完成英文文献翻译工作。…

CDGA|2024年数据治理的六个关键建议

随着数字经济的快速发展&#xff0c;数据已成为企业运营和决策的核心资产。在2024年&#xff0c;做好数据治理对于提升企业的竞争力和运营效率至关重要。以下是六个关键建议&#xff0c;帮助企业有效应对数据治理的挑战。 1. 制定明确的数据治理策略 首先&#xff0c;企业需要…

遥感影像-实例分割数据集:iSAID 从切图到YOLO格式数据集制作详细介绍

背景介绍 开源数据集isaid标注包含实例分割&#xff0c;但是原始影像太大&#xff0c;很吃显存&#xff0c;一般显卡无法用原始影像直接训练&#xff0c;所以需要对影像进行裁剪&#xff0c;并生成对应的标签&#xff0c;因为想用yolo系列跑模型&#xff0c;所以将标签需要转为…

【设计模式-模板】

定义 模板方法模式是一种行为设计模式&#xff0c;它在一个方法中定义了一个算法的骨架&#xff0c;并将一些步骤延迟到子类中实现。通过这种方式&#xff0c;模板方法允许子类在不改变算法结构的情况下重新定义算法中的某些特定步骤。 UML图 组成角色 AbstractClass&#x…

Java 为什么使用 UTF-16 而不是更节省内存的 UTF-8?

Java 选择 UTF-16 编码而不是更节省内存的 UTF-8 这一决定&#xff0c;涉及多个层面的设计权衡&#xff0c;包括历史原因、虚拟机&#xff08;JVM&#xff09;实现的复杂度、性能和字符处理的一致性。要理解这个问题&#xff0c;我们需要从 Java 语言的设计初衷、JVM 的工作机制…

C++:笔试题

1.什么是虚函数&#xff1f;什么是纯虚函数&#xff1f; 虚函数是类中的一个成员函数&#xff0c;使用关键字virtual在函数名前声明。 虚函数主要目的是允许子类重写父类中的同名函数&#xff0c;从而实现多态性&#xff0c;并且子函数重写的是虚函数表中的函数。 当通过父类的…

七、添加攻击音效

一、添加动画事件 1、在动画事件中添加音效 2、添加音频组件 3、代码 public void PlayAttackSound() {AudioSource1.PlayOneShot(AudioClip1, SoundValue);//PlayOneShot播放一个音频剪辑&#xff08;AudioClip&#xff09;一次 }

Oracle 日志文件多路复用

多路复用 PRODCDB 数据库的所有日志组中的 redo log 文件&#xff0c;存放目录&#xff1a; /u01/app/oracle/oradata/MREDO 1.创建目录 mkdir -p /u01/app/oracle/oradata/MREDO 2.查看日志文件路径 select group#,member from v$logfile; 3.增加日志组文件 alter database a…

ElementUI el-tree 树组件 增加辅助线

需求 项目需求给elementUI的el-tree添加辅助线&#xff0c;并且不能使用其他插件&#xff0c;没办法只能该样式了。 效果 代码 html <template><div><el-scrollbar class"long-content"><el-tree node-key"id":data"deptTre…

《程序猿之Redis缓存实战 · 有序集合类型》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

OpenGL ES 索引缓冲区(4)

OpenGL ES 索引缓冲区(4) 简述 本节会介绍索引缓冲区&#xff0c;索引缓冲区和顶点缓冲区类似&#xff0c;也是显存上的一段内存&#xff0c;只不过上面的数据用处不同&#xff0c;索引缓冲区故名思义里面的数据是用于索引&#xff0c;主要作用是用于复用顶点缓冲区里的数据。…