Android开发之科大讯飞语音合成与播报
一、效果图二、打开[讯飞开放平台](/),注册登录后,找到我的应用,点击创建新应用。三、填写相关信息,然后点击提交。四、点击创建好的应用。五、可以看到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 {@Overridepublic 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="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="750dp"android:orientation="vertical"><include layout="@layout/main_title_bar"/><EditTextandroid:id="@+id/et_text"android:gravity="top|left"android:layout_width="match_parent"android:layout_height="wrap_content"android:theme="@style/Theme.TYLDApp"/><Buttonandroid: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"/><Buttonandroid: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"/><Buttonandroid: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="暂停播放" /><Buttonandroid: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="继续播放" /><LinearLayoutandroid:gravity="center_vertical"android:layout_width="match_parent"android:layout_height="50dp"android:orientation="horizontal"android:paddingStart="6dp"android:paddingEnd="6dp"><Viewandroid:layout_width="0dp"android:layout_height="0.5dp"android:layout_weight="1"android:background="#000" /><TextViewandroid: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" /><Viewandroid:layout_width="0dp"android:layout_height="0.5dp"android:layout_weight="1"android:background="#000" /></LinearLayout><!--设置发音人--><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center_vertical"android:orientation="horizontal"android:paddingStart="6dp"android:paddingEnd="6dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="设置发音人:"android:textColor="#000"android:textSize="16sp" /><Spinnerandroid:id="@+id/spinner"android:layout_width="0dp"android:layout_height="50dp"android:layout_weight="1" /></LinearLayout><!--设置语速--><LinearLayoutandroid:layout_width="match_parent"android:layout_height="40dp"android:gravity="center_vertical"android:paddingStart="6dp"android:paddingEnd="6dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="设置语速:"android:textColor="#000"android:textSize="16sp" /><SeekBarandroid:id="@+id/sb_speed"android:layout_width="match_parent"android:layout_height="wrap_content"android:max="100"android:progress="50" /></LinearLayout><!--设置音调--><LinearLayoutandroid:layout_width="match_parent"android:layout_height="40dp"android:gravity="center_vertical"android:paddingStart="6dp"android:paddingEnd="6dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="设置音调:"android:textColor="#000"android:textSize="16sp" /><SeekBarandroid:id="@+id/sb_pitch"android:layout_width="match_parent"android:layout_height="wrap_content"android:max="100"android:progress="50" /></LinearLayout><!--设置音量--><LinearLayoutandroid:layout_width="match_parent"android:layout_height="40dp"android:gravity="center_vertical"android:paddingStart="6dp"android:paddingEnd="6dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="设置音量:"android:textColor="#000"android:textSize="16sp" /><SeekBarandroid: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;@Overrideprotected 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();}}/*** 权限请求返回结果*/@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);}/*** 页面点击事件* @param v 控件*/@Overridepublic 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() {@Overridepublic 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, "");}// 设置播放合成音频打断音乐播放,默认为truemTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "false");// 设置音频保存路径,保存音频格式支持pcm、wavmTts.setParameter(SpeechConstant.AUDIO_FORMAT, "pcm");mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, getExternalFilesDir(null) + "/msc/tts.pcm");}/*** 合成回调监听。*/private SynthesizerListener mTtsListener = new SynthesizerListener() {//开始播放@Overridepublic void onSpeakBegin() {Log.i(TAG, "开始播放");}//暂停播放@Overridepublic void onSpeakPaused() {Log.i(TAG, "暂停播放");}//继续播放@Overridepublic void onSpeakResumed() {Log.i(TAG, "继续播放");}//合成进度@Overridepublic void onBufferProgress(int percent, int beginPos, int endPos, String info) {Log.i(TAG, "合成进度:" + percent + "%");}//播放进度@Overridepublic 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);}//播放完成@Overridepublic 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();}}//事件@Overridepublic void onEvent(int eventType, int arg1, int arg2, Bundle obj) {// 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因// 若使用本地能力,会话id为nullif (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);}}};/*** 选中*/@Overridepublic void onItemSelected(AdapterView<?> parent, View view, int position, long id) {voicer = arrayValue[position];}@Overridepublic void onNothingSelected(AdapterView<?> parent) {}//设置SeekBarprivate void setSeekBar(SeekBar seekBar, final int type) {seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {switch (type) {case 1://设置语速 范围 1~100speedValue = Integer.toString(progress);break;case 2://设置音调 范围 1~100pitchValue = Integer.toString(progress);break;case 3://设置音量 范围 1~100volumeValue = Integer.toString(progress);break;default:break;}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {}});}}
到这里就结束了,你可以试试哦