Android讯飞语音集成【在线语音合成2】

前言:

语音合成:
与语音听写相反,语音合成是将一段文字转换为语音,可根据需要合成出不同音色、语速和语调的声音,让机器像人一样开口说话

效果图:
在这里插入图片描述
2、直接上代码,配置不再重复说明了:

、TTSActivity.java

public class TTSActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "TTSActivity";
    // 语音合成对象
    private SpeechSynthesizer mTts;
    // 默认发音人
    private String voicer = "xiaoyan";
    //云端发音人名称列表(中文)
    private String[] mCloudVoicersEntries;
    //云端发音人名称列表(英文简称)
    private String[] mCloudVoicersValue;
    //音频流名称列表
    private String[] mCloudVoicersName;
    // 缓冲进度
    private int mPercentForBuffering = 0;
    // 播放进度
    private int mPercentForPlaying = 0;
    // 引擎类型
    private String mEngineType = SpeechConstant.TYPE_CLOUD;
    private MemoryFile memFile;
    public volatile long mTotalSize = 0;
    private Vector<byte[]> container = new Vector<>();
    private TextView voice_value1, voice_value2, voice_value3, progress;
    private SeekBar seekBar1, seekBar2, seekBar3;
    private Button ttsBtnPersonType,ttsBtnPersonSelect;
    //起始值50
    private int voiceNumber1 = 50, voiceNumber2 = 50, voiceNumber3 = 50;
    //播报的文字
    private EditText ttsText;
    //音频流类型
    private String voiceName;
    private SpannableStringBuilder builder;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tts);
        //初始化控件
        findViewById(R.id.tts_play).setOnClickListener(this);
        findViewById(R.id.tts_cancel).setOnClickListener(this);
        findViewById(R.id.tts_pause).setOnClickListener(this);
        findViewById(R.id.tts_resume).setOnClickListener(this);
        voice_value1 = this.findViewById(R.id.voice_value1);
        voice_value2 = this.findViewById(R.id.voice_value2);
        voice_value3 = this.findViewById(R.id.voice_value3);
        seekBar1 = this.findViewById(R.id.seek_bar1);
        seekBar2 = this.findViewById(R.id.seek_bar2);
        seekBar3 = this.findViewById(R.id.seek_bar3);
        ttsText = this.findViewById(R.id.tts_text);
        progress = this.findViewById(R.id.progress);
        ttsBtnPersonType = this.findViewById(R.id.tts_btn_person_type);
        ttsBtnPersonSelect = this.findViewById(R.id.tts_btn_person_select);
        ttsBtnPersonType.setOnClickListener(this);
        ttsBtnPersonSelect.setOnClickListener(this);
        //seekbar滑动事件
        initSeekBarListener();

        // 初始化合成对象
        mTts = SpeechSynthesizer.createSynthesizer(this, mTtsInitListener);
        mCloudVoicersEntries = getResources().getStringArray(R.array.voicer_cloud_entries);
        mCloudVoicersValue = getResources().getStringArray(R.array.voicer_cloud_values);
        mCloudVoicersName = getResources().getStringArray(R.array.stream_entries);
    }

    private void initSeekBarListener() {
        //语速
        seekBar1.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                //进度发生改变时会触发
                voice_value1.setText("语速:" + i);
                voiceNumber1 = i;
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                //按住SeekBar时会触发
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                //放开SeekBar时触发
            }
        });
        //音调
        seekBar2.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                voice_value2.setText("音调:" + i);
                voiceNumber2 = i;
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });
        //音量
        seekBar3.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                voice_value3.setText("音量:" + i);
                voiceNumber3 = i;
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });
    }

    @Override
    public void onClick(View view) {
        if (null == mTts) {
            // 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
            showToast("创建对象失败,请确认 libmsc.so 放置正确,且有调用 createUtility 进行初始化");
            return;
        }
        switch (view.getId()) {
            // 开始合成
            // 收到onCompleted 回调时,合成结束、生成合成音频
            // 合成的音频格式:只支持pcm格式
            case R.id.tts_play:
                //恢复背景色为白色
                setTextStyle(0, ttsText.getText().toString().length(), Color.WHITE);
                // 设置参数
                setParam();
                /**
                 * 只保存音频不进行播放接口,调用此接口请注释startSpeaking接口
                 * text:要合成的文本,uri:需要保存的音频全路径,listener:回调接口
                 */
                int code = mTts.startSpeaking(ttsText.getText().toString(), mTtsListener);
                //String path = Environment.getExternalStorageDirectory() + "/tts.pcm";
                //int code = mTts.synthesizeToUri(texts, path, mTtsListener);
                if (code != ErrorCode.SUCCESS) {
                    showToast("语音合成失败,错误码: " + code + ",请点击网址https://www.xfyun.cn/document/error-code查询解决方案");
                }
                break;
            // 取消合成
            case R.id.tts_cancel:
                //恢复背景色为白色
                setTextStyle(0, ttsText.getText().toString().length(), Color.WHITE);
                mTts.stopSpeaking();
                break;
            // 暂停播放
            case R.id.tts_pause:
                mTts.pauseSpeaking();
                break;
            // 继续播放
            case R.id.tts_resume:
                mTts.resumeSpeaking();
                break;
            // 选择发音人
            case R.id.tts_btn_person_select:
                showPresonSelectDialog();
                break;
            // 选择音频流类型
            case R.id.tts_btn_person_type:
                showVoiceSelectDialog();
                break;
        }
    }


    private int selectedNum = 0;
    private int selectedVoiceNum = 0;

    /**
     * 发音人选择。
     */
    private void showPresonSelectDialog() {
        new AlertDialog.Builder(this).setTitle("在线合成发音人选项")
                // 单选框有几项,各是什么名字
                .setSingleChoiceItems(mCloudVoicersEntries, selectedNum,
                        new DialogInterface.OnClickListener() { // 点击单选框后的处理
                            public void onClick(DialogInterface dialog, int which) { // 点击了哪一项
                                voicer = mCloudVoicersValue[which];
                                //凯瑟琳、亨利(阿森纳)、玛丽切换文本至英文
                                if ("catherine".equals(voicer) || "henry".equals(voicer) || "vimary".equals(voicer)) {
                                    ttsText.setText(R.string.text_tts_source_en);
                                } else {
                                    //其他中文
                                    ttsText.setText(R.string.text_tts_source);
                                }
                                ttsBtnPersonSelect.setText("发音人:"+mCloudVoicersEntries[which]);
                                selectedNum = which;
                                dialog.dismiss();
                            }
                        }).show();
    }

    /**
     * 音频流类型选择
     */
    private void showVoiceSelectDialog() {
        new AlertDialog.Builder(this).setTitle("在线合成发音人选项")
                // 单选框有几项,各是什么名字
                .setSingleChoiceItems(mCloudVoicersName, selectedVoiceNum,
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                voiceName = mCloudVoicersName[which];
                                ttsBtnPersonType.setText("音频流类型:" + voiceName);
                                dialog.dismiss();
                            }
                        }).show();
    }

    /**
     * 初始化监听。
     */
    private InitListener mTtsInitListener = new InitListener() {
        @Override
        public void onInit(int code) {
            Log.e(TAG, "InitListener init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
                showToast("初始化失败,错误码:" + code + ",请点击网址https://www.xfyun.cn/document/error-code查询解决方案");
            } else {
                // 初始化成功,之后可以调用startSpeaking方法
                // 注:有的开发者在onCreate方法中创建完合成对象之后马上就调用startSpeaking进行合成,
                // 正确的做法是将onCreate中的startSpeaking调用移至这里
            }
        }
    };

    /**
     * 合成回调监听。
     */
    private SynthesizerListener mTtsListener = new SynthesizerListener() {
        @Override
        public void onSpeakBegin() {
            showToast("开始播放");
        }

        @Override
        public void onSpeakPaused() {
            showToast("暂停播放");
        }

        @Override
        public void onSpeakResumed() {
            showToast("继续播放");
        }

        @Override
        public void onBufferProgress(int percent, int beginPos, int endPos, String info) {
            // 合成进度
            Log.e(TAG, "onBufferProgress percent =" + percent);
            //记录暂停时的进度
            mPercentForBuffering = percent;
            //缓冲与播放进度
            showToast(String.format(getString(R.string.tts_toast_format), mPercentForBuffering, mPercentForPlaying));
        }

        @Override
        public void onSpeakProgress(int percent, int beginPos, int endPos) {
            // 播放进度
            Log.e(TAG, "onSpeakProgress percent =" + percent);
            mPercentForPlaying = percent;
            progress.setText("当前进度:" + percent + "%");
            setTextStyle(beginPos, endPos, Color.GREEN);
        }

        @Override
        public void onCompleted(SpeechError error) {
            if (error == null) {
                //showToast("播放完成");
                Log.e(TAG, "onCompleted: 播放完成" + container.size());
                try {
                    for (int i = 0; i < container.size(); i++) {
                        writeToFile(container.get(i));
                    }
                } catch (IOException e) {
                }
                //播放完成后保存至文件夹
                FileUtil.saveFile(memFile, mTotalSize, Environment.getExternalStorageDirectory() + "/helloword-Tts1.pcm");
            } else if (error != null) {
                showToast(error.getPlainDescription(true));
            }
        }

        @Override
        public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
            //	 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
            //	 若使用本地能力,会话id为null
            if (SpeechEvent.EVENT_SESSION_ID == eventType) {
                String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
                Log.e(TAG, "onEventid =" + sid);
            }
            //当设置SpeechConstant.TTS_DATA_NOTIFY为1时,抛出buf数据
            if (SpeechEvent.EVENT_TTS_BUFFER == eventType) {
                byte[] buf = obj.getByteArray(SpeechEvent.KEY_EVENT_TTS_BUFFER);
                Log.e(TAG, "onEvent bufis =" + buf.length);
                container.add(buf);
            }
        }
    };

    /**
     * 设置文字背景色
     */
    private void setTextStyle(int beginPos, int endPos, int color) {
        builder = new SpannableStringBuilder(ttsText.getText());
        ///当前播放的文字背景设置为蓝色
        builder.setSpan(new BackgroundColorSpan(color), beginPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        ttsText.setText(builder);
        Log.e(TAG, "beginPos = " + beginPos + "  endPos = " + endPos);
    }

    /**
     * 参数设置
     */
    private void setParam() {
        // 清空参数
        mTts.setParameter(SpeechConstant.PARAMS, null);
        // 根据合成引擎设置相应参数
        if (mEngineType.equals(SpeechConstant.TYPE_CLOUD)) {
            mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
            //支持实时音频返回,仅在synthesizeToUri条件下支持
            mTts.setParameter(SpeechConstant.TTS_DATA_NOTIFY, "1");
            //	mTts.setParameter(SpeechConstant.TTS_BUFFER_TIME,"1");
            // 设置在线合成发音人
            mTts.setParameter(SpeechConstant.VOICE_NAME, voicer);
            //设置合成语速(String 类型)
            mTts.setParameter(SpeechConstant.SPEED, voiceNumber1 + "");
            //设置合成音调
            mTts.setParameter(SpeechConstant.PITCH, voiceNumber2 + "");
            //设置合成音量
            mTts.setParameter(SpeechConstant.VOLUME, voiceNumber3 + "");
        } else {
            mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
            mTts.setParameter(SpeechConstant.VOICE_NAME, "");
        }
        //设置播放器音频流类型
        mTts.setParameter(SpeechConstant.STREAM_TYPE, voiceName);
        // 设置播放合成音频打断音乐播放,默认为true
        mTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "false");

        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        mTts.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/helloword-Tts2.wav");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (null != mTts) {
            mTts.stopSpeaking();
            // 退出时释放连接
            mTts.destroy();
        }
    }

    @Override
    protected void onResume() {
        //移动数据统计分析
		/*FlowerCollector.onResume(TtsDemo.this);
		FlowerCollector.onPageStart(TAG);*/
        super.onResume();
    }

    @Override
    protected void onPause() {
        //移动数据统计分析
		/*FlowerCollector.onPageEnd(TAG);
		FlowerCollector.onPause(TtsDemo.this);*/
        super.onPause();
    }

    private void writeToFile(byte[] data) throws IOException {
        if (data == null || data.length == 0)
            return;
        try {
            if (memFile == null) {
                Log.e(TAG, "writeToFile: 写入文件夹");
                String mFilepath = Environment.getExternalStorageDirectory() + "/helloword-Tts1.pcm";
                memFile = new MemoryFile(mFilepath, 1920000);
                memFile.allowPurging(false);
            }
            memFile.writeBytes(data, 0, (int) mTotalSize, data.length);
            mTotalSize += data.length;
        } finally {
        }
    }

    /**
     * 展示吐司
     */
    private void showToast(final String str) {
        Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
    }

}

