Android原生TTS的基本使用以及配合中文语音包实现中文TTS

时间:2024-03-30 20:13:44

      最近因为一些特殊需求,公司希望使用离线TTS,不花钱的中文离线TTS,最好音色还要多一点,语速,语调都要可控;总之一句话,以不花钱的代价达到最好的效果。其实现在很多的开发者平台也都提供一两种音色的离线TTS开发SDK,比如科大,云知声等等,我之前也做过一个百度在线ASR与云知声离线TTS结合实现的一个复读机的小Demo,有兴趣的朋友可以点击看一下

     因为了解过Android有原生的TTS功能,于是就简单使用了一下,Google的产品不支持中文也很容易理解,毕竟市场都不给别人,而且中文的处理相较于英文等处理起来也比较麻烦,所以,原生的TTS是不支持中文的。但是里面是有预留的常量中文普通话,*话等,但是是设置不了的。所以在具体使用的时候需要借助中文TTS引擎的帮助,所以就需要安装其他的软件或者服务,网上推荐比较多的是科大讯飞+这个APK,里面可以设置发音人,语速,语调等。我简单看了一下,里面支持五种普通话音色;但是我们从科大官网上看上面只提供两种离线的音色语音;所以使用这个APK可以丰富一下你的离线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,*;
    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());

//        langs = getResources().getStringArray(R.array.languages); // 得到语言数组
//        langSpinner = (Spinner) findViewById(R.id.spinner);
//        edit = (EditText) findViewById(R.id.edit);
//        btn = (Button) findViewById(R.id.btn);
//        btn.setOnClickListener(new BtnListener());
//
//        for (int i = 0; i < langs.length; i++) {
//            langList.add(langs[i]);
//        }
//        // 设置下拉框的适配器和样式
//        langAdapter = new ArrayAdapter<String>(MainActivity.this,
//                android.R.layout.simple_spinner_item, langList);
//        langAdapter
//                .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
//        langSpinner.setAdapter(langAdapter);
//
//        // 下拉框监听器
//        langSpinner
//                .setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
//                    @Override
//                    public void onItemSelected(AdapterView<?> adapter,
//                                               View view, int position, long id) {
//                        // TODO Auto-generated method stub
//                        curLang = (String) langSpinner.getAdapter().getItem(
//                                (int) id);
//                        if(mSpeech != null)
//                        {
//                            mSpeech.stop();
//                            mSpeech.shutdown();
//                            mSpeech = null;
//                        }
//                        // 创建TTS对象
//                        mSpeech = new TextToSpeech(MainActivity.this, new TTSListener());
//                        Toast.makeText(MainActivity.this, "select = " + curLang, Toast.LENGTH_SHORT).show();
//                    }
//
//                    @Override
//                    public void onNothingSelected(AdapterView<?> arg0) {
//                        // TODO Auto-generated method stub
//
//                    }
//                });
//    }

