精华内容
下载资源
问答
  • 次见到钰莹的时候,她正在海天瑞声的录音间里拍摄工作场景。 齐肩的短发,粉红色的长裙,周身被录音间里的暖色光所包围。...在此之前,阿里人工智能实验室给媒体们寄送了一个特别的邀请函:一个黑色扁...

    第一次见到钰莹的时候,她正在海天瑞声的录音间里拍摄工作场景。

    齐肩的短发,粉红色的长裙,周身被录音间里的暖色光所包围。在一切准备完毕后,她朝着镜头比着“OK”的手势,笑的很甜。

    智能音箱背后的声优:一百多人里挑出一个声音 两个月录了20万字

    百里挑一的“我在,你说”

    7月5日,阿里推出的智能音箱“天猫精灵X1”在北京时间博物馆举行了一场发布会。在此之前,阿里人工智能实验室给媒体们寄送了一个特别的邀请函:一个黑色扁平的圆片,上面印有“未来,开口即来”的字样,并装有一个按钮,按下去后会发出温柔的女声:“我在,你说”。

    这个声音,也是钰莹与天猫精灵的“第一次接触”。

    今年5月,朋友给钰莹介绍了海天瑞声的试音工作,她按照要求念了几段不同场景下的内容。回忆起试音过程,钰莹表示,“当时念完之后没觉得有什么不同,还是正常工作。”两三天之后,钰莹又被叫过去试了第二次音,之后又去了第三次。说到自己被录取,钰莹表示:迷迷糊糊的。就这样,试了三次音,她被幸运地留下了。

    被问及为什么会挑中钰莹,阿里方面则表示,选中钰莹还是和产品调性有关,阿里想让这个音箱拥有“陪伴”的属性,要让“我在,你说”具有亲切感。“其实在此之前我们已经挑选了不下百人了,不管是风格还是档期都没有合适的,但是听到这个声音就给我们一种‘山重水复疑无路,柳暗花明又一村’的感觉。”海天瑞声方面表示。

    钰莹此前也曾为苹果的apple watch功能视频广告配过音,但这么长周期地参与数码产品的配音还是第一次。

    按照规定,钰莹在发布会前是不知道产品信息的。直到7月4日天猫精灵发布会彩排之前,钰莹都不知道自己是为什么东西配音。“只是觉得像siri,淘宝小秘书,因为有快递信息,天气查询这样的文本。当时看到了天猫精灵的文本,但我也不知道那是啥。”

    两个月与20万字

    一直到“天猫精灵”发布会的当天,钰莹才第一次见到了这个与自己有着同样声音的小东西。第一次和“自己”进行了对话的感觉非常奇妙,“也觉得很有成就感,可以让自己的声音陪伴那么多人。”

    智能音箱背后的声优:一百多人里挑出一个声音 两个月录了20万字

    (钰莹在录音间里和天猫精灵互动)

    获得这份成就感尤为不易,因为,这是一次长达两个月的战斗。

    雷锋网记者在采访中了解到,此次天猫精灵的语音录制时间长达两个月,截至目前已经录制20多万字,并且还在进行中,而天猫精灵的整体目标是要做到2万句,30万字。

    这样的数据量其实是有深刻的行业背景的。

    海天瑞声方面的负责人向雷锋网介绍,2014年以前行业都是采用参数合成的技术,提取出声音的特征模拟生成,这需要声优的声音更加端庄、严肃。彼时的配音人员大多是播音员、各大院校播音主持专业学生、主持人等。录音库只需要4、5千句就行。但是从2014年以后,为了让合成的声音更自然,业内都采用了拼接算法,提取出原声中的片段进行合成,这就要求更大的数据量。“因此我们在挑选声音的时候行业就有更多的选择性,配音演员也有了更大的表现空间。”海天瑞声方面表示。

    对于一个人的日常对话来说,20万字的数据量并不是很大。但是,难就难在,这是为智能产品配音。钰莹说,“和影视剧完全不一样,给天猫精灵配音需要绝对的稳定性,语速要保持一致,而且情绪也不能有波动,因此效率不高。每次录三到四个小时,一个小时大概只有100句话左右,但能不能通过还得看录音师的判断。而且,这其中还有一部分英文,念英文的时候还有英文老师辅导,有时都不知道自己在读什么。”钰莹笑着和记者说道。

    智能音箱背后的声优:一百多人里挑出一个声音 两个月录了20万字

    (钰莹和录音师交流)

    一开始的工作强度比较大,海天瑞声方面也想要有更多的样本。

    据钰莹介绍,300-400句/天是一个配音演员比较正常的工作量,但是她那时候是一天录了500-600句,一连录了8天。录完之后她的嗓子就发炎了,只能在家休息。“期间还去了医院做雾化,休息了差不多有一周。休息的时候心里很着急,但是什么也做不了,只想着后面的工作能赶紧开。”

    聚光灯照不到的角落 

    7月31日采访当天正好是木村良平的生日,大批的粉丝在网络上为他送上祝福。说这个名字可能会有很多人不熟悉,但如果说到动漫《黑子的篮球》,不知道的人会少很多。木村良平是一名日本声优,在剧中为黄濑凉太配音。

    “声优”其实是舶来词,来源于日本,如果不是关注这个行业的话,很多人并不知道这个词是什么意思。在中国,更为人们接受的是“配音演员”一词。

    智能音箱背后的声优:一百多人里挑出一个声音 两个月录了20万字

    与“声优”一词带给人的陌生感一样,声优这个群体同样是处在“聚光灯照不到的角落”。配音演员一直都是一个幕后的工作,30年以前这份职业被称呼为表演艺术,在那个物质匮乏的时代,当有限的娱乐产品面对几亿观众突然释放出来的娱乐需求时,所产生的放大效应威力十足,童自荣、刘广宁、毕克等“国宝级”的配音演员一时间成为大众心中的明星人物。但随着市场的开放、英语的普及加上观众口味的变化,市场对中文对白译制片的需求越来越少,像童自荣这样靠一个“佐罗”就成为大众偶像的辉煌再难出现。

    现在的配音工作只是一份普通的职业。很多经典影视剧里面的演员都大红大紫,然而对幕后的配音员并没有多大的影响,上街也不用带上鸭舌帽和墨镜,不会被大家认出来。

    太热只能用冰块降温

    1989年,28岁的钰莹出生在吉林延边,大学就读于北京现代音乐学院。2012年毕业之后,钰莹就开始做配音演员,这一做就做了5年。在接受雷锋网(公众号:雷锋网)记者采访时,钰莹手里一直拿着一个水杯,时不时喝口水,“对配音演员来说,嗓子太重要了,随时带水杯已经成了我们的习惯了。虽然我自认为自己的嗓子比较皮实,但还是要喝。”

    钰莹已经在很多影视剧里面有过演出,“《青丘狐传说》萌狐篇的女一号婴宁,《寻找前世之旅》的女一号叶隐,《锻刀》的女二号沈佩琴,《大仙衙门》的反一号白锦绣,这些我都配过,还有最近上映的电影。”钰莹对自己的作品如数家珍,介绍时也是带着一脸的满足。

    但与行业经验和代表作品不匹配的是,报酬。

    五年的工作经验,放在北京的私企也可以做一个收入不错的小组长了。但一部戏里,台前明星百万至千万的报酬,幕后“音”雄却只能拿到他们的千分之一,这就是配音演员的收入现状。

    周星驰的御用配音演员石班瑜曾对媒体表示:“从我入行前10年开始算,到如今我入行30年,近40年的时间,录一部30分钟卡通片的薪水基本没有变化。1985年我配一集电视剧的价钱,到现在还是这样。但是其他行业的工资已经涨了几十倍了。”

    钰莹对于这种现象也表示无可奈何,“配音演员就是这样,我们不会有专职的配音,国内配音演员的待遇比较普通,跟演员比起来差太多了,给影视剧配音是按集收费的,我们这些普通人也就只有350块钱一集,不可能靠配音发家致富。2000块钱以上的都是大神级别了,全国就那么几个。”

    除了收入,配音演员的工作环境也是相当艰苦。“冬练三九夏练三伏”是他们的常态。为了保证配音效果,录音棚不但全封闭,还不能开电风扇和空调。有时因为情感起伏太大,加上空气不流通,配音演员偶尔还会发生缺氧晕厥的状况,但稍做休息后还得接着来。“像现在这种天气,录音棚里的温度比室外都高,要降温只能用冰块。”

    智能音箱背后的声优:一百多人里挑出一个声音 两个月录了20万字

    (钰莹在挑润喉片)

    此外,配音演员最怕的就是嗓子出问题。为了保护嗓子,钰莹在饮食和作息上也需要格外注意。“辛辣的东西不能吃,但我又是一个‘无肉不欢’的人,同时也需要早睡不能熬夜,不然会影响嗓子。但是像在北京这样的城市,你很难做到饮食和作息的规律。就比如我住在东北角的东坝,但是录音间在五道口这边,一趟地铁就得1个半小时。”

    “因为我喜欢配音”

     8月8日,天猫精灵智能音箱将会正式开售,预购量也达到了8000台,因此钰莹还要进行后续文本的扩展工作,但已经不像录制初期那么忙碌,她现在一周需要来三次录音棚,录大概100句话,然后休息一会儿。由于合同的原因,近两年里,钰莹都不可以用自己的本音去录制其他同类产品,但还是可以用其他诸如“男童”、“女童”等声音。

    钰莹养了一条狗,每天早上9点起床后,钰莹会和狗一块出去透透气,中午随便吃点,然后打车去录音棚录音。“以前为影视剧配音时是从12点开工到晚上11点左右收工。但是为天猫精灵配音时间会充裕一点,晚上回去也能看会儿电影。”

    智能音箱背后的声优:一百多人里挑出一个声音 两个月录了20万字

    随着市场对配音的需求和重视,配音演员的工作机会也越来越多。介绍钰莹参加天猫精灵配音的朋友,现在也正在配一款智能的儿童玩具熊的音。与此同时,这个超小众的群体也慢慢地走向了台前。

    先是模仿配音,“胥渡吧”的《还珠格格模仿配音系列》整个团队都上了春晚,然后是网络听书、电台(FM),喜马拉雅、荔枝、蜻蜓,几乎人人手机上都安装了至少一个。

    “可以看出来,这个市场的需求在慢慢扩大,圈子里的很多人也开始转向智能产品的录制。”

    钰莹的家人都在吉林延边的老家。被问及家人对钰莹工作的态度时,钰莹表示,家里人都很支持她的工作,这也是她坚持下去的一个重要原因,“有时候我妈看电视的时候会打电话问剧中的某个角色是不是我配的音,但通常她都听不准,哈哈。”

    钰莹有男朋友,她说选男朋友的其中一个标准就是他一定要接受自己的工作。幸运的是,男朋友很支持她。

    “做了五年的配音工作了,打算以后一直做这个吗?”

    “目前而言是的,因为我喜欢配音这个职业。”

    “配了这么多角色,有没有最想尝试的角色?”

    “唔...最想配那种藏着坏的人,这样的戏有爆发性,感觉很刺激,很爽。”

    接受完记者的采访后,钰莹又一头钻进了录音间。



    本文作者:李雨晨
    本文转自雷锋网禁止二次转载,原文链接
    展开全文
  • 对中文来说,和语音播报相关的一个技术是汉字转拼音,想想看,拼音本身就是音节拼读的标记,每音节对应段音频,那么句的拼音便能用一连串的音频流合成而来。汉字转拼音的说明参见《Android开发笔记(八十

    智能语音技术

    如今越来越多的app用到了语音播报功能,例如地图导航、天气预报、文字阅读、口语训练等等。语音技术主要分两块,一块是语音转文字,即语音识别;另一块是文字转语音,即语音合成。


    对中文来说,和语音播报相关的一个技术是汉字转拼音,想想看,拼音本身就是音节拼读的标记,每个音节对应一段音频,那么一句的拼音便能用一连串的音频流合成而来。汉字转拼音的说明参见《Android开发笔记(八十三)多语言支持》。


    语音合成通常也简称为TTS,即TextToSpeech(从文本到语言)。语音合成技术把文字智能地转化为自然语音流,当然为了避免机械合成的呆板和停顿感,语音引擎还得对语音流进行平滑处理,确保输出的语音音律流畅、感觉自然。


    TextToSpeech

    Android从1.6开始,就内置了语音合成引擎,即“Pico TTS”。该引擎支持英语、法语、德语、意大利语,但不支持中文,幸好Android从4.0开始允许接入第三方的语音引擎,因此只要我们安装了中文引擎,就能在代码中使用中文语音合成服务。例如,在各大应用市场上下载并安装科大讯飞+,然后在手机操作“系统设置”——“语言和输入法”——“文字转语音(TTS)输出”,如下图所示即可设置中文的语音引擎:



    Android的语音合成控件类名是TextToSpeech,下面是该类常用的方法说明:
    构造函数 : 第二个参数设置TTSListener对象,要重写onInit方法(通常在这里调用setLanguage方法,因为初始化成功后才能设置语言)。第三个参数设置语音引擎,默认是系统自带的pico,要获取系统支持的所有引擎可调用getEngines方法。
    setLanguage : 设置语言。英语为Locale.ENGLISH;法语为Locale.FRENCH;德语为Locale.GERMAN;意大利语为Locale.ITALIAN;汉语普通话为Locale.CHINA(需安装中文引擎,如科大讯飞+)。该方法的返回值有三个,0表示正常,-1表示缺失数据,-2表示不支持该语言。
    setSpeechRate : 设置语速。1.0正常语速;0.5慢一半的语速;2.0;快一倍的语速。
    setPitch : 设置音调。1.0正常音调;低于1.0的为低音;高于1.0的为高音。
    speak : 开始对指定文本进行语音朗读。
    synthesizeToFile : 把指定文本的朗读语音输出到文件。
    stop : 停止朗读。
    shutdown : 关闭语音引擎。
    isSpeaking : 判断是否在语音朗读。
    getLanguage : 获取当前的语言。
    getCurrentEngine : 获取当前的语音引擎。
    getEngines : 获取系统支持的所有语音引擎。


    下面是TextToSpeech处理语音合成的代码示例:
    import java.util.List;
    import java.util.Locale;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.speech.tts.TextToSpeech;
    import android.speech.tts.TextToSpeech.EngineInfo;
    import android.speech.tts.TextToSpeech.OnInitListener;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.AdapterView;
    import android.widget.ArrayAdapter;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.Spinner;
    import android.widget.Toast;
    import android.widget.AdapterView.OnItemSelectedListener;
    
    public class TTSActivity extends Activity implements OnClickListener {
    	
    	private TextToSpeech mSpeech;
    	private EditText et_tts_resource;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_tts);
    
    		et_tts_resource = (EditText) findViewById(R.id.et_tts_resource);
    		Button btn_tts_start = (Button) findViewById(R.id.btn_tts_start);
    		btn_tts_start.setOnClickListener(this);
    
    		initLanguageSpinner();
    		mSpeech = new TextToSpeech(TTSActivity.this, new TTSListener());
    	}
    	
    	private void initLanguageSpinner() {
    		ArrayAdapter<String> starAdapter = new ArrayAdapter<String>(this,
    				R.layout.spinner_item, mLangArray);
    		starAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
    		Spinner sp = (Spinner) findViewById(R.id.sp_tts_language);
    		sp.setPrompt("请选择语言");
    		sp.setAdapter(starAdapter);
    		sp.setOnItemSelectedListener(new LanguageSelectedListener());
    		sp.setSelection(0);
    	}
    
    	private String[] mEngineArray;
    	private int mEngine;
    	
    	private void initEngineSpinner() {
    		mEngineArray = new String[mEngineList.size()];
    		for(int i=0; i<mEngineList.size(); i++) {
    			mEngineArray[i] = mEngineList.get(i).label;
    		}
    		ArrayAdapter<String> starAdapter = new ArrayAdapter<String>(this,
    				R.layout.spinner_item, mEngineArray);
    		starAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
    		Spinner sp = (Spinner) findViewById(R.id.sp_tts_engine);
    		sp.setPrompt("请选择引擎");
    		sp.setAdapter(starAdapter);
    		sp.setOnItemSelectedListener(new EngineSelectedListener());
    		sp.setSelection(0);
    	}
    	
    	@Override
        protected void onDestroy() {
    		recycleSpeech();
            super.onDestroy();
        }
    	
    	private void recycleSpeech() {
            if (mSpeech != null) {
                mSpeech.stop();
                mSpeech.shutdown();
                mSpeech = null;
            }
    	}
    	
    	private String[] mLangArray = {"英语", "法语", "德语", "意大利语", "汉语普通话" };
    	private Locale[] mLocaleArray = {
    			Locale.ENGLISH, Locale.FRENCH, Locale.GERMAN, Locale.ITALIAN, Locale.CHINA };
    	private int mLanguage;
    	private String mTextEN = "hello world. This is a TTS demo.";
    	private String mTextCN = "白日依山尽,黄河入海流。欲穷千里目,更上一层楼。";
    	
    	private class LanguageSelectedListener implements OnItemSelectedListener {
    		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
    			mLanguage = arg2;
    			if (mLocaleArray[mLanguage]==Locale.SIMPLIFIED_CHINESE
    					|| mLocaleArray[mLanguage]==Locale.TRADITIONAL_CHINESE) {
    				et_tts_resource.setText(mTextCN);
    			} else {
    				et_tts_resource.setText(mTextEN);
    			}
            	if (mEngineList != null) {
        			resetLanguage();
            	}
    		}
    
    		public void onNothingSelected(AdapterView<?> arg0) {
    		}
    	}
    
    	private class EngineSelectedListener implements OnItemSelectedListener {
    		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
    			mEngine = arg2;
    			recycleSpeech();
    			mSpeech = new TextToSpeech(TTSActivity.this, new TTSListener(), 
    					mEngineList.get(mEngine).name);
    		}
    
    		public void onNothingSelected(AdapterView<?> arg0) {
    		}
    	}
    	
    	private void resetLanguage() {
            int result = mSpeech.setLanguage(mLocaleArray[mLanguage]);
            //如果打印为-2,说明不支持这种语言;-1说明缺失数据
            Toast.makeText(TTSActivity.this, "您选择的是"+mLangArray[mLanguage]
            		+",result="+result, Toast.LENGTH_SHORT).show();
            if (result == TextToSpeech.LANG_MISSING_DATA
                    || result == TextToSpeech.LANG_NOT_SUPPORTED) {
            }
    	}
    
    	@Override
    	public void onClick(View v) {
    		if (v.getId() == R.id.btn_tts_start) {
    			String content = et_tts_resource.getText().toString();
    			int result = mSpeech.speak(content, TextToSpeech.QUEUE_FLUSH, null); 
                Toast.makeText(TTSActivity.this, "speak result="+result, Toast.LENGTH_SHORT).show();
            }
    	}
    	
    	private List<EngineInfo> mEngineList;
    	private class TTSListener implements OnInitListener {
    
            @Override  
            public void onInit(int status) {
                if (status == TextToSpeech.SUCCESS) {
                	if (mEngineList == null) {
                    	mEngineList = mSpeech.getEngines();
                    	initEngineSpinner();
                	} else {
                    	resetLanguage();
                	}
                }
            }
            
        }
    }


    科大讯飞语音

    前面提到,只要安装了中文引擎,即可在TextToSpeech中使用中文语音;可是我们没法要求用户再额外下载一个app,正确的做法是在自己app中集成语音sdk。目前中文环境常见的语音sdk主要有科大讯飞、百度语音、捷通华声、云知声等等,开发者可自行选择一个。


    sdk集成

    科大讯飞语音sdk的集成步骤如下:
    1、导入sdk包到libs目录,包括libmsc.so、Msc.jar、Sunflower.jar;
    2、到讯飞网站注册并创建新应用,获得appid;
    3、自定义一个Application类,在onCreate函数中加入下面代码,注意appid值为第二步申请到的id:
    		SpeechUtility.createUtility(MainApplication.this, "appid=5763c4cf");
    4、在AndroidManifest.xml中加入必要的权限,以及自定义的Application类;
    5、根据demo工程编写代码与布局文件;
    6、如果使用了RecognizerDialog控件,则要把demo工程assets目录下的文件原样拷过来;
    7、在混淆打包的时候需要添加-keep class com.iflytek.**{*;},


    语音识别

    科大讯飞的语音识别用的是SpeechRecognizer类,主要方法如下:
    createRecognizer : 创建语音识别对象。
    setParameter : 设置语音识别的参数。常用参数包括:
    --SpeechConstant.ENGINE_TYPE : 设置听写引擎。TYPE_LOCAL表示本地,TYPE_CLOUD表示云端,TYPE_MIX表示混合。
    --SpeechConstant.RESULT_TYPE : 设置返回结果格式。json表示json格式。
    --SpeechConstant.LANGUAGE : 设置语言。zh_cn表示中文,en_us表示英文。
    --SpeechConstant.ACCENT : 设置方言。mandarin表示普通话,cantonese表示粤语,henanese表示河南话。
    --SpeechConstant.VAD_BOS : 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理。
    --SpeechConstant.VAD_EOS : 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入,自动停止录音。
    --SpeechConstant.ASR_PTT : 设置标点符号。0表示返回结果无标点,1表示返回结果有标点。
    --SpeechConstant.AUDIO_FORMAT : 设置音频的保存格式。
    --SpeechConstant.ASR_AUDIO_PATH : 设置音频的保存路径。
    --SpeechConstant.AUDIO_SOURCE : 设置音频的来源。-1表示音频流,与writeAudio配合使用;-2表示外部文件,同时设置ASR_SOURCE_PATH指定文件路径。
    --SpeechConstant.ASR_SOURCE_PATH : 设置外部音频文件的路径。
    startListening : 开始监听语音输入。参数为RecognizerListener对象,该对象需重写的方法包括:
    --onBeginOfSpeech : 内部录音机已经准备好了,用户可以开始语音输入。
    --onError : 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。
    --onEndOfSpeech : 检测到了语音的尾端点,已经进入识别过程,不再接受语音输入。
    --onResult : 识别结束,返回结果串。
    --onVolumeChanged : 语音输入过程中的音量大小变化。
    --onEvent : 事件处理,一般是业务出错等异常。
    stopListening : 结束监听语音。
    writeAudio : 把指定的音频流作为语音输入。
    cancel : 取消监听。
    destroy : 回收语音识别对象。


    下面是科大讯飞语音识别的运行截图:



    下面是科大讯飞语音识别的代码例子:
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.os.Bundle;
    import android.os.Environment;
    import android.util.Log;
    import android.view.View;
    import android.view.Window;
    import android.view.View.OnClickListener;
    import android.widget.EditText;
    import android.widget.Toast;
    
    import com.example.exmvoice.R;
    import com.example.exmvoice.SettingsActivity;
    import com.example.exmvoice.xunfei.util.FucUtil;
    import com.example.exmvoice.xunfei.util.JsonParser;
    import com.iflytek.cloud.ErrorCode;
    import com.iflytek.cloud.InitListener;
    import com.iflytek.cloud.RecognizerListener;
    import com.iflytek.cloud.RecognizerResult;
    import com.iflytek.cloud.SpeechConstant;
    import com.iflytek.cloud.SpeechError;
    import com.iflytek.cloud.SpeechRecognizer;
    import com.iflytek.cloud.ui.RecognizerDialog;
    import com.iflytek.cloud.ui.RecognizerDialogListener;
    
    public class XFRecognizeActivity extends Activity implements OnClickListener {
    	private final static String TAG = XFRecognizeActivity.class.getSimpleName();
    	// 语音听写对象
    	private SpeechRecognizer mRecognize;
    	// 语音听写UI
    	private RecognizerDialog mRecognizeDialog;
    	// 用HashMap存储听写结果
    	private HashMap<String, String> mRecognizeResults = new LinkedHashMap<String, String>();
    
    	private EditText mResultText;
    	private SharedPreferences mSharedPreferences;
    	
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		setContentView(R.layout.activity_xunfei_recognize);
    
    		mResultText = ((EditText) findViewById(R.id.xf_recognize_text));
    		findViewById(R.id.xf_recognize_start).setOnClickListener(this);
    		findViewById(R.id.xf_recognize_stop).setOnClickListener(this);
    		findViewById(R.id.xf_recognize_cancel).setOnClickListener(this);
    		findViewById(R.id.xf_recognize_stream).setOnClickListener(this);
    		findViewById(R.id.xf_recognize_setting).setOnClickListener(this);
    		mSharedPreferences = getSharedPreferences(SettingsActivity.PREFER_NAME, Activity.MODE_PRIVATE);
    		
    		// 初始化识别无UI识别对象,使用SpeechRecognizer对象,可根据回调消息自定义界面;
    		mRecognize = SpeechRecognizer.createRecognizer(this, mInitListener);
    		// 初始化听写Dialog,如果只使用有UI听写功能,无需创建SpeechRecognizer
    		// 使用UI听写功能,请将assets下文件拷贝到项目中
    		mRecognizeDialog = new RecognizerDialog(this, mInitListener);
    	}
    
    	@Override
    	protected void onDestroy() {
    		super.onDestroy();
    		// 退出时释放连接
    		mRecognize.cancel();
    		mRecognize.destroy();
    	}
    
    	@Override
    	public void onClick(View v) {
    		int ret = 0; // 函数调用返回值
    		int resid = v.getId();
    		if (resid == R.id.xf_recognize_setting) {  // 进入参数设置页面
    			Intent intent = new Intent(this, SettingsActivity.class);
    			intent.putExtra("type", SettingsActivity.XF_RECOGNIZE);
    			startActivity(intent);
    		} else if (resid == R.id.xf_recognize_start) {  // 开始听写。如何判断一次听写结束:OnResult isLast=true 或者 onError
    			mResultText.setText(null);// 清空显示内容
    			mRecognizeResults.clear();
    			// 设置参数
    			resetParam();
    			boolean isShowDialog = mSharedPreferences.getBoolean("show_dialog", true);
    			if (isShowDialog) {
    				// 显示听写对话框
    				mRecognizeDialog.setListener(mRecognizeDialogListener);
    				mRecognizeDialog.show();
    				showTip("请开始说话………");
    			} else {
    				// 不显示听写对话框
    				ret = mRecognize.startListening(mRecognizeListener);
    				if (ret != ErrorCode.SUCCESS) {
    					showTip("听写失败,错误码:" + ret);
    				} else {
    					showTip("请开始说话…");
    				}
    			}
    		} else if (resid == R.id.xf_recognize_stop) {  // 停止听写
    			mRecognize.stopListening();
    			showTip("停止听写");
    		} else if (resid == R.id.xf_recognize_cancel) {  // 取消听写
    			mRecognize.cancel();
    			showTip("取消听写");
    		} else if (resid == R.id.xf_recognize_stream) {  // 音频流识别
    			mResultText.setText(null);// 清空显示内容
    			mRecognizeResults.clear();
    			// 设置参数
    			resetParam();
    			// 设置音频来源为外部文件
    			mRecognize.setParameter(SpeechConstant.AUDIO_SOURCE, "-1");
    			// 也可以像以下这样直接设置音频文件路径识别(要求设置文件在sdcard上的全路径):
    			// mRecognize.setParameter(SpeechConstant.AUDIO_SOURCE, "-2");
    			// mRecognize.setParameter(SpeechConstant.ASR_SOURCE_PATH, "sdcard/XXX/XXX.pcm");
    			ret = mRecognize.startListening(mRecognizeListener);
    			if (ret != ErrorCode.SUCCESS) {
    				showTip("识别失败,错误码:" + ret);
    			} else {
    				byte[] audioData = FucUtil.readAudioFile(this, "retcognize_est.wav");
    				if (null != audioData) {
    					showTip("开始音频流识别");
    					// 一次(也可以分多次)写入音频文件数据,数据格式必须是采样率为8KHz或16KHz(本地识别只支持16K采样率,云端都支持),位长16bit,单声道的wav或者pcm
    					// 写入8KHz采样的音频时,必须先调用setParameter(SpeechConstant.SAMPLE_RATE, "8000")设置正确的采样率
    					// 注:当音频过长,静音部分时长超过VAD_EOS将导致静音后面部分不能识别
    					mRecognize.writeAudio(audioData, 0, audioData.length);
    					mRecognize.stopListening();
    				} else {
    					mRecognize.cancel();
    					showTip("读取音频流失败");
    				}
    			}
    		}
    	}
    
    	//初始化监听器
    	private InitListener mInitListener = new InitListener() {
    		@Override
    		public void onInit(int code) {
    			Log.d(TAG, "SpeechRecognizer init() code = " + code);
    			if (code != ErrorCode.SUCCESS) {
    				showTip("初始化失败,错误码:" + code);
    			}
    		}
    	};
    
    	//听写监听器
    	private RecognizerListener mRecognizeListener = new RecognizerListener() {
    
    		@Override
    		public void onBeginOfSpeech() {
    			// 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
    			showTip("开始说话");
    		}
    
    		@Override
    		public void onError(SpeechError error) {
    			// 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。
    			// 如果使用本地功能(语记)需要提示用户开启语记的录音权限。
    			showTip(error.getPlainDescription(true));
    		}
    
    		@Override
    		public void onEndOfSpeech() {
    			// 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
    			showTip("结束说话");
    		}
    
    		@Override
    		public void onResult(RecognizerResult results, boolean isLast) {
    			Log.d(TAG, results.getResultString());
    			printResult(results);
    			if (isLast) {
    				// TODO 最后的结果
    			}
    		}
    
    		@Override
    		public void onVolumeChanged(int volume, byte[] data) {
    			showTip("当前正在说话,音量大小:" + volume);
    			Log.d(TAG, "返回音频数据:"+data.length);
    		}
    
    		@Override
    		public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
    			// 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
    			// 若使用本地能力,会话id为null
    			//	if (SpeechEvent.EVENT_SESSION_ID == eventType) {
    			//		String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
    			//		Log.d(TAG, "session id =" + sid);
    			//	}
    		}
    	};
    
    	private void printResult(RecognizerResult results) {
    		String text = JsonParser.parseIatResult(results.getResultString());
    		String sn = null;
    		try {
    			JSONObject resultJson = new JSONObject(results.getResultString());
    			sn = resultJson.optString("sn");
    		} catch (JSONException e) {
    			e.printStackTrace();
    			return;
    		}
    		mRecognizeResults.put(sn, text);
    		StringBuffer resultBuffer = new StringBuffer();
    		for (String key : mRecognizeResults.keySet()) {
    			resultBuffer.append(mRecognizeResults.get(key));
    		}
    		mResultText.setText(resultBuffer.toString());
    		mResultText.setSelection(mResultText.length());
    	}
    
    	//听写UI监听器
    	private RecognizerDialogListener mRecognizeDialogListener = new RecognizerDialogListener() {
    		public void onResult(RecognizerResult results, boolean isLast) {
    			printResult(results);
    		}
    
    		//识别回调错误
    		public void onError(SpeechError error) {
    			showTip(error.getPlainDescription(true));
    		}
    	};
    
    	private void showTip(final String str) {
    		Toast.makeText(this, str, Toast.LENGTH_LONG).show();
    	}
    
    	//参数设置
    	public void resetParam() {
    		// 清空参数
    		mRecognize.setParameter(SpeechConstant.PARAMS, null);
    		// 设置听写引擎。TYPE_LOCAL表示本地,TYPE_CLOUD表示云端,TYPE_MIX 表示混合
    		mRecognize.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
    		// 设置返回结果格式
    		mRecognize.setParameter(SpeechConstant.RESULT_TYPE, "json");
    
    		String lag = mSharedPreferences.getString("recognize_language_preference", "mandarin");
    		if (lag.equals("en_us")) {  // 设置语言
    			mRecognize.setParameter(SpeechConstant.LANGUAGE, "en_us");
    		} else {
    			mRecognize.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
    			// 设置语言区域
    			mRecognize.setParameter(SpeechConstant.ACCENT, lag);
    		}
    
    		// 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
    		mRecognize.setParameter(SpeechConstant.VAD_BOS, mSharedPreferences.getString("recognize_vadbos_preference", "4000"));
    		// 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
    		mRecognize.setParameter(SpeechConstant.VAD_EOS, mSharedPreferences.getString("recognize_vadeos_preference", "1000"));
    		// 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
    		mRecognize.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString("recognize_punc_preference", "1"));
    		// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
    		// 注:AUDIO_FORMAT参数语记需要更新版本才能生效
    		mRecognize.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
    		mRecognize.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory()+"/msc/recognize.wav");
    	}
    
    }


    语音合成

    科大讯飞的语音合成用的是SpeechSynthesizer类,主要方法如下:
    createSynthesizer : 创建语音合成对象。
    setParameter : 设置语音合成的参数。常用参数包括:
    --SpeechConstant.ENGINE_TYPE : 设置合成引擎。TYPE_LOCAL表示本地,TYPE_CLOUD表示云端,TYPE_MIX表示混合。
    --SpeechConstant.VOICE_NAME : 设置朗读者。默认xiaoyan(女青年,普通话)
    --SpeechConstant.SPEED : 设置朗读的语速。
    --SpeechConstant.PITCH : 设置朗读的音调。
    --SpeechConstant.VOLUME : 设置朗读的音量。
    --SpeechConstant.STREAM_TYPE : 设置音频流类型。默认是音乐。
    --SpeechConstant.KEY_REQUEST_FOCUS : 设置是否在播放合成音频时打断音乐播放,默认为true。
    --SpeechConstant.AUDIO_FORMAT : 设置音频的保存格式。
    --SpeechConstant.TTS_AUDIO_PATH : 设置音频的保存路径。
    startSpeaking :  开始语音朗读。参数为SynthesizerListener对象,该对象需重写的方法包括:
    --onSpeakBegin : 朗读开始。
    --onSpeakPaused : 朗读暂停。
    --onSpeakResumed : 朗读恢复。
    --onBufferProgress : 合成进度变化。
    --onSpeakProgress : 朗读进度变化。
    --onCompleted : 朗读完成。
    --onEvent : 事件处理,一般是业务出错等异常。
    synthesizeToUri : 只保存音频不进行播放,调用该接口就不能调用startSpeaking。第一个参数是要合成的文本,第二个参数时要保存的音频全路径,第三个参数是SynthesizerListener回调接口。
    pauseSpeaking : 暂停朗读。
    resumeSpeaking : 恢复朗读。
    stopSpeaking : 停止朗读。
    destroy : 回收语音合成对象。


    下面是科大讯飞语音合成的代码例子:
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.os.Bundle;
    import android.os.Environment;
    import android.util.Log;
    import android.view.View;
    import android.view.Window;
    import android.view.View.OnClickListener;
    import android.widget.EditText;
    import android.widget.Toast;
    
    import com.example.exmvoice.R;
    import com.example.exmvoice.SettingsActivity;
    import com.iflytek.cloud.ErrorCode;
    import com.iflytek.cloud.InitListener;
    import com.iflytek.cloud.SpeechConstant;
    import com.iflytek.cloud.SpeechError;
    import com.iflytek.cloud.SpeechSynthesizer;
    import com.iflytek.cloud.SynthesizerListener;
    
    public class XFComposeActivity extends Activity implements OnClickListener {
    	private static String TAG = XFComposeActivity.class.getSimpleName();
    	// 语音合成对象
    	private SpeechSynthesizer mCompose;
    	// 默认发音人
    	private String voicer = "xiaoyan";
    	private String[] mCloudVoicersEntries;
    	private String[] mCloudVoicersValue ;
    	// 缓冲进度
    	private int mPercentForBuffering = 0;
    	// 播放进度
    	private int mPercentForPlaying = 0;
    
    	private EditText mResourceText;
    	private SharedPreferences mSharedPreferences;
    	
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		setContentView(R.layout.activity_xunfei_compose);
    
    		mResourceText = ((EditText) findViewById(R.id.xf_compose_text));
    		findViewById(R.id.xf_compose_play).setOnClickListener(this);
    		findViewById(R.id.xf_compose_cancel).setOnClickListener(this);
    		findViewById(R.id.xf_compose_pause).setOnClickListener(this);
    		findViewById(R.id.xf_compose_resume).setOnClickListener(this);
    		findViewById(R.id.xf_compose_setting).setOnClickListener(this);
    		findViewById(R.id.xf_compose_person).setOnClickListener(this);
    		mSharedPreferences = getSharedPreferences(SettingsActivity.PREFER_NAME, MODE_PRIVATE);
    		
    		// 初始化合成对象
    		mCompose = SpeechSynthesizer.createSynthesizer(this, mComposeInitListener);
    		// 云端发音人名称列表
    		mCloudVoicersEntries = getResources().getStringArray(R.array.voicer_cloud_entries);
    		mCloudVoicersValue = getResources().getStringArray(R.array.voicer_cloud_values);
    	}
    
    	@Override
    	protected void onDestroy() {
    		super.onDestroy();
    		// 退出时释放连接
    		mCompose.stopSpeaking();
    		mCompose.destroy();
    	}
    	
    	@Override
    	public void onClick(View v) {
    		int resid = v.getId();
    		if (resid == R.id.xf_compose_setting) {
    			Intent intent = new Intent(this, SettingsActivity.class);
    			intent.putExtra("type", SettingsActivity.XF_COMPOSE);
    			startActivity(intent);
    		} else if (resid == R.id.xf_compose_play) {  // 开始合成
    			//收到onCompleted 回调时,合成结束、生成合成音频。合成的音频格式:只支持pcm格式
    			String text = mResourceText.getText().toString();
    			// 设置参数
    			setParam();
    			int code = mCompose.startSpeaking(text, mComposeListener);
    			if (code != ErrorCode.SUCCESS) {
    				showTip("语音合成失败,错误码: " + code);
    			}
    //			//只保存音频不进行播放接口,调用此接口请注释startSpeaking接口
    //			//text:要合成的文本,uri:需要保存的音频全路径,listener:回调接口
    //			String path = Environment.getExternalStorageDirectory()+"/compose.pcm";
    //			int code = mCompose.synthesizeToUri(text, path, mComposeListener);
    		} else if (resid == R.id.xf_compose_cancel) {  // 取消合成
    			mCompose.stopSpeaking();
    		} else if (resid == R.id.xf_compose_pause) {  // 暂停播放
    			mCompose.pauseSpeaking();
    		} else if (resid == R.id.xf_compose_resume) {  // 继续播放
    			mCompose.resumeSpeaking();
    		} else if (resid == R.id.xf_compose_person) {  // 选择发音人
    			showPresonSelectDialog();
    		}
    	}
    	
    	private int selectedNum = 0;
    	//发音人选择
    	private void showPresonSelectDialog() {
    		new AlertDialog.Builder(this).setTitle("在线合成发音人选项")
    		.setSingleChoiceItems(mCloudVoicersEntries, // 单选框有几项,各是什么名字
    			selectedNum, // 默认的选项
    			new DialogInterface.OnClickListener() { // 点击单选框后的处理
    				public void onClick(DialogInterface dialog, int which) { // 点击了哪一项
    					voicer = mCloudVoicersValue[which];
    					if ("catherine".equals(voicer) || "henry".equals(voicer) || "vimary".equals(voicer)
    							|| "Mariane".equals(voicer) || "Allabent".equals(voicer) || "Gabriela".equals(voicer) || "Abha".equals(voicer) || "XiaoYun".equals(voicer)) {
    						mResourceText.setText(R.string.compose_source_en);
    					} else {
    						mResourceText.setText(R.string.compose_source);
    					}
    					selectedNum = which;
    					dialog.dismiss();
    				}
    			}).show();
    	}
    
    	//初始化监听
    	private InitListener mComposeInitListener = new InitListener() {
    		@Override
    		public void onInit(int code) {
    			Log.d(TAG, "InitListener init() code = " + code);
    			if (code != ErrorCode.SUCCESS) {
            		showTip("初始化失败,错误码:"+code);
            	} else {
    				// 初始化成功,之后可以调用startSpeaking方法
            		// 注:有的开发者在onCreate方法中创建完合成对象之后马上就调用startSpeaking进行合成,
            		// 正确的做法是将onCreate中的startSpeaking调用移至这里
    			}		
    		}
    	};
    
    	//合成回调监听
    	private SynthesizerListener mComposeListener = new SynthesizerListener() {
    		
    		@Override
    		public void onSpeakBegin() {
    			showTip("开始播放");
    		}
    
    		@Override
    		public void onSpeakPaused() {
    			showTip("暂停播放");
    		}
    
    		@Override
    		public void onSpeakResumed() {
    			showTip("继续播放");
    		}
    
    		@Override
    		public void onBufferProgress(int percent, int beginPos, int endPos, String info) {
    			// 合成进度
    			mPercentForBuffering = percent;
    			showTip(String.format(getString(R.string.xf_compose_toast_format),
    					mPercentForBuffering, mPercentForPlaying));
    		}
    
    		@Override
    		public void onSpeakProgress(int percent, int beginPos, int endPos) {
    			// 播放进度
    			mPercentForPlaying = percent;
    			showTip(String.format(getString(R.string.xf_compose_toast_format),
    					mPercentForBuffering, mPercentForPlaying));
    		}
    
    		@Override
    		public void onCompleted(SpeechError error) {
    			if (error == null) {
    				showTip("播放完成");
    			} else if (error != null) {
    				showTip(error.getPlainDescription(true));
    			}
    		}
    
    		@Override
    		public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
    			// 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
    			// 若使用本地能力,会话id为null
    			//	if (SpeechEvent.EVENT_SESSION_ID == eventType) {
    			//		String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
    			//		Log.d(TAG, "session id =" + sid);
    			//	}
    		}
    	};
    
    	private void showTip(final String str) {
    		Toast.makeText(this, str, Toast.LENGTH_LONG).show();
    	}
    
    	//参数设置
    	private void setParam(){
    		// 清空参数
    		mCompose.setParameter(SpeechConstant.PARAMS, null);
    		// 根据合成引擎设置相应参数
    		mCompose.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
    		// 设置在线合成发音人
    		mCompose.setParameter(SpeechConstant.VOICE_NAME, voicer);
    		//设置合成语速
    		mCompose.setParameter(SpeechConstant.SPEED, mSharedPreferences.getString("speed_preference", "50"));
    		//设置合成音调
    		mCompose.setParameter(SpeechConstant.PITCH, mSharedPreferences.getString("pitch_preference", "50"));
    		//设置合成音量
    		mCompose.setParameter(SpeechConstant.VOLUME, mSharedPreferences.getString("volume_preference", "50"));
    		//设置播放器音频流类型
    		mCompose.setParameter(SpeechConstant.STREAM_TYPE, mSharedPreferences.getString("stream_preference", "3"));
    		// 设置播放合成音频打断音乐播放,默认为true
    		mCompose.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");
    		
    		// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
    		// 注:AUDIO_FORMAT参数语记需要更新版本才能生效
    		mCompose.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
    		mCompose.setParameter(SpeechConstant.TTS_AUDIO_PATH, Environment.getExternalStorageDirectory()+"/msc/compose.wav");
    	}
    	
    }


    PreferenceFragment

    科大讯飞的demo工程在设置页面使用了PreferenceActivity,看起来代码简炼了许多,正好我们之前还没接触Preference的实际运用,现在就来研究研究。看最新的sdk源码,提示PreferenceActivity的许多方法都过时了,官方建议使用PreferenceFragment来代替。


    下面是PreferenceFragment的常用方法说明
    getPreferenceManager : 获得参数管理的PreferenceManager对象。该对象主要有两个方法:getDefaultSharedPreferences返回系统默认的共享参数对象;setSharedPreferencesName为设置指定名称的共享参数;有关共享参数的说明参见《Android开发笔记(二十九)使用SharedPreferences存取数据》。
    addPreferencesFromResource : 从xml资源文件中添加参数界面。
    findPreference : 从xml资源文件中获取指定id的元素。EditTextPreference表示该项参数为文本输入;ListPreference表示该项参数为列表选择;CheckBoxPreference表示该项参数为复选框勾选;PreferenceScreen是xml文件的根节点。
    setPreferenceScreen : 设置参数屏幕(一般不使用)。


    下面是PreferenceFragment的代码示例:
    import com.example.exmvoice.R;
    import com.example.exmvoice.SettingsActivity;
    import com.example.exmvoice.xunfei.util.SettingTextWatcher;
    
    import android.os.Bundle;
    import android.preference.EditTextPreference;
    import android.preference.Preference;
    import android.preference.Preference.OnPreferenceChangeListener;
    import android.preference.PreferenceFragment;
    
    //语音识别设置界面
    public class XFRecognizeSettingsFragment extends PreferenceFragment implements OnPreferenceChangeListener {
    	
    	private EditTextPreference mVadbosPreference;
    	private EditTextPreference mVadeosPreference;
    
        @Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		getPreferenceManager().setSharedPreferencesName(SettingsActivity.PREFER_NAME);
    		addPreferencesFromResource(R.xml.xf_recognize_setting);
    		
    		mVadbosPreference = (EditTextPreference) findPreference("recognize_vadbos_preference");
    		mVadbosPreference.getEditText().addTextChangedListener(
    				new SettingTextWatcher(getActivity(),mVadbosPreference,0,10000));
    		
    		mVadeosPreference = (EditTextPreference) findPreference("recognize_vadeos_preference");
    		mVadeosPreference.getEditText().addTextChangedListener(
    				new SettingTextWatcher(getActivity(),mVadeosPreference,0,10000));
    	}
    	
    	@Override
    	public boolean onPreferenceChange(Preference preference, Object newValue) {
    		return true;
    	}
    }


    下面是PreferenceFragment的布局示例:
    <?xml version="1.0" encoding="utf-8"?>
    <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > 
        
        <ListPreference
            android:key="recognize_language_preference"
            android:title="语言设置"
            android:entries="@array/language_entries"
            android:entryValues="@array/language_values"
            android:summary="支持:普通话,粤语,河南话,英语 "
            android:defaultValue="mandarin"  />
        
        <EditTextPreference
            android:key="recognize_vadbos_preference"   
    		android:title="前端点超时" 
    		android:dialogTitle="请输入时间(0-10000)ms"
            android:summary="默认值:短信转写5000,其他4000"
    		android:defaultValue="5000" />
        
        <EditTextPreference
            android:key="recognize_vadeos_preference"   
    		android:title="后端点超时" 
    		android:dialogTitle="请输入时间(0-10000)ms"
            android:summary="默认值:短信转写1800,其他700 "
    		android:defaultValue="1800" />
    
        <ListPreference
            android:key="recognize_punc_preference"
            android:title="标点符号"
            android:entries="@array/punc_entries"
            android:entryValues="@array/punc_values"
            android:summary="默认值:有标点 "
            android:defaultValue="1"  />
        
    	<CheckBoxPreference
    		android:key="show_dialog"
    		android:title="显示听写界面"
    		android:defaultValue="true" />
    
    </PreferenceScreen>


    百度语音

    sdk集成

    百度语音sdk的集成比较麻烦,主要步骤如下:
    1、导入sdk包到libs目录,包括语音识别和语音合成两种库
    语音识别的库有:
    libbdEASRAndroid.so
    libBDVoiceRecognitionClient_MFE_V1.so
    VoiceRecognition-2.0.1.jar
    语音合成的库有:
    libbd_etts.so
    libBDSpeechDecoder_V1.so
    libbdtts.so
    libgnustl_shared.so
    com.baidu.tts_2.2.7.20160616_81bcb05_release.jar
    galaxy-v2.0.jar
    2、到百度注册并创建新应用,获得APP_ID、API_KEY、SECRET_KEY;
    3、在AndroidManifest.xml中加入必要的权限,以及meta-data、service和activity设置,注意meta-data的参数值为第二步获得的APP_ID、API_KEY、SECRET_KEY。详细的xml部分例子如下:
            <meta-data android:name="com.baidu.speech.APP_ID" android:value="8282403"/>
            <meta-data android:name="com.baidu.speech.API_KEY" android:value="M2OT6nhn1beu4IxI5GqQk4ev"/>
            <meta-data android:name="com.baidu.speech.SECRET_KEY" android:value="6e448840e00a12881c6d63346771caa5"/>
            <service android:name="com.baidu.speech.VoiceRecognitionService" android:exported="false" />
            <activity
                    android:name="com.baidu.voicerecognition.android.ui.BaiduASRDigitalDialog"
                    android:configChanges="orientation|keyboardHidden|screenLayout"
                    android:theme="@android:style/Theme.Dialog"
                    android:exported="false"
                    android:screenOrientation="portrait">
                <intent-filter>
                    <action android:name="com.baidu.action.RECOGNIZE_SPEECH" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>
    4、demo工程中assets目录下的文件原样拷过来;
    5、demo工程中res目录下的drawable、layout、raw下面的资源原样拷过来;
    6、根据demo工程编写代码与布局文件,注意在语音合成初始化时,setAppId和setApiKey要把第二步获得的APP_ID、API_KEY、SECRET_KEY给填进去;


    下面是我在集成百度语音时遇到的几个问题及处理办法:
    1、语音合成运行报错,日志提示:
    06-21 16:31:37.118: W/System.err(4595): Caused by: java.util.concurrent.ExecutionException: java.lang.Exception: #5, Other client side errors. request token failed, error: unknown, desc: unknown client id, used AK=this/this
    原因:setAppId和setApiKey方法没有设置appkey。
    2、语音合成运行报错,日志提示:
    06-21 16:32:57.830: W/System.err(4769): java.lang.Exception: #5, Other client side errors. The AK can only be used for demo. AK=8MAxI5o7VjKSZOKeBzS4XtxO/Ge5GXVdGQpaxOmLzc8fOM8309ATCz9Ha
    原因:setAppId和setApiKey方法设置的值不对,可能使用了demo的appkey,而不是自己申请的appkey。
    3、语音合成运行报错,日志提示:
    06-22 11:32:00.998: W/MainActivity(31928): onError error=(-15)(-15)online synthesize get was timeout[(cause)java.util.concurrent.TimeoutException]--utteranceId=0
    原因:网络连不上,请检查网络连接。如果使用模拟器测试,最好重启模拟器再试试
    4、调用loadEnglishModel方法加载英语模块时,返回值是-11加载失败(正常要返回5)。
    原因:加载离线英文资源需要在初始化时采用混合模式TtsMode.MIX,不可采用在线模式TtsMode.ONLINE。


    语音识别

    百度语音识别用的是SpeechRecognizer类,主要方法如下:
    createSpeechRecognizer : 创建语音识别对象。
    setRecognitionListener : 设置识别监听器。该监听器需重写的方法包括:
    --onReadyForSpeech : 准备就绪,可以开始说话
    --onBeginningOfSpeech : 检测到用户已经开始说话
    --onRmsChanged : 一般不用处理。
    --onBufferReceived : 一般不用处理。
    --onEndOfSpeech : 检测到用户已经停止说话
    --onError : 识别出错。
    --onResults : 识别完成,返回结果串。
    --onPartialResults : 返回部分的识别结果。
    --onEvent : 事件处理,一般是业务出错等异常。
    startListening : 开始监听语音。
    stopListening : 结束监听语音。
    cancel : 取消监听。
    destroy : 回收语音识别对象。


    注意第一次识别时要跳到com.baidu.action.RECOGNIZE_SPEECH,后面才能调用startListening方法。识别时的参数设置是在activity跳转时传入的,常用参数包括:
    --Constant.EXTRA_LANGUAGE : 说话的语言。cmn-Hans-CN表示普通话,sichuan-Hans-CN表示四川话,yue-Hans-CN表示粤语,en-GB表示英语。
    --Constant.EXTRA_NLU : 是否开启语义解析。
    --Constant.EXTRA_VAD : 语音边界检测。search表示适用输入搜索关键字(默认值),input表示适用于输入短信、微博等长句输入。
    --Constant.EXTRA_PROP : 语音的行业领域。


    下面是百度语音识别的运行截图:



    下面是百度语音识别的代码例子:
    import android.app.Activity;
    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.os.Bundle;
    import android.preference.PreferenceManager;
    import android.speech.RecognitionListener;
    import android.speech.SpeechRecognizer;
    import android.util.Log;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.*;
    import com.baidu.speech.VoiceRecognitionService;
    import com.example.exmvoice.R;
    import com.example.exmvoice.SettingsActivity;
    import com.example.exmvoice.baidu.setting.Constant;
    
    import org.json.JSONObject;
    
    import java.util.*;
    
    public class BDRecognizeActivity extends Activity implements OnClickListener {
        private static final String TAG = BDRecognizeActivity.class.getSimpleName(); 	
        private static final int REQUEST_UI = 1;
        private TextView txtResult;
        private TextView txtLog;
        private Button btnStart;
    
        public static final int STATUS_None = 0;
        public static final int STATUS_WaitingReady = 2;
        public static final int STATUS_Ready = 3;
        public static final int STATUS_Speaking = 4;
        public static final int STATUS_Recognition = 5;
        private SpeechRecognizer speechRecognizer;
        private int status = STATUS_None;
        private long speechEndTime = -1;
        private static final int EVENT_ERROR = 11;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_baidu_recognize);
            
            txtResult = (TextView) findViewById(R.id.bd_recognize_text);
            txtLog = (TextView) findViewById(R.id.bd_recognize_log);
            btnStart = (Button) findViewById(R.id.bd_recognize_start);
            btnStart.setOnClickListener(this);
    		findViewById(R.id.bd_recognize_setting).setOnClickListener(this);
    
            speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this, 
            		new ComponentName(this, VoiceRecognitionService.class));
            speechRecognizer.setRecognitionListener(mRecognitionListener);
        }
    
        @Override
        protected void onDestroy() {
            speechRecognizer.destroy();
            super.onDestroy();
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (resultCode == RESULT_OK) {
            	mRecognitionListener.onResults(data.getExtras());
            } else {
    	        status = STATUS_None;
    	        btnStart.setText("开始");
            }
        }
    
        public void bindParams(Intent intent) {
            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
            if (sp.getBoolean("tips_sound", true)) {
                intent.putExtra(Constant.EXTRA_SOUND_START, R.raw.bdspeech_recognition_start);
                intent.putExtra(Constant.EXTRA_SOUND_END, R.raw.bdspeech_speech_end);
                intent.putExtra(Constant.EXTRA_SOUND_SUCCESS, R.raw.bdspeech_recognition_success);
                intent.putExtra(Constant.EXTRA_SOUND_ERROR, R.raw.bdspeech_recognition_error);
                intent.putExtra(Constant.EXTRA_SOUND_CANCEL, R.raw.bdspeech_recognition_cancel);
            }
            if (sp.contains(Constant.EXTRA_INFILE)) {
                String tmp = sp.getString(Constant.EXTRA_INFILE, "").replaceAll(",.*", "").trim();
                intent.putExtra(Constant.EXTRA_INFILE, tmp);
            }
            if (sp.getBoolean(Constant.EXTRA_OUTFILE, false)) {
                intent.putExtra(Constant.EXTRA_OUTFILE, "sdcard/outfile.pcm");
            }
            if (sp.contains(Constant.EXTRA_SAMPLE)) {
                String tmp = sp.getString(Constant.EXTRA_SAMPLE, "").replaceAll(",.*", "").trim();
                if (null != tmp && !"".equals(tmp)) {
                    intent.putExtra(Constant.EXTRA_SAMPLE, Integer.parseInt(tmp));
                }
            }
            if (sp.contains(Constant.EXTRA_LANGUAGE)) {
                String tmp = sp.getString(Constant.EXTRA_LANGUAGE, "").replaceAll(",.*", "").trim();
                if (null != tmp && !"".equals(tmp)) {
                    intent.putExtra(Constant.EXTRA_LANGUAGE, tmp);
                }
            }
            if (sp.contains(Constant.EXTRA_NLU)) {
                String tmp = sp.getString(Constant.EXTRA_NLU, "").replaceAll(",.*", "").trim();
                if (null != tmp && !"".equals(tmp)) {
                    intent.putExtra(Constant.EXTRA_NLU, tmp);
                }
            }
            if (sp.contains(Constant.EXTRA_VAD)) {
                String tmp = sp.getString(Constant.EXTRA_VAD, "").replaceAll(",.*", "").trim();
                if (null != tmp && !"".equals(tmp)) {
                    intent.putExtra(Constant.EXTRA_VAD, tmp);
                }
            }
            if (sp.contains(Constant.EXTRA_PROP)) {
                String tmp = sp.getString(Constant.EXTRA_PROP, "").replaceAll(",.*", "").trim();
                if (null != tmp && !"".equals(tmp)) {
                    intent.putExtra(Constant.EXTRA_PROP, Integer.parseInt(tmp));
                }
            }
        }
    
        private void start() {
            btnStart.setText("取消");
            txtLog.setText("");
            status = STATUS_WaitingReady;
            print("点击了“开始”");
            Intent intent = new Intent();
            bindParams(intent);
            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
            {
                String args = sp.getString("args", "");
                if (null != args) {
                    print("参数集:" + args);
                    intent.putExtra("args", args);
                }
            }
            boolean api = sp.getBoolean("api", false);
            if (api) {
                speechEndTime = -1;
                speechRecognizer.startListening(intent);
            } else {
                intent.setAction("com.baidu.action.RECOGNIZE_SPEECH");
                startActivityForResult(intent, REQUEST_UI);
            }
    
            txtResult.setText("");
        }
    
        private void stop() {
            speechRecognizer.stopListening();
            status = STATUS_Recognition;
            btnStart.setText("识别中");
            print("点击了“说完了”");
        }
    
        private void cancel() {
            speechRecognizer.cancel();
            btnStart.setText("开始");
            status = STATUS_None;
            print("点击了“取消”");
        }
        
        private RecognitionListener mRecognitionListener = new RecognitionListener() {
    
    		@Override
    		public void onReadyForSpeech(Bundle params) {
    	        status = STATUS_Ready;
    	        print("准备就绪,可以开始说话");
    		}
    
    		@Override
    		public void onBeginningOfSpeech() {
    	        status = STATUS_Speaking;
    	        btnStart.setText("说完了");
    	        print("检测到用户已经开始说话");
    		}
    
    		@Override
    		public void onRmsChanged(float rmsdB) {
    		}
    
    		@Override
    		public void onBufferReceived(byte[] buffer) {
    		}
    
    		@Override
    		public void onEndOfSpeech() {
    	        speechEndTime = System.currentTimeMillis();
    	        status = STATUS_Recognition;
    	        print("检测到用户已经停止说话");
    	        btnStart.setText("识别中");
    		}
    
    		@Override
    		public void onError(int error) {
    	        status = STATUS_None;
    	        StringBuilder sb = new StringBuilder();
    	        switch (error) {
    	            case SpeechRecognizer.ERROR_AUDIO:
    	                sb.append("音频问题");
    	                break;
    	            case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
    	                sb.append("没有语音输入");
    	                break;
    	            case SpeechRecognizer.ERROR_CLIENT:
    	                sb.append("其它客户端错误");
    	                break;
    	            case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
    	                sb.append("权限不足");
    	                break;
    	            case SpeechRecognizer.ERROR_NETWORK:
    	                sb.append("网络问题");
    	                break;
    	            case SpeechRecognizer.ERROR_NO_MATCH:
    	                sb.append("没有匹配的识别结果");
    	                break;
    	            case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
    	                sb.append("引擎忙");
    	                break;
    	            case SpeechRecognizer.ERROR_SERVER:
    	                sb.append("服务端错误");
    	                break;
    	            case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
    	                sb.append("连接超时");
    	                break;
    	        }
    	        sb.append(":" + error);
    	        print("识别失败:" + sb.toString());
    	        btnStart.setText("开始");
    		}
    
    		@Override
    		public void onResults(Bundle results) {
    	        long end2finish = System.currentTimeMillis() - speechEndTime;
    	        ArrayList<String> nbest = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
    	        print("识别成功:" + Arrays.toString(nbest.toArray(new String[nbest.size()])));
    	        String json_res = results.getString("origin_result");
    	        try {
    	            print("origin_result=\n" + new JSONObject(json_res).toString(4));
    	        } catch (Exception e) {
    	            print("origin_result=[warning: bad json]\n" + json_res);
    	        }
    	        String strEnd2Finish = "";
    	        if (end2finish < 60 * 1000) {
    	            strEnd2Finish = "(waited " + end2finish + "ms)";
    	        }
    	        txtResult.setText(nbest.get(0) + strEnd2Finish);
    	        status = STATUS_None;
    	        btnStart.setText("开始");
    		}
    
    		@Override
    		public void onPartialResults(Bundle partialResults) {
    	        ArrayList<String> nbest = partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
    	        if (nbest.size() > 0) {
    	            print("~临时识别结果:" + Arrays.toString(nbest.toArray(new String[0])));
    	            txtResult.setText(nbest.get(0));
    	        }
    		}
    
    		@Override
    		public void onEvent(int eventType, Bundle params) {
    			switch (eventType) {
    			case EVENT_ERROR:
    				String reason = params.get("reason") + "";
    				print("EVENT_ERROR, " + reason);
    		        status = STATUS_None;
    		        btnStart.setText("开始");
    				break;
    			case VoiceRecognitionService.EVENT_ENGINE_SWITCH:
    				int type = params.getInt("engine_type");
    				print("*引擎切换至" + (type == 0 ? "在线" : "离线"));
    				break;
    			}
    		}
        	
        };
    
        private void print(String msg) {
            txtLog.append(msg + "\n");
            ScrollView sv = (ScrollView) txtLog.getParent();
            sv.smoothScrollTo(0, 1000000);
            Log.d(TAG, "----" + msg);
        }
    
    	@Override
    	public void onClick(View v) {
    		int resid = v.getId();
    		if (resid == R.id.bd_recognize_setting) {
    			Intent intent = new Intent(this, SettingsActivity.class);
    			intent.putExtra("type", SettingsActivity.BD_RECOGNIZE);
    			startActivity(intent);
    		} else if (resid == R.id.bd_recognize_start) {
                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
                boolean api = sp.getBoolean("api", false);
                if (api) {
                	if (status == STATUS_None) {
                        start();
                	} else if (status == STATUS_WaitingReady || 
                			status == STATUS_Ready || status == STATUS_Recognition) {
                		cancel();
                	} else if (status == STATUS_Speaking) {
                        stop();
                	}
                } else {
                    start();
                }
    		} 
    	}
    }


    语音合成

    百度语音合成用的是SpeechSynthesizer类,主要方法如下:
    getInstance : 获得语音合成的实例。
    setContext : 设置语音合成的上下文。
    setSpeechSynthesizerListener : 语音合成的监听器。该监听器需重写的方法包括:
    --onSynthesizeStart : 合成开始。
    --onSynthesizeDataArrived : 一般不使用。
    --onSynthesizeFinish : 合成结束。
    --onSpeechStart : 朗读开始。
    --onSpeechProgressChanged : 朗读进度变化。
    --onSpeechFinish : 朗读结束。
    --onError : 处理出错。
    setAppId : 设置appid。
    setApiKey : 设置apikey和secretkey。
    auth : 对appid、apikey和secretkey进行鉴权。
    initTts : 初始化。TtsMode.ONLINE表示在线合成,TtsMode.MIX表示混合(即在线与离线结合)。
    setAudioStreamType : 设置音频流的类型。AudioManager.STREAM_MUSIC表示音乐。
    setParam : 设置语音合成的参数。常用参数包括:
    --SpeechSynthesizer.PARAM_SPEAKER : 设置朗读者。0表示普通女声,1表示普通男声,2表示特别男声,3表示情感男声。
    --SpeechSynthesizer.PARAM_VOLUME : 设置音量。取值范围为0-9,默认5。
    --SpeechSynthesizer.PARAM_SPEED : 设置语速。取值范围为0-9,默认5。
    --SpeechSynthesizer.PARAM_PITCH : 设置音调。取值范围为0-9,默认5。
    --SpeechSynthesizer.PARAM_AUDIO_ENCODE : 设置音频的编码类型。一般设置SpeechSynthesizer.AUDIO_ENCODE_AMR。
    --SpeechSynthesizer.PARAM_AUDIO_RATE : 设置音频的编码速率。一般设置SpeechSynthesizer.AUDIO_BITRATE_AMR_15K85。
    loadEnglishModel : 加载英语模块。
    speak : 开始合成并朗读。
    pause : 暂停朗读。
    resume : 恢复朗读。
    stop : 停止朗读。
    release : 释放语音合成的实例。


    下面是百度语音合成的代码例子:
    import java.io.File;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.content.pm.ApplicationInfo;
    import android.content.pm.PackageManager;
    import android.content.pm.PackageManager.NameNotFoundException;
    import android.media.AudioManager;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.view.View;
    import android.view.Window;
    import android.view.View.OnClickListener;
    import android.widget.EditText;
    import android.widget.RadioButton;
    import android.widget.RadioGroup;
    import android.widget.RadioGroup.OnCheckedChangeListener;
    import android.widget.Toast;
    
    import com.baidu.tts.auth.AuthInfo;
    import com.baidu.tts.client.SpeechError;
    import com.baidu.tts.client.SpeechSynthesizer;
    import com.baidu.tts.client.SpeechSynthesizerListener;
    import com.baidu.tts.client.TtsMode;
    import com.example.exmvoice.R;
    import com.example.exmvoice.SettingsActivity;
    import com.example.exmvoice.baidu.util.AssetsUtil;
    
    public class BDComposeActivity extends Activity implements OnClickListener,OnCheckedChangeListener {
    	private static String TAG = BDComposeActivity.class.getSimpleName();
    
        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 = "bd_temp_license";
        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";
    
    	private boolean bOnline = true;
    	private EditText mResourceText;
    	private SharedPreferences mSharedPreferences;
    	
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		setContentView(R.layout.activity_baidu_compose);
    
    		mResourceText = ((EditText) findViewById(R.id.bd_compose_text));
    		((RadioGroup)findViewById(R.id.bd_compose_mode)).setOnCheckedChangeListener(this);
    		findViewById(R.id.bd_compose_play).setOnClickListener(this);
    		findViewById(R.id.bd_compose_cancel).setOnClickListener(this);
    		findViewById(R.id.bd_compose_pause).setOnClickListener(this);
    		findViewById(R.id.bd_compose_resume).setOnClickListener(this);
    		findViewById(R.id.bd_compose_setting).setOnClickListener(this);
    		mSharedPreferences = getSharedPreferences(SettingsActivity.PREFER_NAME, MODE_PRIVATE);
    
            initialEnv();
            initialEngine();
    	}
    
        private void initialEnv() {
            if (mSampleDirPath == null) {
                String sdcardPath = Environment.getExternalStorageDirectory().toString();
                mSampleDirPath = sdcardPath + "/" + SAMPLE_DIR_NAME;
            }
            File file = new File(mSampleDirPath);
            if (!file.exists()) {
                file.mkdirs();
            }
            AssetsUtil.copyFromAssetsToSdcard(this, false, SPEECH_FEMALE_MODEL_NAME, mSampleDirPath + "/" + SPEECH_FEMALE_MODEL_NAME);
            AssetsUtil.copyFromAssetsToSdcard(this, false, SPEECH_MALE_MODEL_NAME, mSampleDirPath + "/" + SPEECH_MALE_MODEL_NAME);
            AssetsUtil.copyFromAssetsToSdcard(this, false, TEXT_MODEL_NAME, mSampleDirPath + "/" + TEXT_MODEL_NAME);
            AssetsUtil.copyFromAssetsToSdcard(this, false, LICENSE_FILE_NAME, mSampleDirPath + "/" + LICENSE_FILE_NAME);
            AssetsUtil.copyFromAssetsToSdcard(this, false, "english/" + ENGLISH_SPEECH_FEMALE_MODEL_NAME, mSampleDirPath + "/" + ENGLISH_SPEECH_FEMALE_MODEL_NAME);
            AssetsUtil.copyFromAssetsToSdcard(this, false, "english/" + ENGLISH_SPEECH_MALE_MODEL_NAME, mSampleDirPath + "/" + ENGLISH_SPEECH_MALE_MODEL_NAME);
            AssetsUtil.copyFromAssetsToSdcard(this, false, "english/" + ENGLISH_TEXT_MODEL_NAME, mSampleDirPath + "/" + ENGLISH_TEXT_MODEL_NAME);
        }
    
        private void initialEngine() {
            mSpeechSynthesizer = SpeechSynthesizer.getInstance();
            mSpeechSynthesizer.setContext(this);
            mSpeechSynthesizer.setSpeechSynthesizerListener(mSpeechListener);
            
    		ApplicationInfo appInfo = null;
    		try {
    			appInfo = this.getPackageManager().getApplicationInfo(
    					getPackageName(), PackageManager.GET_META_DATA);
    			String app_id = appInfo.metaData.getString("com.baidu.speech.APP_ID");
    			String api_key = appInfo.metaData.getString("com.baidu.speech.API_KEY");
    			String secret_key = appInfo.metaData.getString("com.baidu.speech.SECRET_KEY");
    	        mSpeechSynthesizer.setAppId(app_id);
    	        mSpeechSynthesizer.setApiKey(api_key, secret_key);
    		} catch (NameNotFoundException e) {
    			e.printStackTrace();
                showTip("获取appid失败");
    		}
            AuthInfo authInfo = mSpeechSynthesizer.auth(TtsMode.ONLINE);
            if (authInfo.isSuccess()) {
                showTip("auth success");
            } else {
                String errorMsg = authInfo.getTtsError().getDetailMessage();
                showTip("auth failed errorMsg=" + errorMsg);
            }
            mSpeechSynthesizer.initTts(TtsMode.MIX);
            bOnline = ((RadioButton) findViewById(R.id.bd_compose_online)).isChecked();
    		setParams(bOnline);
        }
    
    	@Override
    	protected void onDestroy() {
    		// 退出时释放连接
            mSpeechSynthesizer.release();
    		super.onDestroy();
    	}
    	
    	@Override
    	public void onClick(View v) {
    		int resid = v.getId();
    		if (resid == R.id.bd_compose_setting) {
    			Intent intent = new Intent(this, SettingsActivity.class);
    			intent.putExtra("type", SettingsActivity.BD_COMPOSE);
    			startActivity(intent);
    		} else if (resid == R.id.bd_compose_play) {  // 开始合成
                speak();
    		} else if (resid == R.id.bd_compose_cancel) {  // 取消合成
                mSpeechSynthesizer.stop();
    		} else if (resid == R.id.bd_compose_pause) {  // 暂停播放
                mSpeechSynthesizer.pause();
    		} else if (resid == R.id.bd_compose_resume) {  // 继续播放
                mSpeechSynthesizer.resume();
    		}
    	}
    
    	@Override
    	public void onCheckedChanged(RadioGroup group, int checkedId) {
    		if (checkedId == R.id.bd_compose_online) {
    			bOnline = true;
    		} else if (checkedId == R.id.bd_compose_offline) {
    			bOnline = false;
    		}
    		Log.d(TAG, "bOnline="+bOnline);
    		setParams(bOnline);
    	}
    
    	private void setParams(boolean online) {
    		mSpeechSynthesizer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    		//setVolumeControlStream(AudioManager.STREAM_MUSIC);
    		mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, mSharedPreferences.getString("bd_person_preference", "0")); //0--普通女声,1--普通男声,2--特别男声,3--情感男声
    		mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, mSharedPreferences.getString("bd_volume_preference", "5")); //音量,取值0-9,默认为5中音量
    		mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, mSharedPreferences.getString("bd_speed_preference", "5")); //语速,取值0-9,默认为5中语速
    		mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, mSharedPreferences.getString("bd_pitch_preference", "5")); //音调,取值0-9,默认为5中语调
    		mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_ENCODE,
    				SpeechSynthesizer.AUDIO_ENCODE_AMR);
    		mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_RATE,
    				SpeechSynthesizer.AUDIO_BITRATE_AMR_15K85);
    		if (online == true) {
    		} else {
    	        // 文本模型文件路径 (离线引擎使用)
    	        mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, mSampleDirPath + "/" + TEXT_MODEL_NAME);
    	        // 声学模型文件路径 (离线引擎使用)
    	        mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, mSampleDirPath + "/" + SPEECH_FEMALE_MODEL_NAME);
    	        // 本地授权文件路径,如未设置将使用默认路径.设置临时授权文件路径,LICENCE_FILE_NAME请替换成临时授权文件的实际路径,仅在使用临时license文件时需要进行设置,如果在[应用管理]中开通了离线授权,不需要设置该参数,建议将该行代码删除(离线引擎)
    	        mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_LICENCE_FILE, mSampleDirPath + "/" + LICENSE_FILE_NAME);
    	        // 设置Mix模式的合成策略
    	        mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_MIX_MODE, SpeechSynthesizer.MIX_MODE_DEFAULT);
    
    	        // 加载离线英文资源(提供离线英文合成功能)
    	        String englishTextPath = mSampleDirPath + "/" + ENGLISH_TEXT_MODEL_NAME;
    	        String englishSpeechPath = mSampleDirPath + "/" + ENGLISH_SPEECH_FEMALE_MODEL_NAME;
    	        Log.d(TAG, "englishTextPath="+englishTextPath+", englishSpeechPath="+englishSpeechPath);
    	        int result = mSpeechSynthesizer.loadEnglishModel(englishTextPath, englishSpeechPath);
    	        showTip("loadEnglishModel result=" + result);
    	        //如果initTts使用的是在线模式TtsMode.ONLINE,则loadEnglishModel会失败返回-11
    		}
    	}
    
        private void speak() {
            final String text = mResourceText.getText().toString();
            if (text==null || text.length()<=0) {
                showTip("请输入要合成语音的文字");
            } else {
                int result = mSpeechSynthesizer.speak(text);
                if (result < 0) {
                    showTip("result="+result+". error,please look up error code in doc or URL:http://yuyin.baidu.com/docs/tts/122 ");
                } else {
                    showTip("合成结果="+result);
                }
            }
        }
    
        private SpeechSynthesizerListener mSpeechListener = new SpeechSynthesizerListener() {
            @Override
            public void onSynthesizeStart(String utteranceId) {
            	toPrint("onSynthesizeStart utteranceId=" + utteranceId);
            }
    
            @Override
            public void onSynthesizeDataArrived(String utteranceId, byte[] data, int progress) {
                // toPrint("onSynthesizeDataArrived");
            }
    
            @Override
            public void onSynthesizeFinish(String utteranceId) {
            	toPrint("onSynthesizeFinish utteranceId=" + utteranceId);
            }
    
            @Override
            public void onSpeechStart(String utteranceId) {
            	toPrint("onSpeechStart utteranceId=" + utteranceId);
            }
    
            @Override
            public void onSpeechProgressChanged(String utteranceId, int progress) {
                // toPrint("onSpeechProgressChanged");
            }
    
            @Override
            public void onSpeechFinish(String utteranceId) {
            	toPrint("onSpeechFinish utteranceId=" + utteranceId);
            }
    
            @Override
            public void onError(String utteranceId, SpeechError error) {
            	toPrint("onError error=" + "(" + error.code + ")" + error.description + "--utteranceId=" + utteranceId);
            }
    
        };
        
    	private void showTip(final String str) {
    		Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
    	}
    
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                String message = (String) msg.obj;
                if (message != null) {
                    Log.d(TAG, message);
                    showTip(message);
                }
            }
        };
    
        private void toPrint(String str) {
            Message msg = Message.obtain();
            msg.obj = str;
            this.mHandler.sendMessage(msg);
        }
    
    }





    点此查看Android开发笔记的完整目录
    展开全文
  • 关于最熟悉的声音作文七百字作文初二年级 每天,当我们行走在路上,便会听到许许多为我们所熟知的声音。禽鸟的鸣叫,车流的穿行,人群的喧哗。下面是为大家带来的初二年级作文:熟悉的声音,希望你们可以喜欢哦。...
  • Description 统计每字母在字符串中出现的次数。 Input 输入数据首先包括一个整数n,表示测试实例的个数,然后...多个测试实例之间由一个空行隔开。 请特别注意:最后块输出后面没有空行:) Sample Input
    Description
    
    统计每个元音字母在字符串中出现的次数。 
    
    Input
    
    输入数据首先包括一个整数n,表示测试实例的个数,然后是n行长度不超过100的字符串。
    
    Output
    
    对于每个测试实例输出5行,格式如下:
    a:num1
    e:num2
    i:num3
    o:num4
    u:num5
    多个测试实例之间由一个空行隔开。
    
    请特别注意:最后一块输出后面没有空行:)
    
    Sample Input
    
    
    2
    aeiou
    my name is ignatius
    
    Sample Output
    
    
    a:1
    e:1
    i:1
    o:1
    u:1
    
    a:2
    e:1
    i:3
    o:0
    u:1
    

      

    #include <stdio.h>
    #include <string.h>
    
    int main()
    {    
         int l;
         int a;
         int e;
         int i;
         int o;
         int u;
         int num;
         int length;
         char string[101];
    
         scanf("%d", &num);
    	 getchar();
    
         while(num--)
         {
             gets(string);
    
             length=strlen(string);
             a=e=i=o=u=0;
    
             for(l=0; l<length; l++)
             {
                 if(string[l]=='a' || string[l]=='A')
                 {
                    a++;
                 }
                 else if(string[l]=='e' || string[l]=='E')
                 {
                    e++;
                 }
                 else if(string[l]=='i' || string[l]=='I')
                 {
                    i++;
                 }
                 else if(string[l]=='o' || string[l]=='O')
                 {
                    o++;
                 }
                 else if(string[l]=='u' || string[l]=='U' )
                 {
                    u++;
                 }
             }
    
             printf("a:%d\ne:%d\ni:%d\no:%d\nu:%d", a, e, i, o, u);
             if(num>0)
             {
                 printf("\n\n");
             }
         }
    
             return 0;
    } 

      


    展开全文
  • 万群直播(简称直播机器人)是一套专业定制的专门用于实现群同步直播的系统,老师只需要在一个群讲课,其它群机器人都会实时自动转播你的语音,图片,文字,链接,小视频,文件,实现了语音,文字,图片,链接,小...

    微淘百课真的做到了方便大家的微信多群直播技术,实现跨群同步直播,将指定的讲师发布的信息(包括图片,文字,链接,小视频,公众号图文等)同步转播至多个群,多群实时同步直播,用户无上限。讲课老师不必在每一个微信群中,所有其他同步的微信群都是由微信群直播机器人来完成的,课程的质量音质和主讲人一模一样。被应用到很多行业中,除了培训,很多访谈类的采访,产品招商发布会,门店开业等等活动也都采取了线上的微信群同步直播。

    常见问题:

    1、微淘百课多群直播软件原理是什么以及怎么操作?

    由于微信自身的一些功能微信限制,无法达到多群进行语音直播需求,我们的多群实时直播就是将主讲人在一个微信群里说的语音,文字,图片,链接,小视频等实时同步转发到其他多个微信群里。简单的说,你只要把微信小助手分别放入主讲群和需要转播的目标群,然后选择讲师即可(讲师可设置多个)。

    2、微信多群直播软件适合什么样的场景?

    多个微信群的培训、讲座、讲课和分享会最适合微信多群实时直播。

    3、我们需要直播,接下来需要怎么做?

    联系我们客服我们会一对一服务,为您远程免费安装。每一个问您远程安装的都是我们专业的技术经理。

    4、请问直播机器人的头像和名字是否可换?

    您购买软件的话,转播机器人的头像,名称都是可以自定义的,您可以把直播机器人的微信号头像改成讲师的头像,也可以把名字改成讲师的,这都是您可以自由控制的。+VX免费测试:wangwen7917


    5是否可以播放讲师提前录制好的语音?

    可以,你要提前把讲课内容提前录制在一个自己的小群里,或者录制在跟一个朋友聊天的内容里,当你要播放课的时候,就把这个内容,一条或者几条一起发送到你要讲课的主群里,这样机器人就会同时把主群的内容同步到其它讲课群里了。

    6我的群不多,但是我每天都要转播,是否可以购买你们的软件?

    可以,我们建议使用次数较多的客户安装我们的微信多群直播系统软件,可联系下方的客户经理免费帮您远程安装到电脑上,软件跟我们正在使用的一模一样,我们包安装调试和售后更新,软件使用次数无限制,只要电脑打开微信即可进行转播,完全可以无人值守为您服务!详细报价请联系我们公司客服或经理。



    展开全文
  • 读书五百字演讲稿.doc

    2021-01-18 18:15:49
    读书五百字演讲稿 书,往往给我们这些负担过重的学生带来太的快乐了。回到家,放下沉重的书包,坐在柔软的沙发上,捧起本小说充实一下自己。这又何尝不是种精神上的休息呢?下面是小编为大家收集关于读书...
  • 随着短视频的发展,大家对文字转语音的需求越来越,... 2、可免费使用:一百字以内免费使用,免费导出; 3、主播拟人声:七十多种主播声音任你挑选,包含十一种语言:普通话、粤语、四川话、东北话、湖南话、台湾...
  • 【实例简介】autojs例子大全,千六百多个脚本,简单的到复杂的例子,统统有,小白学完马上变大神,大神学了变超神。 脚本内容包含: 几十种类型的UI脚本,抖音、QQ、微信、陌陌、支付宝等自动化操作的脚本、还有...
  • 它可以在海底世界里度过三多年的岁月她的寿命比人类长几倍,但他喜欢上了一个王子,为了去人类世界,他放弃了海里的生活,她向巫婆要了双腿,并把声音给了巫婆,忍受了痛苦把自己的鱼尾换成了了双人腿。...
  • 点击进入_更_Java千问1、JavaApplet是什么Applet是采用Java编程语言编写的小应用程序,该程序可以包含在HTML(标准通用标记语言的一个应用)页中,与在页中包含图像的方式大致相同。Applet不需要main()方法,由...
  • 这是一个浏览器插件(webExtension开发),兼容chrome和firefox,数种语言实现翻译,调用Google翻译界面,专注于用户体验和速度。 支持鼠标取词,分区词翻译,语言,人性化界面。 全新的UI,使界面焕然一新 ...
  • 传统的会议讨论系统是话筒一字排开都同时接入... 在模拟式会议系统中,每只话筒对应着电声设备的一个输入口,那就是多少只话筒就必须要布相应数量的话筒线,对于现在大中型会议时,话筒一般少者几十只话筒,者成
  • 时候,如果人工句句话去说,肯定能累半死,如果每天应付几人 能把你累虚脱,那么从技术角度上来讲,能不能实现把事先准备的文字,语音,视频转发到微信里面 , 代替人工操作的 ?? 答案当然是可以的,...
  • 难忘的军训1200.doc

    2021-01-15 12:47:51
     军训的第天,我们全校二十班,千二多人听着老师的教导齐刷刷的站在了操场上,等待着学校为我们分配教官。期间,同学们窃窃私语着,内容大致就是对教官的一些期望。 就在我们纷纷猜想时,教官已经分配完毕...
  •  军训的第天,我们全校二十班,千二多人听着老师的教导齐刷刷的站在了操场上,等待着学校为我们分配教官。期间,同学们窃窃私语着,内容大致就是对教官的一些期望。 就在我们纷纷猜想时,教官已经分配完毕...
  • JAVA上实例源码以及开源项目

    千次下载 热门讨论 2016-01-03 17:37:40
     Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超的网络程序,这是最基础的部分。 递归遍历矩阵 1目标文件,简单! 多人聊天室 3...
  •  《朝花夕拾》读后感800(1): 热爱自然,向往自由 “三味书屋后面也有一个园,虽然小,但在那里也可以爬上花坛去折腊梅花,在地上或桂花树上寻蝉蜕。最好的工作是捉了苍蝇喂蚂蚁,静悄悄地没有声音。然而同窗们...
  • ”每次听到这样的声音,管他有无恶意,是真是假,我定会拿出博尔特的速度来个百米冲刺,逃出“包围圈”,跑出九霄云外。停下后,不是跑太快,而是受到了过度刺激,心跳骤然加速。我为什么这么怕狗...
  • 2目标文件 摘要:Java源码,文件操作,TCP,服务器 Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超的网络程序,这是最基础的部分。...
  •  鲁迅作品集读后感1 三味书屋后面也有一个园,虽然小,但在那里也可以爬上花坛去折腊梅花,在地上或桂花树上寻蝉蜕.最好的工作是捉了苍蝇喂蚂蚁,静悄悄地没有声音.然而同窗们到园里的太,太久,可就不行了,先生在书房...
  • 想入手款音响,到电脑城小逛了圈,但是市面上的三五元的音箱,虽然外型做的很好,其实音质大都一般,而这些音箱大都有改造的空间,如果能调整一番,会让音箱的音质有一个翻天覆地的变化,感兴趣的话您不妨跟我...
  • 点击蓝关注我们目前市场上的键盘品牌和种类很,...对于一个长期码字的人来说,最怕正在忙碌的时候出现按键失灵,除此之外,游戏爱好者也对这系列的问题会感到特别头痛。那么今天,小编就教大家一个快速修复...
  • 想入手款音响,到电脑城小逛了圈,但是市面上的三五元的音箱,虽然外型做的很好,其实音质大都一般,而这些音箱大都有改造的空间,如果能调整一番,会让音箱的音质有一个翻天覆地的变化,感兴趣的话您不妨跟我...
  • 回家一月发现自己只做够了前面一条看影音书,也许是之前在q的影响下,我突然又用回了豆瓣,虽然大多数时间我喜欢标记我今天的看的电影美剧和听的音乐,书很少看,其实是我看得太慢了,也浮躁,阅读一百页的小书...
  • 如果a、b两面镜子相互面对面放着,你往中间站,嘿,两面镜子里都有你的千百个“化身”。 4.8.1斐波那契数列实现 101 4.8.2递归定义 103 4.9栈的应用——四则运算表达式求值 104 4.9.1后缀(逆波兰)表示法定义 104...
  • 功能工具箱、上种配音免费用,还是深夜党必备功能……这5微信小程序,帮你开启微信的更隐藏功能!西楼工具箱一个功能不,但是超级有趣、实用的小程序。语音版计算器,超级有趣!不过,我想先给你讲讲拍照...
  • 所有我在网上找到份中文与拼音的映射数据,支持六千七多汉字,六个多音字。以此为基础写了三个函数。 1、中文转简拼或全拼 不支持多音 ,原理 :循环每个字符,如果是汉字取多音的第个拼音 DROP ...
  • Proteus仿真—40单片机初学程序.

    热门讨论 2009-04-13 13:00:56
    如图4.1.1所示:在P1.0端口上接一个发光二极管L1,使L1在不停地一亮灭,灭的时间间隔为0.2秒。 2. 电路原理图 图4.1.1 3. 系统板上硬件连线 把“单片机系统”区域中的P1.0端口用导线连接到“八路...

空空如也

空空如也

1 2 3 4 5 6
收藏数 111
精华内容 44
关键字:

一百个多音字