diff --git a/local_plugins/bytedance_speech/android/src/main/kotlin/com/deep_voice/bytedance_speech/BytedanceAudioPlayer.kt b/local_plugins/bytedance_speech/android/src/main/kotlin/com/deep_voice/bytedance_speech/BytedanceAudioPlayer.kt index a761c015c..f1c0ec79a 100644 --- a/local_plugins/bytedance_speech/android/src/main/kotlin/com/deep_voice/bytedance_speech/BytedanceAudioPlayer.kt +++ b/local_plugins/bytedance_speech/android/src/main/kotlin/com/deep_voice/bytedance_speech/BytedanceAudioPlayer.kt @@ -15,12 +15,13 @@ import com.deep_voice.speech.tts.AudioDataListener import com.deep_voice.speech.tts.AudioOutputDevice /** - * 高性能音频播放器 - 16kHz采样率 + * 语音通信音频播放器 - 16kHz采样率 * 设计要点: - * • 永远使用 USAGE_MEDIA + MODE_NORMAL 媒体播放通路 + * • 使用 USAGE_VOICE_COMMUNICATION + MODE_IN_COMMUNICATION 通信通路 * • 利用 AudioDeviceCallback 监听设备热插拔 * • API 31+ 使用 setPreferredDevice() 精准路由 * • 有耳机自动从耳机播放,无耳机自动切到扬声器 + * • 内置回声消除和噪音抑制功能 */ class BytedanceAudioPlayer(private val context: Context) : AudioDataListener, AudioDeviceCallback() { companion object { @@ -35,6 +36,35 @@ class BytedanceAudioPlayer(private val context: Context) : AudioDataListener, Au private var isFirstData = true private var totalBytesWritten = 0 private var sessionActive = true + private var audioFocusRequest: Any? = null // 兼容不同API版本 + + // 音频焦点变化监听器 + private val audioFocusChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange -> + when (focusChange) { + AudioManager.AUDIOFOCUS_LOSS -> { + // 永久失去焦点,停止播放 + Log.d(TAG, "音频焦点丢失") + stop() + } + AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> { + // 暂时失去焦点,暂停播放 + Log.d(TAG, "音频焦点暂时丢失") + audioTrack?.pause() + } + AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { + // 暂时失去焦点但可降低音量,这里选择暂停 + Log.d(TAG, "音频焦点暂时丢失(可降低音量)") + audioTrack?.pause() + } + AudioManager.AUDIOFOCUS_GAIN -> { + // 重新获得焦点,恢复播放 + Log.d(TAG, "音频焦点恢复") + if (sessionActive) { + audioTrack?.play() + } + } + } + } // 回调 private var onPlayStarted: (() -> Unit)? = null @@ -62,6 +92,8 @@ class BytedanceAudioPlayer(private val context: Context) : AudioDataListener, Au init { // 注册设备热插拔监听 audioManager.registerAudioDeviceCallback(this, Handler(Looper.getMainLooper())) + // 请求音频焦点 + requestAudioFocus() buildTrack(selectOutputDevice()) // 首次创建 } @@ -126,7 +158,7 @@ class BytedanceAudioPlayer(private val context: Context) : AudioDataListener, Au audioTrack = AudioTrack.Builder() .setAudioAttributes( AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_MEDIA) // 统一媒体通路 + .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) // 语音通信通路 .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) .build() ) @@ -159,8 +191,8 @@ class BytedanceAudioPlayer(private val context: Context) : AudioDataListener, Au track.play() } - // 配置音频管理器 - 统一使用 MODE_NORMAL - audioManager.mode = AudioManager.MODE_NORMAL + // 配置音频管理器 - 使用通信模式以启用回声消除和噪音抑制 + audioManager.mode = AudioManager.MODE_IN_COMMUNICATION // 切换扬声器开关仅在无耳机时打开 audioManager.isSpeakerphoneOn = (device == null) @@ -168,6 +200,71 @@ class BytedanceAudioPlayer(private val context: Context) : AudioDataListener, Au Log.d(TAG, "AudioTrack已创建 - 输出设备: $deviceName, 采样率: ${SAMPLE_RATE}Hz") } + /** + * 请求音频焦点 + */ + private fun requestAudioFocus() { + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // Android O及以上使用新的API + val focusRequest = android.media.AudioFocusRequest.Builder( + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK + ) + .setAudioAttributes( + AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) + .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) + .build() + ) + .setAcceptsDelayedFocusGain(true) + .setOnAudioFocusChangeListener(audioFocusChangeListener) + .build() + + val result = audioManager.requestAudioFocus(focusRequest) + if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { + audioFocusRequest = focusRequest + Log.d(TAG, "音频焦点请求成功") + } else { + Log.w(TAG, "音频焦点请求失败") + } + } else { + // Android O以下使用旧的API + @Suppress("DEPRECATION") + val result = audioManager.requestAudioFocus( + audioFocusChangeListener, + AudioManager.STREAM_VOICE_CALL, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK + ) + if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { + Log.d(TAG, "音频焦点请求成功(旧API)") + } else { + Log.w(TAG, "音频焦点请求失败(旧API)") + } + } + } catch (e: Exception) { + Log.e(TAG, "请求音频焦点异常: ${e.message}") + } + } + + /** + * 释放音频焦点 + */ + private fun releaseAudioFocus() { + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + audioFocusRequest?.let { + audioManager.abandonAudioFocusRequest(it as android.media.AudioFocusRequest) + } + } else { + @Suppress("DEPRECATION") + audioManager.abandonAudioFocus(audioFocusChangeListener) + } + Log.d(TAG, "音频焦点已释放") + } catch (e: Exception) { + Log.e(TAG, "释放音频焦点异常: ${e.message}") + } + } + /** * 开始新的播放会话 */ @@ -262,7 +359,10 @@ class BytedanceAudioPlayer(private val context: Context) : AudioDataListener, Au stop() audioTrack?.release() audioTrack = null + releaseAudioFocus() audioManager.unregisterAudioDeviceCallback(this) + // 恢复音频模式 + audioManager.mode = AudioManager.MODE_NORMAL } /** diff --git a/local_plugins/bytedance_speech/android/src/main/kotlin/com/deep_voice/bytedance_speech/BytedanceTTS.kt b/local_plugins/bytedance_speech/android/src/main/kotlin/com/deep_voice/bytedance_speech/BytedanceTTS.kt index bb446a994..9db3c0e93 100644 --- a/local_plugins/bytedance_speech/android/src/main/kotlin/com/deep_voice/bytedance_speech/BytedanceTTS.kt +++ b/local_plugins/bytedance_speech/android/src/main/kotlin/com/deep_voice/bytedance_speech/BytedanceTTS.kt @@ -116,7 +116,7 @@ class BytedanceTTS(private val context: Context) : ITtsService, CoroutineScope { // TTS配置参数 private var appId: String = "" private var accessToken: String = "" - private var speaker: String = "zh_female_shuangkuaisisi_emo_v2_mars_bigtts" // 默认语音,使用Java示例的音色 + private var speaker: String = "zh_female_yuanqinvyou_moon_bigtts" // 默认语音,使用Java示例的音色 private var format: String = "pcm" // pcm 格式 private var sampleRate: Int = 16000 // 使用16kHz采样率 private var enableTimestamp: Boolean = false