几种文字转语音效果的编程实现

        一,jsp 网页使用JavaScript来实现文字转语音即TTS,TEXT TO speach,

    比如:

javascript语音播报 可用于排队叫号  功能实现

一个jsp项目需要增加 网页语音播报功能,定时查询符合要求的人员显示在 网页上,然后语音播报【请某某某刷身份证后取票进入大厅】。基本要求就是这样了。弄了两天,终于在windows xp,windows7上面把jsp html 网页上的 文都能正常清晰的播报出来,包括测试了大量生僻字,也能播报无误。只是要注意的是windows7下面的语音库比较全,自带 文语音库,不需要额外安装语音库;XP的就不一定了,绝大部分没有 文语音库,需要到网上下载【XP 文女声语音库】安装后才能播报 文。

方案1是通过事先把【请】、【0,1,2,3.....9】、【刷身份证后取票进入大厅】录音成mp3文件,然后播报某某某的时候(之前以为是数字顺序号),动态调用mp3文件依次播放,后来对方说播报的是 文姓名,不是顺序号,所以这个方案就不行了。

方案2是通过第三方TTS 文字转语音SDK来达到目的,网上找了一圈,有几个比如freetts这种,很多人说不支持 文,而且有位朋友说播报 文弄成功了,但是 效果不好听不懂。在网上找了讯飞语音官网,有在线 文转语音的SDK,离线 文转语音的SDK要收费,所以这种第三方语音库也基本放弃了。

方案3通过windows本身自带的语音库 实现,在网上搜索的时候,看到几篇帖子说是让windows会说话,辨别windows性别等等扯淡恶搞贴,看了里面的内容在自己电脑上试了一下,成功的把我输入的 文播放了出来, 效果很不错,于是依葫芦画瓢搬到 网页上调试之后,成功搞定了,具体如下:

//初始化语音播报对象
var VoiceObj = new ActiveXObject("SAPI.SpVoice");
//条件可以为 language=804表示 文 409表示英文
//也可不写,windows7不用修改控制面板,XP在控制面板--语音--语言选择下拉框 选择要用的语音库,
//网上下载【XP 文女声语音库】安装后才能在控制面板 看到。
VoiceObj.Voice=sp.getVoice("language=804").item(0);

//停止播报
function stopReadText(){
VoiceObj.Speak("", 2);
}
//播报文字
function readTextOne(p_str) {
if (p_str != null && p_str != ""){
try {
//开始播报
VoiceObj.Speak(p_str);
VoiceObj.Speak("", 2);//停止播报
} catch (exception) {
alert("error:"+exception);
}
}
}
//语速加
function IncRate() {
if (VoiceObj.Rate < 10) {
VoiceObj.Rate = VoiceObj.Rate + 1;
}
}

//语速减
function DecRate() {
if (VoiceObj.Rate > -10) {
VoiceObj.Rate = VoiceObj.Rate - 1;
}
}

经过实际使用测试是OK的,终于用java在windows xp,windows7下面把jsp html 网页上把 文字转语音播报出来,
实际上是javascript技术 实现的,这个对很多语言都适用ASP, PHP等等,

还是微软官方的方便简单,不然文字转语音网页上播报这种功能真就麻烦了,特别是汉字转语音播报。这是使用js来实现的。

其次,通过Java实现中文到英文朗读出来

code:

import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSlider;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import org.loon.framework.jtts.Engine;
import org.loon.framework.jtts.JTTS;

public class TalkerTest extends JPanel implements ActionListener {
	private static final long serialVersionUID = 1L;
	private JTTS jtts;
	private JButton jButton1;
	private JButton jButton2;
	private JComboBox jComboBox1;
	private JLabel jLabel1;
	private JLabel jLabel2;
	private JLabel jLabel3;
	private JLabel jLabel4;
	private JScrollPane jScrollPane1;
	private JSeparator jSeparator1;
	private JSlider jSlider1;
	private JSlider jSlider2;
	private JTextArea jTextArea1;