、对应布局文件activity_tts.xml:

<?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:gravity="center_horizontal"
    android:orientation="vertical"
    android:padding="10dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="讯飞合成示例"
        android:textSize="30sp" />

    <EditText
        android:id="@+id/tts_text"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:text="@string/text_tts_source"
        android:textSize="20sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/voice_value1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="语速:50"
            android:textSize="18sp" />

        <SeekBar
            android:id="@+id/seek_bar1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:max="100"
            android:maxHeight="2dp"
            android:padding="5dp"
            android:progress="50"
            android:thumb="@drawable/setting_p" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:layout_marginBottom="15dp">

        <TextView
            android:id="@+id/voice_value2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="音调:50"
            android:textSize="18sp" />

        <SeekBar
            android:id="@+id/seek_bar2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:max="100"
            android:maxHeight="2dp"
            android:padding="5dp"
            android:progress="50"
            android:thumb="@drawable/setting_p" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="15dp">

        <TextView
            android:id="@+id/voice_value3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="音量:50"
            android:textSize="18sp" />

        <SeekBar
            android:id="@+id/seek_bar3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:max="100"
            android:maxHeight="2dp"
            android:padding="5dp"
            android:progress="50"
            android:thumb="@drawable/setting_p" />
    </LinearLayout>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="注意:改变值后,需重新合成播放"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp"
        android:text="当前进度:0%"
        android:textSize="18sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/tts_btn_person_select"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="发音人:小燕"
            android:textSize="20sp" />

        <Button
            android:id="@+id/tts_btn_person_type"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="音频流类型:通话"
            android:textSize="18sp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:gravity="center_horizontal"
        android:orientation="horizontal">

        <Button
            android:id="@+id/tts_play"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="开始合成"
            android:textSize="20sp" />

        <Button
            android:id="@+id/tts_cancel"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="取消"
            android:textSize="20sp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:gravity="center_horizontal"
        android:orientation="horizontal">

        <Button
            android:id="@+id/tts_pause"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="暂停播放"
            android:textSize="20sp" />

        <Button
            android:id="@+id/tts_resume"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="继续播放"
            android:textSize="20sp" />
    </LinearLayout>

