Commit 1f58849f by 王涛55

集成百度语音

parent e3a8821f
......@@ -11,6 +11,9 @@
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/app_login" />
<option value="$PROJECT_DIR$/app_passport" />
<option value="$PROJECT_DIR$/buildsrc" />
<option value="$PROJECT_DIR$/lib_app_common" />
<option value="$PROJECT_DIR$/lib_baidu_audiodect" />
<option value="$PROJECT_DIR$/lib_network" />
</set>
</option>
......
......@@ -8,7 +8,7 @@ android {
compileSdkVersion rootProject.compileSdkVersion
defaultConfig {
if (build_module == build_module_app) {
applicationId rootProject.app_id
applicationId rootProject.build_modele_app_id
}
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
......@@ -40,9 +40,6 @@ android {
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
minifyEnabled false
......@@ -54,6 +51,8 @@ android {
renderscriptDebuggable false
signingConfig signingConfigs.sign
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField("boolean","SHOW_LOG","false")
buildConfigField("boolean","SERVER_ADDRESS_ENABLE","false")
}
debug {
minifyEnabled false
......@@ -65,11 +64,11 @@ android {
renderscriptDebuggable true
signingConfig signingConfigs.sign
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField("boolean","SHOW_LOG","true")
buildConfigField("boolean","SERVER_ADDRESS_ENABLE","true")
}
}
compileOptions {
encoding = 'utf-8'
sourceCompatibility JavaVersion.VERSION_1_8
......@@ -95,9 +94,10 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// if (build_module == build_module_app) {
// api project(':app_credit')
// }
if (build_module == build_module_app) {
api project(':lib_app_common')
api project(':app_login')
api project(':app_passport')
}
}
......@@ -4,15 +4,19 @@
package="com.module.hikcreate">
<application
android:name="com.basic.base.BasicApplication"
android:roundIcon="@mipmap/ic_launcher"
android:allowBackup="true"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:allowBackup="false"
android:theme="@android:style/Theme.NoTitleBar"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
tools:ignore="GoogleAppIndexingWarning,HardcodedDebugMode"
tools:replace="android:icon,android:roundIcon,android:theme,android:name,android:label,android:allowBackup">
tools:ignore="GoogleAppIndexingWarning">
<activity android:name="com.main.testActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
......
package com.main;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import com.module.hikcreate.R;
/**
* 类说明
*
* @author wangtao55
* @date 2019/9/18
* @mail wangtao55@hikcreate.com
*/
public class testActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.app_test);
findViewById(R.id.mBtnLogin).setOnClickListener(v -> {
Intent mIntent = new Intent(testActivity.this,testLoginActivity.class);
startActivity(mIntent);
});
}
}
......@@ -11,7 +11,7 @@
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="去信用模块"
android:text="去登录模块"
android:id="@+id/mBtnLogin"
/>
......@@ -25,12 +25,6 @@
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="去预约出行模块"
android:layout_marginTop="10dp"
android:id="@+id/mBtnPlanTrip"
/>
</LinearLayout>
package com.basic.base
import android.app.Application
import com.alibaba.android.arouter.launcher.ARouter
/**
*
* author : taowang
* date :2019/9/11
* description:初始化类,用于app所有的初始化操作
*
**/
class AppContext {
companion object {
val instance: AppContext by lazy { AppContext() }
}
fun init(application: Application) {
//初始化操作
ARouter.init(application)
}
}
\ No newline at end of file
package com.basic.base
import android.app.Activity
import android.os.Bundle
import com.alibaba.android.arouter.launcher.ARouter
/**
* author : taowang
* date :2019/9/11
* description:activity 基类
**/
open class BasicActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ARouter.getInstance().inject(this)
}
}
\ No newline at end of file
package com.basic.base
import android.app.Application
/**
* author : taowang
* date :2019/9/11
* description: 基础的application类
**/
class BasicApplication :Application(){
override fun onCreate() {
super.onCreate()
AppContext.instance.init(this)
}
}
\ No newline at end of file
package com.basic.router
/**
* author : taowang
* date :2019/9/11
* description:
**/
class RouterConstants{
object AppMain {
const val MAIN_TEST = "/main/test"
}
object Credit {
const val CREDIT_TEST = "/credit/test"
}
}
\ No newline at end of file
apply plugin: 'com.android.library'
if (rootProject.build_module == build_module_login) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
defaultConfig {
if (build_module == build_module_credit) {
applicationId "com.app.login"
if (rootProject.build_module == build_module_login) {
applicationId rootProject.build_modele_login_app_id
}
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
versionCode rootProject.versionCode
versionName rootProject.versionName
resourcePrefix rootProject.build_modele_login_resourcePrefix
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
compileSdkVersion rootProject.compileSdkVersion
dexOptions {
javaMaxHeapSize "4g"
preDexLibraries = false
jumboMode = true
}
dataBinding {
enabled = true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
lintOptions {
abortOnError false
}
compileOptions {
encoding = 'utf-8'
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
main {
if (rootProject.build_module == build_module_login) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
java {
exclude 'com/duiafudao/app_login/application/**'
}
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// implementation deps.app_compat
// testImplementation deps.junit
// androidTestImplementation deps.runner
// androidTestImplementation deps.espresso
if (build_module == build_module_app) {
api project(':lib_app_common')
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.app.login" />
xmlns:tools="http://schemas.android.com/tools"
package="com.app.login">
<application>
<activity
android:name="com.main.testLoginActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
</application>
</manifest>
\ No newline at end of file
package com.main;
import android.app.Activity;
import android.os.Bundle;
import com.app.login.R;
/**
* 类说明
*
* @author wangtao55
* @date 2019/9/18
* @mail wangtao55@hikcreate.com
*/
public class testLoginActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.lg_app_test);
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.app.login">
<application
android:roundIcon="@mipmap/lg_ic_launcher"
android:allowBackup="false"
android:theme="@android:style/Theme.NoTitleBar"
android:icon="@mipmap/lg_ic_launcher"
android:label="@string/lg_app_name_test"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name="com.main.testLoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:padding="20dp"
android:orientation="vertical"
android:background="@android:color/white"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="我是登录"
android:id="@+id/mBtnLogin"
/>
</LinearLayout>
<resources>
<string name="app_name">lib_core</string>
<string name="lg_app_name_test">登录测试</string>
</resources>
def build_module_app = 0x00 //壳app module组件
def build_module_welcome = 0x01 // 欢迎组件
def build_module_login = 0x02 //登录组件
def build_module_mine = 0x03 //我的模块组件
def build_module_home = 0x04 //主页面组件
def build_module_credit = 0x05//信用模块组件
def build_module_passport = 0x06//通行证组件
def build_module_plan_trip = 0x07//预约出行组件
ext.build_module_app = build_module_app
ext.build_module_welcome = build_module_welcome
ext.build_module_login = build_module_login
ext.build_module_mine = build_module_mine
ext.build_module_home = build_module_home
ext.build_module_credit = build_module_credit
ext.build_module_passport = build_module_passport
ext.build_module_plan_trip = build_module_plan_trip
ext.app_id = "com.module.hikcreate"
//当前运行的model
ext.build_module = build_module_app
apply plugin: 'com.android.library'
if (rootProject.build_module == build_module_passport) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
compileSdkVersion rootProject.compileSdkVersion
defaultConfig {
if ( rootProject.build_module == build_module_passport) {
applicationId build_modele_login_app_id
}
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
versionCode rootProject.versionCode
versionName rootProject.versionName
resourcePrefix rootProject.build_modele_passport_resourcePrefix
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
compileSdkVersion rootProject.compileSdkVersion
buildTypes {
release {
......@@ -21,8 +30,4 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// implementation deps.app_compat
// testImplementation deps.junit
// androidTestImplementation deps.runner
// androidTestImplementation deps.espresso
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
apply from: 'app_module_config.gradle'
apply from: 'config.gradle'
apply from: 'config/app_module_config.gradle'
apply from: 'config/app_config.gradle'
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:3.1.2"
classpath 'com.android.tools.build:gradle:3.1.2'
}
}
......
apply plugin: 'groovy'
dependencies {
//gradle sdk
compile gradleApi()
//groovy sdk
compile localGroovy()
}
repositories {
jcenter()
}
package com.hikcreate.plugin
import com.hikcreate.plugin.config.PluginConfig
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
/**
* 渠道发布的plugin类
*
* @author wangtao55* @date 2019/9/4
* @mail wangtao55@hikcreate.com
*/
class ChannelPlugin implements Plugin<Project> {
final static String MAKE_CHANNEL_TASK = "makeChannel"
final static String CLEAN_PROJECT_TASK = "cleanProject"
//插件group名称
final static String MAKE_CHANNEL_GROUP = "hikcreate"
final static String MAKE_BUILD_RELEASE_TASK = "buildRelease"
final static String CLEAN_FLAG = "clean"
final static String PUBLIC_FLAG = "assembleRelease"
static def hasInit = false
//tinker 基准包路径
static def bakApkPath = PluginConfig.bakApkPath
//release包产生的路径
static def releaseApkPath = PluginConfig.releaseApkPath
//可执行的python脚本路径
static def pythonShellPath = File.separator + "buildsrc" + File.separator + "walle" + File.separator + "pack.py"
//最终tinker基准包存放的路径
static def tinkerPath = File.separator + "buildsrc" + File.separator + "walle" + File.separator + "ctinker" + File.separator
@Override
void apply(Project project) {
// project.extensions.create('channel', ChannelExtension)
System.out.println("========================")
System.out.println("start make channel package hik gradle plugin!")
System.out.println("========================")
if(!hasInit){
hasInit = true
bakApkPath = project.project.rootDir.path + bakApkPath
releaseApkPath = project.project.rootDir.path + releaseApkPath
pythonShellPath = project.project.rootDir.path + pythonShellPath
tinkerPath = project.project.rootDir.path + tinkerPath
}
makeChannelTake(project)
}
static def createFile(path, createIfNotExist) {
def file = new File(path)
if (!file.exists()) {
if (createIfNotExist) {
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs()
}
file.createNewFile()
} else {
throw NullPointerException("Missing File :" + path)
}
}
return file
}
static def copyFile(fromPath, toPath, createDestIfNotExist) {
def fromFile = new File(fromPath)
def toPathFile = new File(toPath)
if (toPathFile.exists()) {
toPathFile.deleteDir()
}
toPathFile.mkdirs()
if (!fromFile.exists()) {
return false
}
def listFiles = fromFile.listFiles()
listFiles.each { file ->
if (file.isFile()) {
def toFile = createFile(toPath + "/" + file.getName(), createDestIfNotExist)
toFile.withWriter { ffile ->
file.eachLine { line ->
ffile.writeLine(line)
}
}
} else {
copyFile(file.getPath(), toPath + "/" + file.getName(), true)
}
}
}
/**
* 创建相关的task
* @param project
* @return
*/
static def makeChannelTake(Project project) {
//声明clean工程的task
Task cleanTask = project.task(CLEAN_PROJECT_TASK)
cleanTask.group = MAKE_CHANNEL_GROUP
cleanTask.dependsOn(CLEAN_FLAG)
//声明编译release版本的task
Task makeReleaseTask = project.task(MAKE_BUILD_RELEASE_TASK)
makeReleaseTask.group = MAKE_CHANNEL_GROUP
makeReleaseTask.dependsOn(PUBLIC_FLAG)
//声明makeChannelTask
Task makeChannelTask = project.task(MAKE_CHANNEL_TASK)
makeChannelTask.group = MAKE_CHANNEL_GROUP
makeChannelTask.dependsOn(makeReleaseTask)
makeChannelTask.dependsOn(cleanTask)
//noinspection UnstableApiUsage
makeReleaseTask.mustRunAfter(cleanTask)
makeChannelTask.doLast {
project.exec {
workingDir './'
commandLine "python", pythonShellPath, releaseApkPath, bakApkPath
}
}
makeChannelTask.doLast {
println 'gradle task 执行完毕...'
println '拷贝tinker备份包到对应的目录...'
try {
copyFile(bakApkPath, tinkerPath, true)
} catch (Exception e) {
e.printStackTrace()
}
}
}
}
\ No newline at end of file
package com.hikcreate.plugin.config
/**
* 不同的工程需要添加的配置,都是相对路径
*
* @author wangtao55* @date 2019/9/4
* @mail wangtao55@hikcreate.com
*/
class PluginConfig{
//build文件夹路径
static def buildPath = File.separator + "app" + File.separator + "build"+ File.separator
//tinker 基准包路径
static def bakApkPath = buildPath + "bakApk"
//release包产生的路径
static def releaseApkPath = buildPath + "outputs" + File.separator + "apk" + File.separator + "release"
}
\ No newline at end of file
package com.hikcreate.plugin.extension
/**
* 涉及到的扩展属性
*
* @author wangtao55* @date 2019/9/4
* @mail wangtao55@hikcreate.com
*/
class ChannelExtension {
String releaseApkPath //产生的release包位置,用于后续加固以及制作渠道包
String bakApkPath //产生的tinker基准包路径
String pythonShellPath//python脚步文件的路径
String tinkerPath//tinker基准包拷贝
}
\ No newline at end of file
implementation-class=com.hikcreate.plugin.ChannelPlugin
\ No newline at end of file
......@@ -13,7 +13,7 @@ ext {
compileSdkVersion = 28
// Support library
supportLibraryVersion = '28.0.0'
supportLibraryVersion = '26.1.0'
multidexVersion = "1.0.3"
guavaVersion = '22.0-android'
constraintLayout = "1.1.3"
......@@ -42,7 +42,7 @@ ext {
// rx
retrofit2Version = '2.3.0'
rxandroidVersion = '2.0.1'
rxandroidVersion = '2.0.2'
loggingInterceptor = '3.12.+'
// json tool -- to do
......
def build_module_app = 0x00 //壳app module组件
def build_module_login = 0x02 //登录组件
def build_module_passport = 0x06//通行证组件
//壳app
ext.build_module_app = build_module_app
ext.build_modele_app_id = "com.module.hikcreate"
//登录模块
ext.build_module_login = build_module_login
ext.build_modele_login_app_id = "com.app.login"
ext.build_modele_login_resourcePrefix = "lg_"
//通行证模块
ext.build_module_passport = build_module_passport
ext.build_modele_passport_app_id = "com.app.passport"
ext.build_modele_passport_resourcePrefix = "passport_"
//当前运行的model
ext.build_module = build_module_app
#Tue Dec 18 10:38:22 CST 2018
#Wed Sep 18 17:16:33 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
......
apply plugin: 'com.android.library'
android {
resourcePrefix 'app_'
compileSdkVersion rootProject.compileSdkVersion
defaultConfig {
minSdkVersion rootProject.minSdkVersion
......@@ -9,22 +8,9 @@ android {
versionName rootProject.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debugg {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api project(':lib_baidu_audiodect')
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.compileSdkVersion
defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
encoding = 'utf-8'
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
dataBinding {
enabled = true
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
implementation "io.reactivex.rxjava2:rxandroid:$rootProject.rxandroidVersion"
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hikcreate.baiduaudiodect"
android:versionCode="1"
android:versionName="1.0">
<!-- 集成时请添加下列权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<application
android:label="@string/app_name">
<activity
android:name="com.baidu.tts.sample.AudioTestActivity"
android:label="语音合成测试" />
<activity
android:name="com.baidu.tts.sample.SynthActivity"
android:label="离在线语音合成" />
<activity
android:name="com.baidu.tts.sample.MiniActivity"
android:label="精简版合成" />
<activity
android:name="com.baidu.tts.sample.SaveFileActivity"
android:label="保存合成后的音频" />
</application>
</manifest>
\ No newline at end of file
m15
f7 Ů
yyjw ң
as ѾѾ
\ No newline at end of file
package com.baidu.tts.sample;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.hikcreate.baiduaudiodect.R;
import com.hikcreate.baiduaudiodect.SpeechSynthesizerManager;
import com.hikcreate.baiduaudiodect.listener.ISpeedStatusCallback;
import java.util.ArrayList;
/**
* SynthActivity 离在线语音合成
* MiniActivity 精简版合成
* SaveFileActivity 保存合成后的音频
*/
public class AudioTestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audio_test);
initButtons();
initPermission();
}
private void initButtons() {
Button b1 = (Button) findViewById(R.id.synthButton);
Button b2 = (Button) findViewById(R.id.miniButton);
Button b3 = (Button) findViewById(R.id.saveTtsFileButton);
Button b4 = (Button) findViewById(R.id.mineButton);
b1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startAct(SynthActivity.class);
}
}); // 离在线语音合成
b2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startAct(MiniActivity.class);
}
}); // 精简版合成
b3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startAct(SaveFileActivity.class);
}
}); // 保存合成后的音频
b4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callMySpeechSynthesizer();
}
});
}
private void callMySpeechSynthesizer() {
SpeechSynthesizerManager.getInstance().speak(this, new ISpeedStatusCallback() {
@Override
public void onError(int code, String des) {
Toast.makeText(AudioTestActivity.this,des,Toast.LENGTH_LONG).show();
}
@Override
public void onStart(String utteranceId) {
Toast.makeText(AudioTestActivity.this,"start "+utteranceId,Toast.LENGTH_LONG).show();
}
@Override
public void onFinish(String utteranceId) {
Toast.makeText(AudioTestActivity.this,"finish "+utteranceId,Toast.LENGTH_LONG).show();
}
},"欢迎使用百度语音合成,请在代码中修改合成文本");
}
/**
* android 6.0 以上需要动态申请权限
*/
private void initPermission() {
String[] permissions = {
Manifest.permission.INTERNET,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.MODIFY_AUDIO_SETTINGS,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.WRITE_SETTINGS,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.ACCESS_WIFI_STATE,
Manifest.permission.CHANGE_WIFI_STATE
};
ArrayList<String> toApplyList = new ArrayList<String>();
for (String perm : permissions) {
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {
toApplyList.add(perm);
// 进入到这里代表没有权限.
}
}
String[] tmpList = new String[toApplyList.size()];
if (!toApplyList.isEmpty()) {
ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
// 此处为android 6.0以上动态授权的回调,用户自行实现。
}
private void startAct(Class activityClass) {
startActivity(new Intent(this, activityClass));
}
}
\ No newline at end of file
package com.baidu.tts.sample;
import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.method.ScrollingMovementMethod;
import android.text.style.ForegroundColorSpan;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.hikcreate.baiduaudiodect.R;
import java.util.ArrayList;
/**
* Created by fujiayi on 2017/9/13.
* <p>
* 此类 底层UI实现 无SDK相关逻辑
*/
public class BaseActivity extends AppCompatActivity implements MainHandlerConstant {
protected Button mSpeak;
protected Button mPause;
protected Button mResume;
protected Button mStop;
protected Button mSynthesize;
protected Button mBatchSpeak;
protected Button mLoadModel;
protected Button[] buttons;
protected Button mHelp;
protected EditText mInput;
protected TextView mShowText;
protected Handler mainHandler;
private static final String TAG = "MainActivity";
/*
* @param savedInstanceState
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_synth);
mainHandler = new Handler() {
/*
* @param msg
*/
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
handle(msg);
}
};
initialView(); // 初始化UI
initPermission(); // android 6.0以上动态权限申请
}
private void initialView() {
mSpeak = (Button) this.findViewById(R.id.speak);
mPause = (Button) this.findViewById(R.id.pause);
mResume = (Button) this.findViewById(R.id.resume);
mStop = (Button) this.findViewById(R.id.stop);
mSynthesize = (Button) this.findViewById(R.id.synthesize);
mBatchSpeak = (Button) this.findViewById(R.id.batchSpeak);
mLoadModel = (Button) this.findViewById(R.id.loadModel);
buttons = new Button[]{
mSpeak, mPause, mResume, mStop, mSynthesize, mBatchSpeak, mLoadModel
};
mHelp = (Button) this.findViewById(R.id.help);
mInput = (EditText) this.findViewById(R.id.input);
mShowText = (TextView) this.findViewById(R.id.showText);
mShowText.setMovementMethod(new ScrollingMovementMethod());
}
protected void handle(Message msg) {
int what = msg.what;
switch (what) {
case PRINT:
print(msg);
break;
case UI_CHANGE_INPUT_TEXT_SELECTION:
if (msg.arg1 <= mInput.getText().length()) {
mInput.setSelection(0, msg.arg1);
}
break;
case UI_CHANGE_SYNTHES_TEXT_SELECTION:
SpannableString colorfulText = new SpannableString(mInput.getText().toString());
if (msg.arg1 <= colorfulText.toString().length()) {
colorfulText.setSpan(new ForegroundColorSpan(Color.GRAY), 0, msg.arg1, Spannable
.SPAN_EXCLUSIVE_EXCLUSIVE);
mInput.setText(colorfulText);
}
break;
default:
break;
}
}
protected void toPrint(String str) {
Message msg = Message.obtain();
msg.obj = str;
mainHandler.sendMessage(msg);
}
private void print(Message msg) {
String message = (String) msg.obj;
if (message != null) {
scrollLog(message);
}
}
private void scrollLog(String message) {
Spannable colorMessage = new SpannableString(message + "\n");
colorMessage.setSpan(new ForegroundColorSpan(0xff0000ff), 0, message.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mShowText.append(colorMessage);
Layout layout = mShowText.getLayout();
if (layout != null) {
int scrollAmount = layout.getLineTop(mShowText.getLineCount()) - mShowText.getHeight();
if (scrollAmount > 0) {
mShowText.scrollTo(0, scrollAmount + mShowText.getCompoundPaddingBottom());
} else {
mShowText.scrollTo(0, 0);
}
}
}
/**
* android 6.0 以上需要动态申请权限
*/
private void initPermission() {
String[] permissions = {
Manifest.permission.INTERNET,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.MODIFY_AUDIO_SETTINGS,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.WRITE_SETTINGS,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.ACCESS_WIFI_STATE,
Manifest.permission.CHANGE_WIFI_STATE
};
ArrayList<String> toApplyList = new ArrayList<String>();
for (String perm : permissions) {
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {
toApplyList.add(perm);
// 进入到这里代表没有权限.
}
}
String[] tmpList = new String[toApplyList.size()];
if (!toApplyList.isEmpty()) {
ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
// 此处为android 6.0以上动态授权的回调,用户自行实现。
}
}
package com.baidu.tts.sample;
/**
* Created by fujiayi on 2017/9/13.
*/
public interface MainHandlerConstant {
static final int PRINT = 0;
static final int UI_CHANGE_INPUT_TEXT_SELECTION = 1;
static final int UI_CHANGE_SYNTHES_TEXT_SELECTION = 2;
static final int INIT_SUCCESS = 2;
}
package com.baidu.tts.sample;
import com.baidu.tts.client.SpeechSynthesizerListener;
import com.baidu.tts.sample.control.InitConfig;
import com.baidu.tts.sample.control.MySyntherizer;
import com.baidu.tts.sample.listener.FileSaveListener;
import com.baidu.tts.sample.util.FileUtil;
import java.util.Map;
/**
* 点击合成按钮,保存录音文件
* <p>
* Created by fujiayi on 2017/9/15.
*/
public class SaveFileActivity extends SynthActivity {
{
DESC = "本示例展示如何保存合成文件。 点击第一行的前三个按钮都会触发保存文件。\n"
+ "您也可以自行对用于保存文件的音频流实时实时处理。\n"
+ "其它功能请参见“语音合成”Activity。 \n";
}
/**
* 与SynthActivity相比,修改listener为FileSaveListener 可实现保存录音功能。
* 获取的音频内容同speak方法播出的声音
* FileSaveListener 在UiMessageListener的基础上,使用 onSynthesizeDataArrived回调,获取音频流
*/
protected void initialTts() {
String tmpDir = FileUtil.createTmpDir(this);
// 设置初始化参数
// 此处可以改为 含有您业务逻辑的SpeechSynthesizerListener的实现类
SpeechSynthesizerListener listener = new FileSaveListener(mainHandler, tmpDir);
Map<String, String> params = getParams();
// appId appKey secretKey 网站上您申请的应用获取。注意使用离线合成功能的话,需要应用中填写您app的包名。包名在build.gradle中获取。
InitConfig initConfig = new InitConfig(appId, appKey, secretKey, ttsMode, params, listener);
synthesizer = new MySyntherizer(this, initConfig, mainHandler); // 此处可以改为MySyntherizer 了解调用过程
}
}
package com.baidu.tts.sample.control;
import com.baidu.tts.client.SpeechSynthesizerListener;
import com.hikcreate.baiduaudiodect.model.TtsMode;
import java.util.Map;
/**
* 合成引擎的初始化参数
* <p>
* Created by fujiayi on 2017/9/13.
*/
public class InitConfig {
/**
* appId appKey 和 secretKey。注意如果需要离线合成功能,请在您申请的应用中填写包名。
* 本demo的包名是com.baidu.tts.sample,定义在build.gradle中。
*/
private String appId;
private String appKey;
private String secretKey;
/**
* 纯在线或者离在线融合
*/
private TtsMode ttsMode;
/**
* 初始化的其它参数,用于setParam
*/
private Map<String, String> params;
/**
* 合成引擎的回调
*/
private SpeechSynthesizerListener listener;
private InitConfig() {
}
public InitConfig(String appId, String appKey, String secretKey, TtsMode ttsMode,
Map<String, String> params, SpeechSynthesizerListener listener) {
this.appId = appId;
this.appKey = appKey;
this.secretKey = secretKey;
this.ttsMode = ttsMode;
this.params = params;
this.listener = listener;
}
public SpeechSynthesizerListener getListener() {
return listener;
}
public Map<String, String> getParams() {
return params;
}
public String getAppId() {
return appId;
}
public String getAppKey() {
return appKey;
}
public String getSecretKey() {
return secretKey;
}
public TtsMode getTtsMode() {
return ttsMode;
}
}
package com.baidu.tts.sample.control;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.util.Pair;
import com.baidu.tts.auth.AuthInfo;
import com.baidu.tts.client.SpeechSynthesizeBag;
import com.baidu.tts.client.SpeechSynthesizer;
import com.baidu.tts.client.TtsMode;
import com.baidu.tts.sample.MainHandlerConstant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 该类是对SpeechSynthesizer的封装
* <p>
* Created by fujiayi on 2017/5/24.
*/
public class MySyntherizer implements MainHandlerConstant {
protected SpeechSynthesizer mSpeechSynthesizer;
protected Context context;
protected Handler mainHandler;
private static final String TAG = "NonBlockSyntherizer";
private static boolean isInitied = false;
private boolean isCheckFile = true;
public MySyntherizer(Context context, InitConfig initConfig, Handler mainHandler) {
this(context, mainHandler);
init(initConfig);
}
protected MySyntherizer(Context context, Handler mainHandler) {
if (isInitied) {
// SpeechSynthesizer.getInstance() 不要连续调用
throw new RuntimeException("MySynthesizer 类里面 SpeechSynthesizer还未释放,请勿新建一个新类");
}
this.context = context;
this.mainHandler = mainHandler;
isInitied = true;
}
/**
* 注意该方法需要在新线程中调用。且该线程不能结束。详细请参见NonBlockSyntherizer的实现
*
* @param config
* @return
*/
protected boolean init(InitConfig config) {
sendToUiThread("初始化开始");
boolean isMix = config.getTtsMode().equals(TtsMode.MIX);
mSpeechSynthesizer = SpeechSynthesizer.getInstance();
mSpeechSynthesizer.setContext(context);
mSpeechSynthesizer.setSpeechSynthesizerListener(config.getListener());
// 请替换为语音开发者平台上注册应用得到的App ID ,AppKey ,Secret Key ,填写在SynthActivity的开始位置
mSpeechSynthesizer.setAppId(config.getAppId());
mSpeechSynthesizer.setApiKey(config.getAppKey(), config.getSecretKey());
if (isMix) {
// 授权检测接口(只是通过AuthInfo进行检验授权是否成功。选择纯在线可以不必调用auth方法。
AuthInfo authInfo = mSpeechSynthesizer.auth(config.getTtsMode().mode);
if (!authInfo.isSuccess()) {
// 离线授权需要网站上的应用填写包名。本demo的包名是com.baidu.tts.sample,定义在build.gradle中
String errorMsg = authInfo.getTtsError().getDetailMessage();
sendToUiThread("鉴权失败 =" + errorMsg);
return false;
} else {
sendToUiThread("验证通过,离线正式授权文件存在。");
}
}
setParams(config.getParams());
// 初始化tts
int result = mSpeechSynthesizer.initTts(config.getTtsMode().mode);
if (result != 0) {
sendToUiThread("【error】initTts 初始化失败 + errorCode:" + result);
return false;
}
// 此时可以调用 speak和synthesize方法
sendToUiThread(INIT_SUCCESS, "合成引擎初始化成功");
return true;
}
/**
* 合成并播放
*
* @param text 小于1024 GBK字节,即512个汉字或者字母数字
* @return
*/
public int speak(String text) {
Log.i(TAG, "speak text:" + text);
return mSpeechSynthesizer.speak(text);
}
/**
* 合成并播放
*
* @param text 小于1024 GBK字节,即512个汉字或者字母数字
* @param utteranceId 用于listener的回调,默认"0"
* @return
*/
public int speak(String text, String utteranceId) {
return mSpeechSynthesizer.speak(text, utteranceId);
}
/**
* 只合成不播放
*
* @param text
* @return
*/
public int synthesize(String text) {
return mSpeechSynthesizer.synthesize(text);
}
public int synthesize(String text, String utteranceId) {
return mSpeechSynthesizer.synthesize(text, utteranceId);
}
public int batchSpeak(List<Pair<String, String>> texts) {
List<SpeechSynthesizeBag> bags = new ArrayList<SpeechSynthesizeBag>();
for (Pair<String, String> pair : texts) {
SpeechSynthesizeBag speechSynthesizeBag = new SpeechSynthesizeBag();
speechSynthesizeBag.setText(pair.first);
if (pair.second != null) {
speechSynthesizeBag.setUtteranceId(pair.second);
}
bags.add(speechSynthesizeBag);
}
return mSpeechSynthesizer.batchSpeak(bags);
}
public void setParams(Map<String, String> params) {
if (params != null) {
for (Map.Entry<String, String> e : params.entrySet()) {
mSpeechSynthesizer.setParam(e.getKey(), e.getValue());
}
}
}
public int pause() {
return mSpeechSynthesizer.pause();
}
public int resume() {
return mSpeechSynthesizer.resume();
}
public int stop() {
return mSpeechSynthesizer.stop();
}
/**
* 引擎在合成时该方法不能调用!!!
* 注意 只有 TtsMode.MIX 才可以切换离线发音
*
* @return
*/
public int loadModel(String modelFilename, String textFilename) {
int res = mSpeechSynthesizer.loadModel(modelFilename, textFilename);
sendToUiThread("切换离线发音人成功。");
return res;
}
/**
* 设置播放音量,默认已经是最大声音
* 0.0f为最小音量,1.0f为最大音量
*
* @param leftVolume [0-1] 默认1.0f
* @param rightVolume [0-1] 默认1.0f
*/
public void setStereoVolume(float leftVolume, float rightVolume) {
mSpeechSynthesizer.setStereoVolume(leftVolume, rightVolume);
}
public void release() {
mSpeechSynthesizer.stop();
mSpeechSynthesizer.release();
mSpeechSynthesizer = null;
isInitied = false;
}
protected void sendToUiThread(String message) {
sendToUiThread(PRINT, message);
}
protected void sendToUiThread(int action, String message) {
Log.i(TAG, message);
if (mainHandler == null) { // 可以不依赖mainHandler
return;
}
Message msg = Message.obtain();
msg.what = action;
msg.obj = message + "\n";
mainHandler.sendMessage(msg);
}
}
package com.baidu.tts.sample.control;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
/**
* 在新线程中调用initTTs方法。防止UI柱塞
* <p>
* Created by fujiayi on 2017/5/24.
*/
public class NonBlockSyntherizer extends MySyntherizer {
private static final int INIT = 1;
private static final int RELEASE = 11;
private HandlerThread hThread;
private Handler tHandler;
private static final String TAG = "NonBlockSyntherizer";
public NonBlockSyntherizer(Context context, InitConfig initConfig, Handler mainHandler) {
super(context, mainHandler);
initThread();
runInHandlerThread(INIT, initConfig);
}
protected void initThread() {
hThread = new HandlerThread("NonBlockSyntherizer-thread");
hThread.start();
tHandler = new Handler(hThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case INIT:
InitConfig config = (InitConfig) msg.obj;
boolean isSuccess = init(config);
if (isSuccess) {
// speak("初始化成功");
sendToUiThread("NonBlockSyntherizer 初始化成功");
} else {
sendToUiThread("合成引擎初始化失败, 请查看日志");
}
break;
case RELEASE:
NonBlockSyntherizer.super.release();
if (Build.VERSION.SDK_INT < 18) {
getLooper().quit();
}
break;
default:
break;
}
}
};
}
@Override
public void release() {
runInHandlerThread(RELEASE);
if (Build.VERSION.SDK_INT >= 18) {
hThread.quitSafely();
}
}
private void runInHandlerThread(int action) {
runInHandlerThread(action, null);
}
private void runInHandlerThread(int action, Object obj) {
Message msg = Message.obtain();
msg.what = action;
msg.obj = obj;
tHandler.sendMessage(msg);
}
}
package com.baidu.tts.sample.listener;
import android.os.Handler;
import android.util.Log;
import com.baidu.tts.client.SpeechError;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 保存回调音频流到文件。您也可以直接处理音频流
* FileSaveListener 在UiMessageListener的基础上,使用 onSynthesizeDataArrived回调,获取音频流
* Created by fujiayi on 2017/9/15.
*/
public class FileSaveListener extends UiMessageListener {
/**
* 保存的文件名 baseName + utteranceId, 通常是 output-0.pcm
*/
private String baseName = "output-";
/**
* 保存文件的目录
*/
private String destDir;
/**
* 文件
*/
private File ttsFile;
/**
* ttsFile 文件流
*/
private FileOutputStream ttsFileOutputStream;
/**
* ttsFile 文件buffer流
*/
private BufferedOutputStream ttsFileBufferedOutputStream;
private static final String TAG = "FileSaveListener";
public FileSaveListener(Handler mainHandler, String destDir) {
super(mainHandler);
this.destDir = destDir;
}
@Override
public void onSynthesizeStart(String utteranceId) {
String filename = baseName + utteranceId + ".pcm";
// 保存的语音文件是 16K采样率 16bits编码 单声道 pcm文件。
ttsFile = new File(destDir, filename);
Log.i(TAG, "try to write audio file to " + ttsFile.getAbsolutePath());
try {
if (ttsFile.exists()) {
ttsFile.delete();
}
ttsFile.createNewFile();
// 创建FileOutputStream对象
FileOutputStream ttsFileOutputStream = new FileOutputStream(ttsFile);
// 创建BufferedOutputStream对象
ttsFileBufferedOutputStream = new BufferedOutputStream(ttsFileOutputStream);
} catch (IOException e) {
// 请自行做错误处理
e.printStackTrace();
sendMessage("创建文件失败:" + destDir + "/" + filename);
throw new RuntimeException(e);
}
sendMessage("创建文件成功:" + destDir + "/" + filename);
}
/**
* 语音流 16K采样率 16bits编码 单声道 。
*
* @param utteranceId
* @param data 二进制语音 ,注意可能有空data的情况,可以忽略
* @param progress 如合成“百度语音问题”这6个字, progress肯定是从0开始,到6结束。 但progress无法保证和合成到第几个字对应。
*/
@Override
public void onSynthesizeDataArrived(String utteranceId, byte[] data, int progress) {
super.onSynthesizeDataArrived(utteranceId, data, progress);
Log.i(TAG, "合成进度回调, progress:" + progress + ";序列号:" + utteranceId);
try {
ttsFileBufferedOutputStream.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onSynthesizeFinish(String utteranceId) {
super.onSynthesizeFinish(utteranceId);
close();
}
/**
* 当合成或者播放过程中出错时回调此接口
*
* @param utteranceId
* @param speechError 包含错误码和错误信息
*/
@Override
public void onError(String utteranceId, SpeechError speechError) {
close();
super.onError(utteranceId, speechError);
}
/**
* 关闭流,注意可能stop导致该方法没有被调用
*/
private void close() {
if (ttsFileBufferedOutputStream != null) {
try {
ttsFileBufferedOutputStream.flush();
ttsFileBufferedOutputStream.close();
ttsFileBufferedOutputStream = null;
} catch (Exception e2) {
e2.printStackTrace();
}
}
if (ttsFileOutputStream != null) {
try {
ttsFileOutputStream.close();
ttsFileOutputStream = null;
} catch (IOException e) {
e.printStackTrace();
}
}
sendMessage("关闭文件成功");
}
}
package com.baidu.tts.sample.listener;
import android.util.Log;
import com.baidu.tts.client.SpeechError;
import com.baidu.tts.client.SpeechSynthesizerListener;
import com.baidu.tts.sample.MainHandlerConstant;
/**
* SpeechSynthesizerListener 简单地实现,仅仅记录日志
* Created by fujiayi on 2017/5/19.
*/
public class MessageListener implements SpeechSynthesizerListener, MainHandlerConstant {
private static final String TAG = "MessageListener";
/**
* 播放开始,每句播放开始都会回调
*
* @param utteranceId
*/
@Override
public void onSynthesizeStart(String utteranceId) {
sendMessage("准备开始合成,序列号:" + utteranceId);
}
/**
* 语音流 16K采样率 16bits编码 单声道 。
*
* @param utteranceId
* @param bytes 二进制语音 ,注意可能有空data的情况,可以忽略
* @param progress 如合成“百度语音问题”这6个字, progress肯定是从0开始,到6结束。 但progress无法和合成到第几个字对应。
*/
@Override
public void onSynthesizeDataArrived(String utteranceId, byte[] bytes, int progress) {
// Log.i(TAG, "合成进度回调, progress:" + progress + ";序列号:" + utteranceId );
}
/**
* 合成正常结束,每句合成正常结束都会回调,如果过程中出错,则回调onError,不再回调此接口
*
* @param utteranceId
*/
@Override
public void onSynthesizeFinish(String utteranceId) {
sendMessage("合成结束回调, 序列号:" + utteranceId);
}
@Override
public void onSpeechStart(String utteranceId) {
sendMessage("播放开始回调, 序列号:" + utteranceId);
}
/**
* 播放进度回调接口,分多次回调
*
* @param utteranceId
* @param progress 如合成“百度语音问题”这6个字, progress肯定是从0开始,到6结束。 但progress无法保证和合成到第几个字对应。
*/
@Override
public void onSpeechProgressChanged(String utteranceId, int progress) {
// Log.i(TAG, "播放进度回调, progress:" + progress + ";序列号:" + utteranceId );
}
/**
* 播放正常结束,每句播放正常结束都会回调,如果过程中出错,则回调onError,不再回调此接口
*
* @param utteranceId
*/
@Override
public void onSpeechFinish(String utteranceId) {
sendMessage("播放结束回调, 序列号:" + utteranceId);
}
/**
* 当合成或者播放过程中出错时回调此接口
*
* @param utteranceId
* @param speechError 包含错误码和错误信息
*/
@Override
public void onError(String utteranceId, SpeechError speechError) {
sendErrorMessage("错误发生:" + speechError.description + ",错误编码:"
+ speechError.code + ",序列号:" + utteranceId);
}
private void sendErrorMessage(String message) {
sendMessage(message, true);
}
private void sendMessage(String message) {
sendMessage(message, false);
}
protected void sendMessage(String message, boolean isError) {
if (isError) {
Log.e(TAG, message);
} else {
Log.i(TAG, message);
}
}
}
package com.baidu.tts.sample.listener;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
/**
* 在 MessageListener的基础上,和UI配合。
* Created by fujiayi on 2017/9/14.
*/
public class UiMessageListener extends MessageListener {
private Handler mainHandler;
private static final String TAG = "UiMessageListener";
public UiMessageListener(Handler mainHandler) {
super();
this.mainHandler = mainHandler;
}
/**
* 合成数据和进度的回调接口,分多次回调。
* 注意:progress表示进度,与播放到哪个字无关
* @param utteranceId
* @param data 合成的音频数据。该音频数据是采样率为16K,2字节精度,单声道的pcm数据。
* @param progress 文本按字符划分的进度,比如:你好啊 进度是0-3
*/
@Override
public void onSynthesizeDataArrived(String utteranceId, byte[] data, int progress) {
// sendMessage("onSynthesizeDataArrived");
mainHandler.sendMessage(mainHandler.obtainMessage(UI_CHANGE_SYNTHES_TEXT_SELECTION, progress, 0));
}
/**
* 播放进度回调接口,分多次回调
* 注意:progress表示进度,与播放到哪个字无关
*
* @param utteranceId
* @param progress 文本按字符划分的进度,比如:你好啊 进度是0-3
*/
@Override
public void onSpeechProgressChanged(String utteranceId, int progress) {
// sendMessage("onSpeechProgressChanged");
mainHandler.sendMessage(mainHandler.obtainMessage(UI_CHANGE_INPUT_TEXT_SELECTION, progress, 0));
}
protected void sendMessage(String message) {
sendMessage(message, false);
}
@Override
protected void sendMessage(String message, boolean isError) {
sendMessage(message, isError, PRINT);
}
protected void sendMessage(String message, boolean isError, int action) {
super.sendMessage(message, isError);
if (mainHandler != null) {
Message msg = Message.obtain();
msg.what = action;
msg.obj = message + "\n";
mainHandler.sendMessage(msg);
Log.i(TAG, message);
}
}
}
package com.baidu.tts.sample.util;
import android.content.Context;
import android.content.res.AssetManager;
import android.os.Environment;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by fujiayi on 2017/5/19.
*/
public class FileUtil {
private static final String sAudioDir = "baiduTTS";
// 创建一个临时目录,用于复制临时文件,如assets目录下的离线资源文件
public static String createTmpDir(Context context) {
String tmpDir = Environment.getExternalStorageDirectory().toString() + "/" + sAudioDir;
if (!FileUtil.makeDir(tmpDir)) {
tmpDir = context.getExternalFilesDir(sAudioDir).getAbsolutePath();
if (!FileUtil.makeDir(sAudioDir)) {
throw new RuntimeException("create model resources dir failed :" + tmpDir);
}
}
return tmpDir;
}
public static boolean fileCanRead(String filename) {
File f = new File(filename);
return f.canRead();
}
public static boolean makeDir(String dirPath) {
File file = new File(dirPath);
if (!file.exists()) {
return file.mkdirs();
} else {
return true;
}
}
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();
}
}
}
}
}
}
}
package com.baidu.tts.sample.util;
import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;
import com.hikcreate.baiduaudiodect.model.OfflineVoiceType;
import java.io.IOException;
import java.util.HashMap;
import static android.content.ContentValues.TAG;
/**
* Created by fujiayi on 2017/5/19.
*/
public class OfflineResource {
private static final String SAMPLE_DIR = "baiduTTS";
private AssetManager assets;
private String destPath;
private String textFilename;
private String modelFilename;
private static HashMap<String, Boolean> mapInitied = new HashMap<String, Boolean>();
public OfflineResource(Context context, OfflineVoiceType offlineVoiceType) throws IOException {
context = context.getApplicationContext();
this.assets = context.getApplicationContext().getAssets();
this.destPath = FileUtil.createTmpDir(context);
setOfflineVoiceType(offlineVoiceType);
}
public String getModelFilename() {
return modelFilename;
}
public String getTextFilename() {
return textFilename;
}
public void setOfflineVoiceType(OfflineVoiceType offlineVoiceType) throws IOException {
String text = "bd_etts_text.dat";
String model;
switch (offlineVoiceType){
case COMMON_MALE_OFFLINE_INFORMANT:
model = "bd_etts_common_speech_m15_mand_eng_high_am-mix_v3.0.0_20170505.dat";
break;
case COMMON_FEMALE_OFFLINE_INFORMANT:
model = "bd_etts_common_speech_f7_mand_eng_high_am-mix_v3.0.0_20170512.dat";
break;
case EMOTION_DXY_OFFLINE_INFORMANT:
model = "bd_etts_common_speech_yyjw_mand_eng_high_am-mix_v3.0.0_20170512.dat";
break;
case EMOTION_DYY_OFFLINE_INFORMANT:
model = "bd_etts_common_speech_yyjw_mand_eng_high_am-mix_v3.0.0_20170512.dat";
break;
default:
throw new RuntimeException("voice type is not in list");
}
textFilename = copyAssetsFile(text);
modelFilename = copyAssetsFile(model);
}
private String copyAssetsFile(String sourceFilename) throws IOException {
String destFilename = destPath + "/" + sourceFilename;
boolean recover = false;
Boolean existed = mapInitied.get(sourceFilename); // 启动时完全覆盖一次
if (existed == null || !existed) {
recover = true;
}
FileUtil.copyFromAssets(assets, sourceFilename, destFilename, recover);
Log.i(TAG, "文件复制成功:" + destFilename);
return destFilename;
}
}
package com.hikcreate.baiduaudiodect.listener;
import android.content.Context;
import android.util.Pair;
import java.util.List;
/**
* @author yslei
* @date 2019/6/17
* @email leiyongsheng@hikcreate.com
*/
public abstract class ASpeechSynthesizerManager implements ISpeechSynthesizerManager {
public void speak(Context context, String text) {
speak(context, null, text);
}
public void speakWithId(Context context, String text, String utteranceId) {
speakWithId(context, null, text, utteranceId);
}
public void synthesize(Context context, String text) {
synthesize(context, null, text);
}
public void synthesizeWithId(Context context, String text, String utteranceId) {
synthesizeWithId(context, null, text, utteranceId);
}
public void batchSpeak(Context context, List<Pair<String, String>> texts) {
batchSpeak(context, null, texts);
}
public void pause() {
pause(null);
}
public void resume() {
release(null);
}
public void stop() {
stop(null);
}
public void loadModel(Context context, String modelFilename, String textFilename) {
loadModel(context, null, modelFilename, textFilename);
}
public void release() {
release(null);
}
}
package com.hikcreate.baiduaudiodect.listener;
/**
* @author yslei
* @date 2019/6/17
* @email leiyongsheng@hikcreate.com
*/
public interface IInitCallback {
void onTtsInit();
}
package com.hikcreate.baiduaudiodect.listener;
/**
* @author yslei
* @date 2019/6/14
* @email leiyongsheng@hikcreate.com
*/
public interface ISpeechSynthesizerCallback {
void onSynthesizeStart(String utteranceId);
void onSynthesizeDataArrived(String utteranceId, byte[] bytes, int progress);
void onSynthesizeFinish(String utteranceId);
void onSpeechStart(String utteranceId);
void onSpeechProgressChanged(String utteranceId, int progress);
void onSpeechFinish(String utteranceId);
void onError(String utteranceId, String des, int code);
}
package com.hikcreate.baiduaudiodect.listener;
import android.content.Context;
import android.util.Pair;
import java.util.List;
/**
* @author yslei
* @date 2019/6/17
* @email leiyongsheng@hikcreate.com
*/
public interface ISpeechSynthesizerManager {
/**
* 合成并播放
*
* @param text 小于1024 GBK字节,即512个汉字或者字母数字
* @return
*/
void speak(Context context, ISpeedStatusCallback callback, String text);
/**
* 合成并播放
*
* @param text 小于1024 GBK字节,即512个汉字或者字母数字
* @param utteranceId 用于listener的回调,默认"0"
* @return
*/
void speakWithId(Context context, ISpeedStatusCallback callback, String text, String utteranceId);
/**
* 只合成不播放
*
* @param text
* @return
*/
void synthesize(Context context, ISpeedStatusCallback callback, String text);
/**
* 只合成不播放
*
* @param text
* @return
*/
void synthesizeWithId(Context context, ISpeedStatusCallback callback, String text, String utteranceId);
/**
* 批量合成并播放
*
* @param texts
* @return
*/
void batchSpeak(Context context, ISpeedStatusCallback callback, List<Pair<String, String>> texts);
/**
* 暂停
*
* @return
*/
void pause(ISpeedStatusCallback callback);
/**
* 重置播放
*
* @return
*/
void resume(ISpeedStatusCallback callback);
/**
* 停止退出
*
* @return
*/
void stop(ISpeedStatusCallback callback);
/**
* 设置播放音量,默认已经是最大声音
* 0.0f为最小音量,1.0f为最大音量
*
* @param leftVolume [0-1] 默认1.0f
* @param rightVolume [0-1] 默认1.0f
*/
void setStereoVolume(float leftVolume, float rightVolume);
/**
* 引擎在合成时该方法不能调用!!!
* 注意 只有 TtsMode.MIX 才可以切换离线发音
*
* @return
*/
void loadModel(Context context, ISpeedStatusCallback callback, String modelFilename, String textFilename);
/**
* 释放
*
* @return
*/
void release(ISpeedStatusCallback callback);
}
package com.hikcreate.baiduaudiodect.listener;
/**
* @author yslei
* @date 2019/6/17
* @email leiyongsheng@hikcreate.com
*/
public interface ISpeedFocusCallback {
void onFocusGain(boolean gain);
}
package com.hikcreate.baiduaudiodect.listener;
/**
* @author yslei
* @date 2019/6/17
* @email leiyongsheng@hikcreate.com
*/
public interface ISpeedInitStatusCallback {
void onError(int code, String des);
void onSuccess();
}
package com.hikcreate.baiduaudiodect.listener;
/**
* @author yslei
* @date 2019/6/17
* @email leiyongsheng@hikcreate.com
*/
public interface ISpeedStatusCallback {
void onError(int code, String des);
void onStart(String utteranceId);
void onFinish(String utteranceId);
}
package com.hikcreate.baiduaudiodect.listener;
/**
* @author yslei
* @date 2019/6/14
* @email leiyongsheng@hikcreate.com
*/
public class SpeechSynthesizerAdapter implements ISpeechSynthesizerCallback {
@Override
public void onSynthesizeStart(String utteranceId) {
}
@Override
public void onSynthesizeDataArrived(String utteranceId, byte[] bytes, int progress) {
}
@Override
public void onSynthesizeFinish(String utteranceId) {
}
@Override
public void onSpeechStart(String utteranceId) {
}
@Override
public void onSpeechProgressChanged(String utteranceId, int progress) {
}
@Override
public void onSpeechFinish(String utteranceId) {
}
@Override
public void onError(String utteranceId, String des, int code) {
}
}
package com.hikcreate.baiduaudiodect.model;
/**
* @author yslei
* @date 2019/6/14
* @email leiyongsheng@hikcreate.com
*/
public class Config {
public final static String appId = "16122149";
public final static String appKey = "ZBlPogkwdOPsQy0mdaZd0iuEW9EfVsoR";
public final static String secretKey = "eCaO8xWDSY5v4B2elbQlHCHsdIaQ6qIf";
}
package com.hikcreate.baiduaudiodect.model;
/**
* 发音人设置
* 设置在线发声音人: 0 普通女声(默认) 1 普通男声 2 特别男声 3 情感男声<度逍遥> 4 情感儿童声<度丫丫>
*
* @author yslei
* @date 2019/6/14
* @email leiyongsheng@hikcreate.com
*/
public enum Informant {
COMMON_FEMALE_INFORMANT,
COMMON_MALE_INFORMANT,
SPECIAL_MALE_INFORMANT,
EMOTION_DXY_INFORMANT,
EMOTION_DYY_INFORMANT
}
package com.hikcreate.baiduaudiodect.model;
import com.baidu.tts.client.SpeechSynthesizer;
/**
* 该参数设置为TtsMode.MIX生效。即纯在线模式不生效。
* MIX_MODE_DEFAULT 默认 ,wifi状态下使用在线,非wifi离线。在线状态下,请求超时6s自动转离线
* MIX_MODE_HIGH_SPEED_SYNTHESIZE_WIFI wifi状态下使用在线,非wifi离线。在线状态下, 请求超时1.2s自动转离线
* MIX_MODE_HIGH_SPEED_NETWORK , 3G 4G wifi状态下使用在线,其它状态离线。在线状态下,请求超时1.2s自动转离线
* MIX_MODE_HIGH_SPEED_SYNTHESIZE, 2G 3G 4G wifi状态下使用在线,其它状态离线。在线状态下,请求超时1.2s自动转离线
*
* @author yslei
* @date 2019/6/14
* @email leiyongsheng@hikcreate.com
*/
public enum MixMode {
MIX_MODE_DEFAULT(SpeechSynthesizer.MIX_MODE_DEFAULT),
MIX_MODE_HIGH_SPEED_SYNTHESIZE_WIFI(SpeechSynthesizer.MIX_MODE_HIGH_SPEED_SYNTHESIZE_WIFI),
MIX_MODE_HIGH_SPEED_NETWORK(SpeechSynthesizer.MIX_MODE_HIGH_SPEED_NETWORK),
MIX_MODE_HIGH_SPEED_SYNTHESIZE(SpeechSynthesizer.MIX_MODE_HIGH_SPEED_SYNTHESIZE);
public String modeName;
MixMode(String modeName) {
this.modeName = modeName;
}
}
package com.hikcreate.baiduaudiodect.model;
/**
* 离线发音人设置
* 设置在线发声音人: 0 普通女声(默认) 1 普通男声 2 情感男声<度逍遥> 3 情感儿童声<度丫丫>
*
* @author yslei
* @date 2019/6/14
* @email leiyongsheng@hikcreate.com
*/
public enum OfflineVoiceType {
COMMON_FEMALE_OFFLINE_INFORMANT,
COMMON_MALE_OFFLINE_INFORMANT,
EMOTION_DXY_OFFLINE_INFORMANT,
EMOTION_DYY_OFFLINE_INFORMANT
}
package com.hikcreate.baiduaudiodect.model;
/**
* TtsMode.MIX; 离在线融合,在线优先; TtsMode.ONLINE 纯在线; 没有纯离线
*
* @author yslei
* @date 2019/6/14
* @email leiyongsheng@hikcreate.com
*/
public enum TtsMode {
MODE_MIX(com.baidu.tts.client.TtsMode.MIX),
MODE_ONLINE(com.baidu.tts.client.TtsMode.ONLINE);
public com.baidu.tts.client.TtsMode mode;
TtsMode(com.baidu.tts.client.TtsMode mode) {
this.mode = mode;
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<Button
android:id="@+id/synthButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:text="离在线语音合成"
android:textSize="16sp" />
<Button
android:id="@+id/saveTtsFileButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:text="保存合成后的音频"
android:textSize="16sp" />
<Button
android:id="@+id/miniButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:text="精简版合成"
android:textSize="16sp" />
<Button
android:id="@+id/mineButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:text="重构合成"
android:textSize="16sp" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:weightSum="3">
<Button
android:id="@+id/speak"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:lines="2"
android:text="合成并播放"
android:textSize="12dp" />
<Button
android:id="@+id/stop"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="2"
android:lines="2"
android:text="停止合成引擎"
android:textSize="12dp" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/btn"
>
<TextView
android:id="@+id/showText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:background="@android:color/darker_gray"
android:minLines="3"
android:scrollbars="vertical" />
</ScrollView>
</LinearLayout>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment