欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android studio集成百度语音合成

程序员文章站 2022-05-14 13:06:00
...

离开老师的指导有一段时间了,每天都会给自己找点事情做,或则简单的安排计划一下,收货不多不少刚刚好。 
 这是2017年的第一篇比较正式的博客,也算是我在CSDN上的第一次吧!哈哈。今天记录的是最近在集成百度语音上的一些步骤和小部分总结。最开始准备使用讯飞语音的,然而在下载集成后多次测试都没有反应,感觉是哪儿出现了问题,并且讯飞语音在语音唤醒上面也有限制,最后无意间发现了百度语音,就尝试着试试,当然目前只完成了语音合成部分,接下来当然会去玩玩语音识别和唤醒咯。

步骤如下:

  1. Android Studio新建一个Mudule,比如我的:MyTest_AutoAnswer,你可以随意叫啥名。
  2. 登录网址百度语音开发者平台注册账号并创建应用。http://yuyin.baidu.com/ 。android studio集成百度语音合成
  3. 进入离线下载资源,先开通服务,再下载。我这里仅仅下载的为语音合成部分的离在线融合SDK–安卓版本。android studio集成百度语音合成
  4. 进入应用管理在该项目的右边点开管理包名,并在应用包名处输入android Studio中创建的应用包名,比如我的为:com.bk120.mytest_autoanswer。保存修改。此处可以不用下载什么临时授权文件,我就没下载因为你已经把包名都填上了。
  5. 同理点击查看Key,查看当前应用的所需的主要三个参数 AppId APIKey SecretKey,后面会用得到。android studio集成百度语音合成
  6. 将我们前面几步中下载的SDk解压,为如下目录结构:android studio集成百度语音合成 
     BaiduTtsSample:为一个模板代码,eclipse版本的,我就是借鉴里面稍微修改了一下。 
     data:为百度语音资源,声音文件,它为一个必须文件,中英文资源。最后使用是放在手机物理存储下的。 
     doc:为一个pdf的简介使用方法以及网络的使用Api文档说明。我们用不到,可以下去读一读的。 
     libs:为资源jar包和语音引擎文件.so库。也是我们集成必须使用到的。 
    7.接下来的步骤是,我们家语音资源和libs下的资源方法android studio我们的项目里面。将data里面的文件全部复制到Asserts文件夹下。将libs下的两个jar文件复制到项目的libs中,并添加Add As library关联。在项目中的main路径下新建一个jnilibs文件夹,用于放置剩余的libs下的文件。所以后的文件目录为:android studio集成百度语音合成 
     libs下面多余的两个okhttp和okio是我的网络访问的库,此处没有影响,不用添加的。 
    8.最后在AndroidManifest文件中申明如下权限,我申请的比较多,因为做了一个网络判断的事件处理,没事全部Ctrl+C就好没影响的。

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

到此,集成就结束了,接下来就是如何使用。当然可以参照BaiduTtsSample文件夹下的src里面的一个MainActvity的写法。也可以按照我下面的总结的工具类来直接使用,方便快捷省事。

工具类如下


package com.bk120.mytest_autoanswer;

import android.content.Context;
import android.os.Environment;
import com.baidu.tts.client.SpeechError;
import com.baidu.tts.client.SpeechSynthesizer;
import com.baidu.tts.client.SpeechSynthesizerListener;
import com.baidu.tts.client.SynthesizerTool;
import com.baidu.tts.client.TtsMode;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
/*** Created by bk120 on 2017/1/3.
 * 语音合成工具类
 */

