嵌入式项目之Android语音识别——读后听写语音识别(语音识别功能主函数调用)


文章目录


前言

最近在学习嵌入式系统的一个功能——导航语音识别,该功能是基于Android和科大讯飞语音识别语音合成,利用RFID射频识别技术实现的功能,整个导航语音识别功能包括语音识别、语音合成、导航听写、语音提示语音识别的语音合成三大模块。这里我整理了第三模块——读后听写语音识别。该模块是对前面两个模块的调用,也就是导航语音识别功能主函数调用。


一、代码示例

//读完听写的语音合成
private void speakText(String textBeSpeak) {

    SpeechUtility.createUtility(MainActivity.this, SpeechConstant.APPID
            + "=" + APPID);
    //1. 创建 SpeechSynthesizer 对象 , 第二个参数: 本地合成时传 InitListener
    SpeechSynthesizer mTts = SpeechSynthesizer.createSynthesizer( this, null);
    //2.合成参数设置,详见《 MSC Reference Manual》 SpeechSynthesizer 类
    //设置发音人(更多在线发音人,用户可参见)
    mTts.setParameter(SpeechConstant. VOICE_NAME, "xiaoyan" ); // 设置发音人
    mTts.setParameter(SpeechConstant. SPEED, "20" );// 设置语速
    mTts.setParameter(SpeechConstant. VOLUME, "80" );// 设置音量,范围 0~100
    mTts.setParameter(SpeechConstant. ENGINE_TYPE, SpeechConstant. TYPE_CLOUD); //设置云端
    //设置合成音频保存位置(可自定义保存位置),保存在 “./sdcard/iflytek.pcm”
    //保存在 SD 卡需要在 AndroidManifest.xml 添加写 SD 卡权限
    //仅支持保存为 pcm 和 wav 格式, 如果不需要保存合成音频,注释该行代码
    //mTts.setParameter(SpeechConstant. TTS_AUDIO_PATH, "./sdcard/iflytek.pcm" );
    //3.开始合成
    mTts.startSpeaking( textBeSpeak, new MySynthesizerListener()) ;
}
class MySynthesizerListener implements SynthesizerListener {

    @Override
    public void onSpeakBegin() {
        // Log.d(TAG," 开始播放 ");
    }

    @Override
    public void onSpeakPaused() {
        // showTip(" 暂停播放 ");
    }

    @Override
    public void onSpeakResumed() {
        // showTip(" 继续播放 ");
    }

    @Override
    public void onBufferProgress(int percent, int beginPos, int endPos ,
                                 String info) {
        // 合成进度
    }

    @Override
    public void onSpeakProgress(int percent, int beginPos, int endPos) {
        // 播放进度
    }

    @Override
    public void onCompleted(SpeechError error) {
        if (error == null) {
            Speech();
        } else if (error != null ) {
            // showTip(error.getPlainDescription( true));
        }
    }

    @Override
    public void onEvent(int eventType, int arg1 , int arg2, Bundle obj) {

    }
}

private void speakText1(String textBeSpeak) {

    SpeechUtility.createUtility(MainActivity.this, SpeechConstant.APPID
            + "=" + APPID);
    //1. 创建 SpeechSynthesizer 对象 , 第二个参数: 本地合成时传 InitListener
    SpeechSynthesizer mTts = SpeechSynthesizer.createSynthesizer( this, null);
    //2.合成参数设置,详见《 MSC Reference Manual》 SpeechSynthesizer 类
    //设置发音人(更多在线发音人,用户可参见)
    mTts.setParameter(SpeechConstant. VOICE_NAME, "xiaoyan" ); // 设置发音人
    mTts.setParameter(SpeechConstant. SPEED, "20" );// 设置语速
    mTts.setParameter(SpeechConstant. VOLUME, "80" );// 设置音量,范围 0~100
    mTts.setParameter(SpeechConstant. ENGINE_TYPE, SpeechConstant. TYPE_CLOUD); //设置云端
    //设置合成音频保存位置(可自定义保存位置),保存在 “./sdcard/iflytek.pcm”
    //保存在 SD 卡需要在 AndroidManifest.xml 添加写 SD 卡权限
    //仅支持保存为 pcm 和 wav 格式, 如果不需要保存合成音频,注释该行代码
    //mTts.setParameter(SpeechConstant. TTS_AUDIO_PATH, "./sdcard/iflytek.pcm" );
    //3.开始合成
    mTts.startSpeaking( textBeSpeak, new MySynthesizerListener1()) ;
}
class MySynthesizerListener1 implements SynthesizerListener {

