2019-08-07Android 集成科大飛訊的語(yǔ)音合成

在項(xiàng)目中新建Application,在Application中配置項(xiàng)目對(duì)應(yīng)appid和模式,如下圖所示。

具體實(shí)現(xiàn)邏輯如下圖。
package com.lcj.kdf;

import android.annotation.SuppressLint;

import android.app.Activity;

import android.app.AlertDialog;

import android.content.DialogInterface;

import android.content.Intent;

import android.content.SharedPreferences;

import android.os.Bundle;

import android.os.Environment;

import android.util.Log;

import android.view.View;

import android.view.View.OnClickListener;

import android.view.Window;

import android.widget.EditText;

import android.widget.RadioGroup;

import android.widget.RadioGroup.OnCheckedChangeListener;

import android.widget.Toast;

import com.iflytek.cloud.ErrorCode;

import com.iflytek.cloud.InitListener;

import com.iflytek.cloud.SpeechConstant;

import com.iflytek.cloud.SpeechError;

import com.iflytek.cloud.SpeechSynthesizer;

import com.iflytek.cloud.SynthesizerListener;

import com.iflytek.cloud.util.ResourceUtil;

import com.iflytek.cloud.util.ResourceUtil.RESOURCE_TYPE;

public class TtsDemoextends Activityimplements OnClickListener {

private static StringTAG = TtsDemo.class.getSimpleName();

// 語(yǔ)音合成對(duì)象

? private SpeechSynthesizermTts;

// 默認(rèn)云端發(fā)音人

? public static StringvoicerCloud="xiaoyan";

// 默認(rèn)本地發(fā)音人

? public static StringvoicerLocal="xiaoyan";

// 云端發(fā)音人列表

? private String[]cloudVoicersEntries;

private String[]cloudVoicersValue ;

// 本地發(fā)音人列表

? private String[]localVoicersEntries;

private String[]localVoicersValue ;

//緩沖進(jìn)度

? private int mPercentForBuffering =0;

//播放進(jìn)度

? private int mPercentForPlaying =0;

// 云端/本地選擇按鈕

? private RadioGroupmRadioGroup;

// 引擎類(lèi)型

? private StringmEngineType = SpeechConstant.TYPE_CLOUD;

private ToastmToast;

private SharedPreferencesmSharedPreferences;

@SuppressLint("ShowToast")

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

this.requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.ttsdemo);

initLayout();

// 初始化合成對(duì)象

? ? ? mTts = SpeechSynthesizer.createSynthesizer(this,mTtsInitListener);

// 云端發(fā)音人名稱(chēng)列表

? ? ? cloudVoicersEntries = getResources().getStringArray(R.array.voicer_cloud_entries);

cloudVoicersValue = getResources().getStringArray(R.array.voicer_cloud_values);

// 本地發(fā)音人名稱(chēng)列表

? ? ? localVoicersEntries = getResources().getStringArray(R.array.voicer_local_entries);

localVoicersValue = getResources().getStringArray(R.array.voicer_local_values);

mSharedPreferences = getSharedPreferences(TtsSettings.PREFER_NAME, Activity.MODE_PRIVATE);

mToast = Toast.makeText(this,"", Toast.LENGTH_SHORT);

}

/**

* 初始化Layout。

*/

? private void initLayout() {

findViewById(R.id.tts_play).setOnClickListener(this);

findViewById(R.id.tts_cancel).setOnClickListener(this);

findViewById(R.id.tts_pause).setOnClickListener(this);

findViewById(R.id.tts_resume).setOnClickListener(this);

findViewById(R.id.image_tts_set).setOnClickListener(this);

findViewById(R.id.tts_btn_person_select).setOnClickListener(this);

mRadioGroup=((RadioGroup) findViewById(R.id.tts_rediogroup));

mRadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {

@Override

? ? ? ? public void onCheckedChanged(RadioGroup group,int checkedId) {

switch (checkedId) {

case R.id.tts_radioCloud:

mEngineType = SpeechConstant.TYPE_CLOUD;

break;

case R.id.tts_radioLocal:

mEngineType =? SpeechConstant.TYPE_LOCAL;

break;

default:

break;

}

}

} );

}