	@SuppressWarnings("unchecked")
	public TalkerTest() {
		this.jtts = Engine.getTTS();

		this.jSlider1 = new JSlider();
		this.jSlider2 = new JSlider();
		this.jComboBox1 = new JComboBox();
		this.jLabel1 = new JLabel();
		this.jLabel2 = new JLabel();
		this.jLabel3 = new JLabel();
		this.jSeparator1 = new JSeparator();
		this.jButton1 = new JButton();
		this.jButton2 = new JButton();

		this.jScrollPane1 = new JScrollPane();
		this.jTextArea1 = new JTextArea();
		this.jLabel4 = new JLabel();

		setLayout(null);

		this.jSlider1.setName("jSlider1");
		this.jSlider1.setMaximum(500);
		this.jSlider1.setValue(150);
		this.jSlider1.setMinimum(0); add(this.jSlider1);
		this.jSlider1.setBounds(94, 131, 316, 24);
this.jSlider2.setName("jSlider2");
		this.jSlider2.setMaximum(200);
		this.jSlider2.setValue(100);
		this.jSlider2.setMinimum(0);
		add(this.jSlider2);
		this.jSlider2.setBounds(94, 170, 316, 24);

		String[] types = new String[12];
		for (int i = 0; i < types.length; i++) {
			if (i < 5)
				types[i] = ("zh+m" + (i + 1));
			else {
				types[i] = ("zh+f" + (i - 4));
			}
		}
		types[10] = "zh+whisper";
types[11] = "zh+croak";

		this.jtts.setLanguage("zh");

		this.jComboBox1.setModel(new DefaultComboBoxModel(types));
		this.jComboBox1.setName("jComboBox1"); add(this.jComboBox1);
		this.jComboBox1.setBounds(100, 210, 100, 23);
		this.jComboBox1.addActionListener(this);

		this.jLabel1.setText("朗读间隔:");
		this.jLabel1.setName("jLabel1");
		add(this.jLabel1);
		this.jLabel1.setBounds(20, 125, 66, 30);
this.jLabel2.setText("朗读音量:");
		this.jLabel2.setName("jLabel2");
		add(this.jLabel2);
		this.jLabel2.setBounds(20, 165, 70, 30);

		this.jLabel3.setText("发音规则:");
		this.jLabel3.setName("jLabel3"); add(this.jLabel3);
		this.jLabel3.setBounds(20, 216, 80, 15);

		this.jLabel4.setText("目标文本:");
this.jLabel4.setName("jLabel4");
		add(this.jLabel4);
		this.jLabel4.setBounds(20, 10, 70, 20);

		this.jSeparator1.setName("jSeparator1");
		add(this.jSeparator1);
		this.jSeparator1.setBounds(10, 268, 398, 10);

		this.jButton1.setText("朗读文本");
		this.jButton1.setName("jButton2");
		add(this.jButton1);
		this.jButton1.setBounds(232, 284, 83, 25);
		this.jButton1.addActionListener(this);

		this.jButton2.setText("退出应用");
		this.jButton2.setName("jButton3");
add(this.jButton2);
		this.jButton2.setBounds(325, 284, 83, 25);
		this.jButton2.addActionListener(this);

		this.jScrollPane1.setName("jScrollPane1");

		this.jTextArea1.setColumns(20);
		this.jTextArea1.setRows(5);
		this.jTextArea1.setName("jTextArea1");
		this.jTextArea1
				.setText("Hello Java!Hello World!\n前所未有啊,中国踢韩国三比零啦!\n生意兴隆,财源广进,万事如意,虎年大吉!");
		this.jScrollPane1.setViewportView(this.jTextArea1); add(this.jScrollPane1);
		this.jScrollPane1.setBounds(98, 10, 310, 106);
}

	public void actionPerformed(ActionEvent e) {
		Object source = e.getSource();
		if ((source instanceof JButton)) {
			if (source == this.jButton1) {
				this.jtts.setRate(this.jSlider1.getValue());
				this.jtts.setVolume(this.jSlider2.getValue());

				this.jtts.speak(this.jTextArea1.getText().trim());
			}
			if (source == this.jButton2)
				System.exit(0);
		} else if ((source instanceof JComboBox)) {
this.jtts.setLanguage(this.jComboBox1.getSelectedItem().toString()
					.trim());
		}
	}

