Browse Source

add

newdev_shunjiawei
wolfplus 2 years ago
parent
commit
75da2bde26
  1. 60
      android/app/src/main/kotlin/com/example/deep_voice/AzureAsrHelper.kt
  2. 39
      android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt
  3. 193
      android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt
  4. BIN
      android/app/src/main/res/raw/silence.mp3

60
android/app/src/main/kotlin/com/example/deep_voice/AzureAsrHelper.kt

@ -113,7 +113,7 @@ class AzureAsrHelper {
// 获取音频会话 ID 并设置回音消除
audioSessionId = audioRecord?.audioSessionId ?: -1
setupEchoCancellation()
// setupEchoCancellation()
}
// 开始一次性语音识别
@ -538,61 +538,5 @@ class AzureAsrHelper {
Log.e(TAG, "释放回音消除资源时出错: ${e.message}", e)
}
}
// 检查回音消除是否可用
fun isEchoCancellationAvailable(): Boolean {
return AcousticEchoCanceler.isAvailable()
}
// 检查回音消除是否已启用
fun isEchoCancellationEnabled(): Boolean {
return isEchoCancellationEnabled
}
// 启用回音消除
fun enableEchoCancellation(): Boolean {
if (echoCanceler != null) {
try {
echoCanceler?.enabled = true
isEchoCancellationEnabled = echoCanceler?.enabled == true
Log.d(TAG, "回音消除已启用: $isEchoCancellationEnabled")
return isEchoCancellationEnabled
} catch (e: Exception) {
Log.e(TAG, "启用回音消除时出错: ${e.message}", e)
}
} else if (audioSessionId != -1) {
try {
echoCanceler = AcousticEchoCanceler.create(audioSessionId)
if (echoCanceler != null) {
echoCanceler?.enabled = true
isEchoCancellationEnabled = echoCanceler?.enabled == true
Log.d(TAG, "回音消除已创建并启用: $isEchoCancellationEnabled")
return isEchoCancellationEnabled
}
} catch (e: Exception) {
Log.e(TAG, "创建并启用回音消除时出错: ${e.message}", e)
}
}
return false
}
// 禁用回音消除
fun disableEchoCancellation(): Boolean {
if (echoCanceler != null) {
try {
echoCanceler?.enabled = false
isEchoCancellationEnabled = echoCanceler?.enabled == true
Log.d(TAG, "回音消除已禁用")
return !isEchoCancellationEnabled
} catch (e: Exception) {
Log.e(TAG, "禁用回音消除时出错: ${e.message}", e)
}
}
return true
}
// 获取音频会话ID
fun getAudioSessionId(): Int {
return audioSessionId
}
}

39
android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt

