Android开发实现文本到语音和语音到文本转化的示例


介绍
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。
要打开或创建文件,我们可以使用存储访问框架。存储访问框架包含以下元素:
- 文档提供程序,允许从存储设备访问文件。
- 客户端应用程序,它调用ACTION_OPEN_DOCUMENT或ACTION_CREATE_DOCUMENT意图处理文档提供程序返回的文件。
- Picker,提供UI以从文档提供程序访问满足客户端应用程序搜索条件的文件。
在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,操作更方便哦