更新记录

1.1.6(2023-12-27)

以下为针对上架各家应用商店所做的调整

  • 修正耳机初始化监听逻辑,将自动监听改为使用init方法手动监听(应用宝)
  • 将sdk版本调整为min26,target33(小米)

以下为相关BUG修复

  • 修复无TTS引擎时init方法回调无内容
  • 修复安卓版本10以上,在初始化前台服务时应用闪退bug
  • 优化修复其他有可能会导致闪退的逻辑

以下为新增内容

  • 新增跳转到通知设置页面接口 openNotifySetting()
  • 新增检查是否获得通知权限 checkNotifyEnabled()
  • 新增来电通话状态监听

1.1.5(2023-03-16)

  • 新增跳转到电池保护设置页面接口 goBatterySetting
  • 新增跳转到应用自启动设置页面接口 goAutoStartSetting
  • 新增创建、停止前台服务 startForeground 、 stopForeground

1.1.4(2023-03-07)

  • 新增init后回调内返回引擎包列表
  • 新增耳机连接状态监听
查看更多

平台兼容性

Android Android CPU类型 iOS
适用版本区间:8.0 - 14.0 armeabi-v7a:支持,arm64-v8a:支持,x86:支持 ×

原生插件通用使用流程:

  1. 购买插件,选择该插件绑定的项目。
  2. 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
  3. 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
  4. 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
  5. 开发完毕后正式云打包

付费原生插件目前不支持离线打包。
Android 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/android
iOS 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/ios

注意事项:使用HBuilderX2.7.14以下版本,如果同一插件且同一appid下购买并绑定了多个包名,提交云打包界面提示包名绑定不一致时,需要在HBuilderX项目中manifest.json->“App原生插件配置”->”云端插件“列表中删除该插件重新选择


文字转语音 - 更新日志

终版(2023-12-22)

注意:此版本为应用商店版,若更新此版本需设置manifest.json中的 APP常用其他设置 -> minSdkVersion = 26,否则无法正常打包。如果需要minSdkVersion = 19版本的,请移步码云:https://gitee.com/lyq617/uniapp-text-to-speech-plugin自取离线包。

此次更新为该版本最后一版,以后非bug问题,不再更新此版本。

新版本重构了通知栏,支持自定义通知图片、通知标题、通知内容,支持通知栏内点击上一首、下一首、播放、暂停按钮事件监听等实用功能,欢迎使用:https://ext.dcloud.net.cn/plugin?id=16025

以下为针对上架各家应用商店所做的调整

  • 修正耳机初始化监听逻辑,将自动监听改为使用init方法手动监听(应用宝)
  • 将sdk版本调整为min26,target33(小米)

以下为相关BUG修复

  • 修复无TTS引擎时init方法回调无内容
  • 修复安卓版本10以上,在初始化前台服务时应用闪退bug
  • 优化修复其他有可能会导致闪退的逻辑

以下为新增内容

  • 新增跳转到通知设置页面接口 openNotifySetting()
  • 新增检查是否获得通知权限 checkNotifyEnabled()
  • 新增来电通话状态监听

1.1.5(2023-03-07)

  • 新增跳转到电池保护设置页面接口 goBatterySetting
  • 新增跳转到应用自启动设置页面接口 goAutoStartSetting
  • 新增创建、停止前台服务 startForeground 、 stopForeground

1.1.4(2023-03-06)

  • 新增init后回调内返回引擎包列表
  • 新增耳机连接状态监听

1.1.3(2023-01-28)

  • 新增获取设备已安装引擎列表,自定义选择引擎

1.1.2(2022-10-19)

  • 修复1.1.1版本更新内容向下兼容

    (若不想更新,更新1.1.1版本后无法播放的,需修改init方法,该方法新增了一个参数)

1.1.1(2022-10-09)

  • 增加缓存目录管理接口
  • 增加音频播放模式,该模式下首先将文字转为音频,然后调用MediaPlayer进行播放,实现暂停、继续功能,同时支持播放进度回显。

1.1.0 (2022-09-29)

  • 增加播放进度监听
  • 增加获取当前系统默认的语音引擎

1.0.2(2020-01-03)

  • 增加语音播放方式: 1 丢弃之前的播报任务,立即播报本次内容; 2 播放完之前的语音任务后才播报本次内容;
  • 获取播放进度这个可以实现,只是目前因工作原因时间不是很多,先使用定义播放方式解决文章连续阅读
  • TTS一次最大支持字符为4000字,最好将文字分段播放