@Override

? public void onClick(View view) {

if(null ==mTts ){

// 創(chuàng)建單例失敗,與 21001 錯(cuò)誤為同樣原因,參考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688

? ? ? ? this.showTip("創(chuàng)建對(duì)象失敗,請(qǐng)確認(rèn) libmsc.so 放置正確,\n 且有調(diào)用 createUtility 進(jìn)行初始化" );

return;

}

switch(view.getId()) {

case R.id.image_tts_set:

Intent intent =new Intent(TtsDemo.this, TtsSettings.class);

startActivity(intent);

break;

// 開(kāi)始合成

// 收到onCompleted 回調(diào)時(shí),合成結(jié)束、生成合成音頻

// 合成的音頻格式:只支持pcm格式

? ? ? case R.id.tts_play:

String text = ((EditText) findViewById(R.id.tts_text)).getText().toString();

// 設(shè)置參數(shù)

? ? ? ? setParam();

int code =mTts.startSpeaking(text,mTtsListener);

if (code != ErrorCode.SUCCESS) {

showTip("語(yǔ)音合成失敗,錯(cuò)誤碼: " + code+",請(qǐng)點(diǎn)擊網(wǎng)址https://www.xfyun.cn/document/error-code查詢(xún)解決方案");

}

break;

// 取消合成

? ? ? case R.id.tts_cancel:

mTts.stopSpeaking();

break;

// 暫停播放

? ? ? case R.id.tts_pause:

mTts.pauseSpeaking();

break;

// 繼續(xù)播放

? ? ? case R.id.tts_resume:

mTts.resumeSpeaking();

break;

// 選擇發(fā)音人

? ? ? case R.id.tts_btn_person_select:

showPresonSelectDialog();

break;

}

}

private static int selectedNumCloud=0;

private static int selectedNumLocal=0;

/**

* 發(fā)音人選擇。

*/

? private void showPresonSelectDialog() {

switch (mRadioGroup.getCheckedRadioButtonId()) {

// 選擇在線(xiàn)合成

? ? ? case R.id.tts_radioCloud:

new AlertDialog.Builder(this).setTitle("在線(xiàn)合成發(fā)音人選項(xiàng)")

.setSingleChoiceItems(cloudVoicersEntries,// 單選框有幾項(xiàng),各是什么名字

? ? ? ? ? ? ? selectedNumCloud,// 默認(rèn)的選項(xiàng)

? ? ? ? ? ? ? new DialogInterface.OnClickListener() {// 點(diǎn)擊單選框后的處理

? ? ? ? ? ? public void onClick(DialogInterface dialog,

int which) {// 點(diǎn)擊了哪一項(xiàng)

? ? ? ? ? ? ? voicerCloud =cloudVoicersValue[which];

if ("catherine".equals(voicerCloud) ||"henry".equals(voicerCloud) ||"vimary".equals(voicerCloud)) {

((EditText) findViewById(R.id.tts_text)).setText(R.string.text_tts_source_en);

}else {

((EditText) findViewById(R.id.tts_text)).setText(R.string.text_tts_source);

}

selectedNumCloud = which;

dialog.dismiss();

}

}).show();

break;

// 選擇本地合成

? ? ? case R.id.tts_radioLocal:

new AlertDialog.Builder(this).setTitle("本地合成發(fā)音人選項(xiàng)")

.setSingleChoiceItems(localVoicersEntries,// 單選框有幾項(xiàng),各是什么名字

? ? ? ? ? ? ? selectedNumLocal,// 默認(rèn)的選項(xiàng)

? ? ? ? ? ? ? new DialogInterface.OnClickListener() {// 點(diǎn)擊單選框后的處理

? ? ? ? ? ? public void onClick(DialogInterface dialog,

int which) {// 點(diǎn)擊了哪一項(xiàng)

? ? ? ? ? ? ? voicerLocal =localVoicersValue[which];

if ("catherine".equals(voicerLocal) ||"henry".equals(voicerLocal) ||"vimary".equals(voicerLocal)) {

((EditText) findViewById(R.id.tts_text)).setText(R.string.text_tts_source_en);

}else {

((EditText) findViewById(R.id.tts_text)).setText(R.string.text_tts_source);

}

selectedNumLocal = which;

dialog.dismiss();

}

}).show();

break;

default:

break;

}

}

