# Concept TTS stands for TextToSpeech, also known as speech synthesis technology. TTS technology converts text files in real time, and the conversion time can be calculated in seconds. At present, the mainstream voice engine providers in China are mainly IFlytek, Baidu and Yunzhisheng. As the announcement of cashier amount is involved in the project, it is mainly used in noisy environments such as canteens and food stalls, so it needs to meet the requirements of fast speech, high volume and queue playback. Initially found online through the MediaPlayer play the recorded audio file plan implementation, reference [] (segmentfault.com/a/119000001… [code] : github.com/javaexcepti…

provider Offline or not Whether the charge Synthetic quality insufficient
Hkust xunfei is is good Does not support cable network, need to maintain queue
baidu is no good Offline authorization is prone to failure, and synthesis fails
Cloud know sound is no general The sound is not natural, the queue needs to be maintained, and the technical support is not effective

Finally, combined with the complex application scenario of the project, we chose the offline TTS of Yunzhisheng and maintained a simple queue, which met our requirements. Online netizens to provide a own packaging tool (blog.csdn.net/fengyuzheng… Ok, too much nonsense, let’s go to the code:

package com.ing.tts; import android.content.Context; import android.media.AudioManager; import com.unisound.client.SpeechConstants; import com.unisound.client.SpeechSynthesizer; import com.unisound.client.SpeechSynthesizerListener; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** ** @author ing * @date 2018/3/27 */ public class SpeechUtilOffline {public static final String appKey ="_appKey_"; // After testing, the offline package key and secret can not be filled with real. public static final String secret ="_secret_";
    private static SpeechUtilOffline instance;
    private SpeechSynthesizer mTTSPlayer;
    private boolean isSpeaking = false;
    private List<SpeechItem> speechList = new ArrayList<>();
    private boolean released = false;
    protected OfflineResource offlineResource;

    private SpeechUtilOffline(Context context) {
        init(context);
        released = false;
    }

    public static SpeechUtilOffline getInstance(Context context) {
        if (instance == null) {
            instance = new SpeechUtilOffline(context);
        }
        returninstance; } private void init(final Context Context) {try {private void init(final Context Context) { offlineResource = new OfflineResource(context); } catch (IOException e) { LogUtils.e("offlineResouce failed , error msg : "+e.getMessage()); e.printStackTrace(); MTTSPlayer = new SpeechSynthesizer(context, appKey, secret); // Set the local synthesis mttsPlayer.setoption (speechConstants.tts_service_mode, speechConstants.tts_service_mode_local); mTTSPlayer.setOption(SpeechConstants.TTS_KEY_VOICE_PITCH, 50); // Mttsplayer.setoption (SpeechConstants.TTS_KEY_VOICE_SPEED, 52); // Mttsplayer. setOption(SpeechConstants.TTS_KEY_VOICE_VOLUME, 100); // volume mttsplayer.setoption (speechconstants.tts_key_stream_type, Audiomanager.stream_notification); mTTSPlayer.setOption(SpeechConstants.TTS_KEY_FRONTEND_MODEL_PATH, offlineResource.getModelFilename()); / / set the back-end model mTTSPlayer. SetOption (SpeechConstants. TTS_KEY_BACKEND_MODEL_PATH, offlineResource getBackFilename ()); / / set the callback listener mTTSPlayer. SetTTSListener (newSpeechSynthesizerListener() {

            @Override
            public void onEvent(int type) {
                switch (type) {
                    caseSpeechconstants.tts_event_init: // Initialize the successful callback loggerutils.d ("onInitFinish");
                        break;
                    caseSpeechConstants.TTS_EVENT_SYNTHESIZER_START: // Start the speechConstants.d ("beginSynthesizer");
                        break;
                    caseSpeechConstants.TTS_EVENT_SYNTHESIZER_END: // SpeechConstants.TTS_EVENT_SYNTHESIZER_END: // SpeechConstants."endSynthesizer");
                        break;
                    caseSpeechConstants.TTS_EVENT_BUFFER_BEGIN: // Start the cache callback loggerutils.d ("beginBuffer");
                        break;
                    caseSpeechconstants.tts_event_buffer_ready: // Cache-completed callbackbreak;
                    caseSpeechConstants.TTS_EVENT_PLAYING_START: // Start playing the callback loggerutils.d ("onPlayBegin");
                        break;
                    caseSpeechConstants.TTS_EVENT_PLAYING_END: // Play completion callbackbreak;
                    caseSpeechConstants.TTS_EVENT_PAUSE: // Pause the callback loggerutils.d ("pause");
                        break;
                    caseSpeechConstants.TTS_EVENT_RESUME: // Recovery callback loggerutils.d (SpeechConstants."resume");
                        break;
                    caseSpeechConstants.TTS_EVENT_STOP: // Stop the callback loggerutils.d (SpeechConstants.TTS_EVENT_STOP: // Stop the callback loggerutils.d ("stop");
                        break;
                    caseSpeechConstants.TTS_EVENT_RELEASE: // Release resource callback loggerutils.d (SpeechConstants.TTS_EVENT_RELEASE: // Release resource callback loggerutils.d ("release");
                        break;
                    default:
                        break;
                }

            }

            @Override
            public void onError(int type, String errorMSG) {// Voice composition error callback loggerutils.ttserrorlog ("TTS onError __ type : "+ type +" errorMsg : "+errorMSG ); }}); // initialize the resultant engine mttsplayer.init (""); } /** * stop playback ** @author JPH * @date 2015-4-14 PM 7:50:35 */ public voidstop() { mTTSPlayer.stop(); } public void play(String content) {playImmediately(content); } public void play(String content, PLAY_MODE playMode) { switch (playMode) {case QUEUED: {
                playQueued(content);
                break;
            }
            case IMMEDIATELY: {
                playImmediately(content);
                break;
            }
        }
    }

    private void updateSpeech() {
        if(! isSpeaking) {if(speechList.size() > 0) { speak(speechList.remove(speechList.size() - 1).content); } } } private void speak(String content) { mTTSPlayer.playText(content); } public void playQueued(String content) { speechList.add(new SpeechItem(content, PLAY_MODE.QUEUED)); updateSpeech(); } public void playImmediately(String content) { speak(content); } /** * @author JPH * @date 2015-4-14 PM 7:27:56 */ public voidrelease() {// Release the offline engineif (released) {
            return;
        }
        if(mTTSPlayer ! = null) { mTTSPlayer.stop(); mTTSPlayer.release(SpeechConstants.TTS_RELEASE_ENGINE, null); } instance = null; released =true; } public enum PLAY_MODE { QUEUED, IMMEDIATELY } private class SpeechItem { public String content; public PLAY_MODE playMode; public SpeechItem(String content, PLAY_MODE mode) { this.content = content; this.playMode = mode; }}}Copy the code

The above is the offline tool class used in our project. The main Settings are:

mTTSPlayer.setOption(SpeechConstants.TTS_SERVICE_MODE, SpeechConstants.TTS_SERVICE_MODE_LOCAL); // Offline mttsplayer.setoption (SpeechConstants.TTS_KEY_VOICE_PITCH, 50); // Mttsplayer.setoption (SpeechConstants.TTS_KEY_VOICE_SPEED, 52); // Mttsplayer. setOption(SpeechConstants.TTS_KEY_VOICE_VOLUME, 100); // Max volume mttsplayer.setoption (speechconstants.tts_key_stream_type, Audiomanager.stream_notification);Copy the code

Finally, TTS_KEY_STREAM_TYPE, the SDK default is STREAM_MUSIC (media volume), but after decibel testing, it was found that among the centralized system volume types, the notification class is the loudest, so the STREAM_NOTIFICATION mode was selected here. In addition, the direct download SDK provides the results of offline file copying to the local method is not very good, here I modified the result, OfflineResource. Java:

package com.ing.tts; import android.content.Context; import android.content.res.AssetManager; import android.util.Log; import com.ing.tts.FileUtils; import java.io.IOException; import static android.content.ContentValues.TAG; /** * @author ing * @date 2018/3/27 */ public class OfflineResource {private AssetManager assets; private String destPath; private String backFilename; private String modelFilename; public OfflineResource(Context context) throws IOException { this.assets = context.getAssets(); this.destPath = FileUtils.createTmpDir(context);setOfflineVoiceType();
    }

    public String getModelFilename() {
        return modelFilename;
    }

    public String getBackFilename() {
        return backFilename;
    }

    public void setOfflineVoiceType() throws IOException {
        String back = "backend_lzl";
        String model = "frontend_model";
        backFilename = copyAssetsFile(back);
        modelFilename = copyAssetsFile(model);

    }


    private String copyAssetsFile(String sourceFilename) throws IOException {
        String destFilename = destPath + "/" + sourceFilename;
        FileUtils.copyFromAssets(assets, sourceFilename, destFilename, false);
        Log.i(TAG, "Assets to sdcard successed:" + destFilename);
        returndestFilename; }}Copy the code

FileUtils.java :

/** * Create a temporary directory for copying temporary files, such as offline resource files in assets * @param context * @return
     */

    public static String createTmpDir(Context context) {
        String sampleDir = "/ing/tts"; / / here to your local path String tmpDir = Environment. External.getexternalstoragedirectory (). The toString () + sampleDir;if(! FileUtils.makeDir(tmpDir)) { tmpDir = context.getExternalFilesDir(sampleDir).getAbsolutePath();if(! FileUtils.makeDir(sampleDir)) { throw new RuntimeException("create model resources dir failed :"+ tmpDir); }}returntmpDir; } /** * assets file 2 sdcard * @param assets * @paramsource
     * @param dest
     * @param isCover
     * @throws IOException
     */
    public static void copyFromAssets(AssetManager assets, String source, String dest, boolean isCover) throws IOException {
        File file = new File(dest);
        if(isCover || (! isCover && ! file.exists())) { InputStream is = null; FileOutputStream fos = null; try { is = assets.open(source);
                String path = dest;
                fos = new FileOutputStream(path);
                byte[] buffer = new byte[1024];
                int size = 0;
                while ((size = is.read(buffer, 0, 1024)) >= 0) {
                    fos.write(buffer, 0, size);
                }
            } finally {
                if(fos ! = null) { try { fos.close(); } finally {if(is ! = null) { is.close(); } } } } } }Copy the code

Then it’s easy to use, for example:

SpeechUtilOffline.getInstance(MainActivity.this).play("Receivables $1024", SpeechUtilOffline.PLAY_MODE.QUEUED);
Copy the code

Finally attach source code download: download.csdn.net/download/le…