	public static void createGUI() {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception localException) {
		}
JFrame frame = new JFrame("Java版TTS组件发音测试(eSpeak封装)");
		frame.setDefaultCloseOperation(3);
		frame.getContentPane().add(new TalkerTest());
		frame.pack();
		frame.setSize(new Dimension(420, 345));
		frame.setResizable(false);
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
	}

	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				TalkerTest.createGUI();
}
		});
	}
}

图:

  二,通过

Android插件,TTS语音合成(文字转化语音)

语音合成是实现人机语音交互,跟机器说话,建立一个有听和讲能力的交互系统所必需的关键技术。随着语音技术的发展,百度自主研发了语音合成系统(TTS),功能是接受用户发送的文本,生成语音发送给用户。
对本文中将提到的概念约定如下:
语音合成:将文本合成为语音,即声音文件。
合成引擎:将文本合成为语音的核心模块。
TTS:Text To Speech,即“从文本到语音”。
BDTTSClient:语音合成 SDK 简称,详见下条。
语音合成 SDK:即本开发包,文中简称为 BDTTSClient。BDTTSClient 是一个封装了网络收发、音频播放功能的语音合成解决方案。借助 BDTTSClient 可以快速地在应用程序中集成语音合成功能。

   

准备事项

1.下载Android 平台 HTML5+ SDK 。最好Android studio
2.注册百度开发者,创建应用,开通服务,请参考集成指南
3.下载在线语音合成SDK_Android版。

实现步骤

1.先导入5+SDK就不多说了,参考Android平台第三方插件开发指导
2.添加BDTTSClient到工程(将开发包中的 libs 目录整体拷贝到工程目录,libs 目录包括了各平台的 SO 库,开发者视应用需要可以进行删减。galaxy_lite.jar 是百度 Android 公共基础库。)
3.权限声明(需要在 AndroidManifest.xml 文件,增加以上三个权限)

<uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 <uses-permission android:name="android.permission.READ_PHONE_STATE" />

集成java代码


import org.json.JSONArray;

import com.baidu.speechsynthesizer.SpeechSynthesizer;

import com.baidu.speechsynthesizer.SpeechSynthesizerListener;

import com.baidu.speechsynthesizer.publicutility.SpeechError;

import android.media.AudioManager;

import android.util.Log;

import io.dcloud.common.DHInterface.IWebview;

import io.dcloud.common.DHInterface.StandardFeature;

import io.dcloud.common.util.JSUtil;

public class baidutts extends StandardFeature implements SpeechSynthesizerListener{

    private IWebview iwv ;

    private String text;

    private SpeechSynthesizer speechSynthesizer;

    private static final String TAG = "zlz";

    public void speak(IWebview pWebview, JSONArray array)

    {

        iwv = pWebview;

        String CallBackID = array.optString(0);

        JSONArray newArray = new JSONArray();

        newArray.put(array.optString(1));

        //前台传过来的文本信息

        text = array.optString(1);

        //Log.i(TAG, text);

        initialTts();

        JSUtil.execCallback(pWebview, CallBackID, newArray, JSUtil.OK, false);

    }

    //百度TTS初始化

    private void initialTts() {

        speechSynthesizer = new SpeechSynthesizer(iwv.getContext(),

                "holder", this);

        // 此处需要将setApiKey方法的两个参数替换为你在百度开发者中心注册应用所得到的apiKey和secretKey

        speechSynthesizer.setApiKey("your-apiKey", "your-secretKey");

        speechSynthesizer.setAudioStreamType(AudioManager.STREAM_MUSIC);

        //setVolumeControlStream(AudioManager.STREAM_MUSIC);

        new Thread(new Runnable() {

            @Override

            public void run() {

                setParams();

                int ret = speechSynthesizer.speak(text);

                if (ret != 0) {

                    Log.i(TAG,"开始合成器失败" );

                }

            }

        }).start();


    }