/**

* 初始化監(jiān)聽(tīng)。

*/

? private InitListenermTtsInitListener =new InitListener() {

@Override

? ? ? public void onInit(int code) {

Log.d(TAG,"InitListener init() code = " + code);

if (code != ErrorCode.SUCCESS) {

showTip("初始化失敗,錯(cuò)誤碼:"+code+",請(qǐng)點(diǎn)擊網(wǎng)址https://www.xfyun.cn/document/error-code查詢(xún)解決方案");

}else {

// 初始化成功,之后可以調(diào)用startSpeaking方法

// 注:有的開(kāi)發(fā)者在onCreate方法中創(chuàng)建完合成對(duì)象之后馬上就調(diào)用startSpeaking進(jìn)行合成,

// 正確的做法是將onCreate中的startSpeaking調(diào)用移至這里

? ? ? ? }

}

};

/**

* 合成回調(diào)監(jiān)聽(tīng)。

*/

? private SynthesizerListenermTtsListener =new SynthesizerListener() {

@Override

? ? ? public void onSpeakBegin() {

showTip("開(kāi)始播放");

}

@Override

? ? ? public void onSpeakPaused() {

showTip("暫停播放");

}

@Override

? ? ? public void onSpeakResumed() {

showTip("繼續(xù)播放");

}

@Override

? ? ? public void onBufferProgress(int percent,int beginPos,int endPos,

String info) {

// 合成進(jìn)度

? ? ? ? mPercentForBuffering = percent;

showTip(String.format(getString(R.string.tts_toast_format),

mPercentForBuffering,mPercentForPlaying));

}

@Override

? ? ? public void onSpeakProgress(int percent,int beginPos,int endPos) {

// 播放進(jìn)度

? ? ? ? mPercentForPlaying = percent;

showTip(String.format(getString(R.string.tts_toast_format),

mPercentForBuffering,mPercentForPlaying));

}

@Override

? ? ? public void onCompleted(SpeechError error) {

if (error ==null) {

showTip("播放完成");

}else if (error !=null) {

showTip(error.getPlainDescription(true));

}

}

@Override

? ? ? public void onEvent(int eventType,int arg1,int arg2, Bundle obj) {

// 以下代碼用于獲取與云端的會(huì)話(huà)id,當(dāng)業(yè)務(wù)出錯(cuò)時(shí)將會(huì)話(huà)id提供給技術(shù)支持人員,可用于查詢(xún)會(huì)話(huà)日志,定位出錯(cuò)原因

// 若使用本地能力,會(huì)話(huà)id為null

// if (SpeechEvent.EVENT_SESSION_ID == eventType) {

//? ? String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);

//? ? Log.d(TAG, "session id =" + sid);

// }

//實(shí)時(shí)音頻流輸出參考

/*if (SpeechEvent.EVENT_TTS_BUFFER == eventType) {

byte[] buf = obj.getByteArray(SpeechEvent.KEY_EVENT_TTS_BUFFER);

Log.e("MscSpeechLog", "buf is =" + buf);

}*/

? ? ? }

};

private void showTip(final String str){

runOnUiThread(new Runnable() {

@Override

? ? ? ? public void run() {

mToast.setText(str);

mToast.show();

}

});

}

