Android App开发语音处理之系统自带的语音引擎、文字转语音、语音识别的讲解及实战(超详细 附源码)

简介: Android App开发语音处理之系统自带的语音引擎、文字转语音、语音识别的讲解及实战(超详细 附源码)

需要源码请点赞关注收藏后评论区留下QQ~~~

一、系统自带的语音引擎

语音播报的本质是将书面文字转换成自然语言的音频流,这个转换操作被称作语音合成,又称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(); // 销毁语音识别器
        }
    }
}

创作不易 觉得有帮助请点赞关注收藏~~~

相关实践学习
达摩院智能语音交互 - 声纹识别技术
声纹识别是基于每个发音人的发音器官构造不同,识别当前发音人的身份。按照任务具体分为两种: 声纹辨认:从说话人集合中判别出测试语音所属的说话人,为多选一的问题 声纹确认:判断测试语音是否由目标说话人所说,是二选一的问题(是或者不是) 按照应用具体分为两种: 文本相关:要求使用者重复指定的话语,通常包含与训练信息相同的文本(精度较高,适合当前应用模式) 文本无关:对使用者发音内容和语言没有要求,受信道环境影响比较大,精度不高 本课程主要介绍声纹识别的原型技术、系统架构及应用案例等。 讲师介绍: 郑斯奇,达摩院算法专家,毕业于美国哈佛大学,研究方向包括声纹识别、性别、年龄、语种识别等。致力于推动端侧声纹与个性化技术的研究和大规模应用。
相关文章
|
14天前
|
XML 搜索推荐 Android开发
Android改变进度条控件progressbar的样式(根据源码修改)
本文介绍了如何基于Android源码自定义ProgressBar样式。首先分析了系统源码中ProgressBar样式的定义,发现其依赖一张旋转图片实现动画效果。接着分两步指导开发者实现自定义:1) 模仿源码创建一个旋转动画XML文件(放置在drawable文件夹),修改图片为自定义样式;2) 在UI控件中通过`indeterminateDrawable`属性应用该动画。最终实现简单且个性化的ProgressBar效果,附带效果图展示。
|
14天前
|
Android开发 开发者
Android企业级实战-界面篇-3
本文是《Android企业级实战-界面篇》系列的第三篇,主要介绍分割线和条形跳转框的实现方法,二者常用于设置和个人中心界面。文章通过具体代码示例展示了如何实现这两种UI组件,并提供了效果图。实现前需准备`dimens.xml`、`ids.xml`、`colors.xml`等文件,部分资源可参考系列第一、二篇文章。代码中详细说明了布局文件的配置,如分割线的样式定义和条形跳转框的组件组合,帮助开发者快速上手并应用于实际项目中。
|
3月前
|
JavaScript Linux 网络安全
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
521 76
|
14天前
|
XML Android开发 数据格式
Android企业级实战-界面篇-2
本文为《Android企业级实战-界面篇》系列第二篇,主要介绍三个UI模块的实现:用户资料模块、关注与粉丝统计模块以及喜欢和收藏功能模块。通过详细的XML代码展示布局设计,包括dimens、ids、colors配置文件的使用,帮助开发者快速构建美观且功能齐全的界面。文章结合实际效果图,便于理解和应用。建议配合第一篇文章内容学习,以获取完整工具类支持。
|
14天前
|
算法 Java Android开发
Android企业级实战-界面篇-1
本文详细介绍了Android企业级开发中界面实现的过程,涵盖效果展示、实现前准备及代码实现。作者通过自身经历分享了Android开发经验,并提供了`dimens.xml`、`ids.xml`、`colors.xml`和`strings.xml`等配置文件内容,帮助开发者快速构建规范化的UI布局。文章以一个具体的用户消息界面为例,展示了如何使用线性布局(LinearLayout)和相对布局(RelativeLayout)实现功能模块排列,并附带注意事项及使用方法,适合初学者和进阶开发者参考学习。
|
2月前
|
NoSQL 应用服务中间件 PHP
布谷一对一直播源码android版环境配置流程及功能明细
部署需基于 CentOS 7.9 系统,硬盘不低于 40G,使用宝塔面板安装环境,包括 PHP 7.3(含 Redis、Fileinfo 扩展)、Nginx、MySQL 5.6、Redis 和最新 Composer。Swoole 扩展需按步骤配置。2021.08.05 后部署需将站点目录设为 public 并用 ThinkPHP 伪静态。开发环境建议 Windows 操作系统与最新 Android Studio,基础配置涉及 APP 名称修改、接口域名更换、包名调整及第三方登录分享(如 QQ、微信)的配置,同时需完成阿里云与腾讯云相关设置。
|
4月前
|
Android开发 开发者 Kotlin
Android实战经验之Kotlin中快速实现MVI架构
MVI架构通过单向数据流和不可变状态,提供了一种清晰、可预测的状态管理方式。在Kotlin中实现MVI架构,不仅提高了代码的可维护性和可测试性,还能更好地应对复杂的UI交互和状态管理。通过本文的介绍,希望开发者能够掌握MVI架构的核心思想,并在实际项目中灵活应用。
122 8
|
11月前
|
机器学习/深度学习 自然语言处理 算法
未来语音交互新纪元:FunAudioLLM技术揭秘与深度评测
人类自古以来便致力于研究自身并尝试模仿,早在2000多年前的《列子·汤问》中,便记载了巧匠们创造出能言善舞的类人机器人的传说。
12938 116
|
7月前
|
机器学习/深度学习 人工智能 自然语言处理
医疗行业的语音识别技术解析:AI多模态能力平台的应用与架构
AI多模态能力平台通过语音识别技术,实现实时转录医患对话,自动生成结构化数据,提高医疗效率。平台具备强大的环境降噪、语音分离及自然语言处理能力,支持与医院系统无缝集成,广泛应用于门诊记录、多学科会诊和急诊场景,显著提升工作效率和数据准确性。
550 3
|
7月前
|
机器学习/深度学习 自然语言处理 搜索推荐
智能语音交互技术:构建未来人机沟通新桥梁####
【10月更文挑战第28天】 本文深入探讨了智能语音交互技术的发展历程、当前主要技术框架、核心算法原理及其在多个领域的应用实例,旨在为读者提供一个关于该技术全面而深入的理解。通过分析其面临的挑战与未来发展趋势,本文还展望了智能语音交互技术如何继续推动人机交互方式的革新,以及它在未来社会中的潜在影响。 ####
628 0