public class VoiceUtils {
//////////////////需配置部分////////////////////////
//AppId
private String APPID="9156383";
//ApiKey
private String APIKEY="BZQ1owOxjYzf0ADT1agq8SxU";
//SecretKey
private String SECRETKEY="43b8a384182f2af0b935cf4ee9dce03f";
////////////////////配置结束////////////////////////////////////

private SpeechSynthesizer mSpeechSynthesizer;
private String mSampleDirPath;
private static final String SAMPLE_DIR_NAME = "baiduTTS";
private static final String SPEECH_FEMALE_MODEL_NAME = "bd_etts_speech_female.dat";
private static final String SPEECH_MALE_MODEL_NAME = "bd_etts_speech_male.dat";
private static final String TEXT_MODEL_NAME = "bd_etts_text.dat";
private static final String LICENSE_FILE_NAME = "temp_license.txt";
private static final String ENGLISH_SPEECH_FEMALE_MODEL_NAME = "bd_etts_speech_female_en.dat";
private static final String ENGLISH_SPEECH_MALE_MODEL_NAME = "bd_etts_speech_male_en.dat";
private static final String ENGLISH_TEXT_MODEL_NAME = "bd_etts_text_en.dat";
//初始化
public  void init(Context context,int speaker){
    initialEnv(context);
    initialTts(context,speaker);
}
//获取解析器
public SpeechSynthesizer getSyntheszer(){
    return mSpeechSynthesizer;
}
//初始化配置文件
private void initialEnv(Context context) {
    if (mSampleDirPath == null) {
        String sdcardPath = Environment.getExternalStorageDirectory().toString();
        mSampleDirPath = sdcardPath + "/" + SAMPLE_DIR_NAME;
    }
    makeDir(mSampleDirPath);
    copyFromAssetsToSdcard(context,false, SPEECH_FEMALE_MODEL_NAME, mSampleDirPath + "/" + SPEECH_FEMALE_MODEL_NAME);
    copyFromAssetsToSdcard(context,false, SPEECH_MALE_MODEL_NAME, mSampleDirPath + "/" + SPEECH_MALE_MODEL_NAME);
    copyFromAssetsToSdcard(context,false, TEXT_MODEL_NAME, mSampleDirPath + "/" + TEXT_MODEL_NAME);
    copyFromAssetsToSdcard(context,false, LICENSE_FILE_NAME, mSampleDirPath + "/" + LICENSE_FILE_NAME);
    copyFromAssetsToSdcard(context,false, "english/" + ENGLISH_SPEECH_FEMALE_MODEL_NAME, mSampleDirPath + "/"
            + ENGLISH_SPEECH_FEMALE_MODEL_NAME);
    copyFromAssetsToSdcard(context,false, "english/" + ENGLISH_SPEECH_MALE_MODEL_NAME, mSampleDirPath + "/"
            + ENGLISH_SPEECH_MALE_MODEL_NAME);
    copyFromAssetsToSdcard(context,false, "english/" + ENGLISH_TEXT_MODEL_NAME, mSampleDirPath + "/"
            + ENGLISH_TEXT_MODEL_NAME);
}

private void makeDir(String dirPath) {
    File file = new File(dirPath);
    if (!file.exists()) {
        file.mkdirs();
    }
}
/**
 * 将资源语音文件复制到手机SD卡中
 * @param isCover 是否覆盖已存在的目标文件
 * @param source
 * @param dest
 */
private void copyFromAssetsToSdcard(Context context,boolean isCover, String source, String dest) {
    File file = new File(dest);
    if (isCover || (!isCover && !file.exists())) {
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            is = context.getResources().getAssets().open(source);
            String path = dest;
            fos = new FileOutputStream(path);
            byte[] buffer = new byte[1024];
            int size = 0;
            while ((size = is.read(buffer, 0, 1024)) >= 0) {
                fos.write(buffer, 0, size);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
//初始化解析器
private void initialTts(Context context,int speaker) {
    this.mSpeechSynthesizer = SpeechSynthesizer.getInstance();
    this.mSpeechSynthesizer.setContext(context);
    this.mSpeechSynthesizer.setSpeechSynthesizerListener(new MyListnener());
    // 文本模型文件路径 (离线引擎使用)
    this.mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, mSampleDirPath + "/"
            + TEXT_MODEL_NAME);
    // 声学模型文件路径 (离线引擎使用)
    this.mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, mSampleDirPath + "/"
            + SPEECH_FEMALE_MODEL_NAME);
    // 请替换为语音开发者平台上注册应用得到的App ID (离线授权)
    this.mSpeechSynthesizer.setAppId(APPID);
    // 请替换为语音开发者平台注册应用得到的apikey和secretkey (在线授权)
    this.mSpeechSynthesizer.setApiKey(APIKEY,
            SECRETKEY);
    // 发音人(在线引擎),可用参数为0,1,2,3。。。(服务器端会动态增加,各值含义参考文档,以文档说明为准。0--普通女声,4--情感女声,3--普通男声,6--特殊男声。。。)
    this.mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, speaker+"");
    // 设置Mix模式的合成策略
    this.mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_MIX_MODE, SpeechSynthesizer.MIX_MODE_DEFAULT);
    // 授权检测接口(只是通过AuthInfo进行检验授权是否成功。)
    // 初始化tts
    mSpeechSynthesizer.initTts(TtsMode.MIX);
    // 加载离线英文资源(提供离线英文合成功能)
    mSpeechSynthesizer.loadEnglishModel(mSampleDirPath + "/" + ENGLISH_TEXT_MODEL_NAME, mSampleDirPath
                    + "/" + ENGLISH_SPEECH_FEMALE_MODEL_NAME);

}
//合成监听器
class MyListnener implements SpeechSynthesizerListener{
    @Override
    public void onSynthesizeStart(String s) {
        //合成准备工作
    }
    @Override
    public void onSynthesizeDataArrived(String s, byte[] bytes, int i) {
        //合成数据和进度的回调接口,分多次回调
    }
    @Override
    public void onSynthesizeFinish(String s) {
        //合成正常结束,每句合成正常结束都会回调,如果过程中出错,则回调onError,不再回调此接口
    }
    @Override
    public void onSpeechStart(String s) {
        //播放开始,每句播放开始都会回调
    }
    @Override
    public void onSpeechProgressChanged(String s, int i) {
        //播放进度回调接口,分多次回调
    }
    @Override
    public void onSpeechFinish(String s) {
        // 播放正常结束,每句播放正常结束都会回调,如果过程中出错,则回调onError,不再回调此接口
    }
    @Override
    public void onError(String s, SpeechError speechError) {
        //当合成或者播放过程中出错时回调此接口
    }
}
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175

 该工具类我就不多解释了,唯一需要配置的地方在前面获取到的AppId,ApiKey和SecretKey直接替换成你自己的就可以了。该工具类做的事就是第一步将由于资源文件及Asserts下面的文件复制到SD卡上,然后初始化一个语音合成器SpeechSynthesizer。并提供放回的方法getSyntheszer供外部使用。合成监听器SpeechSynthesizerListener能处理各个阶段的事件。当然这个工具类不好,存在构造器还需要传入参数,有待改进,int speaker 为发声人,从0到7都可以使用,男声,女声,自己可以试着玩玩。