/**

* "engine_type = local, text_encoding = UTF8, tts_res_path = fo|res/tts/xiaoyan.jet;fo|res/tts/common.jet, sample_rate = 16000, speed = 50, volume = 50, pitch = 50, rdn = 2"

* 參數(shù)設(shè)置

* 無(wú)效的參數(shù)值? 參數(shù)值錯(cuò)誤,離線(xiàn)資源沒(méi)有正確導(dǎo)入? "按照文檔說(shuō)明,輸入正確的參數(shù)值、導(dǎo)入資源到相應(yīng)位置等.

* 檢查參數(shù)值是否超過(guò)范圍或不符合要求." "http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=14142&highlight=10107

* http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=15920&highlight=10107"

*/

? private void setParam(){

// 清空參數(shù)

? ? ? mTts.setParameter(SpeechConstant.PARAMS,null);

//設(shè)置合成

? ? ? if(mEngineType.equals(SpeechConstant.TYPE_CLOUD))

{

//設(shè)置使用云端引擎

? ? ? ? mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);

//設(shè)置發(fā)音人

? ? ? ? mTts.setParameter(SpeechConstant.VOICE_NAME,voicerCloud);

}else {

//設(shè)置使用本地引擎

? ? ? ? mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);

//設(shè)置發(fā)音人資源路徑

? ? ? ? mTts.setParameter(ResourceUtil.TTS_RES_PATH,getResourcePath());

//設(shè)置發(fā)音人

? ? ? ? mTts.setParameter(SpeechConstant.VOICE_NAME,voicerLocal);

}

//mTts.setParameter(SpeechConstant.TTS_DATA_NOTIFY,"1");//支持實(shí)時(shí)音頻流拋出,僅在synthesizeToUri條件下支持

//設(shè)置合成語(yǔ)速

? ? ? mTts.setParameter(SpeechConstant.SPEED,mSharedPreferences.getString("speed_preference","50"));

//設(shè)置合成音調(diào)

? ? ? mTts.setParameter(SpeechConstant.PITCH,mSharedPreferences.getString("pitch_preference","50"));

//設(shè)置合成音量

? ? ? mTts.setParameter(SpeechConstant.VOLUME,mSharedPreferences.getString("volume_preference","50"));

//設(shè)置播放器音頻流類(lèi)型

? ? ? mTts.setParameter(SpeechConstant.STREAM_TYPE,mSharedPreferences.getString("stream_preference","3"));

// 設(shè)置播放合成音頻打斷音樂(lè)播放,默認(rèn)為true

? ? ? mTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS,"true");

// 設(shè)置音頻保存路徑,保存音頻格式支持pcm、wav,設(shè)置路徑為sd卡請(qǐng)注意WRITE_EXTERNAL_STORAGE權(quán)限

? mTts.setParameter(SpeechConstant.AUDIO_FORMAT,"wav");

mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, Environment.getExternalStorageDirectory()+"/msc/tts.wav");

}

//獲取發(fā)音人資源路徑

// private String getResourcePath(){

//? ? StringBuffer tempBuffer = new StringBuffer();

//? ? //合成通用資源

//? ? tempBuffer.append(ResourceUtil.generateResourcePath(this, RESOURCE_TYPE.assets, "tts/common.jet"));

//? ? tempBuffer.append(";");

//? ? //發(fā)音人資源

//? ? tempBuffer.append(ResourceUtil.generateResourcePath(this, RESOURCE_TYPE.assets, "tts/"+TtsDemo.voicerLocal+".jet"));

//? ? return tempBuffer.toString();

// }

? private String getResourcePath(){

StringBuffer tempBuffer =new StringBuffer();

//合成通用資源

? ? ? tempBuffer.append(ResourceUtil.generateResourcePath(this, RESOURCE_TYPE.assets,"tts/common.jet"));

tempBuffer.append(";");

//發(fā)音人資源

? ? ? tempBuffer.append(ResourceUtil.generateResourcePath(this, RESOURCE_TYPE.assets,"tts/"+TtsDemo.voicerLocal+".jet"));

return tempBuffer.toString();

}

@Override

? protected void onDestroy() {

super.onDestroy();

if(null !=mTts ){

mTts.stopSpeaking();

// 退出時(shí)釋放連接

? ? ? ? mTts.destroy();

}

}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容