    @Override
    public void onSpeakBegin() {
        // Log.d(TAG," 开始播放 ");
    }

    @Override
    public void onSpeakPaused() {
        // showTip(" 暂停播放 ");
    }

    @Override
    public void onSpeakResumed() {
        // showTip(" 继续播放 ");
    }

    @Override
    public void onBufferProgress(int percent, int beginPos, int endPos ,
                                 String info) {
        // 合成进度
    }

    @Override
    public void onSpeakProgress(int percent, int beginPos, int endPos) {
        // 播放进度
    }

    @Override
    public void onCompleted(SpeechError error) {
        if (error == null) {
            daoda = false;
            vitSpeech();
        }
        else if (error != null ) {
            // showTip(error.getPlainDescription( true));
        }
    }
    @Override
    public void onEvent(int eventType, int arg1 , int arg2, Bundle obj) {
    }
}

private void speakText2(String textBeSpeak) {

    SpeechUtility.createUtility(MainActivity.this, SpeechConstant.APPID
            + "=" + APPID);
    //1. 创建 SpeechSynthesizer 对象 , 第二个参数: 本地合成时传 InitListener
    SpeechSynthesizer mTts = SpeechSynthesizer.createSynthesizer( this, null);
    //2.合成参数设置,详见《 MSC Reference Manual》 SpeechSynthesizer 类
    //设置发音人(更多在线发音人,用户可参见)
    mTts.setParameter(SpeechConstant. VOICE_NAME, "xiaoyan" ); // 设置发音人
    mTts.setParameter(SpeechConstant. SPEED, "20" );// 设置语速
    mTts.setParameter(SpeechConstant. VOLUME, "80" );// 设置音量,范围 0~100
    mTts.setParameter(SpeechConstant. ENGINE_TYPE, SpeechConstant. TYPE_CLOUD); //设置云端
    //设置合成音频保存位置(可自定义保存位置),保存在 “./sdcard/iflytek.pcm”
    //保存在 SD 卡需要在 AndroidManifest.xml 添加写 SD 卡权限
    //仅支持保存为 pcm 和 wav 格式, 如果不需要保存合成音频,注释该行代码
    //mTts.setParameter(SpeechConstant. TTS_AUDIO_PATH, "./sdcard/iflytek.pcm" );
    //3.开始合成
    mTts.startSpeaking( textBeSpeak, new MySynthesizerListener2()) ;
}
class MySynthesizerListener2 implements SynthesizerListener {

    @Override
    public void onSpeakBegin() {
        // Log.d(TAG," 开始播放 ");
    }

    @Override
    public void onSpeakPaused() {
        // showTip(" 暂停播放 ");
    }

    @Override
    public void onSpeakResumed() {
        // showTip(" 继续播放 ");
    }

    @Override
    public void onBufferProgress(int percent, int beginPos, int endPos ,
                                 String info) {
        // 合成进度
    }

    @Override
    public void onSpeakProgress(int percent, int beginPos, int endPos) {
        // 播放进度
    }

    @Override
    public void onCompleted(SpeechError error) {
        if (error == null) {
            if(kaiqi){
                route();
            }else{
                route1();
            }
        }
        else if (error != null ) {
        }
    }
    @Override
    public void onEvent(int eventType, int arg1 , int arg2, Bundle obj) {
    }
}