@ -69,9 +69,9 @@ class MainActivity: FlutterActivity() {
// 使用伴生对象存储配置信息,使其在整个应用中可访问
companion object {
// Azure 语音服务配置
var azureSpeechKey: String = ""
var azureSpeechRegion: String = ""
var volcanoAiApiKey: String = ""
var azureSpeechKey: String = "2WDq2KL45QUC5Gk0o8LgQ5bOpnV1kF0ZAEWBTl76AUoG90bbYO4uJQQJ99BCAC3pKaRXJ3w3AAAYACOGzvU1"
var azureSpeechRegion: String = "eastasia"
var volcanoAiApiKey: String = "168deb3d-fd0c-4912-b9f1-aaee5c6743e6"
// 标记是否已初始化
var isAzureHelpersInitialized = false
@ -99,8 +99,6 @@ class MainActivity: FlutterActivity() {
// 注册广播接收器
registerVoiceInteractionReceiver()
// 自动启动语音交互服务功能已被移除
Log.d(TAG, "MainActivity 已创建")
// 确保 Flutter 引擎准备好后立即设置事件通道
flutterEngine?.let { engine ->
@ -202,36 +200,7 @@ class MainActivity: FlutterActivity() {
* 设置方法通道
*/
private fun setupMethodChannels(flutterEngine: FlutterEngine) {
// 音频处理通道
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, AUDIO_PROCESSING_CHANNEL).setMethodCallHandler { call, result ->
// 为所有方法返回默认值或成功状态
when (call.method) {
"isEchoCancellationAvailable" -> {
result.success(azureAsrHelper.isEchoCancellationAvailable())
}
"isNoiseSuppressionAvailable" -> {
result.success(false)
}
"enableEchoCancellation" -> {
result.success(azureAsrHelper.enableEchoCancellation())
}
"disableEchoCancellation" -> {
result.success(azureAsrHelper.disableEchoCancellation())
}
"enableNoiseSuppression" -> {
result.success(true)
}
"disableNoiseSuppression" -> {
result.success(true)
}
"getAudioSessionId" -> {
result.success(azureAsrHelper.getAudioSessionId())
}
else -> {
result.notImplemented()
}
}
}
// Azure ASR 方法通道
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, AZURE_ASR_CHANNEL).setMethodCallHandler { call, result ->

193
android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt

@ -22,6 +22,9 @@ import android.os.Handler
import android.os.Looper
import java.util.concurrent.atomic.AtomicBoolean
import org.json.JSONArray
import android.media.MediaPlayer
import android.media.AudioAttributes
import android.net.Uri
/**
* 后台语音交互 Service:
@ -93,6 +96,9 @@ class VoiceInteractionService : Service() {
用户用语音和你交互.
""".trimIndent()
// 添加无声媒体播放器
private var silentPlayer: SilentMediaPlayer? = null
override fun onCreate() {
super.onCreate()
Log.d(TAG, "onCreate - 语音交互服务正在创建")
@ -109,6 +115,11 @@ class VoiceInteractionService : Service() {
initMediaSession()
registerMediaButtonReceiver()
silentPlayer = SilentMediaPlayer(applicationContext)
silentPlayer?.start()
// Log.d(TAG, "无声播放器已启动")
// 设置为媒体播放状态
setPlaybackState(PlaybackStateCompat.STATE_PLAYING)
Log.d(TAG, "设置播放状态为PLAYING")
@ -440,53 +451,26 @@ class VoiceInteractionService : Service() {
override fun onSessionStopped() {
isRecognitionActive = false
// // 如果不是因超时暂停,尝试重新启动
// if (isActive && !isTimeoutPaused) {
// handler.postDelayed({
// if (isActive && !isRecognitionActive && !isTtsSpeaking) {
// startVoiceRecognition()
// }
// }, 1000)
// }
}
override fun onCanceled(reason: String, errorDetails: String) {
isRecognitionActive = false
// // 如果不是因超时暂停,尝试重新启动
// if (isActive && !isTimeoutPaused) {
// handler.postDelayed({
// if (isActive && !isRecognitionActive && !isTtsSpeaking) {
// startVoiceRecognition()
// }
// }, 1000)
// }
}
override fun onError(error: String) {
isRecognitionActive = false
playNotification("语音识别出错")
// 如果不是因超时暂停,尝试重新启动
// if (isActive && !isTimeoutPaused) {
// handler.postDelayed({
// if (isActive && !isRecognitionActive && !isTtsSpeaking) {
// startVoiceRecognition()
// }
// }, 2000)
// }
}
})
} catch (e: Exception) {
isRecognitionActive = false
playNotification("启动语音识别失败")
// 延迟重试
// handler.postDelayed({
// if (isActive && !isRecognitionActive && !isTtsSpeaking) {
// startVoiceRecognition()
// }
// }, 3000)
}
}
@ -588,6 +572,11 @@ class VoiceInteractionService : Service() {
*/
private fun playNotification(message: String) {
isTtsSpeaking = true
// 更新播放状态为播放中,增加接收蓝牙按键事件的几率
setPlaybackState(PlaybackStateCompat.STATE_PLAYING)
// 确保媒体会话处于活跃状态
mediaSession.isActive = true
azureTtsHelper.speakText(message, object : AzureTtsHelper.TTSCallback {
override fun onSuccess(message: String) {
isTtsSpeaking = false
@ -628,10 +617,20 @@ class VoiceInteractionService : Service() {
PlaybackStateCompat.ACTION_PAUSE or
PlaybackStateCompat.ACTION_PLAY_PAUSE or
PlaybackStateCompat.ACTION_SKIP_TO_NEXT or
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or
PlaybackStateCompat.ACTION_STOP or
PlaybackStateCompat.ACTION_SEEK_TO or
PlaybackStateCompat.ACTION_FAST_FORWARD or
PlaybackStateCompat.ACTION_REWIND
)
.setState(state, 0, 1.0f)
// 使用更高的播放速度,让系统认为是真正的播放
.setState(state, 0, 1.0f, System.currentTimeMillis())
mediaSession.setPlaybackState(playbackStateBuilder.build())
// 确保MediaSession激活
if (!mediaSession.isActive) {
mediaSession.isActive = true
}
}
/**
@ -786,6 +785,10 @@ class VoiceInteractionService : Service() {
override fun onDestroy() {
Log.d(TAG, "onDestroy - 语音交互服务正在销毁")
// 停止无声播放器
silentPlayer?.release()
silentPlayer = null
// 停止监控
stopMonitoring()
@ -901,4 +904,128 @@ class VoiceInteractionService : Service() {
Log.e(TAG, "测试媒体按钮事件处理失败: ${e.message}", e)
}
}
/**
* 无声媒体播放器类
* 用于播放一个1秒的静音音频,确保系统认为app在实际播放声音
*/
inner class SilentMediaPlayer(private val context: Context) {
private var mediaPlayer: MediaPlayer? = null
private var isPlayingAudio = false
init {
try {
setupMediaPlayer()
} catch (e: Exception) {
Log.e(TAG, "初始化无声播放器失败: ${e.message}", e)
}
}
private fun setupMediaPlayer() {
try {
// 创建一个MediaPlayer
mediaPlayer = MediaPlayer()
// 设置一个自带的静音资源
val afd = context.resources.openRawResourceFd(R.raw.silence)
if (afd != null) {
mediaPlayer?.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length)
afd.close()
} else {
// 如果没有资源,尝试使用一个虚拟数据源
Log.w(TAG, "未找到静音资源,尝试使用虚拟数据源")
mediaPlayer?.setDataSource(context, Uri.parse("android.resource://${context.packageName}/raw/silence"))
}
// 设置循环播放
mediaPlayer?.isLooping = false
// 设置音量为0
mediaPlayer?.setVolume(0f, 0f)
// 设置音频特性
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
mediaPlayer?.setAudioAttributes(audioAttributes)
} else {
@Suppress("DEPRECATION")
mediaPlayer?.setAudioStreamType(AudioManager.STREAM_MUSIC)
}
// 准备播放
mediaPlayer?.prepare()
// 设置完成监听器 - 虽然设置了循环播放,但以防万一
mediaPlayer?.setOnCompletionListener {
Log.d(TAG, "无声播放完成")
isPlayingAudio = false
// start()
}
// 设置错误监听器
mediaPlayer?.setOnErrorListener { _, what, extra ->
Log.e(TAG, "无声播放错误: what=$what, extra=$extra")
isPlayingAudio = false
// 尝试重新创建
setupMediaPlayer()
true
}
Log.d(TAG, "无声播放器初始化成功")
} catch (e: Exception) {
Log.e(TAG, "设置无声播放器失败: ${e.message}", e)
mediaPlayer?.release()
mediaPlayer = null
}
}
fun start() {
try {
if (mediaPlayer == null) {
setupMediaPlayer()
}
if (mediaPlayer != null && !isPlayingAudio) {
mediaPlayer?.start()
isPlayingAudio = true
Log.d(TAG, "无声播放开始")
}
} catch (e: Exception) {
Log.e(TAG, "开始无声播放失败: ${e.message}", e)
}
}
fun stop() {
try {
if (mediaPlayer != null && isPlayingAudio) {
mediaPlayer?.pause()
isPlayingAudio = false
Log.d(TAG, "无声播放停止")
}
} catch (e: Exception) {
Log.e(TAG, "停止无声播放失败: ${e.message}", e)
}
}
fun release() {
try {
mediaPlayer?.stop()
mediaPlayer?.release()
mediaPlayer = null
isPlayingAudio = false
Log.d(TAG, "无声播放器已释放")
} catch (e: Exception) {
Log.e(TAG, "释放无声播放器失败: ${e.message}", e)
}
}
fun isPlaying(): Boolean {
return isPlayingAudio && mediaPlayer != null
}
}
}

BIN
android/app/src/main/res/raw/silence.mp3

Binary file not shown.
Loading…
Cancel
Save