在MainActivity使用:

  1. 布局文件我们用一个输入框EditText和四个按钮来测试不同的发声。 
    布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical"
android:background="#7EBF4B"
tools:context="com.bk120.mytest_autoanswer.MainActivity">
<EditText
    android:layout_height="120dp"
    android:layout_width="match_parent"
    android:hint="输入:"
    android:layout_marginTop="10dp"
    android:id="@+id/mainactivity_et"
    android:paddingLeft="10dp"
    android:background="@drawable/et_shape"
    />

<LinearLayout
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:orientation="horizontal"
    android:layout_marginTop="10dp"
    >
    <Button
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:onClick="puTongWoman"
        android:layout_weight="1"
        android:text="普通女声"
        android:textSize="15sp"
        android:textColor="#71A7C7"
        android:background="@drawable/btn_shape"
        />
    <Button
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:onClick="qingGanWoman"
        android:layout_weight="1"
        android:text="情感女声"
        android:textSize="15sp"
        android:textColor="#71A7C7"
        android:background="@drawable/btn_shape"
        />
    <Button
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:onClick="puTongMan"
        android:layout_weight="1"
        android:textSize="15sp"
        android:textColor="#71A7C7"
        android:text="普通男声"
        android:background="@drawable/btn_shape"
        />
    <Button
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:onClick="qingGanMan"
        android:layout_weight="1"
        android:textSize="15sp"
        android:textColor="#71A7C7"
        android:text="情感男声"
        android:background="@drawable/btn_shape"
        />
</LinearLayout>
</LinearLayout>  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

2.Activity中直接获取EditText对象并处理点击事件,如下: 
MainActivity中:

– 
package com.bk120.mytest_autoanswer;

import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import com.baidu.tts.client.SpeechSynthesizer;

public class MainActivity extends Activity {
    private EditText mInput;
    private SpeechSynthesizer mSpeechSynthesizer;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mInput= (EditText) this.findViewById(R.id.mainactivity_et);
}
//普通女声
public void puTongWoman(View view){
    speak(0);
}
//特殊女声
public void qingGanWoman(View view){
    speak(4);
}
//普通男声
public void puTongMan(View view){
    speak(3);
}
//特殊男声
public void qingGanMan(View view){
    speak(6);
}
private void speak(int speaker) {
    //若是每次都这样是不是会有内存问题呢?需要思考改进
    VoiceUtils utils=new VoiceUtils();
    utils.init(this,speaker);
    mSpeechSynthesizer=utils.getSyntheszer();
    String text = this.mInput.getText().toString();
    //需要合成的文本text的长度不能超过1024个GBK字节。
    if (TextUtils.isEmpty(mInput.getText())) {
        text = "你好!";
        mInput.setText(text);
    }
   this.mSpeechSynthesizer.speak(text);
}
//释放缓存
@Override
protected void onDestroy() {
    this.mSpeechSynthesizer.release();
    super.onDestroy();
}
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

通过上面的步骤语音合成就算完事了,在没有网络连接的情况下默认speaker为0–普通女声,即点击所有按钮都是普通女声朗读出文本框中的文字,英文中文都可以,网络状态下其他按钮才能发出不同的声音。 
android studio集成百度语音合成

 结果就是这样,点击按钮就能听见朗读声音咯。图中的按钮和输入框shape文件如下,文件名分别为btn_shape和et_shape,都放在res下的drawable文件夹中,适可参考Ctrl+C吧! 
btn_shape:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:color="#8BE5E8" android:width="1dp"/>
<corners android:radius="5dp"/>
<solid android:color="#C46F5F"/>
</shape>  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

et_shape:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:color="#facd89" android:width="5dp"/>
<corners android:radius="8dp"/>
</shape>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

 第一次写这个东东,是不是太详细了竟然写了老久了,这打字速度有点坑啊,还要多锻炼!