//    private int SetLanguage(String lang) {
//        int result = 0;
//        if (lang.equals("CANADA")) {
//            result = mSpeech.setLanguage(Locale.CANADA);
//        } else if (lang.equals("CANADA_FRENCH")) {
//            result = mSpeech.setLanguage(Locale.CANADA_FRENCH);
//        } else if (lang.equals("CHINA")) {
//            result = mSpeech.setLanguage(Locale.CHINA);
//        } else if (lang.equals("CHINESE")) {
//            result = mSpeech.setLanguage(Locale.CHINESE);
//        } else if (lang.equals("ENGLISH")) {
//            result = mSpeech.setLanguage(Locale.ENGLISH);
//        } else if (lang.equals("FRANCE")) {
//            result = mSpeech.setLanguage(Locale.FRANCE);
//        } else if (lang.equals("FRENCH")) {
//            result = mSpeech.setLanguage(Locale.FRENCH);
//        } else if (lang.equals("GERMAN")) {
//            result = mSpeech.setLanguage(Locale.GERMAN);
//        } else if (lang.equals("GERMANY")) {
//            result = mSpeech.setLanguage(Locale.GERMANY);
//        } else if (lang.equals("ITALIAN")) {
//            result = mSpeech.setLanguage(Locale.ITALIAN);
//        } else if (lang.equals("ITALY")) {
//            result = mSpeech.setLanguage(Locale.ITALY);
//        } else if (lang.equals("JAPAN")) {
//            result = mSpeech.setLanguage(Locale.JAPAN);
//        } else if (lang.equals("JAPANESE")) {
//            result = mSpeech.setLanguage(Locale.JAPANESE);
//        } else if (lang.equals("KOREA")) {
//            result = mSpeech.setLanguage(Locale.KOREA);
//        } else if (lang.equals("KOREAN")) {
//            result = mSpeech.setLanguage(Locale.KOREAN);
//        } else if (lang.equals("PRC")) {
//            result = mSpeech.setLanguage(Locale.PRC);
//        } else if (lang.equals("ROOT")) {
//            result = mSpeech.setLanguage(Locale.ROOT);
//        } else if (lang.equals("SIMPLIFIED_CHINESE")) {
//            result = mSpeech.setLanguage(Locale.SIMPLIFIED_CHINESE);
//        } else if (lang.equals("*")) {
//            result = mSpeech.setLanguage(Locale.*);
//        } else if (lang.equals("TRADITIONAL_CHINESE")) {
//            result = mSpeech.setLanguage(Locale.TRADITIONAL_CHINESE);
//        } else if (lang.equals("UK")) {
//            result = mSpeech.setLanguage(Locale.UK);
//        } else if (lang.equals("US")) {
//            result = mSpeech.setLanguage(Locale.US);
//        }
//        return result;
//    }
//
//    private class TTSListener implements OnInitListener {
//
//        @Override
//        public void onInit(int status) {
//            // TODO Auto-generated method stub
//            if (status == TextToSpeech.SUCCESS) {
//                //int result = mSpeech.setLanguage(Locale.ENGLISH);
//                int result = SetLanguage(curLang);
//                //如果打印为-2,说明不支持这种语言
//                Toast.makeText(MainActivity.this, "-------------result = " + result, Toast.LENGTH_LONG).show();
//                if (result == TextToSpeech.LANG_MISSING_DATA
//                        || result == TextToSpeech.LANG_NOT_SUPPORTED) {
//                    System.out.println("-------------not use");
//                } else {
//                    mSpeech.speak("i love you", TextToSpeech.QUEUE_FLUSH, null);
//                }
//            }
//        }
//
//    }
//
//    private class BtnListener implements OnClickListener {
//
//        @Override
//        public void onClick(View v) {
//            // TODO Auto-generated method stub
//            mSpeech.speak(edit.getText().toString(), TextToSpeech.QUEUE_FLUSH,
//                    null);
//        }
//
//    }
//
//    @Override
//    protected void onDestroy() {
//        // TODO Auto-generated method stub
//        if (mSpeech != null) {
//            mSpeech.stop();
//            mSpeech.shutdown();
//            mSpeech = null;
//        }
//        super.onDestroy();
//    }
//
//}
    }

    private void initUI() {
        edit = findViewById(R.id.test_text);
        languageGroup=findViewById(R.id.language_Group);
        english = findViewById(R.id.language_English);
        chainese = findViewById(R.id.language_Chainese);
        german = findViewById(R.id.language_German);
        french = findViewById(R.id.language_French);
        *=findViewById(R.id.language_*);
//        View.OnClickListener handle =  new View.OnClickListener(){
//            public void onClick(View v){
//                switch(v.getId()){
//                    case R.id.language_English:
//                        choosedLanguage="英文";
//                        break;
//                    case R.id.language_Chainese:
//                        choosedLanguage="中文";
//                        break;
//                    case R.id.language_German:
//                       choosedLanguage="德语";
//                        break;
//                    case R.id.language_French:
//                       choosedLanguage="法语";
//                        break;
//                }
//
//            }
//        };
//        english.setOnClickListener(handle);
//        chainese.setOnClickListener(handle);
//        german.setOnClickListener(handle);
//        french.setOnClickListener(handle);
    }

    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.*);
    }

    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==*.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();
    }

