更新记录
2.0.0(2023-12-23)
-
全新通知条
支持自定义图片、通知标题、通知内容,支持上一首、下一首、播放、暂停按钮事件监听
支持通知布局界面跟随日间夜间主题模式切换
-
新增跳转到通知设置页面接口 openNotifySetting
-
新增通话状态回调监听
-
新增日渐夜间模式切换回调监听
平台兼容性
Android | Android CPU类型 | iOS |
---|---|---|
适用版本区间:8.0 - 14.0 | armeabi-v7a:支持,arm64-v8a:支持,x86:支持 | × |
原生插件通用使用流程:
- 购买插件,选择该插件绑定的项目。
- 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
- 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
- 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
- 开发完毕后正式云打包
付费原生插件目前不支持离线打包。
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原生插件配置”->”云端插件“列表中删除该插件重新选择
文字转语音多功能通知版 - 更新日志
2.0.0(2023-12-18)
老版本传送门:https://ext.dcloud.net.cn/plugin?id=899
此版本需设置manifest.json中的 APP常用其他设置 -> minSdkVersion = 26,否则无法正常打包。
- 全新通知条
支持自定义图片、通知标题、通知内容,支持上一首、下一首、播放、暂停按钮事件监听
支持通知布局界面跟随日间夜间主题模式切换
- 新增跳转到通知设置页面接口 openNotifySetting
- 新增通话状态回调监听
- 新增日渐夜间模式切换回调监听
针对上架各家应用商店所做的调整
- 修正耳机监听初始化逻辑,将自动监听改为手动监听(应用宝)
- 将sdk版本调整为min26,target33(小米)
注:不可与老版本同时混用
特别鸣谢:
@胖虎777(TT阅读器:http://tt.oxking.cn)
@**276***@qq.com(安卓通知栏播放器:https://ext.dcloud.net.cn/plugin?id=9018)
介绍
TTS(Text To Speech,文本转语音)是语音合成应用的一种,它将储存于电脑中的文件,如帮助文件或者网页,转换成自然语音输出。
使用说明
需要搭配第三方语音引擎实现中文转语音(一般来说手机都会自带语音引擎,如果没有的话可以自行下载安装,讯飞等TTS语音引擎)
测试机型
华为MATE20 、华为 MATE20PRO、小米MIX2S、PDA安卓10系统、 vivo S9e、小米10 至尊纪念版、红米 9、Vivo X70、OPPO Find X5 Pro、真我 8 5G等
测试安卓版本
安卓9~安卓14
接口说明
- init(JSCallback jsCallback, JSONObject options) options可不传。 options.savePath 初始化音频文件保存目录
TTSSpeaker.init(
function(result) {
// result有两个状态,code = 0 初始化正常 code > 0 初始化失败
if (result.code > 0) {
uni.showToast({title: result.msg});
return false;
}
// 此处注意,需要根据实际返回内容进行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() 跳转到应用自启动设置页面
-
checkNotifyEnabled() 检查是否有允许通知权限
-
checkFilePermission() 检查是否有允许文件读写权限
通知栏
- startForeground(JSONObject options, UniJSCallback callback) 创建通知
TTSSpeaker.startForeground({
notifyType: 'normal', // normal 普通自定义布局, media 媒体通知布局
title: '水浒传', // 书名
subTitle: '第一章', // 章节
image: '', // 封面图,支持远程(http|https)或本地(plus.io.convertLocalFileSystemURL(plus.io.PRIVATE_WWW).slice(0, -1))
keepAlive: 60 // 通知保活周期,单位:秒。最低60秒
},
data => {
console.log(data);
uni.showToast({
title: data.type,
icon: 'none'
});
switch (data.type) {
case 'play':
// 点击播放按钮
break;
case 'pause':
// 点击暂停按钮
break;
case 'next':
// 点击下一章
break;
case 'pre':
// 点击上一章
break;
}
});
- setlastOrnext(JSONObject options) 设置上一章、下一章按钮是否可点击
TTSSpeaker.setlastOrnext({
last: false, // 上一章
next: true // 下一章
});
- setTitleOrimage(JSONObject options) 更新书名、章节、封面图
TTSSpeaker.setTitleOrimage({
title: '水浒传', // 书名
subTitle: '第二章', // 章节
image: '' // 封面图
});
- setplayicon(Boolean isPlay) 设置播放状态,注意是布尔型,不是字符串 true 正在播放 false 暂停播放
TTSSpeaker.setplayicon(true);
- setOptions(JSONObject options) 统一更新通知布局
TTSSpeaker.setOptions({
title: '水浒传', // 书名
subTitle: '第三章', // 章节
image: '', // 封面图
last: true, // 上一章按钮是否可用
next: false, // 下一章按钮是否可用
isPlay: false // 是否在播放
});
- 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);
if (e.data.code == 2) {
TTSSpeaker.speak(this.words, this.playModel + 1);
}
});
// 文字转音频播放监听播放状态
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);
});
// 系统日夜模式切换
speakListener.addEventListener('uimodeListener', function(e) {
uni.showToast({
title: e.code,
icon: 'none'
});
});
示例代码
<template>
<view class="uni-padding-wrap">
<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 class="flex-box">
<view>
<p>设置页</p>
</view>
<view><button @click="goBatterySetting">跳转到电量保护设置页</button></view>
<view><button @click="goAutoStartSetting">跳转到应用自启动设置页</button></view>
<view><button @click="openNotifySetting">跳转到通知设置页面</button></view>
<view><button @click="checkFilePermission">检查文件读写权限</button></view>
<view><button @click="checkNotifyEnabled">检查通知权限</button></view>
<view>
<p>媒体通知栏</p>
</view>
<view><button @click="startForeground('media')">创建媒体通知</button></view>
<view>
<p>自定义通知栏</p>
</view>
<view><button @click="startForeground('normal')">创建自定义通知</button></view>
<view>
<p>更新通知栏</p>
</view>
<view><button @click="setlastOrnext">修改按钮状态</button></view>
<view><button @click="setTitleOrimage">更新通知内容</button></view>
<view><button @click="setOptions">统一更新通知内容</button></view>
<view><button @click="setplayicon">修改播放状态</button></view>
<view><button @click="stopForeground">------停止通知------</button></view>
<view>
<p>TTS</p>
</view>
<view><button @click="init">初始化</button></view>
<view><button @click="getEngines">获取设备已安装的引擎列表</button></view>
<view><button @click="getDefaultEngine">获取当前设备默认引擎</button></view>
<view><button @click="setEngine">设置引擎</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="openSetting">打开TTS引擎设置页</button></view>
<view>
<p>tts音频</p>
</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>
<p>工具</p>
</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>
</view>
</template>
<script>
const TTSSpeaker = uni.requireNativePlugin('Karma617-SuperTTSSpeaker');
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: '追加模式'
}
],
tips: '',
engine: '',
isPlay: true,
notifyType: 'normal',
};
},
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);
if (e.data.code == 2) {
TTSSpeaker.speak(this.words, this.playModel + 1);
}
});
// 文字转音频播放监听播放状态
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);
});
// 日夜切换
speakListener.addEventListener('uimodeListener', function(e) {
uni.showToast({
title: e.code,
icon: 'none'
});
});
},
onUnload() {
// 记得注销通话状态监听事件
TTSSpeaker.distoryTelePhoneListener();
},
methods: {
openSetting() {
TTSSpeaker.openSetting();
},
checkNotifyEnabled() {
TTSSpeaker.checkNotifyEnabled();
},
checkFilePermission() {
TTSSpeaker.checkFilePermission();
},
setEngine() {
if (this.engine) {
TTSSpeaker.setEngine(this.engine);
}
},
startForeground(type) {
this.notifyType = type;
TTSSpeaker.startForeground({
notifyType: type || 'normal',
title: '银河创新成长混合',
subTitle: '诺安小弟',
image: 'https://img01-xusong.91q.com/8F11E8B2-14CF-4A23-B4A7-7E45FEDE2791.jpg',
keepAlive: 60
},
data => {
console.log(data);
uni.showToast({
title: data.type,
icon: 'none'
});
switch (data.type) {
case 'play':
TTSSpeaker.speak(this.words, this.playModel + 1);
this.setplayicon();
break;
case 'pause':
TTSSpeaker.stop();
this.setplayicon();
break
}
}
);
},
stopForeground() {
TTSSpeaker.stopForeground();
},
setlastOrnext() {
TTSSpeaker.setlastOrnext({
last: false,
next: true
});
},
setOptions() {
TTSSpeaker.setOptions({
title: '在Android通知中,小图标(',
subTitle: '在如果你想要修改',
image: 'https://img01-xusong.91q.com/8F11E8B2-14CF-4A23-B4A7-7E45FEDE2791.jpg',
last: true,
next: false,
isPlay: false
});
},
setTitleOrimage() {
TTSSpeaker.setTitleOrimage({
title: '混合银河创新成长混合银河创新成长',
subTitle: '诺安小弟混合银河创新成长混合银河创新成长混合银河创新成长',
image: 'https://img01-xusong.91q.com/3F92300D-11A6-4575-8509-7CABAE420464.jpg'
});
},
setplayicon() {
TTSSpeaker.setplayicon(this.isPlay);
this.isPlay = !this.isPlay;
},
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);
if (this.engine) {
TTSSpeaker.setEngine(this.engine);
}
// this.destorySpeaker();
// let _this = this;
// setTimeout(v => {
// _this.init(_this.engine);
// }, 500);
},
init(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 || ''
}
);
},
openNotifySetting() {
TTSSpeaker.openNotifySetting();
},
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);
this.startForeground(this.notifyType);
},
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();
this.stopForeground();
},
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 lang="less">
.padding {
padding: 20px;
}
.flex-box {
display: flex;
flex-direction: column;
flex-wrap: wrap;
>view {
text-align: center;
display: flex;
flex-direction: row;
justify-content: center;
>p {
font-size: 26px;
font-weight: bold;
}
>button {
flex: 1;
}
}
}
</style>