diff --git a/libraries/mogo-tts/app/src/main/AndroidManifest.xml b/libraries/mogo-tts/app/src/main/AndroidManifest.xml index 53a64e0..8364116 100644 --- a/libraries/mogo-tts/app/src/main/AndroidManifest.xml +++ b/libraries/mogo-tts/app/src/main/AndroidManifest.xml @@ -31,6 +31,15 @@ + + + + + \ No newline at end of file diff --git a/libraries/mogo-tts/app/src/main/java/com/mogo/tts/MainActivity.kt b/libraries/mogo-tts/app/src/main/java/com/mogo/tts/MainActivity.kt index 6206685..68fd248 100644 --- a/libraries/mogo-tts/app/src/main/java/com/mogo/tts/MainActivity.kt +++ b/libraries/mogo-tts/app/src/main/java/com/mogo/tts/MainActivity.kt @@ -5,8 +5,18 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.AppCompatButton import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import com.mogo.tts.common.IMogoTTSCallback +import com.mogo.tts.common.log.TtsLogManager +import kotlin.random.Random class MainActivity : AppCompatActivity() { + + private val ttsManager = TtsManager() + + private val TAG = "MainActivity" + + private val ranx = Random(10) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) @@ -17,10 +27,31 @@ class MainActivity : AppCompatActivity() { } findViewById(R.id.actv_test_tts1).setOnClickListener { - TtsManager.speakTTSVoice("此时每个单兵要负责自己的一个视线防区") + //TtsInnerManager.speakTTSVoice("此时每个单兵要负责自己的一个视线防区") + val info = "此时每个单兵要负责自己的一个视线防区${ranx.nextInt()}" + TtsLogManager.d(TAG,"要播放的语音:${info}") + ttsManager.speakTTSVoice(info,object :IMogoTTSCallback{ + override fun onSpeakStart(speakText: String?) { + TtsLogManager.d(TAG,"onSpeakStart:${speakText}") + } + + override fun onSpeakComple(speakText: String?) { + TtsLogManager.d(TAG,"onSpeakComple:${speakText}") + } + + override fun onStopTts(speakText: String?) { + TtsLogManager.d(TAG,"onStopTts:${speakText}") + } + + override fun onSpeakError(speakText: String?, errorMsg: String?) { + TtsLogManager.d(TAG,"onSpeakError:${speakText}") + } + }) } findViewById(R.id.actv_test_tts2).setOnClickListener { - TtsManager.stopTTS() + ttsManager.stopTTS() } + + ttsManager.bindService(this) } } \ No newline at end of file diff --git a/libraries/mogo-tts/app/src/main/java/com/mogo/tts/MoGoApplication.kt b/libraries/mogo-tts/app/src/main/java/com/mogo/tts/MoGoApplication.kt index 70601d4..5851846 100644 --- a/libraries/mogo-tts/app/src/main/java/com/mogo/tts/MoGoApplication.kt +++ b/libraries/mogo-tts/app/src/main/java/com/mogo/tts/MoGoApplication.kt @@ -30,7 +30,7 @@ class MoGoApplication : Application() { } private fun initTts() { - TtsManager.init(this) + TTSService.start(this) } diff --git a/libraries/mogo-tts/app/src/main/java/com/mogo/tts/utils/PhoneUtilsExtend.kt b/libraries/mogo-tts/app/src/main/java/com/mogo/tts/utils/PhoneUtilsExtend.kt index 4fcb04c..2945920 100644 --- a/libraries/mogo-tts/app/src/main/java/com/mogo/tts/utils/PhoneUtilsExtend.kt +++ b/libraries/mogo-tts/app/src/main/java/com/mogo/tts/utils/PhoneUtilsExtend.kt @@ -8,6 +8,6 @@ import android.os.Build object PhoneUtilsExtend { fun getDevicesSn(): String { - return Build.getSerial() + return "name" } } diff --git a/libraries/mogo-tts/tts-base/src/main/aidl/com/mogo/tts/ICoreBinder.aidl b/libraries/mogo-tts/tts-base/src/main/aidl/com/mogo/tts/ICoreBinder.aidl new file mode 100644 index 0000000..d9f6bbb --- /dev/null +++ b/libraries/mogo-tts/tts-base/src/main/aidl/com/mogo/tts/ICoreBinder.aidl @@ -0,0 +1,24 @@ +package com.mogo.tts; + +import com.mogo.tts.ITTSCallbackBinder; + +interface ICoreBinder { + + void speakTTSVoice1(String tts); + + void speakTTSVoice2(String tts,in ITTSCallbackBinder callback); + + void speakTTSVoic3(String tts, int languageType,in ITTSCallbackBinder callback); + + void registerTtsListener(String tag,in ITTSCallbackBinder callback); + + void unRegisterTtsListener(String tag); + + void clearTtsListener(); + + void stopTts(); + + void stopTtsByText(String tts); + +} + diff --git a/libraries/mogo-tts/tts-base/src/main/aidl/com/mogo/tts/ITTSCallbackBinder.aidl b/libraries/mogo-tts/tts-base/src/main/aidl/com/mogo/tts/ITTSCallbackBinder.aidl new file mode 100644 index 0000000..f1a6fb5 --- /dev/null +++ b/libraries/mogo-tts/tts-base/src/main/aidl/com/mogo/tts/ITTSCallbackBinder.aidl @@ -0,0 +1,10 @@ +package com.mogo.tts; + + +oneway interface ITTSCallbackBinder { + void onSpeakStart(String speakText); + void onSpeakComple(String speakText); + void onStopTts(String speakText); + void onSpeakError(String speakText,String errorMsg); +} + diff --git a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/TTSService.kt b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/TTSService.kt new file mode 100644 index 0000000..377ed7f --- /dev/null +++ b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/TTSService.kt @@ -0,0 +1,254 @@ +package com.mogo.tts + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.Service +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.IBinder +import androidx.core.app.NotificationCompat +import com.mogo.tts.common.IMogoTTSCallback +import com.mogo.tts.common.LangTtsEntity +import com.mogo.tts.common.R +import com.mogo.tts.common.log.TtsLogManager +import java.util.concurrent.ConcurrentHashMap + +class TTSService : Service() { + + private val TAG = "TTSService" + + private val globalStart = "global_" + + // 🔥 最佳方案:ConcurrentHashMap 管理回调(key = tag,value = 回调) + private val callbackMap = ConcurrentHashMap() + + + private val iCorebinder = object : ICoreBinder.Stub() { + override fun speakTTSVoice1(tts: String?) { + // 防御空字符串 + val text = tts?.takeIf { it.isNotEmpty() } ?: return + TtsInnerManager.speakTTSVoice(tts) + } + + override fun speakTTSVoice2(tts: String?, callback: ITTSCallbackBinder?) { + // 防御空字符串 + val text = tts?.takeIf { it.isNotEmpty() } ?: return + if(callback==null){ + TtsInnerManager.speakTTSVoice(tts) + return + } + // 先缓存 callback + callbackMap[text] = callback + + TtsInnerManager.speakTTSVoice(tts,object :IMogoTTSCallback{ + override fun onSpeakStart(speakText: String?) { + val cb = callbackMap[speakText] ?: return + safeCallback { + cb.onSpeakStart(speakText) + } + } + + override fun onSpeakComple(speakText: String?){ + val cb = callbackMap[speakText] ?: return + safeCallback { + cb.onSpeakComple(speakText) + } + // ✅ 执行完自动销毁 callback + callbackMap.remove(speakText) + } + + override fun onStopTts(speakText: String?) { + val cb = callbackMap[speakText] ?: return + safeCallback { + cb.onStopTts(speakText) + } + // ✅ 执行完自动销毁 callback + callbackMap.remove(speakText) + } + + override fun onSpeakError(speakText: String?, errorMsg: String?) { + val cb = callbackMap[speakText] ?: return + safeCallback { + cb.onSpeakError(speakText,errorMsg) + } + // ✅ 执行完自动销毁 callback + callbackMap.remove(speakText) + } + }) + } + + override fun speakTTSVoic3(tts: String?, languageType: Int, callback: ITTSCallbackBinder?) { + // 防御空字符串 + val text = tts?.takeIf { it.isNotEmpty() } ?: return + if(callback==null){ + TtsInnerManager.speakTTSVoice(tts) + return + } + // 先缓存 callback + callbackMap[text] = callback + + val langTtsEntity = LangTtsEntity.getLangTtsEntity(tts, languageType) + + TtsInnerManager.speakTTSVoice(langTtsEntity,object :IMogoTTSCallback{ + override fun onSpeakStart(speakText: String?) { + val cb = callbackMap[speakText] ?: return + safeCallback { + cb.onSpeakStart(speakText) + } + } + + override fun onSpeakComple(speakText: String?) { + val cb = callbackMap[speakText] ?: return + safeCallback { + cb.onSpeakComple(speakText) + } + // ✅ 执行完自动销毁 callback + callbackMap.remove(speakText) + } + + override fun onStopTts(speakText: String?) { + val cb = callbackMap[speakText] ?: return + safeCallback { + cb.onStopTts(speakText) + } + // ✅ 执行完自动销毁 callback + callbackMap.remove(speakText) + } + + override fun onSpeakError(speakText: String?, errorMsg: String?) { + val cb = callbackMap[speakText] ?: return + safeCallback { + cb.onSpeakError(speakText,errorMsg) + } + // ✅ 执行完自动销毁 callback + callbackMap.remove(speakText) + } + }) + } + + override fun registerTtsListener(tag: String?, callback: ITTSCallbackBinder?) { + if(tag.isNullOrEmpty()){ + return + } + if(callback==null){ + return + } + val key = "${globalStart}${tag}" + callbackMap[key] = callback + } + + override fun unRegisterTtsListener(tag: String?) { + if(tag.isNullOrEmpty()){ + return + } + val key = "${globalStart}${tag}" + callbackMap.remove(key) + } + + override fun clearTtsListener() { + callbackMap.clear() + } + + override fun stopTts() { + TtsInnerManager.stopTTS() + } + + override fun stopTtsByText(tts: String?) { + // TODO: 如果正在播放直接停止、如果在排队播放从队伍中移除 + } + + } + + companion object{ + fun start(context: Context) { + val serviceIntent = Intent(context, TTSService::class.java) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(serviceIntent) + } else { + context.startService(serviceIntent) + } + } + } + + override fun onCreate() { + super.onCreate() + TtsLogManager.d(TAG,"onCreate") + TtsInnerManager.init(applicationContext) + TtsInnerManager.registerTtsListener(object :IMogoTTSCallback{ + override fun onSpeakStart(speakText: String?) { + callbackMap.forEach { + if (it.key.startsWith(globalStart)) { + safeCallback { + it.value.onSpeakStart(speakText) + } + } + } + } + + + override fun onSpeakComple(speakText: String?) { + callbackMap.forEach { + if (it.key.startsWith(globalStart)) { + safeCallback { + it.value.onSpeakComple(speakText) + } + } + } + } + + override fun onSpeakError(speakText: String?, errorMsg: String) { + callbackMap.forEach { + if (it.key.startsWith(globalStart)) { + safeCallback { + it.value.onSpeakError(speakText,errorMsg) + } + } + } + } + + }) + } + + override fun onBind(intent: Intent?): IBinder? { + TtsLogManager.d(TAG,"onBind") + return iCorebinder + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + TtsLogManager.d(TAG,"onStartCommand") + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + if (manager != null) { + val nc = NotificationChannel("Voice", "语音服务", NotificationManager.IMPORTANCE_HIGH) + manager.createNotificationChannel(nc) + } + } + val notification: Notification = NotificationCompat.Builder(applicationContext, "Voice") + .setContentTitle("语音服务运行中") + .setSmallIcon(R.drawable.voice_manager_icon) + .build() + + startForeground(1, notification) + return START_STICKY + } + + override fun onDestroy() { + TtsLogManager.d(TAG,"onDestroy") + super.onDestroy() + TtsInnerManager.unRegisterTtsListener() + TtsInnerManager.release() + } + + + // 跨进程回调安全工具方法 + private inline fun safeCallback(call: () -> Unit) { + try { + call() + } catch (e: Exception) { + TtsLogManager.e(TAG,e.toString(),e) + } + } + +} \ No newline at end of file diff --git a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/TtsInnerManager.kt b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/TtsInnerManager.kt new file mode 100644 index 0000000..64d4721 --- /dev/null +++ b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/TtsInnerManager.kt @@ -0,0 +1,71 @@ +package com.mogo.tts + +import android.content.Context +import com.mogo.tts.common.IMogoTTS +import com.mogo.tts.common.IMogoTTSCallback +import com.mogo.tts.common.LangTtsEntity +import com.mogo.tts.common.LanguageType +import com.mogo.tts.common.log.TtsLogManager + +object TtsInnerManager { + + private const val TAG = "TtsManager" + + private var mTTS: IMogoTTS? = null + + fun init(context: Context) { + try { + // 暂时换成反射,解决死锁问题 + var clazz1: Class<*>? = null + try { +// clazz1 = Class.forName("com.mogo.tts.iflytek.offline.IFlyTekOfflineTts") + clazz1 = Class.forName("com.k2fsa.sherpa.onnx.MogoOfflineTTS") + } catch (ignored: Exception) { + } + + if (clazz1 != null) { + mTTS = clazz1.getConstructor().newInstance() as IMogoTTS + } + mTTS?.initTts(context) + } catch (e: Exception) { + e.printStackTrace() + TtsLogManager.d(TAG, "TTS 模块初始化异常") + } + } + + fun speakTTSVoice(tts: String?) { + if(tts.isNullOrEmpty()) return + speakTTSVoice(tts,null) + } + + + fun speakTTSVoice(tts: String?, callBack: IMogoTTSCallback?) { + if(tts.isNullOrEmpty()) return + speakTTSVoice(LangTtsEntity(tts, LanguageType.CHINESE), callBack); + } + + fun speakTTSVoice(ttsEntity: LangTtsEntity, callBack: IMogoTTSCallback?) { + if(ttsEntity.ttsContent.isEmpty()){ + return + } + mTTS?.speakTTSVoice(ttsEntity, callBack) + } + + fun registerTtsListener(callback: IMogoTTSCallback) { + mTTS?.registerTtsListener(callback) + } + + fun unRegisterTtsListener() { + mTTS?.unRegisterTtsListener() + } + + fun stopTTS() { + mTTS?.stopTts() + } + + fun release() { + mTTS?.release() + } + + +} \ No newline at end of file diff --git a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/TtsManager.kt b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/TtsManager.kt index b46a105..d943c45 100644 --- a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/TtsManager.kt +++ b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/TtsManager.kt @@ -1,42 +1,33 @@ package com.mogo.tts +import android.content.ComponentName import android.content.Context -import com.mogo.tts.common.IGlobalTtsCallback -import com.mogo.tts.common.IMogoTTS +import android.content.Intent +import android.content.ServiceConnection +import android.content.pm.PackageManager +import android.os.Build +import android.os.IBinder +import android.os.RemoteException +import com.elegant.utils.UiThreadHandler import com.mogo.tts.common.IMogoTTSCallback import com.mogo.tts.common.LangTtsEntity import com.mogo.tts.common.LanguageType -import com.mogo.tts.common.log.TtsLogManager +import java.util.concurrent.atomic.AtomicBoolean -object TtsManager { - - private const val TAG = "TtsManager" - - private var mTTS: IMogoTTS? = null - - fun init(context: Context) { - try { - // 暂时换成反射,解决死锁问题 - var clazz1: Class<*>? = null - try { - //clazz1 = Class.forName("com.mogo.tts.iflytek.offline.IFlyTekOfflineTts") - clazz1 = Class.forName("com.k2fsa.sherpa.onnx.MogoOfflineTTS") - } catch (ignored: Exception) { - } - - if (clazz1 != null) { - mTTS = clazz1.getConstructor().newInstance() as IMogoTTS - } - mTTS?.initTts(context) - } catch (e: Exception) { - e.printStackTrace() - TtsLogManager.d(TAG, "TTS 模块初始化异常") - } +class TtsManager { + companion object { + private const val SERVER_PACKAGE: String = "com.mogo.tts" } + private val isBind = AtomicBoolean(false) + private val isServiceException = AtomicBoolean(false) //服务绑定成功后出现异常 + protected var binder: ICoreBinder? = null + + private var context: Context? = null + fun speakTTSVoice(tts: String?) { if(tts.isNullOrEmpty()) return - speakTTSVoice(tts,null) + speakTTSVoice(LangTtsEntity(tts, LanguageType.CHINESE), null); } @@ -49,24 +40,172 @@ object TtsManager { if(ttsEntity.ttsContent.isEmpty()){ return } - mTTS?.speakTTSVoice(ttsEntity, callBack) + if(binder!=null){ + try { + binder?.speakTTSVoic3(ttsEntity.ttsContent,ttsEntity.getLanguageDef(),object : + ITTSCallbackBinder.Stub() { + override fun onSpeakStart(speakText: String?) { + callBack?.onSpeakStart(speakText) + } + + override fun onSpeakComple(speakText: String?) { + callBack?.onSpeakComple(speakText) + } + + override fun onStopTts(speakText: String?) { + callBack?.onStopTts(speakText) + } + + override fun onSpeakError(speakText: String?, errorMsg: String?) { + callBack?.onSpeakError(speakText,errorMsg) + } + }) + } catch (e: RemoteException) { + e.printStackTrace() + } + } } - fun registerTtsListener(key: String, callback: IGlobalTtsCallback) { - mTTS?.registerTtsListener(key, callback) + fun registerTtsListener(tag:String,callback: IMogoTTSCallback) { + if(binder!=null){ + try { + binder?.registerTtsListener(tag,object :ITTSCallbackBinder.Stub(){ + override fun onSpeakStart(speakText: String?) { + callback.onSpeakStart(speakText) + } + + override fun onSpeakComple(speakText: String?) { + callback.onSpeakComple(speakText) + } + + override fun onStopTts(speakText: String?) { + callback.onStopTts(speakText) + } + + override fun onSpeakError(speakText: String?, errorMsg: String?) { + callback.onSpeakError(speakText,errorMsg) + } + }) + } catch (e: RemoteException) { + e.printStackTrace() + } + } } - fun unRegisterTtsListener(key: String) { - mTTS?.unRegisterTtsListener(key) + fun unRegisterTtsListener(tag: String) { + if(binder!=null){ + try { + binder?.unRegisterTtsListener(tag) + } catch (e: RemoteException) { + e.printStackTrace() + } + } } fun stopTTS() { - mTTS?.stopTts() + if(binder!=null){ + try { + binder?.stopTts() + } catch (e: RemoteException) { + e.printStackTrace() + } + } } - fun release() { - mTTS?.release() + fun stopTTS(tts:String) { + if(binder!=null){ + try { + binder?.stopTtsByText(tts) + } catch (e: RemoteException) { + e.printStackTrace() + } + } } + private val connection = object : ServiceConnection { + override fun onServiceConnected(name: ComponentName?, service: IBinder?) { + binder = ICoreBinder.Stub.asInterface(service); + } + override fun onServiceDisconnected(name: ComponentName?) { + isServiceException.set(true); + unbind() + delayBind() + } + } + + fun bindService( + context: Context, + ) { + this.context = context + bindService() + } + + private fun bindService() { + context?.let { + if (!isBind.get()) { + if (isServerInstalled()) { + isBind.set(true) + val intent = Intent("com.mogo.tts.ttsservice") + intent.setPackage(SERVER_PACKAGE) + val isSuccess: Boolean = + it.bindService(intent, connection, Context.BIND_AUTO_CREATE) + if (!isSuccess) { + unbind() + } + } else { + + } + } + } + } + + private fun unbind() { + if (binder != null) { + try { + binder?.clearTtsListener() + } catch (e: RemoteException) { + e.printStackTrace() + } + binder = null + } + context?.let { + if (isBind.get()) { + isBind.set(false) + try { + it.unbindService(connection) + } catch (e: Exception) { + e.printStackTrace() + } + } + } + } + + private fun delayBind() { + if (isServiceException.get()) { + isServiceException.set(false) + UiThreadHandler.postDelayed({ + bindService() + }, 4000L) + } + } + + private fun isServerInstalled(): Boolean { + context?.let { + val pm: PackageManager = it.getPackageManager() + return try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + pm.getApplicationInfo( + SERVER_PACKAGE, + PackageManager.ApplicationInfoFlags.of(0) + ).enabled + } else { + pm.getApplicationInfo(SERVER_PACKAGE, 0).enabled + } + } catch (e: PackageManager.NameNotFoundException) { + false + } + } + return false + } } \ No newline at end of file diff --git a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/IGlobalTtsCallback.kt b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/IGlobalTtsCallback.kt index bd2322b..ea7f6de 100644 --- a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/IGlobalTtsCallback.kt +++ b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/IGlobalTtsCallback.kt @@ -1,7 +1,11 @@ package com.mogo.tts.common interface IGlobalTtsCallback { - fun onTtsSpeakStart() + fun onTtsSpeakStart(speakText: String?) - fun onTtsSpeakEnd() + fun onTtsSpeakEnd(speakText: String?) + + fun onTtsSpeakComple(speakText: String?) + + fun onTtsSpeakError(speakText: String?, errorMsg: String) } \ No newline at end of file diff --git a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/IMogoTTS.java b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/IMogoTTS.java index 699013a..fb7ba69 100644 --- a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/IMogoTTS.java +++ b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/IMogoTTS.java @@ -18,9 +18,9 @@ interface IMogoTTS { */ void release(); - void registerTtsListener(String key, IGlobalTtsCallback callback); + void registerTtsListener(IMogoTTSCallback callback); - void unRegisterTtsListener(String key); + void unRegisterTtsListener(); void speakTTSVoice(LangTtsEntity ttsEntity, IMogoTTSCallback callBack); diff --git a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/IMogoTTSCallback.java b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/IMogoTTSCallback.java index 8dc8723..f7da63e 100644 --- a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/IMogoTTSCallback.java +++ b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/IMogoTTSCallback.java @@ -9,46 +9,10 @@ public */ interface IMogoTTSCallback { - /** - * 新SDK接口 - * - * @param ttsId - * @param tts - */ - @Deprecated - default void onTTSStart( String ttsId, String tts ) { - } - - /** - * 新SDK接口 - * - * @param ttsId - * @param tts - */ - @Deprecated - default void onTTSEnd( String ttsId, String tts ) { - } - - /** - * 新SDK接口 - * - * @param ttsId - * @param tts - */ - @Deprecated - default void onTTSError( String ttsId, String tts ) { - } - - default void onSpeakStart( String speakText ) { } - /** - * 语音播报完毕 - * - * @param speakText 播报内容 - */ - default void onSpeakEnd( String speakText ) { + default void onSpeakComple( String speakText ) { } default void onStopTts( String speakText ) { diff --git a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/LangTtsEntity.kt b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/LangTtsEntity.kt new file mode 100644 index 0000000..0ddf3b2 --- /dev/null +++ b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/LangTtsEntity.kt @@ -0,0 +1,38 @@ +package com.mogo.tts.common + +data class LangTtsEntity( + var ttsContent: String, + var language: LanguageType +) { + override fun toString(): String { + return "ttsContent is:$ttsContent,language is:${language.langName}" + } + + fun getLanguageDef(): Int { + return when (language) { + LanguageType.CHINESE -> LanguageTypeDef.MODE_CHINESE + LanguageType.ENGLISH -> LanguageTypeDef.MODE_ENGLISH + LanguageType.KOREAN -> LanguageTypeDef.MODE_KOREAN + } + } + + + companion object { + fun getLangTtsEntity(tts: String, languageType: Int): LangTtsEntity { + val languageType = when (languageType) { + LanguageTypeDef.MODE_ENGLISH -> { + LanguageType.ENGLISH + } + + LanguageTypeDef.MODE_KOREAN -> { + LanguageType.KOREAN + } + + else -> { + LanguageType.CHINESE + } + } + return LangTtsEntity(tts, languageType) + } + } +} \ No newline at end of file diff --git a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/LanguageTypeDef.java b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/LanguageTypeDef.java new file mode 100644 index 0000000..31708c4 --- /dev/null +++ b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/LanguageTypeDef.java @@ -0,0 +1,14 @@ +package com.mogo.tts.common; + +import androidx.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.SOURCE) +public @interface LanguageTypeDef{ + public static final int MODE_CHINESE = 0; + public static final int MODE_ENGLISH = 1; + public static final int MODE_KOREAN = 2; +} + diff --git a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/MultiLangTtsEntity.kt b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/MultiLangTtsEntity.kt deleted file mode 100644 index 2aaad26..0000000 --- a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/MultiLangTtsEntity.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.mogo.tts.common - -data class MultiLangTtsEntity( - private var ttsList: List -) { - companion object { - private const val TIMEOUT_MILLIS = 60000 - } - - private val stringBuffer by lazy { - StringBuffer() - } - - private var ttsIndex = 0 - private var timeStamp: Long = 0 - - fun ttsNext(): LangTtsEntity? { - return if (ttsIndex in ttsList.indices) { - ttsList[ttsIndex++] - } else { - null - } - } - - fun markTime() { - timeStamp = System.currentTimeMillis() - } - - fun isTimeout():Boolean { - return timeStamp > 0 && System.currentTimeMillis() - timeStamp >= TIMEOUT_MILLIS - } - - override fun toString(): String { - return stringBuffer.let { - it.setLength(0) - ttsList.forEachIndexed { index, langTtsEntity -> - if (index != ttsList.size - 1) { - it.append("${langTtsEntity};") - } else { - it.append(langTtsEntity) - } - } - it.toString() - } - } -} - -data class LangTtsEntity( - var ttsContent: String, - var language: LanguageType -) { - override fun toString(): String { - return "ttsContent is:$ttsContent,language is:${language.langName}" - } -} \ No newline at end of file diff --git a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/impl/BaseMogoTTS.java b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/impl/BaseMogoTTS.java index 20951e9..56a19c2 100644 --- a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/impl/BaseMogoTTS.java +++ b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/impl/BaseMogoTTS.java @@ -4,7 +4,6 @@ import android.content.Context; import android.os.Looper; import com.elegant.utils.UiThreadHandler; -import com.mogo.tts.common.IGlobalTtsCallback; import com.mogo.tts.common.IMogoTTS; import com.mogo.tts.common.IMogoTTSCallback; import com.mogo.tts.common.LangTtsEntity; @@ -20,7 +19,7 @@ public abstract class BaseMogoTTS implements IMogoTTS { protected volatile LangTtsEntity curTtsEntity = null; protected HashMap speakVoiceMap = new HashMap<>(); - protected HashMap mGlobalTtsCallback = new HashMap<>(); + protected IMogoTTSCallback mGlobalTtsCallback = null; protected String getTAG() { return "BaseMogoTTS"; @@ -31,14 +30,12 @@ public abstract class BaseMogoTTS implements IMogoTTS { this.context = context; } - public void registerTtsListener(String key,IGlobalTtsCallback callback) { - if (!mGlobalTtsCallback.containsKey(key)) { - this.mGlobalTtsCallback.put(key,callback); - } + public void registerTtsListener(IMogoTTSCallback callback) { + this.mGlobalTtsCallback = callback; } - public void unRegisterTtsListener(String key){ - this.mGlobalTtsCallback.remove(key); + public void unRegisterTtsListener(){ + this.mGlobalTtsCallback = null; } @Override @@ -59,7 +56,6 @@ public abstract class BaseMogoTTS implements IMogoTTS { } protected void speakMultiLangTTS(LangTtsEntity ttsEntity){ - this.curTtsEntity = ttsEntity; // 合成并播放 TtsLogManager.d(getTAG(), "tts准备合成:"+ttsEntity); } @@ -80,7 +76,7 @@ public abstract class BaseMogoTTS implements IMogoTTS { if (speakVoiceMap.containsKey(key)) { IMogoTTSCallback remove = speakVoiceMap.remove(key); if(remove!=null) { - remove.onStopTts(key); + remove.onStopTts(curTtsEntity.getTtsContent()); } } curTtsEntity = null; @@ -89,31 +85,27 @@ public abstract class BaseMogoTTS implements IMogoTTS { } public void onSpeakBegin() { - if(!mGlobalTtsCallback.isEmpty()) { - for (IGlobalTtsCallback callback : mGlobalTtsCallback.values()) { - callback.onTtsSpeakStart(); - } + if(mGlobalTtsCallback != null) { + mGlobalTtsCallback.onSpeakStart(curTtsContent); } if (curTtsEntity!=null) { String key = curTtsEntity.toString(); IMogoTTSCallback iMogoTTSCallback = speakVoiceMap.get(key); if(iMogoTTSCallback!=null){ - iMogoTTSCallback.onSpeakStart(key); + iMogoTTSCallback.onSpeakStart(curTtsEntity.getTtsContent()); } } } public void handleCompleteEvent() { - if(!mGlobalTtsCallback.isEmpty()) { - for (IGlobalTtsCallback callback : mGlobalTtsCallback.values()) { - callback.onTtsSpeakEnd(); - } + if(mGlobalTtsCallback!=null) { + mGlobalTtsCallback.onSpeakComple(curTtsContent); } if (curTtsEntity!=null) { String key = curTtsEntity.toString(); IMogoTTSCallback iMogoTTSCallback = speakVoiceMap.get(key); if(iMogoTTSCallback!=null){ - iMogoTTSCallback.onSpeakEnd(key); + iMogoTTSCallback.onSpeakComple(curTtsEntity.getTtsContent()); } } curTtsEntity = null; @@ -121,18 +113,19 @@ public abstract class BaseMogoTTS implements IMogoTTS { } public void handleErrorEvent(String error) { + if(mGlobalTtsCallback!=null) { + mGlobalTtsCallback.onSpeakError(curTtsContent,error); + } if (curTtsEntity != null) { String key = curTtsEntity.toString(); IMogoTTSCallback iMogoTTSCallback = speakVoiceMap.get(key); if(iMogoTTSCallback!=null){ - iMogoTTSCallback.onSpeakError(key, - error); + iMogoTTSCallback.onSpeakError(curTtsEntity.getTtsContent(), error); } } else { IMogoTTSCallback iMogoTTSCallback = speakVoiceMap.get(curTtsContent); if(iMogoTTSCallback!=null){ - iMogoTTSCallback.onSpeakError(curTtsContent, - error); + iMogoTTSCallback.onSpeakError(curTtsContent, error); } } curTtsEntity = null; diff --git a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/log/TtsLogManager.kt b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/log/TtsLogManager.kt index d96c9f7..7ef622e 100644 --- a/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/log/TtsLogManager.kt +++ b/libraries/mogo-tts/tts-base/src/main/java/com/mogo/tts/common/log/TtsLogManager.kt @@ -48,4 +48,12 @@ object TtsLogManager { } logListener?.writeLog(LogLevel.error,tag,message) } + + @JvmStatic + fun e(tag: String, message: String,tr:Throwable?) { + if(isDebug) { + Log.e(tag, message,tr) + } + logListener?.writeLog(LogLevel.error,tag,message) + } } \ No newline at end of file diff --git a/libraries/mogo-tts/tts-base/src/main/res/drawable/voice_manager_icon.xml b/libraries/mogo-tts/tts-base/src/main/res/drawable/voice_manager_icon.xml new file mode 100644 index 0000000..6d4a792 --- /dev/null +++ b/libraries/mogo-tts/tts-base/src/main/res/drawable/voice_manager_icon.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/libraries/mogo-tts/tts-iflytek-offline/src/main/java/com/mogo/tts/iflytek/offline/IFlyTekOfflineTts.kt b/libraries/mogo-tts/tts-iflytek-offline/src/main/java/com/mogo/tts/iflytek/offline/IFlyTekOfflineTts.kt index a5f10eb..39715d3 100644 --- a/libraries/mogo-tts/tts-iflytek-offline/src/main/java/com/mogo/tts/iflytek/offline/IFlyTekOfflineTts.kt +++ b/libraries/mogo-tts/tts-iflytek-offline/src/main/java/com/mogo/tts/iflytek/offline/IFlyTekOfflineTts.kt @@ -129,6 +129,7 @@ class IFlyTekOfflineTts : BaseMogoTTS() { private fun startSpeak(langTtsEntity: LangTtsEntity?) { langTtsEntity?.let { + curTtsEntity = it curTtsContent = it.ttsContent realSpeak(it.ttsContent) } diff --git a/libraries/mogo-tts/tts-mogo/src/main/java/com/k2fsa/sherpa/onnx/MogoOfflineTTS.kt b/libraries/mogo-tts/tts-mogo/src/main/java/com/k2fsa/sherpa/onnx/MogoOfflineTTS.kt index 0c80749..17f48fa 100644 --- a/libraries/mogo-tts/tts-mogo/src/main/java/com/k2fsa/sherpa/onnx/MogoOfflineTTS.kt +++ b/libraries/mogo-tts/tts-mogo/src/main/java/com/k2fsa/sherpa/onnx/MogoOfflineTTS.kt @@ -150,6 +150,7 @@ class MogoOfflineTTS : BaseMogoTTS() { private fun startSpeak(langTtsEntity: LangTtsEntity?) { stopTts() langTtsEntity?.let { + this.curTtsEntity = langTtsEntity; curTtsContent = it.ttsContent realSpeak(it.ttsContent) }