//    private int SetLanguage(String lang) {
//        int result = 0;
//        if (lang.equals("CANADA")) {
//            result = mSpeech.setLanguage(Locale.CANADA);
//        } else if (lang.equals("CANADA_FRENCH")) {
//            result = mSpeech.setLanguage(Locale.CANADA_FRENCH);
//        } else if (lang.equals("CHINA")) {
//            result = mSpeech.setLanguage(Locale.CHINA);
//        } else if (lang.equals("CHINESE")) {
//            result = mSpeech.setLanguage(Locale.CHINESE);
//        } else if (lang.equals("ENGLISH")) {
//            result = mSpeech.setLanguage(Locale.ENGLISH);
//        } else if (lang.equals("FRANCE")) {
//            result = mSpeech.setLanguage(Locale.FRANCE);
//        } else if (lang.equals("FRENCH")) {
//            result = mSpeech.setLanguage(Locale.FRENCH);
//        } else if (lang.equals("GERMAN")) {
//            result = mSpeech.setLanguage(Locale.GERMAN);
//        } else if (lang.equals("GERMANY")) {
//            result = mSpeech.setLanguage(Locale.GERMANY);
//        } else if (lang.equals("ITALIAN")) {
//            result = mSpeech.setLanguage(Locale.ITALIAN);
//        } else if (lang.equals("ITALY")) {
//            result = mSpeech.setLanguage(Locale.ITALY);
//        } else if (lang.equals("JAPAN")) {
//            result = mSpeech.setLanguage(Locale.JAPAN);
//        } else if (lang.equals("JAPANESE")) {
//            result = mSpeech.setLanguage(Locale.JAPANESE);
//        } else if (lang.equals("KOREA")) {
//            result = mSpeech.setLanguage(Locale.KOREA);
//        } else if (lang.equals("KOREAN")) {
//            result = mSpeech.setLanguage(Locale.KOREAN);
//        } else if (lang.equals("PRC")) {
//            result = mSpeech.setLanguage(Locale.PRC);
//        } else if (lang.equals("ROOT")) {
//            result = mSpeech.setLanguage(Locale.ROOT);
//        } else if (lang.equals("SIMPLIFIED_CHINESE")) {
//            result = mSpeech.setLanguage(Locale.SIMPLIFIED_CHINESE);
//        } else if (lang.equals("*")) {
//            result = mSpeech.setLanguage(Locale.*);
//        } else if (lang.equals("TRADITIONAL_CHINESE")) {
//            result = mSpeech.setLanguage(Locale.TRADITIONAL_CHINESE);
//        } else if (lang.equals("UK")) {
//            result = mSpeech.setLanguage(Locale.UK);
//        } else if (lang.equals("US")) {
//            result = mSpeech.setLanguage(Locale.US);
//        }
//        return result;
//    }

}

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_*"
                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,运行效果图


                       Android原生TTS的基本使用以及配合中文语音包实现中文TTS

                                                                                              主界面


Android原生TTS的基本使用以及配合中文语音包实现中文TTS

                                                                                       运行日志


Android原生TTS的基本使用以及配合中文语音包实现中文TTS

                                                                                      保存的音频文件


注:

(1)这里我把语速,语调做了上限2.0,事实不是这样的

(2)这里没有展示具体中文TTS引擎配置,比较简单,上面说了步骤,只是没有附图

(3)很多其他好玩的东西,可以自己结合兴趣和API学习

(4)关于语音识别,语音合成,语义识别,包括上下文等等和语音相关以及人际交互相关的应用知识,这段时间看了不少,也写过一些博客,有兴趣的朋友可以参考一下。当然,我只是停留在应用层。