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


今天做一个稍微复杂点的,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">
    <include layout="@layout/main_title_bar"/>
    <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
        android:gravity="center_vertical"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal"
        android:paddingStart="6dp"
        android:paddingEnd="6dp">

        <View
            android:layout_width="0dp"
            android:layout_height="0.5dp"
            android:layout_weight="1"
            android:background="#000" />

        <TextView
            android:layout_marginStart="6dp"
            android:layout_marginEnd="6dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="设置"
            android:textColor="#000"
            android:textSize="16sp" />

        <View
            android:layout_width="0dp"
            android:layout_height="0.5dp"
            android:layout_weight="1"
            android:background="#000" />
    </LinearLayout>

    <!--设置发音人-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:paddingStart="6dp"
        android:paddingEnd="6dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="设置发音人:"
            android:textColor="#000"
            android:textSize="16sp" />

        <Spinner
            android:id="@+id/spinner"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1" />
    </LinearLayout>
    <!--设置语速-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:gravity="center_vertical"
        android:paddingStart="6dp"
        android:paddingEnd="6dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="设置语速:"
            android:textColor="#000"
            android:textSize="16sp" />

        <SeekBar
            android:id="@+id/sb_speed"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:max="100"
            android:progress="50" />
    </LinearLayout>

    <!--设置音调-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:gravity="center_vertical"
        android:paddingStart="6dp"
        android:paddingEnd="6dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="设置音调:"
            android:textColor="#000"
            android:textSize="16sp" />

        <SeekBar
            android:id="@+id/sb_pitch"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:max="100"
            android:progress="50" />
    </LinearLayout>

    <!--设置音量-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:gravity="center_vertical"
        android:paddingStart="6dp"
        android:paddingEnd="6dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="设置音量:"
            android:textColor="#000"
            android:textSize="16sp" />

        <SeekBar
            android:id="@+id/sb_volume"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:max="100"
            android:progress="50" />
    </LinearLayout>

</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,AdapterView.OnItemSelectedListener {
    //语速
    private String speedValue = "50";
    //音调
    private String pitchValue = "50";
    //音量
    private String volumeValue = "50";
    //输入框
    private EditText etText;
    // 语音合成对象
    private SpeechSynthesizer mTts;
    // 默认发音人
    private String voicer = "xiaoyan";
    //发音人名称
    private static final String[] arrayName = {"讯飞小燕", "讯飞许久", "讯飞小萍", "讯飞小婧", "讯飞许小宝"};

    //发音人值
    private static final String[] arrayValue = {"xiaoyan", "aisjiuxu", "aisxping", "aisjinger", "aisbabyxu"};

    //数组适配器
    private ArrayAdapter<String> arrayAdapter;
    // 引擎类型
    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);
         //请求权限
        requestPermissions();
        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);
       SeekBar sbSpeed = findViewById(R.id.sb_speed);
        SeekBar sbPitch = findViewById(R.id.sb_pitch);
        SeekBar sbVolume = findViewById(R.id.sb_volume);
        Spinner spinner = findViewById(R.id.spinner);
         //将可选内容与ArrayAdapter连接起来
        arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, arrayName);
        //设置下拉列表的风格
        arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        //将adapter 添加到spinner中
        spinner.setAdapter(arrayAdapter);
        //添加事件Spinner事件监听
        spinner.setOnItemSelectedListener(this);

        setSeekBar(sbSpeed, 1);
        setSeekBar(sbPitch, 2);
        setSeekBar(sbVolume, 3);
    }

    /**
     * 请求权限
     */
    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);
             //设置语速
            mTts.setParameter(SpeechConstant.VOICE_NAME, voicer);
            //设置音调
            mTts.setParameter(SpeechConstant.PITCH, pitchValue);
            //设置音量
            mTts.setParameter(SpeechConstant.VOLUME, volumeValue);
        } 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);
            }
        }

    };
/**
     * 选中
     */
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        voicer = arrayValue[position];
    }
    @Override
    public void onNothingSelected(AdapterView<?> parent) {
    }
    //设置SeekBar
    private void setSeekBar(SeekBar seekBar, final int type) {

        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                switch (type) {
                    case 1://设置语速 范围 1~100
                        speedValue = Integer.toString(progress);
                        break;
                    case 2://设置音调  范围 1~100
                        pitchValue = Integer.toString(progress);
                        break;
                    case 3://设置音量  范围 1~100
                        volumeValue = Integer.toString(progress);
                        break;
                    default:
                        break;
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) { }
            @Override
            public void onStopTrackingTouch(SeekBar seekBar) { }
        });
    }

}

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

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不服输的小乌龟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值