    private void setParams() {

        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");

        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "5");

        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "5");

        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5");

        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_ENCODE, SpeechSynthesizer.AUDIO_ENCODE_AMR);

        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_RATE, SpeechSynthesizer.AUDIO_BITRATE_AMR_15K85);

    }

    @Override

    public void onBufferProgressChanged(SpeechSynthesizer arg0, int arg1) {}

    @Override

    public void onCancel(SpeechSynthesizer arg0) {}

    @Override

    public void onError(SpeechSynthesizer arg0, SpeechError arg1) {}

    @Override

    public void onNewDataArrive(SpeechSynthesizer arg0, byte[] arg1, boolean arg2) {}

    @Override

    public void onSpeechFinish(SpeechSynthesizer arg0) {}

    @Override

    public void onSpeechPause(SpeechSynthesizer arg0) {}

    @Override

    public void onSpeechProgressChanged(SpeechSynthesizer arg0, int arg1) {}

    @Override

    public void onSpeechResume(SpeechSynthesizer arg0) {}

    @Override

    public void onSpeechStart(SpeechSynthesizer arg0) {}

    @Override

    public void onStartWorking(SpeechSynthesizer arg0) {}

    @Override

    public void onSynthesizeFinish(SpeechSynthesizer arg0) {}

}

前台javascript代码


document.addEventListener( "plusready",  function()

{

    var _BARCODE = 'kxdPlugins',

        B = window.plus.bridge;

    var kxdPlugins = 

    {

        // 声明异步返回方法

        speak : function (Argus, successCallback, errorCallback ) 

        {

            var success = typeof successCallback !== 'function' ? null : function(args) 

            {

                successCallback(args);

            },

            fail = typeof errorCallback !== 'function' ? null : function(code) 

            {

                errorCallback(code);

            };

            callbackID = B.callbackId(success, fail);

            // 通知Native层plugintest扩展插件运行”speak”方法

            return B.exec(_BARCODE, "speak", [callbackID, Argus]);

        }

    };

    window.plus.kxdPlugins = kxdPlugins;

}, true );

使用方式

和普通的API调用方式一样:plus.kxdPlugins.speak("这是语音播放的文字内容");


注意事项

1.在/assets/data/properties.xml中添加插件权限。

2.在/assets/apps/appid/www/manifest.json 中添加对应插件权限。


3.在/AndroidManifest.xml 文件中添加对应的百度语音合成权限。

 

三,

前台javascript代码

Argus]); } }; window.plus.kxdPlugins = kxdPlugins; }, true );

使用方式

和普通的API调用方式一样:plus.kxdPlugins.speak("这是语音播放的文字内容");

注意事项

1.在/assets/data/properties.xml中添加插件权限。
2.在/assets/apps/appid/www/manifest.json 中添加对应插件权限。
3.在/AndroidManifest.xml 文件中添加对应的百度语音合成权限。

这是调用百度语音API

四,Android语音

code:

public class TextToSpeechDemo implements TextToSpeech.OnInitListener {
private final TextToSpeech mTextToSpeech;//TTS对象
private final ConcurrentLinkedQueue mBufferedMessages;//消息队列
private Context mContext;
private boolean mIsReady;//标识符


public TextToSpeechDemo(Context context){
this.mContext=context;//获取上下文
this.mBufferedMessages=new ConcurrentLinkedQueue();//实例化队列
this.mTextToSpeech=new TextToSpeech(this.mContext,this);//实例化TTS
}

//初始化TTS引擎
@Override
public void onInit(int status) {
Log.i("TextToSpeechDemo",String.valueOf(status));
if(status==TextToSpeech.SUCCESS){
int result = this.mTextToSpeech.setLanguage(Locale.CHINA);//设置识别语音为中文
synchronized (this){
this.mIsReady=true;//设置标识符为true
for(String bufferedMessage : this.mBufferedMessages){
speakText(bufferedMessage);//读语音
}
this.mBufferedMessages.clear();//读完后清空队列
}
}
}
//释放资源
public void release(){
synchronized (this){
this.mTextToSpeech.shutdown();
this.mIsReady=false;
}
}

//更新消息队列,或者读语音
public void notifyNewMessage(String lanaugh){
String message=lanaugh;
synchronized (this){
if(this.mIsReady){
speakText(message);
}else{
this.mBufferedMessages.add(message);
}
}
}

//读语音处理
private void speakText(String message){
Log.i("liyuanjinglyj",message);
HashMap params=new HashMap();
params.put(TextToSpeech.Engine.KEY_PARAM_STREAM,"STREAM_NOTIFICATION");//设置播放类型(音频流类型)
this.mTextToSpeech.speak(message, TextToSpeech.QUEUE_ADD, params);//将这个发音任务添加当前任务之后
this.mTextToSpeech.playSilence(100,TextToSpeech.QUEUE_ADD,params);//间隔多长时间
}
}

