当前位置: 首页 > news >正文

Android开发之科大讯飞语音合成与播报

Android开发之科大讯飞语音合成与播报

  • 一、效果图
  • 二、打开[讯飞开放平台](https://www.xfyun.cn/),注册登录后,找到我的应用,点击创建新应用。
  • 三、填写相关信息,然后点击提交。
  • 四、点击创建好的应用。
  • 五、可以看到APPID信息。
  • 六、往下滑可以下载SDK。
  • 七、下载到本地后,解压。
  • 八、把刚才解压后的libs中的文件复制到项目中的libs下。
    • **1.此时这个Msc.jar还是不可用的,下面鼠标右键点击它。**
    • 2.点击Add As Library…
    • 3.点击OK,此时你可以看到它可以展开了,就说明你的app模块中已经添加了这个jar的依赖了。
  • 九、复制assets文件夹到项目中。
  • 十、粘贴到main下面,如下图所示。
  • 十一、配置项目,打开build.gradle。
  • 十二、AndroidManifest.xml中配置权限。
  • 十三、新建一个MyApplication类。
    • 1.然后在AndroidManifest.xml中配置
  • 十四、主布局文件(activity_yy)
  • 十五、主java文件(YybbActivity)

今天做一个稍微复杂点的,Android集成科大讯飞实现语音合成与播报功能。
老规矩,直接进入正题。

一、效果图

在这里插入图片描述

二、打开讯飞开放平台,注册登录后,找到我的应用,点击创建新应用。

在这里插入图片描述

三、填写相关信息,然后点击提交。

在这里插入图片描述

四、点击创建好的应用。

在这里插入图片描述

五、可以看到APPID信息。

在这里插入图片描述

六、往下滑可以下载SDK。

在这里插入图片描述

七、下载到本地后,解压。

在这里插入图片描述
这里重点就是这个libs文件夹中的内容了。

八、把刚才解压后的libs中的文件复制到项目中的libs下。

在这里插入图片描述

1.此时这个Msc.jar还是不可用的,下面鼠标右键点击它。

在这里插入图片描述

2.点击Add As Library…

在这里插入图片描述

3.点击OK,此时你可以看到它可以展开了,就说明你的app模块中已经添加了这个jar的依赖了。

在这里插入图片描述

九、复制assets文件夹到项目中。

在这里插入图片描述

十、粘贴到main下面,如下图所示。

在这里插入图片描述

十一、配置项目,打开build.gradle。

	//资源设置
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }

十二、AndroidManifest.xml中配置权限。

	<!--连接网络权限,用于执行云端语音能力 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <!--获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <!--读取网络信息状态 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!--获取当前wifi状态 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <!--允许程序改变网络连接状态 -->
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
    <!--读取手机信息权限 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <!--外存储写权限,构建语法需要用到此权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!--外存储读权限,构建语法需要用到此权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

十三、新建一个MyApplication类。

在这里插入图片描述

package com.example;

import android.app.Application;

import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechUtility;
import org.xutils.x;
public class MyApplication extends Application {

    @Override
    public void onCreate() {
        SpeechUtility.createUtility(MyApplication.this, SpeechConstant.APPID +"=\n" +
                "7b6e5ba7");
        super.onCreate();
        x.Ext.init(this);
        x.Ext.setDebug(BuildConfig.DEBUG);  //这个会影响性能
    }
}


1.然后在AndroidManifest.xml中配置

在这里插入图片描述

十四、主布局文件(activity_yy)

在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="750dp"
    android:orientation="vertical">
    <EditText
        android:id="@+id/et_text"
        android:gravity="top|left"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/Theme.TYLDApp"/>

    <Button
        android:id="@+id/btn_play"
        android:text="开始播放"
        android:layout_width="200dp"
        android:textSize="20dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:background="@drawable/register_selector"
        android:layout_marginLeft="100dp"
        android:textColor="@color/white"/>
    <Button
        android:id="@+id/btn_cancel"
        android:text="取消"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:textColor="@color/white"
        android:textSize="20dp"
        android:background="@drawable/register_selector"
        android:layout_marginLeft="100dp"/>

    <Button
        android:id="@+id/btn_pause"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:textColor="@color/white"
        android:textSize="20dp"
        android:background="@drawable/register_selector"
        android:layout_marginLeft="100dp"
        android:layout_marginTop="10dp"
        android:text="暂停播放" />

    <Button
        android:id="@+id/btn_resume"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:textColor="@color/white"
        android:textSize="20dp"
        android:background="@drawable/register_selector"
        android:layout_marginLeft="100dp"
        android:layout_marginTop="10dp"
        android:text="继续播放" />

</LinearLayout>

十五、主java文件(YybbActivity)

package com.example.activity;


import static com.iflytek.cloud.VerifierResult.TAG;

