首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > 移动开发 >

app 轻巧实现中文语音智能播报, 不必依赖本地引擎

2013-11-29 
app 轻松实现中文语音智能播报, 不必依赖本地引擎import java.net.URLEncoderimport android.media.Media

app 轻松实现中文语音智能播报, 不必依赖本地引擎
import java.net.URLEncoder;import android.media.MediaPlayer;import android.util.Log;import com.gitom.framwork.util.FileUtil;import com.gitom.framwork.util.HttpDownloader;public class PlayerHelper {private VoiceQueue queue = new VoiceQueue();private VoicePlayer player = new VoicePlayer(queue);/** * 播放器处理监听状态,等待queue中队列新数据 */public void start() {Thread tc = new Thread(player);tc.start();}/** * 播放声音,可由线程压入 * * @param text */public void play(String text) {VoiceDownloader downloader = new VoiceDownloader(queue);downloader.setSource(text);Thread tp = new Thread(downloader);tp.start();}public void setPlayEnabled(boolean playEnabled) {player.setPlayEnabled(playEnabled);if(playEnabled) {start();}}}//声音对象class VoiceItem {private String text;public VoiceItem(String text) {this.text = text;}public String getText() {return text;}public String toString() {return "Voice :" + text;}}// 共享栈空间class VoiceQueue {VoiceItem sm[] = new VoiceItem[6];int index = 0;/** * * @param m * 元素 * @return 没有返回值 */public synchronized void push(VoiceItem m) {try {while (index == sm.length) {System.out.println("!!!!!!!!!超过最大堆数量,执行等待,再压入!!!!!!!!!");this.wait();}this.notify();} catch (InterruptedException e) {e.printStackTrace();} catch (IllegalMonitorStateException e) {e.printStackTrace();}sm[index] = m;index++;}/** * * @param b * true 表示显示,false 表示隐藏 * @return 没有返回值 */public synchronized VoiceItem pop() {try {while (index == 0) {System.out.println("!!!!!!!!!消费光了!!!!!!!!!");this.wait();}this.notify();} catch (InterruptedException e) {e.printStackTrace();} catch (IllegalMonitorStateException e) {e.printStackTrace();}index--;return sm[index];}}class VoiceDownloader implements Runnable {private String text;private VoiceQueue ss = new VoiceQueue();public VoiceDownloader(VoiceQueue ss) {this.ss = ss;}public void setSource(String string) {this.text = string;}/** * show 生产进程. */public void run() {while (true) {if (text != null) {final String fileName = text + ".mp3";HttpDownloader lo = new HttpDownloader();StringBuilder url = new StringBuilder();url.append("http://translate.google.cn/translate_tts?ie=UTF-8&q=");url.append(URLEncoder.encode(text));url.append("&tl=zh-CN&total=1&idx=0&textlen=8");lo.downFile(url.toString(), SoundUtils.dir, fileName);VoiceItem voice = new VoiceItem(text);System.out.println("准备播放:" + text);ss.push(voice);text = null;}// 在上面一行进行测试是不妥的,对index的访问应该在原子操作里,因为可能在push之后此输出之前又消费了,会产生输出混乱try {Thread.sleep((int) (Math.random() * 500));} catch (InterruptedException e) {e.printStackTrace();}}}}class VoicePlayer implements Runnable {private MediaPlayer mp = new MediaPlayer();private VoiceQueue ss = new VoiceQueue();private boolean playEnabled = true;public VoicePlayer(VoiceQueue ss) {this.ss = ss;}public void setPlayEnabled(boolean playEnabled) {this.playEnabled = playEnabled;}/** * show 消费进程. */public void run() {while (playEnabled) {if (mp.isPlaying()) {// 有文件正在播放,则等待,至播放器状态空闲 继续播放, 播放器本身是异步播放try {Thread.sleep((int) (Math.random() * 1000));} catch (InterruptedException e) {e.printStackTrace();}continue;}VoiceItem m = ss.pop();final String fileName = m.getText() + ".mp3";try {boolean fileExist = FileUtil.isFileExist(SoundUtils.dir+ fileName);if (fileExist) {mp.stop();mp.reset();mp.setDataSource(FileUtil.getSDPATH() + SoundUtils.dir+ fileName);mp.prepare();mp.start();}} catch (Exception e) {Log.e("mediaPlayer", "error", e);}System.out.println("播放了:---------" + m.getText());// 同上 在上面一行进行测试也是不妥的,对index的访问应该在原子操作里,因为可能在pop之后此输出之前又生产了,会产生输出混乱try {Thread.sleep((int) (Math.random() * 1000));} catch (InterruptedException e) {e.printStackTrace();}}}}

?
下载后保存至本地缓存中


app 轻巧实现中文语音智能播报, 不必依赖本地引擎
?

?

然后通过一个静态类实现调用

import java.util.HashMap;import java.util.Map;import android.content.Context;import com.gitom.framwork.xst.db.helper.AppHelper;import com.gitom.framwork.xst.db.helper.NetworkHelper;public class SoundUtils {private static PlayerHelper player = new PlayerHelper();private static Map<String, String> map = new HashMap<String, String>();static {map.put("号", "浩");map.put("x1", "1份");map.put("x2", "2份");map.put("x3", "3份");map.put("x4", "4份");map.put("x5", "5份");map.put("x6", "6份");map.put("x7", "7份");map.put("x8", "8份");map.put("x9", "9份");map.put("\n", "");player.start();}public static String dir = "catering/";public static void play(Context context, String argText) {if (!AppHelper.getInstance().isVoiceEnabled(context)) {return;}int status = NetworkHelper.getInstance().NetworkConnectStatus();if (status != 2) {// 仅 WIFI 下语音提示}final String text = replace(argText);player.play(text);}public static void setPlayEnabled(boolean playEnabled) {player.setPlayEnabled(playEnabled);}public static String replace(String argText) {String result = argText.replace(" ", "").toLowerCase().trim();for (String key : map.keySet()) {result = result.replace(key, map.get(key));}return result;}}

?

上述代码中map并无特殊用途, 只是google在翻译时有时候可能偶尔单个字符不太准确 我们进行字符替换用, 或者特殊字符替换成语音用途字符

值得指出的是, google在线播报确实是一款强大的语音引擎, 帮我们省去了不少麻烦, 减小app体积, 当然有缺点所在, 比如下载需要网络, 上面的代码可以看出, 我们下载一次以后如果下次继续同样的播报内容不会再下载, 使用缓存即可, 当然mp3一般不会大,几十K对用户影响不大, 也可以设置为wifi下才启用.

?

以上为android代码, ios大体类似, 只是语法稍修改即可, 希望本文能帮助到大家, 让我们的app更加强大具有吸引力.


最后欢迎体验移动餐厅,如果有项目难题或项目需求, 可以和我探讨 QQ:285264911
http://app.gitom.com/mobileapp/list/12

?

?

?

?

?

?

?

?

?

热点排行