当然一般手机是不支持中文的可以百度下载讯飞TTS引擎后在测试。见最后附图。

 

由于TTS引擎的初始化是异步的,所以在执行实际的文字转语音之前需要把消息放到队列中。

 

可以给TTS引擎发送多个参数。前面展示了如何决定口语消息使用的音频流。在这种情况下,通知声音也使用相同的音频流。

 

最后,如果处理连续多个消息,最好在每个消息结束后暂停一会在播放下一个消息。这样做会清楚的告诉用户消息的结束和开始。

效果图:

五,安装语音库,假如要中文发音,使用科大讯飞语音3.0

code:

package com.example.tts; 
import java.util.Locale; 
import android.speech.tts.TextToSpeech; 
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle; 
import android.view.Menu; 
import android.view.MenuItem; 
import android.widget.Toast; 
public class MainActivity extends ActionBarActivity 
implements TextToSpeech.OnInitListener{ 
 TextToSpeech textToSpeech = null;  @Override 
 protected void onCreate(Bundle savedInstanceState) {  
 super.onCreate(savedInstanceState);   setContentView(R.layout.activity_main);   textToSpeech = new TextToSpeech(this, this);  
 textToSpeech.speak("此处无声", TextToSpeech.QUEUE_ADD, null);  }  @Override 
 public boolean onCreateOptionsMenu(Menu menu) {   getMenuInflater().inflate(R.menu.main, menu);   
return true;  } 
 protected void onDestroy()  {   super.onDestroy();  
 if (textToSpeech!=null) {    textToSpeech.shutdown();   }  }  @Override 
 public boolean onOptionsItemSelected(MenuItem item) {   
int id = item.getItemId();   
if (id == R.id.action_settings) {   
 textToSpeech.speak("此处有声", TextToSpeech.QUEUE_FLUSH, null);   
return true;   }   
return super.onOptionsItemSelected(item);  }  @Override  
public void onInit(int status) {   // TODO Auto-generated method stub   
if (status == TextToSpeech.SUCCESS) {   
 int result = textToSpeech.setLanguage(Locale.CHINESE);   
 if (result == TextToSpeech.LANG_MISSING_DATA      || result == TextToSpeech.LANG_NOT_SUPPORTED      ||
result == TextToSpeech.ERROR) {    
 Toast.makeText(this, "数据丢失或语言不支持",
Toast.LENGTH_SHORT).show();    }    
if (result == TextToSpeech.LANG_AVAILABLE) {    
 Toast.makeText(this, "支持该语言", Toast.LENGTH_SHORT).show();    }   
 Toast.makeText(this, "初始化成功", Toast.LENGTH_SHORT).show();   }  }
}

无需任何权限,这里有个问题,就是在动态创建一个对象之后,在onCreate里面调用speak方法,并不能发出声音。

可以把文字保存为语音文件,也可以读取语音文件

public void saveToFile(TextToSpeech speech,String text,String file)  {   String destFileName = "/sdcard/tts/"+file+".wav";   speech.synthesizeToFile(text, null, destFileName);  }    public void readFromFile(TextToSpeech speech,String file)  {   String destFileName = "/sdcard/tts/"+file+".wav";   speech.addSpeech("2", destFileName);   speech.speak("2", TextToSpeech.QUEUE_ADD, null);     }

这样就可以了。

接下来讲一下如何实现语音识别