import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.MemoryFile;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.BackgroundColorSpan;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import com.example.R;
import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechEvent;
import com.iflytek.cloud.SpeechSynthesizer;
import com.iflytek.cloud.SynthesizerListener;
import com.iflytek.cloud.msc.util.FileUtil;
import com.iflytek.cloud.msc.util.log.DebugLog;

import java.util.Vector;

public class YybbActivity extends AppCompatActivity implements View.OnClickListener {
    //输入框
    private EditText etText;
    // 语音合成对象
    private SpeechSynthesizer mTts;
    // 默认发音人
    private String voicer = "xiaoyan";

    // 引擎类型
    private String mEngineType = SpeechConstant.TYPE_CLOUD;
    //播放的文字
    String text = " 属性:昔刘涓子晋末于丹阳郊外照射,忽见一物,高二丈许,射而中之,如雷电声若风雨。其夜不敢前追,诘旦率门徒子弟数人,寻纵至山下,见一小儿提罐,问何往为。";
    private Vector<byte[]> container = new Vector<>();
    //内存文件
    MemoryFile memoryFile;
    //总大小
    public volatile long mTotalSize = 0;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_yy);
        initView();
        // 初始化合成对象
        mTts = SpeechSynthesizer.createSynthesizer(this, mTtsInitListener);
    }
    /**
     * 初始化页面
     */
    private void initView() {
        etText = findViewById(R.id.et_text);
        findViewById(R.id.btn_play).setOnClickListener(this);
        findViewById(R.id.btn_cancel).setOnClickListener(this);
        findViewById(R.id.btn_pause).setOnClickListener(this);
        findViewById(R.id.btn_resume).setOnClickListener(this);
      
    }

    /**
     * 请求权限
     */
    private void requestPermissions() {
        try {
            //Android6.0及以上版本
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                int permission = ActivityCompat.checkSelfPermission(this,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE);
                if (permission != PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(this, new String[]
                            {Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                    Manifest.permission.WRITE_SETTINGS,
                                    Manifest.permission.READ_EXTERNAL_STORAGE}, 0x0010);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 权限请求返回结果
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
    /**
     * 页面点击事件
     * @param v 控件
     */
    @Override
    public void onClick(View v) {
        if (mTts == null) {
            this.showTip("创建对象失败,请确认 libmsc.so 放置正确,且有调用 createUtility 进行初始化");
            return;
        }
        switch (v.getId()) {
            case R.id.btn_play://开始合成
                //输入文本
                String etStr = etText.getText().toString().trim();
                if (!etStr.isEmpty()) {
                    text = etStr;
                }
                //设置参数
                setParam();
                //开始合成播放
                int code = mTts.startSpeaking(text, mTtsListener);
                if (code != ErrorCode.SUCCESS) {
                    showTip("语音合成失败,错误码: " + code);
                }
                break;

            case R.id.btn_cancel://取消合成
                mTts.stopSpeaking();
                break;
            case R.id.btn_pause://暂停播放
                mTts.pauseSpeaking();
                break;
            case R.id.btn_resume://继续播放
                mTts.resumeSpeaking();
                break;

            default:
                break;
        }
    }
    /**
     * 初始化监听。
     */
    private InitListener mTtsInitListener = new InitListener() {
        @Override
        public void onInit(int code) {
            Log.i(TAG, "InitListener init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
                showTip("初始化失败,错误码:" + code);
            } else {
                showTip("初始化成功");
            }
        }
    };
    /**
     * Toast提示
     * @param msg
     */
    private void showTip(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
    /**
     * 参数设置
     *
     * @return
     */
    private void setParam() {
        // 清空参数
        mTts.setParameter(SpeechConstant.PARAMS, null);
        // 根据合成引擎设置相应参数
        if (mEngineType.equals(SpeechConstant.TYPE_CLOUD)) {
            mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
            //支持实时音频返回,仅在synthesizeToUri条件下支持
            mTts.setParameter(SpeechConstant.TTS_DATA_NOTIFY, "1");
            // 设置在线合成发音人
            mTts.setParameter(SpeechConstant.VOICE_NAME, voicer);
        } else {
            mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
            mTts.setParameter(SpeechConstant.VOICE_NAME, "");
        }
        // 设置播放合成音频打断音乐播放,默认为true
        mTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "false");
        // 设置音频保存路径,保存音频格式支持pcm、wav
        mTts.setParameter(SpeechConstant.AUDIO_FORMAT, "pcm");
        mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, getExternalFilesDir(null) + "/msc/tts.pcm");
    }
    /**
     * 合成回调监听。
     */
    private SynthesizerListener mTtsListener = new SynthesizerListener() {
        //开始播放
        @Override
        public void onSpeakBegin() {
            Log.i(TAG, "开始播放");
        }

        //暂停播放
        @Override
        public void onSpeakPaused() {
            Log.i(TAG, "暂停播放");
        }

        //继续播放
        @Override
        public void onSpeakResumed() {
            Log.i(TAG, "继续播放");
        }

        //合成进度
        @Override
        public void onBufferProgress(int percent, int beginPos, int endPos, String info) {
            Log.i(TAG, "合成进度:" + percent + "%");
        }

        //播放进度
        @Override
        public void onSpeakProgress(int percent, int beginPos, int endPos) {
            // 播放进度
            Log.i(TAG, "播放进度:" + percent + "%");
            SpannableStringBuilder style = new SpannableStringBuilder(text);
            style.setSpan(new BackgroundColorSpan(Color.RED), beginPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            etText.setText(style);
        }

        //播放完成
        @Override
        public void onCompleted(SpeechError error) {
            if (error == null) {
                Log.i(TAG, "播放完成," + container.size());
                DebugLog.LogD("播放完成," + container.size());
                for (int i = 0; i < container.size(); i++) {
                    //写入文件
                    writeToFile(container.get(i));
                }
                //保存文件
                FileUtil.saveFile(memoryFile, mTotalSize, getExternalFilesDir(null) + "/1.pcm");
            } else {
                //异常信息
                showTip(error.getPlainDescription(true));
            }
        }
        /**
         * 写入文件
         */
        private void writeToFile(byte[] data) {
            if (data == null || data.length == 0) {
                return;
            }
            try {
                if (memoryFile == null) {
                    Log.i(TAG, "memoryFile is null");
                    String mFilepath = getExternalFilesDir(null) + "/1.pcm";
                    memoryFile = new MemoryFile(mFilepath, 1920000);
                    memoryFile.allowPurging(false);
                }
                memoryFile.writeBytes(data, 0, (int) mTotalSize, data.length);
                mTotalSize += data.length;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //事件
        @Override
        public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
            //	 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
            //	 若使用本地能力,会话id为null
            if (SpeechEvent.EVENT_SESSION_ID == eventType) {
                String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
                Log.i(TAG, "session id =" + sid);
            }

            //当设置SpeechConstant.TTS_DATA_NOTIFY为1时,抛出buf数据
            if (SpeechEvent.EVENT_TTS_BUFFER == eventType) {
                byte[] buf = obj.getByteArray(SpeechEvent.KEY_EVENT_TTS_BUFFER);
                Log.i(TAG, "bufis =" + buf.length);
                container.add(buf);
            }
        }

    };

}

到这里就结束了,你可以试试哦

相关文章:

  • 【问题思考总结】截得两部分质量相等的点是否就是质心?
  • Word处理控件Aspose.Words功能演示:使用C#对PDF文件进行进一步修改和转换
  • 手把手教你做多重线性逐步回归
  • 猿创征文|前端进阶必备——WebSockt实现聊天室(附源码)
  • 计算机大一新生,想卷却找不到方向,恳请前辈指指路?
  • Spring-02 IOC与DI
  • EasyExcel知识【Java程序进行读写生成Excel操作】
  • HALCON边缘检测
  • ATC‘22顶会论文RunD:高密高并发的轻量级 Serverless 安全容器运行时 | 龙蜥技术
  • 一篇经典的 Redis 面试资料「处女座笔记」「吐血推荐」...
  • 李沐d2l(十一)--目标检测
  • 美国上周初请人数23.2万人是两个月最低水平 美联储加息75基点稳了
  • 【技术美术知识储备】图形渲染管线1.0-基本概念CPU负责的应用阶段
  • 教你如何使用关键词获取淘宝和天猫的商品信息
  • lodash笔记(语言篇)
  • Android 架构优化~MVP 架构改造
  • Angular 响应式表单 基础例子
  • angular2 简述
  • ES6语法详解(一)
  • Git 使用集
  • JavaScript 奇技淫巧
  • java多线程
  • Median of Two Sorted Arrays
  • mysql innodb 索引使用指南
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • Wamp集成环境 添加PHP的新版本
  • windows下mongoDB的环境配置
  • 技术胖1-4季视频复习— (看视频笔记)
  • 将回调地狱按在地上摩擦的Promise
  • 前端存储 - localStorage
  • 使用putty远程连接linux
  • 通过git安装npm私有模块
  • 我的zsh配置, 2019最新方案
  • 学习笔记TF060:图像语音结合,看图说话
  • 一份游戏开发学习路线
  • !!Dom4j 学习笔记
  • #pragam once 和 #ifndef 预编译头
  • #vue3 实现前端下载excel文件模板功能
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • (2)nginx 安装、启停
  • (20050108)又读《平凡的世界》
  • (8)STL算法之替换
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (二)springcloud实战之config配置中心
  • (九)One-Wire总线-DS18B20
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (强烈推荐)移动端音视频从零到上手(下)
  • (新)网络工程师考点串讲与真题详解
  • (转)LINQ之路
  • (转载)OpenStack Hacker养成指南
  • .net Stream篇(六)
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • .NET设计模式(8):适配器模式(Adapter Pattern)