1.0.1(2019-10-30)

增加保存合成音频

1.0.0(2019-10-24)

中文文字转语音

介绍

TTS(Text To Speech,文本转语音)是语音合成应用的一种,它将储存于电脑中的文件,如帮助文件或者网页,转换成自然语音输出。

使用说明

需要搭配第三方语音引擎实现中文转语音(一般来说手机都会自带语音引擎,如果没有的话可以自行下载安装,讯飞等TTS语音引擎)

测试机型

  • 华为MATE20 、华为 MATE20PRO、小米MIX2S、PDA安卓10系统

接口说明

  • init(JSCallback jsCallback) 初始化引擎

1.1.1版本:init(JSONObject options, JSCallback jsCallback) options必传。 options.savePath 初始化音频文件保存目录

复制代码        TTSSpeaker.init(
            {
                savePath: ''    // 音频保存目录
            },
            function(result) {
                console.log(result);

                // 返回设置的语速和音调
                _this.pitch = result.data.voice_pitch;
                _this.speed = result.data.voice_speed;
                uni.showToast({
                    title: JSON.stringify(result),
                    icon: 'none'
                });
            }
        );

1.1.2版本以后:init(JSCallback jsCallback, JSONObject options) options可不传。 options.savePath 初始化音频文件保存目录

复制代码        TTSSpeaker.init(
            function(result) {
                // 此处注意,需要根据实际返回内容进行json格式判断是否需要parse
                // init回调内不可调用任何tts插件方法,如speak、setVoiceSpeed等,因为这些方法全部基于init初始化方法创建的tts对象,此处回调是在init tts对象内,若在此处调用基于tts对象的方法将报错
                let obj = JSON.parse(result.data)
                // 返回设置的语速和音调
                _this.pitch = obj.voice_pitch;
                _this.speed = obj.voice_speed;
                // 返回当前已安装的引擎列表
                _this.engineList = JSON.parse(obj.engines);
                _this.engineList.forEach(v => {
                _this.array.push(v.app_name)
                })
                uni.showToast({
                    title: JSON.stringify(result),
                    icon: 'none'
                });
            },
            {
                savePath: '',    // 音频保存目录
                engine: engine | 'com.iflytek.speechsuite'    // 自定义语音引擎 使用 getEngines 方法返回的 package_name ,留空默认使用设备默认引擎
            }
        );
  • speak(String words, Integer model, String taskId)

    words:待播放的文字 model: 1 丢弃之前的播报任务,立即播报本次内容; 2 播放完之前的语音任务后才播报本次内容; taskid:自定播放任务id,用于监听播放进度回调,id全局唯一,不可重复

  • setVoiceSpeed(Float speed, JSCallback jsCallback) 设置语速,最高为3

  • setVoicePicth(Float speed, JSCallback jsCallback) 设置语调,最高为3(有些手机不支持)

  • stop() 停止播放

  • saveAudioFile(JSONObject options, JSCallback jsCallback) 保存合成音频

  • destorySpeaker() 销毁注册的播放任务

  • getDefaultEngine() 获取当前系统默认语音引擎包名,若为空则说明系统内缺少TTS引擎,需要下载安装(讯飞等)

  • playerSpeak(String words, String savePath, JSCallback jsCallback)音频模式-生成文件并播放,返回文件目录

  • playerPlay(String audioPath); 播放本地音频

  • playerStop() 停止播放

  • playerResume() 恢复播放

  • playerPause() 暂停播放

  • getFolderSize(String path) 递归获取指定目录下总文件大小,留空默认目录为 /storage/emulated/0/Android/data/包名/cache/voices/

  • getCurrentFolderSize(String path) 只获取指定目录下总文件大小,留空默认目录为 /storage/emulated/0/Android/data/包名/cache/voices/

  • deleteFolderFile(String path, Boolean deleteSelf) 递归删除指定目录下的文件夹及文件 deleteSelf:是否同时删除祖目录

  • deleteFile(String path) 删除指定目录下的文件

  • goBatterySetting() 跳转到电池保护设置页面

  • goAutoStartSetting() 跳转到应用自启动设置页面

  • startForeground(String notifyTitle, String notifyContent, String notifyTicker) 创建前台服务

