Android SDK文本到语音引擎是一个非常有用的工具,可以在您的Android应用中集成语音。在本文中,我们将介绍如何使用TTS引擎将文本转换为语音以及将语音转换为文本。在这个过程中,我们还将看到如何在具有语音功能的记事本应用程序中实际使用TTS。我已将该应用命名为TalkingNotePad。此应用程序具有标准的记事本功能,如打开和保存文本文件以及其他功能,如录音,说出文件内容和使用语音命令执行操作。此外,我们将简要介绍使用存储访问框架(SAF)执行文本文件输入和输出操作。在此应用程序中,可以使用按钮或使用语音执行命令。
通过直接使用按钮或使用语音命令可以使用以下选项:
要在任何应用程序中将文本转换为语音,都需要TextToSpeech类和TextToSpeech.OnInitListener接口的实例。该TextToSpeech.OnInitListener包含onInit()的时被调用方法TextToSpeech的发动机初始化完成。该onInit()方法有一个整数参数,表示TextToSpeech引擎初始化的状态。一旦TextToSpeech引擎初始化完成,我们就可以调用类的speak()方法TextToSpeech来播放文本作为语音。该speak()方法的第一个参数是要说出的文本,第二个参数是队列模式。队列模式参数可以是QUEUE_ADD在回放队列的末尾添加新条目,或者QUEUE_FLUSH用新条目覆盖回放队列中的条目。
要将语音转换为文本,我们可以使用RecognizerIntent带有ACTION_RECOGNIZE_SPEECH操作和startActivityForResult()方法的类,并在方法中处理结果onActivityResult()。
该ACTION_RECOGNIZE_SPEECH操作启动一个活动,提示用户发送语音并通过语音识别器发送,如下所示:
识别结果存储在被ArrayList调用的中EXTRA_RESULTS。
要打开或创建文件,我们可以使用存储访问框架。存储访问框架包含以下元素:
在SAF中,我们可以分别使用ACTION_OPEN_DOCUMENT和ACTION_CREATE_DOCUMENT意图打开和创建文件。打开和创建文件的实际任务可以在onActivityResult()方法中实现。
打开和保存屏幕如下:
以下布局为记事本应用程序创建界面:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center"> <ScrollView android:layout_width="600px" android:layout_height="600px" android:scrollbars="vertical" android:background="@drawable/shape"> <EditText android:id="@+id/txtFileContents" android:layout_width="match_parent" android:layout_height="match_parent" /> </ScrollView> <TableLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> <TableRow> <Button android:id="@+id/btnOpen" android:text="Open" android:drawableLeft="@drawable/open" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnSave" android:text="Save" android:drawableLeft="@drawable/save" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </TableRow> <TableRow> <Button android:id="@+id/btnSpeak" android:text="Speak" android:drawableLeft="@drawable/speak" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnRecord" android:text="Record" android:drawableLeft="@drawable/record" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </TableRow> <TableRow> <Button android:id="@+id/btnVoiceCommand" android:text="Voice Command" android:drawableLeft="@drawable/command" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnClear" android:text="Clear Text" android:drawableLeft="@drawable/clear" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </TableRow> <TableRow> <Button android:id="@+id/btnHelp" android:text="Help" android:drawableLeft="@drawable/help" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnAbout" android:text="About" android:drawableLeft="@drawable/about" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </TableRow> </TableLayout> </LinearLayout>
它的背景EditText由drawable文件夹中的以下标记创建:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" android:width="300px" android:height="600px"> <corners android:radius="50px" /> <solid android:color="#FFFF00" /> <stroke android:width="2px" android:color="#FFFF00" /> </shape>
以下函数触发ACTION_OPEN_DOCUMENTintent:
public void open() { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); startActivityForResult(intent,OPEN_FILE); }
上面的代码触发在方法中执行以下代码,该onActivityResult()方法使用流类打开所选文件并在EditText控件上显示其内容:
if (resultCode == RESULT_OK) { try { Uri uri = data.getData(); String filename=uri.toString().substring (uri.toString().indexOf("%")).replace ("%2F","/").replace("%3A","/storage/emulated/0/"); //Here I have retrieved the filename by replacing characters in the uri. //It works on my device. Not sure about other devices. FileInputStream stream=new FileInputStream(new File(filename)); InputStreamReader reader=new InputStreamReader(stream); BufferedReader br=new BufferedReader(reader); StringBuffer buffer=new StringBuffer(); String s=br.readLine(); while(s!=null) { buffer.append(s+"\n"); s=br.readLine(); } txtFileContents.setText(buffer.toString().trim()); br.close(); reader.close(); stream.close(); } catch(Exception ex) { AlertDialog.Builder builder=new AlertDialog.Builder(this); builder.setCancelable(true); builder.setTitle("Error"); builder.setMessage(ex.getMessage()); builder.setIcon(R.drawable.error); AlertDialog dialog=builder.create(); dialog.show(); } }
同样,以下函数触发ACTION_CREATE_DOCUMENTintent:
public void save() { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_TITLE,"newfile.txt"); startActivityForResult(intent,SAVE_FILE); }
这导致执行以下代码以将EditText控件的内容保存到文件中:
if(resultCode==RESULT_OK) { try { Uri uri = data.getData(); String filename=uri.toString().substring (uri.toString().indexOf("%")).replace ("%2F","/").replace("%3A","/storage/emulated/0/"); FileOutputStream stream=new FileOutputStream(new File(filename)); OutputStreamWriter writer=new OutputStreamWriter(stream); BufferedWriter bw=new BufferedWriter(writer); bw.write(txtFileContents.getText().toString(),0, txtFileContents.getText().toString().length()); bw.close(); writer.close(); stream.close(); } catch(Exception ex) { AlertDialog.Builder builder=new AlertDialog.Builder(this); builder.setCancelable(true); builder.setTitle("Error"); builder.setMessage(ex.getMessage()); builder.setIcon(R.drawable.error); AlertDialog dialog=builder.create(); dialog.show(); } }
为了说出EditText控件的内容,使用以下用户定义的函数:
public void speak() { if(txtFileContents.getText().toString().trim().length()==0) { AlertDialog.Builder builder=new AlertDialog.Builder(this); builder.setCancelable(true); builder.setTitle("Error"); builder.setMessage("Nothing to speak. Please type or record some text."); builder.setIcon(R.drawable.error); AlertDialog dialog=builder.create(); dialog.show(); } else { tts=new TextToSpeech(getApplicationContext(),new TextToSpeech.OnInitListener() { public void onInit(int status) { if(status!=TextToSpeech.ERROR) { tts.setLanguage(Locale.US); String str=txtFileContents.getText().toString(); tts.speak(str,TextToSpeech.QUEUE_ADD,null); } } }); } }
上面的代码初始化TextToSpeech引擎并将语言设置为Locale.US。然后它将EditText控件的内容检索到string变量中,最后调用speak()函数将文本转换为语音。
以下代码用于使用ACTION_RECOGNIZE_SPEECHintent 记录语音:
public void record() { Intent intent=new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE,Locale.getDefault()); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); if(voiceCommandMode && !recording) { intent.putExtra(RecognizerIntent.EXTRA_PROMPT,"Speak a command to be executed..."); } else { intent.putExtra(RecognizerIntent.EXTRA_PROMPT,"Say something to record..."); } startActivityForResult(intent,RECORD_VOICE); }
上面的代码检查我们是在执行语音命令还是录制正常语音,并根据具体情况显示不同的提示。然后它会触发onActivityResult()函数中以下代码的执行:
if(resultCode==RESULT_OK) { ArrayList result=data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); if(voiceCommandMode) { String command=result.get(0).toUpperCase(); if(command.equals("OPEN")||command.startsWith("OP")||command.startsWith("OB")) { Toast.makeText(getBaseContext(),"Executing Open Command",Toast.LENGTH_SHORT).show(); open(); } else if(command.equals("SAVE")||command.startsWith("SA")||command.startsWith("SE")) { Toast.makeText(getBaseContext(),"Executing Save Command",Toast.LENGTH_SHORT).show(); save(); } else if(command.equals("SPEAK")||command.startsWith("SPA")|| command.startsWith("SPE")||command.startsWith("SPI")) { Toast.makeText(getBaseContext(),"Executing Speak Command",Toast.LENGTH_SHORT).show(); speak(); } else if(command.equals("RECORD")||command.startsWith("REC")||command.startsWith("RAC")|| command.startsWith("RAK")||command.startsWith("REK")) { Toast.makeText(getBaseContext(),"Executing Record Command",Toast.LENGTH_SHORT).show(); recording=true; record(); } else if(command.equals("CLEAR")||command.equals("KLEAR")|| command.startsWith("CLA")||command.startsWith("CLE")|| command.startsWith("CLI")||command.startsWith("KLA")|| command.startsWith("KLE")||command.startsWith("KLI")) { Toast.makeText(getBaseContext(),"Executing Clear Command",Toast.LENGTH_SHORT).show(); clear(); } else if(command.equals("HELP")||command.startsWith("HAL")|| command.startsWith("HEL")||command.startsWith("HIL")||command.startsWith("HUL")) { Toast.makeText(getBaseContext(),"Executing Help Command",Toast.LENGTH_SHORT).show(); help(); } else if(command.equals("ABOUT")||command.startsWith("ABA")||command.startsWith("ABO")) { Toast.makeText(getBaseContext(),"Executing About Command",Toast.LENGTH_SHORT).show(); about(); } else { Toast.makeText(getBaseContext(),"Unrecognized command",Toast.LENGTH_SHORT).show(); } voiceCommandMode=false; } else { txtFileContents.setText(result.get(0)); } } }
如果单击“ 语音命令 ”按钮,上面的代码将执行一个语音命令。否则,它只是在EditText控件上显示语音文本。代码使用getStringArrayListExtra()带EXTRA_RESULTS参数的方法来获取结果ArrayList。然后,它使用该get()方法提取语音文本作为第一个元素。
注意:为了避免语音命令无法识别的问题,我将语音与类似的发声词进行了比较。我不确定它是否是最好的出路,但它似乎是一个快速的解决方案。
也可以通过单击按钮来执行命令。方法中的以下代码onClick()根据单击的按钮启动操作:
voiceCommandMode=false; recording=false; Button b=(Button)v; if(b.getId()==R.id.btnOpen) { open(); } if(b.getId()==R.id.btnSave) { save(); } if(b.getId()==R.id.btnSpeak) { speak(); } if(b.getId()==R.id.btnRecord) { record(); } if(b.getId()==R.id.btnVoiceCommand) { voiceCommandMode=true; record(); } if(b.getId()==R.id.btnClear) { clear(); } if(b.getId()==R.id.btnHelp) { help(); } if(b.getId()==R.id.btnAbout) { about(); }
需要将以下权限添加到androidmanifest.xml文件,以便读取和写入外部存储:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
这是使用TextToSpeechAPI的基于语音的Android应用程序的一个示例。可以使用TextToSpeechAPI 创建更多这样令人兴奋的应用程序。
实例源码下载
链接: https://pan.baidu.com/s/1-pfR0nzA7t_DgFU38YPbnQ 提取码: sfqk 复制这段内容后打开百度网盘手机App,操作更方便哦
热门源码