最近因为一些特殊需求,公司希望使用离线TTS,不花钱的中文离线TTS,最好音色还要多一点,语速,语调都要可控;总之一句话,以不花钱的代价达到最好的效果。其实现在很多的开发者平台也都提供一两种音色的离线TTS开发SDK,比如科大,云知声等等,我之前也做过一个百度在线ASR与云知声离线TTS结合实现的一个复读机的小Demo,
github项目以及本篇(本篇github地址)的介绍,就可以实现TTS播报,暂停以及恢复等功能
离线TTS音色;从另一个方面来说,不知道有没有考虑过,如果我们能找到一个开源框架可以实现加载这些离线语音包,然后想办法获取到这些离线语音包,那么我们就可以多使用其另外的三种离线语音包。但是这样的开源框架估计不好找。其他还有度秘语音引擎,google的文字转语音引擎;可以点击下载,感谢这位博主。说的好像有点多了,下面进入正题;今天主要介绍一下Android原生的TTS接口的基本使用。
1,主要使用步骤
(1)新建一个类(内部类也是可以的),实现OnInitListener接口,重写onInit()方法,通常是判断TTS引擎初始化的状态
private class TTSListener implements OnInitListener {
@Override
public void onInit(int status) {
// TODO Auto-generated method stub
if (status == TextToSpeech.SUCCESS) {
// int supported = mSpeech.setLanguage(Locale.US);
// if ((supported != TextToSpeech.LANG_AVAILABLE) && (supported != TextToSpeech.LANG_COUNTRY_AVAILABLE)) {
// Toast.makeText(MainActivity.this, "不支持当前语言!", Toast.LENGTH_SHORT).show();
// Log.i(TAG, "onInit: 支持当前选择语言");
// }else{
//
// }
Log.i(TAG, "onInit: TTS引擎初始化成功");
}
else{
Log.i(TAG, "onInit: TTS引擎初始化失败");
}
}
}
(2)获取TTS引擎
mSpeech = new TextToSpeech(MainActivity.this, new TTSListener());
(3)在使用的时候,如果有需要可以调整TTS引擎参数,包括上面说的语速,语调,语言等等(当然,当前不支持中文,使用的话,先下载上面提到的服务或者应用并安装,然后在“设置”--》“语音与输入”--》“文本转语音输出”--》选择你安装中文TTS就可以了)
mSpeech.setLanguage(SharedData.languageList.get(choosedLanguage));
mSpeech.setSpeechRate(SharedData.voice_speed);
mSpeech.setPitch(SharedData.voice_pitch);
2,示例代码:
示例代码很简单,我简单加了一点东西,也很好理解。
MainActivity.java代码:
package com.hfut.operationandroidtts;
import android.app.AlertDialog;
import android.speech.tts.TextToSpeech;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private TextToSpeech mSpeech = null;
private EditText edit = null;
private String choosedLanguage;
private RadioButton english,chainese,german,french,taiWan;
private RadioGroup languageGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUI();
initLanguageList();
mSpeech = new TextToSpeech(MainActivity.this, new TTSListener());
}
private void initLanguageList() {
SharedData.languageList.put("英语",Locale.ENGLISH);
SharedData.languageList.put("中文",Locale.CHINESE);
SharedData.languageList.put("德语",Locale.GERMAN);
SharedData.languageList.put("法语",Locale.FRENCH);
SharedData.languageList.put("台湾话",Locale.TAIWAN);
}
public void openAudioFile(View view) {
choosedLanguage=getLanguage(languageGroup);
int supported = mSpeech.setLanguage(SharedData.languageList.get(choosedLanguage));
mSpeech.setSpeechRate(SharedData.voice_speed);
mSpeech.setPitch(SharedData.voice_pitch);
Log.i(TAG, "选择语言: "+choosedLanguage+"--"+SharedData.languageList.get(choosedLanguage));
//mSpeech.setAudioAttributes(new AudioAttributes());
// mSpeech.setVoice(new Voice(null,Locale.US,Voice.QUALITY_HIGH,Voice.LATENCY_NORMAL,false,null));
if((supported != TextToSpeech.LANG_AVAILABLE) && (supported != TextToSpeech.LANG_COUNTRY_AVAILABLE)){
//语言设置失败
Log.i(TAG, "语言设置失败: "+choosedLanguage);
}
else{
Log.i(TAG, "语言设置成功: "+choosedLanguage);
}
String tempStr = edit.getText().toString();
mSpeech.speak(tempStr, TextToSpeech.QUEUE_FLUSH, null);
Log.i(TAG, "测试文本: "+tempStr);
Log.i(TAG, "当前语速: "+SharedData.voice_speed+", 最快语速1.5");
Log.i(TAG, "当前音调:"+SharedData.voice_pitch+", 最高音调2.0");
//Log.i(TAG, "test: 执行了");
}
//保存音频文件
public void saveAudioFile(View view) {
HashMap<String, String> myHashRender = new HashMap<>();
myHashRender.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, edit.getText().toString());
mSpeech.synthesizeToFile(edit.getText().toString(), myHashRender,
"/mnt/sdcard/"+ new Date().toString().replace(" ","_").trim()+".wav");
Log.i(TAG, "saveAudioFile: "+"/mnt/sdcard/"+ new Date().toString().replace(" ","_").trim()+".wav"+"文件保存成功");
Toast.makeText(this,"文件保存成功",Toast.LENGTH_SHORT).show();
}
private String getLanguage(RadioGroup languageGroup) {
int choosedButtonID=languageGroup.getCheckedRadioButtonId();
String tempStr="";
if(choosedButtonID==english.getId()){
tempStr="英语";
}
else if(choosedButtonID==chainese.getId()){
tempStr="中文";
}
else if(choosedButtonID==german.getId()){
tempStr="德语";
}
else if(choosedButtonID==french.getId()){
tempStr="法语";
}
else if(choosedButtonID==taiWan.getId()){
tempStr="台湾话";
}
else{
}
return tempStr;
}
//增加音量
public void increVoice(View view){
if(SharedData.voice_speed>=1.5f){
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setMessage("速度已经最快,无法调整");
dialog.show();
}
else{
SharedData.voice_speed+=0.1f;
}
}
//减小音量
public void decreVoice(View view){
if(SharedData.voice_speed<=0.1f){
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setMessage("速度已经最慢,无法调整");
dialog.show();
}
else{
SharedData.voice_speed-=0.1f;
}
}
//升高音调
public void increPitch(View view){
if(SharedData.voice_pitch>=2.0f){
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setMessage("音调已经最高,无法调整");
dialog.show();
}
else{
SharedData.voice_pitch+=0.1f;
}
}
//减低音调
public void decrePitch(View view){
if(SharedData.voice_pitch<=0.1f){
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setMessage("音调已经最低,无法调整");
dialog.show();
}
else{
SharedData.voice_pitch-=0.1f;
}
}
private class TTSListener implements OnInitListener {
@Override
public void onInit(int status) {
// TODO Auto-generated method stub
if (status == TextToSpeech.SUCCESS) {
// int supported = mSpeech.setLanguage(Locale.US);
// if ((supported != TextToSpeech.LANG_AVAILABLE) && (supported != TextToSpeech.LANG_COUNTRY_AVAILABLE)) {
// Toast.makeText(MainActivity.this, "不支持当前语言!", Toast.LENGTH_SHORT).show();
// Log.i(TAG, "onInit: 支持当前选择语言");
// }else{
//
// }
Log.i(TAG, "onInit: TTS引擎初始化成功");
}
else{
Log.i(TAG, "onInit: TTS引擎初始化失败");
}
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
if (mSpeech != null) {
mSpeech.stop();
mSpeech.shutdown();
mSpeech = null;
}
super.onDestroy();
}
}
SharedData.java代码:
package com.hfut.operationandroidtts;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* author:why
* created on: 2018/6/5 11:28
* description:
*/
public class SharedData {
//语速
public static float voice_speed=0.5f;
//音调
public static float voice_pitch=1.0f;
//
public static Map<String,Locale> languageList=new HashMap<String,Locale>();
}
activity_main.xml代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<EditText
android:id="@+id/test_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textSize="20dp" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="openAudioFile"
android:text="播放合成语音"
android:textSize="20dp" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:onClick="saveAudioFile"
android:text="保存合成音频"
android:textSize="20dp" />
<LinearLayout
android:layout_marginLeft="160dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioGroup
android:id="@+id/language_Group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="bottom"
android:orientation="vertical"
android:paddingTop="2.0dip">
<RadioButton
android:id="@+id/language_English"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="英文" />
<RadioButton
android:id="@+id/language_Chainese"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="中文" />
<RadioButton
android:id="@+id/language_German"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="德语" />
<RadioButton
android:id="@+id/language_French"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="法语" />
<RadioButton
android:id="@+id/language_TaiWan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="台湾话" />
</RadioGroup>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_marginLeft="80dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/voice_incre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="increVoice"
android:text="加快语速"
android:textSize="20dp" />
<Button
android:id="@+id/voice_decre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="decreVoice"
android:text="减慢语速"
android:textSize="20dp" />
</LinearLayout>
<LinearLayout
android:layout_marginLeft="80dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/pitch_incre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="increPitch"
android:text="升高音调"
android:textSize="20dp" />
<Button
android:id="@+id/pitch_decre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="decrePitch"
android:text="降低音调"
android:textSize="20dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
主配置文件AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hfut.operationandroidtts">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
3,运行效果图
主界面
运行日志
保存的音频文件
(1)这里我把语速,语调做了上限2.0,事实不是这样的
(2)这里没有展示具体中文TTS引擎配置,比较简单,上面说了步骤,只是没有附图
(3)很多其他好玩的东西,可以自己结合兴趣和API学习
(4)关于语音识别,语音合成,语义识别,包括上下文等等和语音相关以及人际交互相关的应用知识,这段时间看了不少,也写过一些博客,有兴趣的朋友可以参考一下。当然,我只是停留在应用层。