private void speakText4(String textBeSpeak) {

    SpeechUtility.createUtility(MainActivity.this, SpeechConstant.APPID
            + "=" + APPID);
    //1. 创建 SpeechSynthesizer 对象 , 第二个参数: 本地合成时传 InitListener
    SpeechSynthesizer mTts = SpeechSynthesizer.createSynthesizer( this, null);
    //2.合成参数设置,详见《 MSC Reference Manual》 SpeechSynthesizer 类
    //设置发音人(更多在线发音人,用户可参见)
    mTts.setParameter(SpeechConstant. VOICE_NAME, "xiaoyan" ); // 设置发音人
    mTts.setParameter(SpeechConstant. SPEED, "20" );// 设置语速
    mTts.setParameter(SpeechConstant. VOLUME, "80" );// 设置音量,范围 0~100
    mTts.setParameter(SpeechConstant. ENGINE_TYPE, SpeechConstant. TYPE_CLOUD); //设置云端
    //设置合成音频保存位置(可自定义保存位置),保存在 “./sdcard/iflytek.pcm”
    //保存在 SD 卡需要在 AndroidManifest.xml 添加写 SD 卡权限
    //仅支持保存为 pcm 和 wav 格式, 如果不需要保存合成音频,注释该行代码
    //mTts.setParameter(SpeechConstant. TTS_AUDIO_PATH, "./sdcard/iflytek.pcm" );
    //3.开始合成
    mTts.startSpeaking( textBeSpeak, new MySynthesizerListener4()) ;
}
class MySynthesizerListener4 implements SynthesizerListener {

    @Override
    public void onSpeakBegin() {
        // Log.d(TAG," 开始播放 ");
    }

    @Override
    public void onSpeakPaused() {
        // showTip(" 暂停播放 ");
    }

    @Override
    public void onSpeakResumed() {
        // showTip(" 继续播放 ");
    }

    @Override
    public void onBufferProgress(int percent, int beginPos, int endPos ,
                                 String info) {
        // 合成进度
    }

    @Override
    public void onSpeakProgress(int percent, int beginPos, int endPos) {
        // 播放进度
    }

    @Override
    public void onCompleted(SpeechError error) {
        if (error == null) {
            Speech2();
        } else if (error != null ) {
            // showTip(error.getPlainDescription( true));
        }
    }

    @Override
    public void onEvent(int eventType, int arg1 , int arg2, Bundle obj) {

    }
}

private void speakText3(String textBeSpeak) {

    SpeechUtility.createUtility(MainActivity.this, SpeechConstant.APPID
            + "=" + APPID);
    //1. 创建 SpeechSynthesizer 对象 , 第二个参数: 本地合成时传 InitListener
    SpeechSynthesizer mTts = SpeechSynthesizer.createSynthesizer( this, null);
    //2.合成参数设置,详见《 MSC Reference Manual》 SpeechSynthesizer 类
    //设置发音人(更多在线发音人,用户可参见)
    mTts.setParameter(SpeechConstant. VOICE_NAME, "xiaoyan" ); // 设置发音人
    mTts.setParameter(SpeechConstant. SPEED, "20" );// 设置语速
    mTts.setParameter(SpeechConstant. VOLUME, "80" );// 设置音量,范围 0~100
    mTts.setParameter(SpeechConstant. ENGINE_TYPE, SpeechConstant. TYPE_CLOUD); //设置云端
    //设置合成音频保存位置(可自定义保存位置),保存在 “./sdcard/iflytek.pcm”
    //保存在 SD 卡需要在 AndroidManifest.xml 添加写 SD 卡权限
    //仅支持保存为 pcm 和 wav 格式, 如果不需要保存合成音频,注释该行代码
    //mTts.setParameter(SpeechConstant. TTS_AUDIO_PATH, "./sdcard/iflytek.pcm" );
    //3.开始合成
    mTts.startSpeaking( textBeSpeak, new MySynthesizerListener2()) ;
}
class MySynthesizerListener3 implements SynthesizerListener {

