package org.cocos2dx.javascript; import static android.media.AudioAttributes.CONTENT_TYPE_SPEECH; import static android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY; import static android.media.AudioAttributes.USAGE_MEDIA; import static android.media.AudioTrack.MODE_STREAM; import android.content.Context; import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioTrack; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.speech.tts.UtteranceProgressListener; import android.speech.tts.Voice; import android.util.Log; import android.widget.Toast; import com.iflytek.aikit.core.AeeEvent; import com.iflytek.aikit.core.AiHandle; import com.iflytek.aikit.core.AiHelper; import com.iflytek.aikit.core.AiInput; import com.iflytek.aikit.core.AiRequest; import com.iflytek.aikit.core.AiResponse; import com.iflytek.aikit.core.AiResponseListener; import com.iflytek.aikit.core.CoreListener; import com.iflytek.aikit.core.ErrType; import com.iflytek.aikit.core.JLibrary; import org.cocos2dx.lib.BuildConfig; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.Stack; public class TextToSpeakAikit implements AiResponseListener, ITextToSpeak { private static final String TAG = "XzTextToSpeechAikit"; private static final String UTTERANCE_ID_PREFIX = "talkback_"; public static final String AiKit_Start = "aikit"; /** * Denotes a successful operation. */ public static final int SUCCESS = 0; /** * Denotes a generic operation failure. */ public static final int ERROR = -1; /** * Denotes a stop requested by a client. It's used only on the service side of the API, client should never expect * to see this result code. */ public static final int STOPPED = -2; /** * Denotes a failure of a TTS engine to synthesize the given input. */ public static final int ERROR_SYNTHESIS = -3; /** * Denotes a failure of a TTS service. */ public static final int ERROR_SERVICE = -4; /** * Denotes a failure related to the output (audio device or a file). */ public static final int ERROR_OUTPUT = -5; /** * Denotes a failure caused by a network connectivity problems. */ public static final int ERROR_NETWORK = -6; /** * Denotes a failure caused by network timeout. */ public static final int ERROR_NETWORK_TIMEOUT = -7; /** * Denotes a failure caused by an invalid request. */ public static final int ERROR_INVALID_REQUEST = -8; /** * Denotes a failure caused by an unfinished download of the voice data. * * @see Engine#KEY_FEATURE_NOT_INSTALLED */ public static final int ERROR_NOT_INSTALLED_YET = -9; /** * Queue mode where all entries in the playback queue (media to be played and text to be synthesized) are dropped * and replaced by the new entry. Queues are flushed with respect to a given calling app. Entries in the queue from * other callees are not discarded. */ public static final int QUEUE_FLUSH = 0; /** * Queue mode where the new entry is added at the end of the playback queue. */ public static final int QUEUE_ADD = 1; /** * Queue mode where the entire playback queue is purged. This is different from {@link #QUEUE_FLUSH} in that all * entries are purged, not just entries from a given caller. * * @hide */ static final int QUEUE_DESTROY = 2; public static final int SPEECH_FLUSH_ALL = 3; /** * Denotes the language is available exactly as specified by the locale. */ public static final int LANG_COUNTRY_VAR_AVAILABLE = 2; /** * Denotes the language is available for the language and country specified by the locale, but not the variant. */ public static final int LANG_COUNTRY_AVAILABLE = 1; /** * Denotes the language is available for the language by the locale, but not the country and variant. */ public static final int LANG_AVAILABLE = 0; /** * Denotes the language data is missing. */ public static final int LANG_MISSING_DATA = -1; /** * Denotes the language is not supported. */ public static final int LANG_NOT_SUPPORTED = -2; private final Context mContext; private OnInitListener mInitListener; // Written from an unspecified application thread, read from // a binder thread. private final Object mStartLock = new Object(); // Whether to initialize this TTS object with the default engine, // if the requested engine is not available. Valid only if mRequestedEngine // is not null. Used only for testing, though potentially useful API wise // too. public final boolean mUseFallback; private final Bundle mParams = new Bundle(); // private String uriPath = ""; public Toast mToast; // // 默认发音人 private String voicer = ""; // private int mSpeed = 0; // private int mRate = 0; // 缓冲进度 private int mPercentForBuffering = 0; // 播放进度 private int mPercentForPlaying = 0; private String mUId = ""; // private boolean mIsUseAccessibility=true; private boolean mIsUseA11yThis = false; private boolean mIsBufferComplete = false; private int mBufferSize; private AudioThread mAudioRunnable; private String vcn = "xiaoyan"; private int lan = 2; private int mId; private final static String lans = "01xiaoyan01xiaofeng01chongchong01xiaofang01xiaoyuan02catherine02john03mariane05xiaolin05zhongcun06keshu08abha09christiance12xiaomei15anna16kim23felisa48rania48mohamed"; private LinkedList<String> mSpeakBuf = new LinkedList<>(); private boolean mSpeaking; public interface OnInitListener { void onInit(int status); } private static String aikitPathReal = "/xzhd/aikit/"; private static String aikitPathInData = ""; public TextToSpeakAikit(Context context, String voicer) { mContext = context; mInitListener = null; mUseFallback = true; vcn = voicer.replaceAll("aikit", ""); int index = lans.indexOf(vcn); if (index > 0) { lan = Integer.parseInt(lans.substring(index - 2, index)); } else { lan = 1; } lan = 6; mIsBufferComplete = false; initSuccess = false; copyXttsResToAikit(); String workDir = aikitPathInData; initSdk("5ad41b5a", APIKey, APISecret, workDir); int sampleRateInHz = 16000; int audioFormat = AudioFormat.ENCODING_PCM_16BIT; int channelCount = AudioFormat.CHANNEL_OUT_MONO; min_buffer_size = AudioTrack.getMinBufferSize(sampleRateInHz, channelCount, audioFormat); initAudioA11y(); initAudioMedia(); startAudioThread(); } void copyXttsResToAikit() { ///data/user_de/0/org.cocos2d.demo/files/xzhd/aikit/ File sdDir = new File(mContext.getFilesDir(), aikitPathReal);///data/user_de/0/org.cocos2d.demo/files/xzhd/aikit/ aikitPathInData = sdDir.getAbsolutePath() + "/"; FileUtils.createDirectory(aikitPathInData); try { FileUtils.copyFiles(mContext, "xtts", aikitPathInData); } catch (IOException e) { e.printStackTrace(); } } private AiHandle aiHandle; private AiInput.Builder paramBuilder = AiInput.builder(); private AiInput.Builder dataBuilder = AiInput.builder(); private volatile UtteranceProgressListener mUtteranceProgressListener; public void setOnUtteranceProgressListener(UtteranceProgressListener listener) { mUtteranceProgressListener = listener; } public Set<Voice> getVoices() { return null; } private final static String APIKey = "763f5e91d4bf53c1bb61e37d54b57407"; private final static String APISecret = "cc07aae65ad3b1372f09e0695b6af57f"; void initSdk(String appid, String apiKey, String apiSecret, String workDir) { //设定初始化参数 int interval = 555; final JLibrary.Params params = JLibrary.Params.builder() .appId(appid) .apiKey(apiKey) .apiSecret(apiSecret) .workDir(workDir) .authInterval(interval) .build(); if (BuildConfig.DEBUG) Log.w(TAG, "initSdk: " + Arrays.toString(new File(workDir).listFiles())); //注册鉴权监听 // appId=appid; JLibrary.getInst().registerListener(coreListener); //注册能力回调监听 JLibrary.getInst().registerListener(this); JLibrary.getInst().registerListener(this); JLibrary.getInst().initEntry(mContext.getApplicationContext(), params); } private String abilityId = "e2e44feff"; private void engineInit(String abilityId) { //引擎初始化 int ret = AiHelper.getInst().engineInitNoParams(abilityId); if (ret == 0) { showInfo("引擎初始化成功: " + ret); initSuccess = true; } else { showInfo("引擎初始化失败: " + ret); } } //授权结果回调 private CoreListener coreListener = new CoreListener() { @Override public void onAuthStateChange(final ErrType type, final int code) { switch (type) { case AUTH: showInfo("授权状态:授权结果码" + code); if (code == 0) { engineInit(abilityId); } break; case HTTP: showInfo("授权状态:HTTP认证结果" + code); break; default: showInfo("授权状态:其他错误"); } } }; //能力执行结果返回 @Override public void onResult(String ability, int handleID, List<AiResponse> outputData, Object usrContext) { // System.out.println("---------onResult-----------ability:" + ability + ",handleID:" + handleID); if (null != outputData && outputData.size() > 0) { for (int i = 0; i < outputData.size(); i++) { byte[] bytes = outputData.get(i).getValue(); if (bytes == null) { continue; } if (!isAudioPlayStart) { // Log.i(TAG, "onEvent: start"); isAudioPlayStart = true; showToast("开始播放"); if (mIsUseA11yThis) { if (mAudio_a11y != null) { mAudio_a11y.play(); } } else { if (mAudio_media != null) { mAudio_media.play(); } } mBufferSize = 0; mBufferTime = System.currentTimeMillis(); // mUtteranceProgressListener.onStart(mUId); } else { // } addBufferToList(bytes); } } } //事件回调 @Override public void onEvent(String ability, int handleID, int event, List<AiResponse> eventData, Object usrContext) { // System.out.println("---------onEvent-----------ability:" + ability + ",handleID:" + handleID + ",event:" + event); if (event == AeeEvent.AEE_EVENT_END.getValue()) { // 引擎计算结束事件 if (aiHandle != null) { int ret = AiHelper.getInst().end(aiHandle); // ttsFinished.set(true); if (ret == 0) { aiHandle = null; // if (ttsModel == 1) { // showInfo("开始将文件写入本地"); // writeFile(getCacheArray()); // } } } mIsBufferComplete = true; // System.out.println("---------onEvent-----AEE_EVENT_END------ability:" + ability + ",handleID:" + handleID + ",event:" + event); synchronized (mAudioRunnable.lock) { mAudioRunnable.lock.notify(); } if (!isAudioPlayStart) { if (mUtteranceProgressListener != null) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mUtteranceProgressListener.onError(mUId, Engine.Speak_Error_onDone_Unkown); } mSpeaking = false; } // if (isChangePeople.get()) { // ttsFinished.set(true); // showInfo("合成结束···"); // if (playerFragment != null) { // //可以开始播放 // playerFragment.setPlayStatus(1); // playerFragment.setTTSStatus(2); // } // Message dataMsg = Message.obtain(); // dataMsg.what = MSG_END_HANDLE; // eventHandle.sendMessage(dataMsg); // } else { // if (playerFragment != null) { // //可以开始播放 // playerFragment.setPlayStatus(1); // playerFragment.setTTSStatus(2); // } // Message dataMsg = Message.obtain(); // dataMsg.what = MSG_END_HANDLE; // eventHandle.sendMessage(dataMsg); // } // if (saveLocalFragment != null && !isPress.get()) { // saveLocalFragment.setTvStartIsSelect(true); // } // ttsFinished.set(true); // showInfo("合成结束···"); } else if (event == AeeEvent.AEE_EVENT_START.getValue()) { // 引擎计算开始事件 if (mUtteranceProgressListener != null) mUtteranceProgressListener.onStart(mUId); mSpeaking = true; } else if (event == AeeEvent.AEE_EVENT_PROGRESS.getValue()) { // 引擎计算进度事件 int pos = -1; int len = -1; for (int i = 0; i < eventData.size(); i++) { AiResponse aiOutput = eventData.get(i); if (aiOutput.getKey().equals("progress_pos")) { pos = bytes2int(aiOutput.getValue()); } else if (aiOutput.getKey().equals("progress_len")) { len = bytes2int(aiOutput.getValue()); } } } } private int bytes2int(byte[] bytes) { //如果不与0xff进行按位与操作,转换结果将出错。 int int1 = bytes[0] & 0xff; int int2 = (bytes[1] & 0xff) << 8; int int3 = (bytes[2] & 0xff) << 16; int int4 = (bytes[3] & 0xff) << 24; return int1 | int2 | int3 | int4; } //错误通知,能力执行终止 @Override public void onError(String ability, int handleID, int err, String msg, Object usrContext) { // System.out.println("---------onError-----------ability:" + ability + ",handleID:" + handleID + ",err:" + err + ",msg:" + msg); // ttsFinished.set(true); // if (playerFragment != null) { // playerFragment.setTTSStatus(0); // playerFragment.setPlayStatus(0); // } // // if (saveLocalFragment != null && !isPress.get()) { // saveLocalFragment.setTvStartIsSelect(true); // } } private void showInfo(final String info) { } private void initAudioA11y() { try { if (mAudio_a11y == null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { mAudio_a11y = new AudioTrack.Builder().setAudioAttributes(new AudioAttributes.Builder() .setUsage(USAGE_ASSISTANCE_ACCESSIBILITY) .setContentType(CONTENT_TYPE_SPEECH) .build()) .setBufferSizeInBytes(256) //.setSessionId(AUDIO_SESSION_ID_GENERATE) .setTransferMode(MODE_STREAM) .setAudioFormat((new AudioFormat.Builder()) .setChannelMask(channelCount) .setEncoding(audioFormat) .setSampleRate(sampleRateInHz) .build()).build(); } } mAudio_a11y.play(); } catch (Exception e) { e.printStackTrace(); } } private void initAudioMedia() { try { if (mAudio_media == null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { mAudio_media = new AudioTrack.Builder().setAudioAttributes(new AudioAttributes.Builder() .setUsage(USAGE_MEDIA) .setContentType(CONTENT_TYPE_SPEECH) .build()) .setBufferSizeInBytes(256) //.setSessionId(AUDIO_SESSION_ID_GENERATE) .setTransferMode(MODE_STREAM) .setAudioFormat((new AudioFormat.Builder()) .setChannelMask(channelCount) .setEncoding(audioFormat) .setSampleRate(sampleRateInHz) .build()).build(); } } mAudio_media.play(); } catch (Exception e) { e.printStackTrace(); } } private boolean initSuccess = false; public boolean isInitSuccess() { // return mTts != null; return initSuccess; } // public void showParam() { // if (mTts == null) { // // return; // } // //SpeechConstant.VOICE_NAME: 发音人 // // //SpeechConstant.SPEED: 合成语速 // // //SpeechConstant.VOLUME: 合成音量 // // //SpeechConstant.PITCH: 合成语调 // // //SpeechConstant.BACKGROUND_SOUND: 背景音乐 // // //SpeechConstant.TTS_BUFFER_TIME: 合成音频缓冲时间 // // //SpeechConstant.STREAM_TYPE: 播放类型 // // //SpeechConstant.SAMPLE_RATE: 采样率 // // //SpeechConstant.TTS_AUDIO_PATH: 合成录音保存路径 // // //SpeechConstant.ENGINE_TYPE:引擎类型; // // // //ResourceUtil.TTS_RES_PATH:离线资源路径; // // //ResourceUtil.ENGINE_START:启动离线引擎; // // //SpeechConstant.TTS_FADING : 合成淡入淡出; // // //SpeechConstant.AUDIO_FORMAT_AUE:音频流编解码格式; // // // // } // public void showParamError() { // if (mTts == null) { // // return; // } // //SpeechConstant.TTS_BUFFER_TIME: 合成音频缓冲时间 // // //SpeechConstant.STREAM_TYPE: 播放类型 // // //SpeechConstant.SAMPLE_RATE: 采样率 // // //SpeechConstant.TTS_AUDIO_PATH: 合成录音保存路径 // // //SpeechConstant.ENGINE_TYPE:引擎类型; // // // //ResourceUtil.TTS_RES_PATH:离线资源路径; // // //ResourceUtil.ENGINE_START:启动离线引擎; // // //SpeechConstant.TTS_FADING : 合成淡入淡出; // // //SpeechConstant.AUDIO_FORMAT_AUE:音频流编解码格式; // // } //获取发音人资源路径 // private String getResourcePath(String voicerLocal) { // StringBuffer tempBuffer = new StringBuffer(); // //合成通用资源 // tempBuffer.append(ResourceUtil.generateResourcePath(mContext, RESOURCE_TYPE.assets, "tts/common.jet")); // tempBuffer.append(";"); // //发音人资源 // tempBuffer.append(ResourceUtil.generateResourcePath(mContext, RESOURCE_TYPE.assets, "tts/" + voicerLocal + ".jet")); // // return tempBuffer.toString(); // } // private String getResourcePathXtts(String voicerLocal) { // StringBuffer tempBuffer = new StringBuffer(); // //合成通用资源 // tempBuffer.append(ResourceUtil.generateResourcePath(mContext, RESOURCE_TYPE.assets, "xtts/common.jet")); // tempBuffer.append(";"); // //发音人资源 // tempBuffer.append(ResourceUtil.generateResourcePath(mContext, RESOURCE_TYPE.assets, "xtts/" + voicerLocal + ".jet")); // // return tempBuffer.toString(); // } //获取发音人资源路径 // private String getResourcePathXtts(String voicerLocal, String jetPath) { // // StringBuffer tempBuffer = new StringBuffer(); // //合成通用资源 // tempBuffer.append(ResourceUtil.generateResourcePath(mContext, RESOURCE_TYPE.assets, "xtts/common.jet")); // tempBuffer.append(";"); // //发音人资源 // tempBuffer.append(ResourceUtil.generateResourcePath(mContext, RESOURCE_TYPE.path, jetPath + "/" + voicerLocal + ".jet")); // // return tempBuffer.toString(); // } // //获取发音人资源路径 // private String getResourcePath(String voicerLocal, String jetPath) { // // StringBuffer tempBuffer = new StringBuffer(); // //合成通用资源 // tempBuffer.append(ResourceUtil.generateResourcePath(mContext, RESOURCE_TYPE.assets, "tts/common.jet")); // tempBuffer.append(";"); // //发音人资源 // tempBuffer.append(ResourceUtil.generateResourcePath(mContext, RESOURCE_TYPE.path, jetPath + "/" + voicerLocal + ".jet")); // // return tempBuffer.toString(); // } // /** // * 初始化监听。 // */ // private InitListener mTtsInitListener = new InitListener() { // @Override // public void onInit(int code) { // // if (code != ErrorCode.SUCCESS) { // showToast("初始化失败,错误码:" + code); // } else { // //mTts.startSpeaking("欢迎使用心智助手", mTtsListener); // // // mTts.startSpeaking("初始化成功", mTtsListener); // // 初始化成功,之后可以调用startSpeaking方法 // // 注:有的开发者在onCreate方法中创建完合成对象之后马上就调用startSpeaking进行合成, // // 正确的做法是将onCreate中的startSpeaking调用移至这里 // } // } // }; private void showToast(final String str) { if (BuildConfig.DEBUG) Log.w(TAG, "showToast: " + str); // mToast.setText(str); // mToast.show(); // LogTool.log(TAG, "showToast str: " + str); } private String mLastSpeak = ""; public void stopSpeaking() { // mTts.stopSpeaking(); } public List<String> spiltString(String text, int sumInLine) { List<String> strs = new ArrayList<String>(); int startIndex = 0; int endIndex = 0; for (int i = 0; i < text.length(); i++) { char ch = text.charAt(i); endIndex = i + 1; if (endIndex == text.length()) { if (ch == 10) { endIndex = i; } strs.add(text.substring(startIndex, endIndex)); break; } if (endIndex - startIndex == sumInLine) { if (ch == 10) { endIndex = i; } strs.add(text.substring(startIndex, endIndex)); startIndex = i + 1; } else { if (ch == 10) { strs.add(text.substring(startIndex, endIndex - 1)); startIndex = i + 1; } } } return strs; } /** * Checks whether the TTS engine is busy speaking. Note that a speech item is considered complete once it's audio * data has been sent to the audio mixer, or written to a file. There might be a finite lag between this point, and * when the audio hardware completes playback. * * @return {@code true} if the TTS engine is speaking. */ public boolean isSpeaking() { // if (null == mTts) { return mSpeaking; // } // return mTts.isSpeaking(); } @Override public void destroy() { shutdown(); } public boolean isNeedClearBuffers = false; public synchronized void setNeedClearBuffers(boolean needClearBuffers) { // Log.i(TAG, "setNeedClearBuffers: " + needClearBuffers); isNeedClearBuffers = needClearBuffers; } @Override public boolean speak(String text, int speed, int pitch, int volume, int scale) { buffers.clear(); // AppTool.printCallStatck(TAG); int code = 0; mBufferTime = -1; mLastSpeak = text; mIsBufferComplete = false; isAudioPlayStart = false; showToast("speak " + text); if (aiHandle != null) { int ret = AiHelper.getInst().end(aiHandle); if (ret == 0) { aiHandle = null; } } setSpeedUp(scale); paramBuilder.clear(); System.out.println("---------speak text=" + text+",speed="+speed+",pitch="+pitch+",volume="+volume+",scale="+scale); System.out.println("---------speak vcn=" + vcn+",lan="+lan+",speed="+speed+",pitch="+pitch+",volume="+volume); paramBuilder.param("vcn", vcn). //必选参数,60020 // :xiaoyan发音人60030:xiaofeng发音人69010:john英文发音人69020:catherine英文发音人,chongchong :65620 param("vcnModel", vcn). //1:中文, 2:英文, 3:法语, 5:日语, 6:俄语, 9:德语, 15:意大利语, 16:韩语, 23:西班牙语, 48:阿拉伯语, 50:阿拉伯语(Eg), 12:粤语, 8:印地语 param("language", lan). //reg 英文发音方式 int 0:引擎自动判断, 1:按字母发音, 2:按单词发音 否 0 param("reg", 2). //rdn 数字发音方式 int 0:引擎自动判断, 1:按数字发音, 2:按字符串发音 否 0 param("rdn", 0). param("speed", speed). //可选参数,默认为0 param("pitch", pitch). //可选参数,默认为0 param("volume", volume). //可选参数,默认为0 param("textEncoding", "UTF-8"); //可选参数,文本编码格式,默认为65001,UTF8格式 // ttsFinished.set(false); // if(eventHandle != null){ // eventHandle.removeCallbacksAndMessages(null); // } aiHandle = AiHelper.getInst().start(abilityId, paramBuilder.build(), null); if (aiHandle.getCode() != 0) { showInfo("start失败:" + aiHandle.getCode()); // ttsFinished.set(true); // playerFragment.setTTSStatus(0); // playerFragment.setPlayStatus(0); // saveLocalFragment.setTvStartIsSelect(true); return false; } dataBuilder.clear(); dataBuilder.text("text", mLastSpeak); // 2.3 能力输出数据 数据段名称:audio 数据类型:音频 // 字段 含义 数据类型 取值范围 默认值 说明 必填 // encoding 音频编码 string lame, speex, opus, speex-wb speex-wb 取值范围可枚举 否 dataBuilder.audio("encoding", "raw"); // sample_rate 采样率 int 16000, 8000 16000 音频采样率,可枚举 否 dataBuilder.audio("sample_rate", sampleRateInHz + ""); // channels 声道数 int 1, 2 1 声道数,可枚举 否 dataBuilder.audio("channels", numChannels + ""); // bit_depth 位深 int 16, 8 16 单位bit,可枚举 否 dataBuilder.audio("bit_depth", "16"); // data 音频数据 string 音频大小:0-10M 否 // frame_size 帧大小 int 最小值:0, 最大值:1024 0 帧大小,默认0 否 // totalLen = text.length(); // IFlyPlayerManager.getInst().reSetPercent(totalLen); int ret = AiHelper.getInst().write(dataBuilder.build(), aiHandle); if (ret != 0) { showInfo(" write失败:" + ret); // ttsFinished.set(true); // playerFragment.setTTSStatus(0); // playerFragment.setPlayStatus(0); // saveLocalFragment.setTvStartIsSelect(true); return false; } // mIsUseA11yThis = false; mUId = getId(); System.out.println("--------------------mUId=" + mUId); return true; } private String getId() { return String.valueOf(mId++); } @Override public boolean appendSpeak(String text, int speed, int pitch, int volume, int scale) { if (!isSpeaking()) return speak(text, speed, pitch, volume, scale); mSpeakBuf.addLast(text); return true; } /** * Interrupts the current utterance (whether played or rendered to file) and discards other utterances in the queue. */ public void stop() { // Log.i(TAG, "stop: "); mBufferTime = -1; /*if (null != mTts) { mTts.stopSpeaking(); }*/ buffers.clear(); //setNeedClearBuffers(true); // Log.i(TAG, "stop:2 "); if (mIsUseA11yThis) { if (null != mAudio_a11y) { mAudio_a11y.stop(); } } else { if (null != mAudio_media) { mAudio_media.stop(); } } // Log.i(TAG, "stop:3 "); } public void shutdown() { // if (null != mTts) { mTts.destroy(); // } if (null != mAudio_a11y) { mAudio_a11y.stop(); } if (null != mAudio_media) { mAudio_media.stop(); } stopAudioThread(); } public void stopAll() { // if (null == mTts) { // return; // } buffers.clear(); //setNeedClearBuffers(true); if (null != mAudio_a11y) { mAudio_a11y.stop(); } if (null != mAudio_media) { mAudio_media.stop(); } // if (mTts.isSpeaking()) { // stopSpeaking(); // } } /** * Sets the speech rate. * <p> * This has no effect on any pre-recorded speech. * * @param speechRate Speech rate. {@code 1.0} is the normal speech rate, lower values slow down the speech ({@code 0.5} is * half the normal speech rate), greater values accelerate it ({@code 2.0} is twice the normal speech * rate). * @return {@link #ERROR} or {@link #SUCCESS}. */ public int setSpeechRate(float speechRate) { if (speechRate > 0.0f) { int intRate = (int) (speechRate * 100); if (intRate > 0) { synchronized (mStartLock) { } return SUCCESS; } } return ERROR; } /** * @hide */ public String getCurrentEngine() { return ""; } @Deprecated public int setEngineByPackageName(String enginePackageName) { return SUCCESS; } /** * Gets the package name of the default speech synthesis engine. * * @return Package name of the TTS engine that the user has chosen as their default. */ public String getDefaultEngine() { return ""; } /** * Limit of length of input string passed to speak and synthesizeToFile. */ public static int getMaxSpeechInputLength() { return 4000; } private boolean isAudioPlayStart = false; private static AudioTrack mAudio_a11y; private static AudioTrack mAudio_media; private static final int MIN_AUDIO_BUFFER_SIZE = 8192; private long mBufferTime; private boolean isAudioThreadStop = false; private LinkedList<byte[]> buffers = new LinkedList<>(); private void startAudioThread() { isAudioThreadStop = false; mAudioRunnable = new AudioThread(); new Thread(mAudioRunnable).start(); } private void stopAudioThread() { isAudioThreadStop = true; } public void setSpeedUp(int speedUp) { if (sonic == null) { initSonic(); } sonic.setSpeed(speedUp); } private void addBufferToList(byte[] buffer) { // if (isAudioThreadStop) { startAudioThread(); } if (sonic == null) { initSonic(); } buffers.addLast(buffer); // System.out.println("---------addBufferToList-----------isAudioThreadStop:" + isAudioThreadStop + ",sonic:" + sonic + ",buffers.size():" + buffers.size()); synchronized (mAudioRunnable.lock) { mAudioRunnable.lock.notify(); } } Sonic sonic = null; int sampleRateInHz = 16000; int audioFormat = AudioFormat.ENCODING_PCM_16BIT; int channelCount = AudioFormat.CHANNEL_OUT_MONO; int numChannels = 1; int min_buffer_size = 0; private void initSonic() { sonic = new Sonic(sampleRateInHz, numChannels); int numRead, numWritten; boolean emulateChordPitch = false; int quality = 0; min_buffer_size = AudioTrack.getMinBufferSize(sampleRateInHz, channelCount, audioFormat); sonic.setSpeed(1.0f); //sonic.setPitch(0.5f); //sonic.setRate(1.0f); sonic.setVolume(1f); //sonic.setChordPitch(emulateChordPitch); //sonic.setQuality(quality); } private Handler myHandle = new Handler(); private class AudioThread implements Runnable { private final Object lock = new Object(); @Override public void run() { while (!isAudioThreadStop) { // // // try { byte[] buffer = buffers.pollFirst(); if (buffer != null) { try { // saveBufferToFile(buffer,mUId); if (sonic.getSpeed() > .0f) { // int len = buffer.length; sonic.writeBytesToStream(buffer, buffer.length); int numWritten = 0; byte[] outBuffer = new byte[len * 2]; numWritten = sonic.readBytesFromStream(outBuffer, len * 2); mBufferSize += numWritten; // if (mIsUseA11yThis) { mAudio_a11y.write(outBuffer, 0, numWritten); } else { mAudio_media.write(outBuffer, 0, numWritten); } // } else { mBufferSize += buffer.length; if (mIsUseA11yThis) { mAudio_a11y.write(buffer, 0, buffer.length); } else { mAudio_media.write(buffer, 0, buffer.length); } } } catch (Exception e) { e.printStackTrace(); } } else { if (mIsBufferComplete) { // Log.i(TAG, "mIsBufferComplete: "); mIsBufferComplete = false; int time = mBufferSize / (16000 * 2 / 1000); long utime = System.currentTimeMillis() - mBufferTime; // Log.i("fly", "run: " + time + ";" + utime); mBufferTime = 0; //if (utime < time) { myHandle.postDelayed(new Runnable() { @Override public void run() { if (mBufferTime == 0) { if (mIsUseA11yThis) { if (null != mAudio_a11y) { mAudio_a11y.stop(); } } else { if (null != mAudio_media) { mAudio_media.stop(); } } if (mUtteranceProgressListener != null) mUtteranceProgressListener.onDone(mUId); mSpeaking = false; } } }, Math.max(time - utime, 60)); } try { synchronized (lock) { lock.wait(); //Log.i(TAG, "addBufferToList: wait"); } //Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } if (isNeedClearBuffers) { setNeedClearBuffers(false); if (buffers.size() > 0) { //buffers.clear(); continue; } if (mIsUseA11yThis) { if (null != mAudio_a11y) { mAudio_a11y.stop(); mAudio_a11y.play(); } } else { if (null != mAudio_media) { mAudio_media.stop(); mAudio_a11y.play(); } } } } catch (Exception e) { e.printStackTrace(); } } } } public class Engine { public static final int DEFAULT_RATE = 100; public static final int DEFAULT_PITCH = 100; public static final float DEFAULT_VOLUME = 1.0f; public static final float DEFAULT_PAN = 0.0f; public static final int USE_DEFAULTS = 0; // false // public static final int DEFAULT_STREAM = AudioManager.STREAM_MUSIC; public static final int CHECK_VOICE_DATA_PASS = 1; public static final int CHECK_VOICE_DATA_FAIL = 0; public static final int Speak_Error_onEvent_21002 = 10; public static final int Speak_Error_onDone_Unkown = 11; public static final int Speak_Error_onError_20999 = 12; public static final String EXTRA_AVAILABLE_VOICES = "availableVoices"; public static final String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices"; public static final String KEY_PARAM_RATE = "rate"; public static final String KEY_PARAM_VOICE_NAME = "voiceName"; public static final String KEY_PARAM_LANGUAGE = "language"; public static final String KEY_PARAM_COUNTRY = "country"; public static final String KEY_PARAM_VARIANT = "variant"; public static final String KEY_PARAM_ENGINE = "engine"; public static final String KEY_PARAM_PITCH = "pitch"; public static final String KEY_PARAM_STREAM = "streamType"; public static final String KEY_PARAM_AUDIO_ATTRIBUTES = "audioAttributes"; public static final String KEY_PARAM_UTTERANCE_ID = "utteranceId"; public static final String KEY_PARAM_VOLUME = "volume"; public static final String KEY_PARAM_PAN = "pan"; public static final String KEY_PARAM_SESSION_ID = "sessionId"; public static final String KEY_FEATURE_NOT_INSTALLED = "notInstalled"; public static final String KEY_FEATURE_NETWORK_TIMEOUT_MS = "networkTimeoutMs"; public static final String KEY_FEATURE_NETWORK_RETRIES_COUNT = "networkRetriesCount"; } public String getVoicer() { return voicer; } public void setVoicer(String voicer) { this.voicer = voicer; } public int getPercentForBuffering() { return mPercentForBuffering; } public void setPercentForBuffering(int mPercentForBuffering) { this.mPercentForBuffering = mPercentForBuffering; } public int getPercentForPlaying() { return mPercentForPlaying; } public void setPercentForPlaying(int mPercentForPlaying) { this.mPercentForPlaying = mPercentForPlaying; } }
TextToSpeakAikit
最新推荐文章于 2025-04-30 11:28:26 发布