</LinearLayout>

、app\src\main\res\values\strings.xml添加:

 <string-array name="voicer_cloud_entries">
        <item>小燕—女青、中英、普通话</item>
        <item>小宇—男青、中英、普通话</item>
        <item>凯瑟琳—女青、英</item>
        <item>亨利—男青、英</item>
        <item>玛丽—女青、英</item>
        <item>小研—女青、中英、普通话</item>
        <item>小琪—女青、中英、普通话</item>
        <item>小峰—男青、中英、普通话</item>
        <item>小梅—女青、中英、粤语</item>
        <item>小莉—女青、中英、台湾普通话</item>
        <item>小蓉—女青、中、四川话</item>
        <item>小芸—女青、中、东北话</item>
        <item>小坤—男青、中、河南话</item>
        <item>小强—男青、中、湖南话</item>
        <item>小莹—女青、中、陕西话</item>
        <item>小新—男童、中、普通话</item>
        <item>楠楠—女童、中、普通话</item>
        <item>老孙—男老、中、普通话</item>
    </string-array>
    <string-array name="voicer_cloud_values">
        <item>xiaoyan</item>
        <item>xiaoyu</item>
        <item>catherine</item>
        <item>henry</item>
        <item>vimary</item>
        <item>vixy</item>
        <item>xiaoqi</item>
        <item>vixf</item>
        <item>xiaomei</item>
        <item>xiaolin</item>
        <item>xiaorong</item>
        <item>xiaoqian</item>
        <item>xiaokun</item>
        <item>xiaoqiang</item>
        <item>vixying</item>
        <item>xiaoxin</item>
        <item>nannan</item>
        <item>vils</item>
    </string-array>
    <string-array name="stream_entries">
        <item>通话</item>
        <item>系统</item>
        <item>铃声</item>
        <item>音乐</item>
        <item>闹铃</item>
        <item>通知</item>
    </string-array>

    <string formatted="false" name="tts_toast_format">缓冲进度为%d%%,播放进度为%d%%</string>
    <string name="text_tts_source_en">iFLYTEK is a national key software enterprise dedicated to the research of intelligent speech and language technologies, development of software and chip products, provision of speech information services, and integration of E-government systems. The intelligent speech technology of iFLYTEK, the core technology of the company, represents the top level in the world.</string>
    <string name="text_tts_source">科大讯飞作为中国最大的智能语音技术提供商,在智能语音技术领域有着长期的研究积累,并在中文语音合成、语音识别、口语评测等多项技术上拥有国际领先的成果。科大讯飞是我国唯一以语音技术为产业化方向的“国家863计划成果产业化基地”…</string>

关于离线合成功能貌似要收费:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_34536167/article/details/104638723