    @Override
    public void onSpeakBegin() {
        // Log.d(TAG," 开始播放 ");
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onSpeakPaused() {
        // showTip(" 暂停播放 ");
    }

    @Override
    public void onSpeakResumed() {
        // showTip(" 继续播放 ");
    }

    @Override
    public void onBufferProgress(int percent, int beginPos, int endPos ,
                                 String info) {
        // 合成进度
    }

    @Override
    public void onSpeakProgress(int percent, int beginPos, int endPos) {
        // 播放进度
    }

    @Override
    public void onCompleted(SpeechError error) {
        if (error == null) {
           notifyAll();
        }
        else if (error != null ) {
        }
    }
    @Override
    public void onEvent(int eventType, int arg1 , int arg2, Bundle obj) {
    }
}

@Override
protected void onDestroy()
{
    super.onDestroy();
    //解除广播接收器
    unregisterReceiver(mGattUpdateReceiver);
    mBluetoothLeService = null;
}

// Activity出来时候,绑定广播接收器,监听蓝牙连接服务传过来的事件
@Override
protected void onResume()
{
    super.onResume();
    //绑定广播接收器
    registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
    if (mBluetoothLeService != null)
    {
        //根据蓝牙地址,建立连接
        final boolean result = mBluetoothLeService.connect(mDeviceAddress);
    }
}

/* 意图过滤器 */
private static IntentFilter makeGattUpdateIntentFilter()
{
    final IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
    intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
    intentFilter
            .addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
    intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
    return intentFilter;
}
/* BluetoothLeService绑定的回调函数 */
private final ServiceConnection mServiceConnection = new ServiceConnection()
{

    @Override
    public void onServiceConnected(ComponentName componentName,
                                   IBinder service)
    {
        mBluetoothLeService = ((BluetoothLeService.LocalBinder) service)
                .getService();
        if (!mBluetoothLeService.initialize())
        {
            //Log.e(TAG, "Unable to initialize Bluetooth");
            finish();
        }
        mBluetoothLeService.connect(mDeviceAddress);
    }
    @Override
    public void onServiceDisconnected(ComponentName componentName)
    {
        mBluetoothLeService = null;
    }
};

/**
 * 广播接收器,负责接收BluetoothLeService类发送的数据
 */
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        final String action = intent.getAction();
        if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action))//Gatt连接成功
        {
            mConnected = true;
            //status = "connected";
            //更新连接状态
            //updateConnectionState(status);
            System.out.println("BroadcastReceiver :" + "device connected");

        } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED//Gatt连接失败
                .equals(action))
        {
            mConnected = false;
            //status = "disconnected";
            //更新连接状态
            //updateConnectionState(status);
            System.out.println("BroadcastReceiver :"
                    + "device disconnected");

        } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED//发现GATT服务器
                .equals(action))
        {
            // Show all the supported services and characteristics on the
            // user interface.
            //获取设备的所有蓝牙服务
            displayGattServices(mBluetoothLeService
                    .getSupportedGattServices());
            System.out.println("BroadcastReceiver :"
                    + "device SERVICES_DISCOVERED");
        } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action))//有效数据
        {
            //处理发送过来的数据
            try {
                if (intent.getExtras().getString(
                        BluetoothLeService.EXTRA_DATA)!=null) {
                    displayData(intent.getExtras().getString(
                            BluetoothLeService.EXTRA_DATA), intent);
                    System.out.println("BroadcastReceiver onData:"
                            + intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
};

 语音合成:

依据科大讯飞的语音在线合成技术得以实现。在科大讯飞服务器申请了集成语音在线转写和语音在线合成两大功能的应用,所以仅需要同一个APPID,导入到同一个SDK即可。开发过程中,我们将SDK源码中关于语音合成的APPID更改为我们系统的ID,在activity文件中,设置语音合成的参数,其中需要设置参数包括发音人、语调、语速、音量等。由于采用的是科大讯飞语音在线合成功能,因此,APP应用需要在联网的情况下使用,通过调用语音合成方法,将该方法中的参数,即合成内容,发送到科大讯飞与服务器中,并接收服务器返回的音频文件进行播放。

 

二、代码分析

1.创建 SpeechSynthesizer 对象

SpeechSynthesizer mTts = SpeechSynthesizer.createSynthesizer( this, null);

 <!--语音合成,又称文语转换(Text to Speech,TTS)技术,解决的主要问题 是如何将文字信息转化为可听的声音信息(即音频数据)。-->

2.设置合成参数

//2.合成参数设置,详见《 MSC Reference Manual》 SpeechSynthesizer 类
//设置发音人(更多在线发音人,用户可参见)
mTts.setParameter(SpeechConstant. VOICE_NAME, "xiaoyan" ); // 设置发音人
mTts.setParameter(SpeechConstant. SPEED, "20" );// 设置语速
mTts.setParameter(SpeechConstant. VOLUME, "80" );// 设置音量,范围 0~100
mTts.setParameter(SpeechConstant. ENGINE_TYPE, SpeechConstant. TYPE_CLOUD); //设置云端
//设置合成音频保存位置(可自定义保存位置),保存在 “./sdcard/iflytek.pcm”
//保存在 SD 卡需要在 AndroidManifest.xml 添加写 SD 卡权限
//仅支持保存为 pcm 和 wav 格式, 如果不需要保存合成音频,注释该行代码
//mTts.setParameter(SpeechConstant. TTS_AUDIO_PATH, "./sdcard/iflytek.pcm" );

其中:

<!--VOICE_NAME-->

  public static final java.lang.String VOICE_NAME

<!--合成发音人-->

<!--通过此参数,在合成中使用不同的语言和方言性别等。设置发音人,语速、语调、音量,在一次合成时开始后,就会在合成的音频中生效,此时,若要中途改变这些参数,则应该从想要改变的文本处重新开始合成,因为已经返回的音频 是无法再改变的。-->

<!--默认值: xiaoyan-->-->

<!--SPEED-->

 public static final java.lang.String SPEED

<!合成语速-->

<!--通过此参数,设置合成返回音频的语速。-->

<!--是否必须设置:否-->

<!--默认值:50-->

<!--值范围:[0, 100]-->

<!--VOLUME-->

 public static final java.lang.String VOLUME

<!合成音量-->

<!--通过此参数,设置合成返回音频的音量。合成音量,影响的是合成到的音频本身的 音量大小(振幅),而非播放时系统的音量。关于播放时操作系统的音量,则请自行 查找操作系统相关API开发接口说明。-->

<!--是否必须设置:否-->

<!--默认值:50-->

<!--值范围:[0, 100]-->

<!--ENGINE_TYPE-->

public static final java.lang.String ENGINE_TYPE

<!引擎类型-->

<!--设置使用的引擎类型:在线、离线。在申请了离线合成资源和权限, 可以选择使用本地或在线的方式进行语音服务。-->

<!--使用在线模式([`TYPE_CLOUD`]又称云端模式)时,需要使用网络,产生一定流量,但有更好的识别 或合成的效果,如更高的识别匹配度,更多的发音人等。-->

<!--使用离线模式([`TYPE_LOCAL`]又称本地模式)时,不需要使用网络,且识别和合成的速度更快。-->

3.开始合成

mTts.startSpeaking( textBeSpeak, new MySynthesizerListener()) ;

<!--startSpeaking-->

public int startSpeaking(java.lang.String text,
                         SynthesizerListener listener)

参数: text - 待合成的文本 listener - 合成状态监听器

<!--开始合成 调用此函数,开始合成文本并播放音频。-->

4.设置合成状态监听器

class MySynthesizerListener implements SynthesizerListener {

    @Override
    public void onSpeakBegin() {
        // Log.d(TAG," 开始播放 ");
    }

    @Override
    public void onSpeakPaused() {
        // showTip(" 暂停播放 ");
    }

    @Override
    public void onSpeakResumed() {
        // showTip(" 继续播放 ");
    }

    @Override
    public void onBufferProgress(int percent, int beginPos, int endPos ,
                                 String info) {
        // 合成进度
    }

    @Override
    public void onSpeakProgress(int percent, int beginPos, int endPos) {
        // 播放进度
    }

    @Override
    public void onCompleted(SpeechError error) {
        if (error == null) {
            Speech();
        } else if (error != null ) {
            // showTip(error.getPlainDescription( true));
        }
    }

    @Override
    public void onEvent(int eventType, int arg1 , int arg2, Bundle obj) {

    }
}

 其中Speech()在首页语音识别合成中已经介绍。

5. 五个speakText类函数的作用

相信大家在刚看到代码时,会很纳闷,为啥要定义四个相似的函数,但是请仔细分析代码,这五个函数其实用于四个不同功能函数的调用。也就是对前两个板块的总结。

5.1 speakText()的作用

代码示例

private void speakText(String textBeSpeak) {
    ...
    mTts.startSpeaking( textBeSpeak, new MySynthesizerListener()) ;
}
class MySynthesizerListener implements SynthesizerListener {
   ...
    @Override
    public void onCompleted(SpeechError error) {
        if (error == null) {
            Speech();
        } else if (error != null ) {
            // showTip(error.getPlainDescription( true));
        }
    }
   ...
}

该函数调用功能函数Speech(),其作用是:执行通过rfid射频技术实现的确定当前位置。(Speech()具体代码在我另一篇博客——语音识别合成中)

5.2 speakText1()的作用

代码示例

private void speakText1(String textBeSpeak) {
    ...
    mTts.startSpeaking( textBeSpeak, new MySynthesizerListener1()) ;
}
class MySynthesizerListener1 implements SynthesizerListener {
   ...
    @Override
    public void onCompleted(SpeechError error) {
        if (error == null) {
            daoda = false;
            vitSpeech();
        }
        else if (error != null ) {
            // showTip(error.getPlainDescription( true));
        }
    }
   ...
}

该函数调用功能函数vitSpeech(),其作用是:导航听写,利用rfid射频技术实现的确定当前位置并获取目的地。

5.3 speakText2()的作用

代码示例

private void speakText2(String textBeSpeak) {
    ...
    mTts.startSpeaking( textBeSpeak, new MySynthesizerListener2()) ;
}
class MySynthesizerListener2 implements SynthesizerListener {
​
    ...
    @Override
    public void onCompleted(SpeechError error) {
        if (error == null) {
            if(kaiqi){
                route();
            }else{
                route1();
            }
        }
        else if (error != null ) {
        }
    }
  ...
}

该函数根据if条件判断分别调用route()和route1(),其作用是:导向方法,其中route()和route1()的区别在route()增加开启识别前方障碍物并打开相机。

  if (!takephoto && distance <= 50) {
                        wancheng = true;
                        takephoto = true;
                        try {
                            speakText2("识别到前方有障碍物。正在识别中,请稍候!");
                            sleep(1000);
                        } catch (InterruptedException e) {
                            //InterruptedException 异常处理
                            //printStackTrace()方法的意思是:在命令行打印异常信息在程序中出错的位置及原因。
                            e.printStackTrace();
                        } finally {
                            //打开相机
                            turnOnCamera();
//                            rev_tv.post(new Runnable() {
//                                @Override
//                                public void run() {
//                                    photo_btn.performClick();
//                                }
//                            });
                        }
                        while(wancheng){
​
                        }
                    }

5.4 speakText4()的作用

代码示例

private void speakText4(String textBeSpeak) {
    ...
    mTts.startSpeaking( textBeSpeak, new MySynthesizerListener4()) ;
}
class MySynthesizerListener4 implements SynthesizerListener {
    ...
    @Override
    public void onCompleted(SpeechError error) {
        if (error == null) {
            Speech2();
        } else if (error != null ) {
            // showTip(error.getPlainDescription( true));
        }
    }
   ...
}

该函数根据if条件判断当程序正常执行时,调用Speech2(),其作用是:启动路况识别功能并获取目的地。

5.5 speakText3()的作用

代码示例

private void speakText3(String textBeSpeak) {
    ...
    mTts.startSpeaking( textBeSpeak, new MySynthesizerListener2()) ;
}
class MySynthesizerListener3 implements SynthesizerListener {
   ...
    @Override
    public void onCompleted(SpeechError error) {
        if (error == null) {
           notifyAll();
        }
        else if (error != null ) {
        }
    }
    ...
}

该函数根据if条件判断程序正常执行时调用notifyAll(),其作用是:使得所有功能函数可再执行。

其中:

Java Object notifyAll() 方法

用于唤醒在该对象上等待的所有线程。notifyAll() 方法跟 notify() 方法一样,区别在于 notifyAll() 方法唤醒在此对象监视器上等待的所有线程,notify() 方法是一个线程。如果当前线程不是对象监视器的所有者,那么调用 notifyAll() 方法同样会发生 IllegalMonitorStateException 异常。(确保所有线程都有一次再执行的权力)


总结

嵌入式系统,基于Android+科大讯飞语音识别、语音合成、语音提示技术以及RFID射频识别技术、蓝牙连接、路况标识等功能集合而成的导航语音识别合成之读后听写语音识别(功能函数调用)

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