语音识别首先可以考虑使用科大讯飞,但是目前使用必须要联网,而且申请一个APPID。

用起来不难

1、语音合成功能

private SpeechSynthesizer speechSynthesizer;

SpeechUser.getUser().login(MainActivity.this, null, null, "appid=54d304cf", null);

speechSynthesizer = SpeechSynthesizer.createSynthesizer(this);

speechSynthesizer.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");

speechSynthesizer.setParameter(SpeechConstant.SPEED, "50");

speechSynthesizer.setParameter(SpeechConstant.VOLUME, "50");

speechSynthesizer.setParameter(SpeechConstant.PITCH, "50");

String text = editText.getText().toString();speechSynthesizer.startSpeaking(text, null);

注册--设置--播放。文字转语音功能需要联网,但不需要正确的APPID,可以直接使用。但是语音识别就需要联网和正确的APPID

2、语音识别

private RecognizerDialog recognizerDialog;

SpeechUser.getUser().login(MainActivity.this, null, null, "appid=54d304cf", null);

recognizerDialog = new RecognizerDialog(this);

recognizerDialog.setParameter(SpeechConstant.DOMAIN, "iat");

recognizerDialog.setParameter(SpeechConstant.SAMPLE_RATE, "16000");

//显示Dialog

recognizerDialog.setListener(dialogListener);

recognizerDialog.show();

private RecognizerDialogListener dialogListener = new RecognizerDialogListener() {

//识别结果回调

@Override

public void onResult(RecognizerResult arg0, boolean arg1) {

// TODO Auto-generated method stub

String text = JsonParser.parseIatResult(arg0.getResultString());

editText.append(text);

editText.setSelection(editText.length());

}

//识别结束回调

@Override

public void onError(SpeechError arg0) {

// TODO Auto-generated method stub

}

};

package com.example.viocedemo; 
import org.json.JSONArray;
import org.json.JSONObject; 
import org.json.JSONTokener; 
import android.text.TextUtils; /**
 * 对云端返回的Json结果进行解析
 * @author iFlytek
 * @since 20131211
 */ public class JsonParser {    /**   * 听写结果的Json格式解析   * @param json   * @return   */ 
 public static String parseIatResult(String json) {  
 if(TextUtils.isEmpty(json))    
return "";      StringBuffer ret = new StringBuffer();  
 try {    JSONTokener tokener = new JSONTokener(json);   
 JSONObject joResult = new JSONObject(tokener);    
JSONArray words = joResult.getJSONArray("ws");    
for (int i = 0; i < words.length(); i++) {    
 JSONArray items = words.getJSONObject(i).getJSONArray("cw");    
 JSONObject obj = items.getJSONObject(0);    
 ret.append(obj.getString("w"));    }   } 
catch (Exception e) {    e.printStackTrace();   }  
 return ret.toString();  }   }

这样就可以实现语音输入了,非常简单。

PHP文字转语音,这类的文章少,可能要集成PHP第三方插件来实现。

Android默认没有安装TTS数据包,无法文字转语音,而在设置里推荐的语音包是Pico TTS,并不支持中文,如果需要读中文,需要下载另外的第三方语音包,如:eSpeak,Svox,个人建议Svox,eSpeak非常生硬,而且很多汉字都读不出,不支持中英文混读(英文当拼音读)。下载链接自己Google下吧,很多软件市场都有下载。下载安装后打开设置-语音输入和输出-文字转语音设置,勾选Svox Classic TTS,语言选择中文或广东话(Svox安装后还要下载安装相应的语言包的,类似插件)。

中文普通话语音包:http://www.nduoa.com/web/soft/show/id/4954
粤语语音包:http://www.nduoa.com/web/soft/show/id/4955

实现普通话,粤语

文字转语音编程可以实现更多地方语言的扩展,比如四川话,闽南语,江西话等,办法是有的,可以提供更多的语音扩展。。。。。。

文字转语音,相信不再是英文,汉语,韩语,也许是家乡话更亲切。

拭目以待

end




转载于:https://my.oschina.net/bigfool007139/blog/548984

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值