一、系统自带的语音引擎
语音播报的本质是将书面文字转换成自然语言的音频流,这个转换操作被称作语音合成,又称TTS(从文本到语音)在转换过程中,为了避免机械合成的呆板和停顿感,语音合成技术还得对语音流进行平滑处理,以确保输出得语音音律流畅自然。
不管是Android原生的西文引擎还是手机厂商集成的中文引擎,都支持通过系统提供的API处理语音,其中的语音合成工具是TextToSpeech 常用方法如下
构造方法:第二个输入参数为语音监听器
setLanguage:设置引擎语言
setSpeechRate 设置语速
setPitch 设置音调
synthesizeToFile 把指定文本的朗读语言输出到文件
实战效果如下
代码如下
Java类
package com.example.voice;
import android.content.Intent;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Spinner;
import androidx.appcompat.app.AppCompatActivity;
import com.example.voice.adapter.LanguageListAdapter;
import com.example.voice.bean.Language;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class SpeechEngineActivity extends AppCompatActivity {
private final static String TAG = "SpeechEngineActivity";
private TextToSpeech mSpeech; // 声明一个文字转语音对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_speech_engine);
findViewById(R.id.btn_jump_setting).setOnClickListener(v -> {
Intent intent = new Intent();
intent.setAction("com.android.settings.TTS_SETTINGS");
startActivity(intent);
});
// 创建一个文字转语音对象,初始化结果在监听器TTSListener中返回
mSpeech = new TextToSpeech(this, new TTSListener());
}
private List<TextToSpeech.EngineInfo> mEngineList; // 语音引擎列表
// 定义一个文字转语音的初始化监听器
private class TTSListener implements TextToSpeech.OnInitListener {
// 在初始化完成时触发
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) { // 初始化成功
if (mEngineList == null) { // 首次初始化
// 获取系统支持的所有语音引擎
mEngineList = mSpeech.getEngines();
initEngineSpinner(); // 初始化语音引擎下拉框
}
initLanguageList(); // 初始化语言列表
}
}
}
// 初始化语音引擎下拉框
private void initEngineSpinner() {
String[] engineArray = new String[mEngineList.size()];
for(int i=0; i<mEngineList.size(); i++) {
engineArray[i] = mEngineList.get(i).label;
}
ArrayAdapter<String> engineAdapter = new ArrayAdapter<>(this,
R.layout.item_select, engineArray);
Spinner sp_engine = findViewById(R.id.sp_engine);
sp_engine.setPrompt("请选择语音引擎");
sp_engine.setAdapter(engineAdapter);
sp_engine.setOnItemSelectedListener(new EngineSelectedListener());
sp_engine.setSelection(0);
}
private class EngineSelectedListener implements AdapterView.OnItemSelectedListener {
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
//recycleSpeech(); // 回收文字转语音对象
// 创建指定语音引擎的文字转语音对象
mSpeech = new TextToSpeech(SpeechEngineActivity.this, new TTSListener(),
mEngineList.get(arg2).name);
}
public void onNothingSelected(AdapterView<?> arg0) {}
}
private String[] mLanguageArray = {"中文普通话", "英语", "法语", "德语", "意大利语", };
private Locale[] mLocaleArray = { Locale.CHINA, Locale.ENGLISH, Locale.FRENCH, Locale.GERMAN, Locale.ITALIAN };
// 初始化语言列表
private void initLanguageList() {
List<Language> languageList = new ArrayList<>();
// 下面遍历语言数组,从中挑选出当前引擎所支持的语言列表
for (int i=0; i<mLanguageArray.length; i++) {
String desc = "正常使用";
// 设置朗读语言
int result = mSpeech.setLanguage(mLocaleArray[i]);
if (result == TextToSpeech.LANG_MISSING_DATA) {
desc = "缺少数据";
} else if (result == TextToSpeech.LANG_NOT_SUPPORTED) {
desc = "暂不支持";
}
languageList.add(new Language(mLanguageArray[i], desc));
}
// 下面把该引擎对各语言的支持情况展示到列表视图上
ListView lv_language = findViewById(R.id.lv_language);
LanguageListAdapter adapter = new LanguageListAdapter(this, languageList);
lv_language.setAdapter(adapter);
}
}
二、文字转语音
既然明确了一个引擎能够支持哪些语言,接下来就可以大胆设置朗读的语言了,并且设置好语言后,还得提供对应的文字才可以,下面是一个语音播报页面的实例
中文普通话播报
英文版播报
可以在下拉框中选择自己想要朗读的语言
代码如下
Java类
package com.example.voice;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.voice.bean.Language;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class SpeechComposeActivity extends AppCompatActivity {
private final static String TAG = "SpeechComposeActivity";
private TextToSpeech mSpeech; // 声明一个文字转语音对象
private EditText et_tts; // 声明一个编辑框对象
private List<TextToSpeech.EngineInfo> mEngineList; // 语音引擎列表
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_speech_compose);
et_tts = findViewById(R.id.et_tts);
findViewById(R.id.btn_read).setOnClickListener(v -> {
String content = et_tts.getText().toString();
// 开始朗读指定文本
int result = mSpeech.speak(content, TextToSpeech.QUEUE_FLUSH, null, null);
String desc = String.format("朗读%s", result==TextToSpeech.SUCCESS?"成功":"失败");
Toast.makeText(this, desc, Toast.LENGTH_SHORT).show();
});
// 创建一个文字转语音对象,初始化结果在监听器的onInit方法中返回
mSpeech = new TextToSpeech(this, mListener);
}
// 创建一个文字转语音的初始化监听器实例
private TextToSpeech.OnInitListener mListener = status -> {
if (status == TextToSpeech.SUCCESS) { // 初始化成功
if (mEngineList == null) { // 首次初始化
mEngineList = mSpeech.getEngines(); // 获取系统支持的所有语音引擎
initEngineSpinner(); // 初始化语音引擎下拉框
}
initLanguageSpinner(); // 初始化语言下拉框
}
};
// 初始化语音引擎下拉框
private void initEngineSpinner() {
String[] engineArray = new String[mEngineList.size()];
for(int i=0; i<mEngineList.size(); i++) {
engineArray[i] = mEngineList.get(i).label;
}
ArrayAdapter<String> engineAdapter = new ArrayAdapter<>(this,
R.layout.item_select, engineArray);
Spinner sp_engine = findViewById(R.id.sp_engine);
sp_engine.setPrompt("请选择语音引擎");
sp_engine.setAdapter(engineAdapter);
sp_engine.setOnItemSelectedListener(new EngineSelectedListener());
sp_engine.setSelection(0);
}
private class EngineSelectedListener implements AdapterView.OnItemSelectedListener {
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
recycleSpeech(); // 回收文字转语音对象
// 创建指定语音引擎的文字转语音对象
mSpeech = new TextToSpeech(SpeechComposeActivity.this, mListener,
mEngineList.get(arg2).name);
}
public void onNothingSelected(AdapterView<?> arg0) {}
}
// 回收文字转语音对象
private void recycleSpeech() {
if (mSpeech != null) {
mSpeech.stop(); // 停止文字转语音
mSpeech.shutdown(); // 关闭文字转语音
mSpeech = null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
recycleSpeech(); // 回收文字转语音对象
}
private String[] mLanguageArray = {"中文普通话", "英语", "法语", "德语", "意大利语"};
private Locale[] mLocaleArray = { Locale.CHINA, Locale.ENGLISH, Locale.FRENCH, Locale.GERMAN, Locale.ITALIAN };
private String[] mValidLanguageArray; // 当前引擎支持的语言名称数组
private Locale[] mValidLocaleArray; // 当前引擎支持的语言类型数组
private String mTextCN = "离离原上草,一岁一枯荣。野火烧不尽,春风吹又生。";
private String mTextEN = "Hello World. Nice to meet you. This is a TTS demo.";
// 初始化语言下拉框
private void initLanguageSpinner() {
List<Language> languageList = new ArrayList<>();
// 下面遍历语言数组,从中挑选出当前引擎所支持的语言列表
for (int i=0; i<mLanguageArray.length; i++) {
// 设置朗读语言。通过检查方法的返回值,判断引擎是否支持该语言
int result = mSpeech.setLanguage(mLocaleArray[i]);
Log.d(TAG, "language="+mLanguageArray[i]+",result="+result);
if (result != TextToSpeech.LANG_MISSING_DATA
&& result != TextToSpeech.LANG_NOT_SUPPORTED) { // 语言可用
languageList.add(new Language(mLanguageArray[i], mLocaleArray[i]));
}
}
mValidLanguageArray = new String[languageList.size()];
mValidLocaleArray = new Locale[languageList.size()];
for(int i=0; i<languageList.size(); i++) {
mValidLanguageArray[i] = languageList.get(i).name;
mValidLocaleArray[i] = languageList.get(i).locale;
}
// 下面初始化语言下拉框
ArrayAdapter<String> languageAdapter = new ArrayAdapter<>(this,
R.layout.item_select, mValidLanguageArray);
Spinner sp_language = findViewById(R.id.sp_language);
sp_language.setPrompt("请选择朗读语言");
sp_language.setAdapter(languageAdapter);
sp_language.setOnItemSelectedListener(new LanguageSelectedListener());
sp_language.setSelection(0);
}
private class LanguageSelectedListener implements AdapterView.OnItemSelectedListener {
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
if (mValidLocaleArray[arg2]==Locale.CHINA) { // 汉语
et_tts.setText(mTextCN);
} else { // 其他语言
et_tts.setText(mTextEN);
}
mSpeech.setLanguage(mValidLocaleArray[arg2]); // 设置选中的朗读语言
}
public void onNothingSelected(AdapterView<?> arg0) {}
}
}
三、原生的语音识别
Android提供了语音识别器SpeechRecognizer 该工具常用方法如下
isRecognitionAvailable 检查系统是否支持原生的语音识别
createSpeechRecognizer 创建原生的语音识别器对象
setRecognitionListener 设置语音识别监听器
startListening 开始语音识别
stopListening 停止语音识别
cancel 取消语音识别
destroy 销毁语音识别器
识别结果监听器提供了许多回调方法,其中onResults方法可获得识别后的文本信息,然而每个引擎对文本结果的包装结构不尽相同,比如百度语音返回JSON格式的字符串,而讯飞语音返回字符串列表,为此要分别尝试几种格式的文本识别 效果如下
结果如下 点击开始识别后对着麦克风说话 停止识别后则会自动输出识别内容
此处连接真机测试更佳 模拟机可能没有麦克风~~~
代码如下
java类
package com.example.voice;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.provider.Settings;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.List;
import java.util.Locale;
@SuppressLint("SetTextI18n")
public class SpeechRecognizeActivity extends AppCompatActivity implements RecognitionListener {
private final static String TAG = "SpeechRecognizeActivity";
private TextView tv_result; // 声明一个文本视图对象
private Button btn_recognize; // 声明一个按钮对象
private SpeechRecognizer mRecognizer; // 声明一个语音识别对象
private boolean isRecognizing = false; // 是否正在识别
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_speech_recognize);
tv_result = findViewById(R.id.tv_result);
btn_recognize = findViewById(R.id.btn_recognize);
// 检查系统是否支持原生的语音识别
boolean enable = SpeechRecognizer.isRecognitionAvailable(this);
if (enable) {
initRecognize(); // 初始化语音识别
} else {
tv_result.setText("原生的语音识别服务不可用");
btn_recognize.setEnabled(false);
btn_recognize.setTextColor(Color.GRAY);
}
}
// 初始化语音识别
private void initRecognize() {
String serviceComponent = Settings.Secure.getString(
getContentResolver(), "voice_recognition_service");
// 获得系统内置的语音识别服务
ComponentName component = ComponentName.unflattenFromString(serviceComponent);
Log.d(TAG, "getPackageName="+component.getPackageName()+",getClassName="+component.getClassName());
tv_result.setText("原生的语音识别服务采用"+component.getPackageName()
+"里的服务"+component.getClassName());
// 创建原生的语音识别器对象
mRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
mRecognizer.setRecognitionListener(this); // 设置语音识别监听器
btn_recognize.setOnClickListener(v -> {
if (!isRecognizing) { // 未在识别
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.CHINA);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 5);
mRecognizer.startListening(intent); // 开始语音识别
} else { // 正在识别
mRecognizer.stopListening(); // 停止语音识别
//mRecognizer.cancel(); // 取消语音识别
}
isRecognizing = !isRecognizing;
btn_recognize.setText(isRecognizing?"停止识别":"开始识别");
});
}
@Override
public void onReadyForSpeech(Bundle params) {}
@Override
public void onBeginningOfSpeech() {}
@Override
public void onRmsChanged(float rmsdB) {}
@Override
public void onBufferReceived(byte[] buffer) {}
@Override
public void onEndOfSpeech() {}
@Override
public void onError(int error) {
Log.d(TAG, "Recognize error:"+error);
}
@Override
public void onResults(Bundle results) {
Log.d(TAG, "onResults Start");
String desc = "";
String key = SpeechRecognizer.RESULTS_RECOGNITION;
try { // 百度语音分支
String result = results.getString(key);
Log.d(TAG, "result="+result);
JSONObject object = new JSONObject(result);
String recognizeResult = object.getString("recognizeResult");
JSONArray recognizeArray = new JSONArray(recognizeResult);
for (int i=0; i<recognizeArray.length(); i++) {
JSONObject item = (JSONObject) recognizeArray.get(i);
String se_query = item.getString("se_query");
desc = desc + "\n" + se_query;
Log.d(TAG, "desc="+desc);
}
} catch (Exception e) { // 讯飞语音分支
e.printStackTrace();
List<String> resultList = results.getStringArrayList(key);
for (String str : resultList) {
desc = desc + "\n" + str;
Log.d(TAG, "desc="+desc);
}
}
tv_result.setText("识别到的文字为:"+desc);
Log.d(TAG, "onResults End");
}
@Override
public void onPartialResults(Bundle partialResults) {
Log.d(TAG, "onPartialResults");
}
@Override
public void onEvent(int eventType, Bundle params) {}
@Override
protected void onDestroy() {
super.onDestroy();
if (mRecognizer != null) {
mRecognizer.destroy(); // 销毁语音识别器
}
}
}