复制代码  notifyTitle:通知标题  默认为应用名
  notifyContent:通知简介 默认为 应用名 + 运行中...
  notifyTicker:通知提示文本 默认为 应用名 + 运行中... 
  • stopForeground() 停止前台服务

回调监听

复制代码        const speakListener = weex.requireModule('globalEvent');

        // TTS实时模式监听播放状态
        speakListener.addEventListener('speakListener', function(e) {
            // {"data":{"taskid":"666234324563543","code":1,"msg":"播放开始"},"code":0,"msg":"实时播放任务监听"} 
            //  data.code 1 播放开始 2 播放结束 -1 播放失败
            console.log(e);
        });

        // 文字转音频播放监听播放状态
        speakListener.addEventListener('mediaListener', function(e) {
            // {"data":{"status":1,"data":{"position":97,"progress":6792},"msg":"正在播放"},"code":0,"msg":"音频模式播放任务监听"} 
            //  data.status 1 正在播放 2 播放结束 -1 播放失败;data.data.position 播放进度百分比 data.data.progress 播放进度
            console.log(e);
        });

        // 耳机
        speakListener.addEventListener('headsetListener', function(e) {
            // {"data":{"code":0,"msg":"有线耳机已拔出"},"code":0,"msg":"实时播放任务监听"} 
            // {"data":{"code":11,"msg":"蓝牙耳机已连接"},"code":1,"msg":"耳机状态监听"} 
            // e.code 0 耳机已拔出或断开 1 耳机已连接
            console.log(e);
        });

        // 通话状态
        speakListener.addEventListener('phoneCallListener', function(e) {
            // {"data":{"code":0,"msg":"通话结束或未通话"},"code":0,"msg":"通话状态监听"}
            // {"data":{"code":1,"msg":"来电响铃"},"code":1,"msg":"通话状态监听"}
            // {"data":{"code":2,"msg":"接听电话或拨出电话"},"code":1,"msg":"通话状态监听"}
            // e.code 0 通话结束或未通话 1 来电响铃 2 正在接听电话或拨出电话
            console.log(e);
        });

示例代码

复制代码<template>
    <view class="uni-padding-wrap uni-common-mt">
        <view>
            <textarea maxlength="-1" v-model="words" placeholder="输入要说的内容"></textarea>
        </view>
        <view>
            <text>{{ tips }}</text>
        </view>
        <view class="uni-title">设置语速</view>
        <view>
            <slider
                v-model="speed"
                step="0.5"
                min="0.5"
                max="3"
                @change="setVoiceSpeed"
                show-value
            />
        </view>

        <view class="uni-title">设置音调</view>
        <view>
            <slider
                v-model="pitch"
                step="0.5"
                min="0.5"
                max="3"
                @change="setVoicePicth"
                show-value
            />
        </view>

        <view>
            <view class="uni-list">
                <view class="uni-list-cell">
                    <view class="uni-list-cell-left">当前选择</view>
                    <view class="uni-list-cell-db">
                        <picker @change="bindPickerChange" :value="index" :range="array">
                            <view class="uni-input">{{ array[index] }}</view>
                        </picker>
                    </view>
                </view>
            </view>
        </view>

        <view class="padding"></view>

        <view>
            <radio-group @change="modelChange">
                <label
                    class="uni-list-cell uni-list-cell-pd"
                    v-for="(item, index) in items"
                    :key="item.value"
                >
                    <view><radio :value="item.value" :checked="index === playModel" /></view>
                    <view>{{ item.name }}</view>
                </label>
            </radio-group>
        </view>

        <view><button @click="goBatterySetting">跳转到电量保护设置页</button></view>
        <view><button @click="goAutoStartSetting">跳转到应用自启动设置页</button></view>
        <view><button @click="openNotifySetting">跳转到通知设置页面</button></view>
        <view><button @click="checkNotifyEnabled">检查通知权限</button></view>

        <view><button @click="init">初始化</button></view>
        <view><button @click="getEngines">获取设备已安装的引擎列表</button></view>
        <view><button @click="getDefaultEngine">获取当前设备默认引擎</button></view>

        <view><button @click="startForeground">创建前台服务</button></view>
        <view><button @click="stopForeground">停止前台服务</button></view>

        <view><button @click="speak">实时模式播放</button></view>
        <view><button @click="stop">停止播放</button></view>
        <view><button @click="saveAudioFile">生成音频文件</button></view>
        <view><button @click="destorySpeaker">销毁</button></view>

        <view><button @click="playerSpeak">音频模式-生成文件并播放</button></view>
        <view><button @click="playerPlay">音频模式-播放</button></view>
        <view><button @click="playerPause">音频模式-暂停</button></view>
        <view><button @click="playerResume">音频模式-继续</button></view>
        <view><button @click="playerStop">音频模式-停止播放</button></view>

        <view><button @click="getFolderSize">递归获取文件夹大小</button></view>
        <view><button @click="getCurrentFolderSize">获取文件夹大小</button></view>
        <view><button @click="deleteFolderFile">删除指定目录下的文件及文件夹</button></view>
        <view><button @click="deleteFile">删除指定目录下的文件</button></view>
    </view>
</template>

<script>
const TTSSpeaker = uni.requireNativePlugin('Karma617-TTSSpeaker');
const speakListener = weex.requireModule('globalEvent');
export default {
    data() {
        return {
            engineList: [],
            array: ['请选择引擎'],
            index: 0,
            words:
                'TTS(Text To Speech,文本转语音)是语音合成应用的一种,它将储存于电脑中的文件,如帮助文件或者网页,转换成自然语音输出。',
            speed: 2,
            pitch: 1,
            playModel: 1,
            items: [
                {
                    value: 0,
                    name: '舍弃模式'
                },
                {
                    value: 1,
                    name: '追加模式'
                }
            ],
            savePath: '',
            tips: '',
            engine: ''
        };
    },
    onReady() {
        let _this = this;
        _this.init();
        // TTS实时模式监听播放状态
        speakListener.addEventListener('speakListener', function(e) {
            // {"data":{"taskid":"666234324563543","code":1,"msg":"播放开始"},"code":0,"msg":"实时播放任务监听"}
            //  data.code 1 播放开始 2 播放结束 -1 播放失败
            console.log(e);
        });

        // 文字转音频播放监听播放状态
        speakListener.addEventListener('mediaListener', function(e) {
            // {"data":{"status":1,"data":{"position":97,"progress":6792},"msg":"正在播放"},"code":0,"msg":"音频模式播放任务监听"}
            //  data.status 1 正在播放 2 播放结束 -1 播放失败;data.data.position 播放进度百分比 data.data.progress 播放进度
            console.log(e);
        });

        // 耳机
        speakListener.addEventListener('headsetListener', function(e) {
            // {"data":{"code":0,"msg":"有线耳机已拔出"},"code":0,"msg":"实时播放任务监听"} 
            // {"data":{"code":11,"msg":"蓝牙耳机已连接"},"code":1,"msg":"耳机状态监听"} 
            // e.code 0 耳机已拔出或断开 1 耳机已连接
            console.log(e);
        });
    },
    methods: {
        checkNotifyEnabled() {
            TTSSpeaker.checkNotifyEnabled();
        },
        openNotifySetting() {
            TTSSpeaker.openNotifySetting();
        },
        startForeground () {
            TTSSpeaker.startForegroundListening();
        },
        stopForeground () {
            TTSSpeaker.stopListening();
        },
        goBatterySetting () {
            TTSSpeaker.goBatterySetting();
        },
        goAutoStartSetting () {
            TTSSpeaker.goAutoStartSetting();
        },
        bindPickerChange(e) {
            this.index = e.detail.value;
            this.engine = this.engineList[this.index-1].package_name;
            console.log(this.engine);
            this.destorySpeaker();
            let _this = this;
            setTimeout(v => {
                _this.init(_this.engine);
            }, 500)
        },
        init(engine) {
            console.log(engine);
            let _this = this;
            _this.array = ['请选择引擎'];
            TTSSpeaker.init(
                function(result) {
                    let obj = JSON.parse(result.data)
                    // 返回设置的语速和音调
                    _this.pitch = obj.voice_pitch;
                    _this.speed = obj.voice_speed;
                    // 返回当前已安装的引擎列表
                    _this.engineList = JSON.parse(obj.engines);
                    _this.engineList.forEach(v => {
                        _this.array.push(v.app_name)
                    })
                },
                {
                    engine: engine || '' 
                }
            );
        },
        modelChange: function(evt) {
            for (let i = 0; i < this.items.length; i++) {
                if (this.items[i].value === evt.detail.value) {
                    this.playModel = i;
                    break;
                }
            }
        },
        speak() {
            // 对应参数: 待播放文本,播放模式(1 丢弃之前的播报任务,立即播报本次内容 2 播放完之前的语音任务后才播报本次内容),任务id(用来获取播放状态)
            TTSSpeaker.speak(this.words, this.playModel + 1);
        },
        setVoiceSpeed(e) {
            TTSSpeaker.setVoiceSpeed(e.detail.value, function(result) {
                uni.showToast({
                    title: JSON.stringify(result),
                    icon: 'none'
                });
            });
        },
        setVoicePicth(e) {
            TTSSpeaker.setVoicePicth(e.detail.value, function(result) {
                uni.showToast({
                    title: JSON.stringify(result),
                    icon: 'none'
                });
            });
        },
        stop() {
            TTSSpeaker.stop();
        },
        destorySpeaker() {
            TTSSpeaker.destorySpeaker();
        },
        getDefaultEngine() {
            TTSSpeaker.getDefaultEngine(v => {
                uni.showToast({
                    title: JSON.stringify(v),
                    icon: 'none'
                });
            });
        },
        getEngines() {
            // 该方法必须在init初始化之后才能调用。1.1.4版本以后,初始化成功后将一并返回tts引擎列表
            this.array = ['请选择引擎'];
            TTSSpeaker.getEngines(res => {
                this.engineList = res.data;
                res.forEach(v => {
                    this.array.push(v.app_name);
                });
                uni.showToast({
                    title: JSON.stringify(v),
                    icon: 'none'
                });
            });
        },
        saveAudioFile() {
            // 文字过长时,请加延时,测试8000字,需延时最少2500毫秒。
            // 建议将文本分段后再进行合成保存
            TTSSpeaker.saveAudioFile(
                {
                    words: this.words,
                    save_path: '' // 音频文件保存路径,可选,默认保存路径为本应用的cache目录下,例:/xxx/xxx/xxx/audio.wav。
                },
                function(result) {
                    // 返回文件目录信息
                    uni.showToast({
                        title: JSON.stringify(result),
                        icon: 'none'
                    });
                }
            );
        },
        playerSpeak() {
            let _this = this;
            // words待播放文本 , savePath音频文件保存路径,可选,默认保存路径为本应用的cache目录下,例:/xxx/xxx/xxx/audio.wav。
            TTSSpeaker.playerSpeak(_this.words, '', res => {
                console.log(res);
                // {"data":"/storage/emulated/0/Android/data/com.qdapi.ttspeaker/cache/voices/a44fb3b2bffdb007de81935476457587.wav","code":0,"msg":"保存成功"}
                _this.savePath = res.data;
            });
        },
        playerPlay() {
            TTSSpeaker.playerPlay(this.savePath);
        },
        playerStop() {
            TTSSpeaker.playerStop();
        },
        playerResume() {
            TTSSpeaker.playerResume();
        },
        playerPause() {
            TTSSpeaker.playerPause();
        },
        getFolderSize() {
            TTSSpeaker.getFolderSize('', res => {
                // {"data":"217.38KB","code":0,"msg":"获取成功"}
                console.log(res);
            });
        },
        getCurrentFolderSize() {
            TTSSpeaker.getCurrentFolderSize('', res => {
                // {"data":"217.38KB","code":0,"msg":"获取成功"}
                console.log(res);
            });
        },
        deleteFolderFile() {
            TTSSpeaker.deleteFolderFile('', false, res => {
                // {"data":"","code":0,"msg":"删除成功"}
                console.log(res);
            });
        },
        deleteFile() {
            TTSSpeaker.deleteFile('', res => {
                // {"data":"","code":0,"msg":"删除成功"}
                console.log(res);
            });
        }
    }
};
</script>
<style>
    .padding{
        padding: 20px;
    }
</style>

隐私、权限声明

1. 本插件需要申请的系统权限列表:

"android.permission.INTERNET", "android.permission.ACCESS_NETWORK_STATE", "android.permission.ACCESS_WIFI_STATE", "android.permission.CHANGE_WIFI_MULTICAST_STATE", "android.permission.CHANGE_WIFI_STATE", "android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE", "android.permission.MODIFY_AUDIO_SETTINGS", "android.permission.READ_PHONE_STATE", "android.permission.BLUETOOTH", "android.permission.BLUETOOTH_ADMIN", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.RECORD_AUDIO", "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", "android.permission.FOREGROUND_SERVICE"

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问