|
|
@ -1,5 +1,6 @@ |
|
|
package com.deep_voice.bytedance_speech |
|
|
package com.deep_voice.bytedance_speech |
|
|
|
|
|
|
|
|
|
|
|
import android.content.Context |
|
|
import android.media.AudioAttributes |
|
|
import android.media.AudioAttributes |
|
|
import android.media.AudioFormat |
|
|
import android.media.AudioFormat |
|
|
import android.media.AudioManager |
|
|
import android.media.AudioManager |
|
|
@ -7,12 +8,13 @@ import android.media.AudioTrack |
|
|
import android.os.Build |
|
|
import android.os.Build |
|
|
import android.util.Log |
|
|
import android.util.Log |
|
|
import com.deep_voice.speech.tts.AudioDataListener |
|
|
import com.deep_voice.speech.tts.AudioDataListener |
|
|
|
|
|
import com.deep_voice.speech.tts.AudioOutputDevice |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 简化的音频流播放器 |
|
|
* 简化的音频流播放器 |
|
|
* 基于位置标记触发播放完成回调 |
|
|
* 基于位置标记触发播放完成回调 |
|
|
*/ |
|
|
*/ |
|
|
class BytedanceAudioPlayer : AudioDataListener { |
|
|
class BytedanceAudioPlayer(private val context: Context) : AudioDataListener { |
|
|
companion object { |
|
|
companion object { |
|
|
private const val TAG = "BytedanceAudioPlayer" |
|
|
private const val TAG = "BytedanceAudioPlayer" |
|
|
private const val SAMPLE_RATE = 24000 |
|
|
private const val SAMPLE_RATE = 24000 |
|
|
@ -24,6 +26,8 @@ class BytedanceAudioPlayer : AudioDataListener { |
|
|
private var isFirstData = true // 是否是第一次接收数据 |
|
|
private var isFirstData = true // 是否是第一次接收数据 |
|
|
private var totalBytesWritten = 0 // 总共写入的字节数 |
|
|
private var totalBytesWritten = 0 // 总共写入的字节数 |
|
|
private var sessionActive = true // 会话是否活跃 |
|
|
private var sessionActive = true // 会话是否活跃 |
|
|
|
|
|
private var audioOutputDevice = AudioOutputDevice.DEFAULT // 音频输出设备 |
|
|
|
|
|
private var audioManager: AudioManager? = null |
|
|
|
|
|
|
|
|
// 回调 |
|
|
// 回调 |
|
|
private var onPlayStarted: (() -> Unit)? = null |
|
|
private var onPlayStarted: (() -> Unit)? = null |
|
|
@ -40,6 +44,12 @@ class BytedanceAudioPlayer : AudioDataListener { |
|
|
// 不使用周期性通知 |
|
|
// 不使用周期性通知 |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
init { |
|
|
|
|
|
Log.d(TAG, "BytedanceAudioPlayer 初始化") |
|
|
|
|
|
audioManager = context.getSystemService(Context.AUDIO_SERVICE) as? AudioManager |
|
|
|
|
|
initAudioTrack() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -198,6 +208,23 @@ class BytedanceAudioPlayer : AudioDataListener { |
|
|
return audioTrack?.playState == AudioTrack.PLAYSTATE_PLAYING |
|
|
return audioTrack?.playState == AudioTrack.PLAYSTATE_PLAYING |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 设置音频输出设备 |
|
|
|
|
|
*/ |
|
|
|
|
|
fun setAudioOutputDevice(device: AudioOutputDevice) { |
|
|
|
|
|
if (audioOutputDevice != device) { |
|
|
|
|
|
audioOutputDevice = device |
|
|
|
|
|
// 如果AudioTrack已初始化,需要重新创建以应用新的输出设备设置 |
|
|
|
|
|
if (audioTrack != null) { |
|
|
|
|
|
val wasPlaying = isPlaying() |
|
|
|
|
|
initAudioTrack() |
|
|
|
|
|
if (wasPlaying) { |
|
|
|
|
|
audioTrack?.play() |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 初始化 AudioTrack |
|
|
* 初始化 AudioTrack |
|
|
*/ |
|
|
*/ |
|
|
@ -209,15 +236,36 @@ class BytedanceAudioPlayer : AudioDataListener { |
|
|
val minBufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT) |
|
|
val minBufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT) |
|
|
val bufferSize = minBufferSize * 2 |
|
|
val bufferSize = minBufferSize * 2 |
|
|
|
|
|
|
|
|
// 创建 AudioTrack |
|
|
// 根据输出设备配置AudioAttributes |
|
|
audioTrack = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
|
|
val audioAttributes = when (audioOutputDevice) { |
|
|
AudioTrack.Builder() |
|
|
AudioOutputDevice.EARPIECE -> { |
|
|
.setAudioAttributes( |
|
|
// 听筒模式 |
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
|
|
|
|
|
AudioAttributes.Builder() |
|
|
|
|
|
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) |
|
|
|
|
|
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) |
|
|
|
|
|
.build() |
|
|
|
|
|
} else { |
|
|
|
|
|
null |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
else -> { |
|
|
|
|
|
// 默认、耳机、扬声器模式 |
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
|
|
AudioAttributes.Builder() |
|
|
AudioAttributes.Builder() |
|
|
.setUsage(AudioAttributes.USAGE_MEDIA) |
|
|
.setUsage(AudioAttributes.USAGE_MEDIA) |
|
|
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) |
|
|
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) |
|
|
.build() |
|
|
.build() |
|
|
) |
|
|
} else { |
|
|
|
|
|
null |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 创建 AudioTrack |
|
|
|
|
|
audioTrack = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && audioAttributes != null) { |
|
|
|
|
|
AudioTrack.Builder() |
|
|
|
|
|
.setAudioAttributes(audioAttributes) |
|
|
.setAudioFormat( |
|
|
.setAudioFormat( |
|
|
AudioFormat.Builder() |
|
|
AudioFormat.Builder() |
|
|
.setEncoding(AUDIO_FORMAT) |
|
|
.setEncoding(AUDIO_FORMAT) |
|
|
@ -230,8 +278,12 @@ class BytedanceAudioPlayer : AudioDataListener { |
|
|
.build() |
|
|
.build() |
|
|
} else { |
|
|
} else { |
|
|
@Suppress("DEPRECATION") |
|
|
@Suppress("DEPRECATION") |
|
|
|
|
|
val streamType = when (audioOutputDevice) { |
|
|
|
|
|
AudioOutputDevice.EARPIECE -> AudioManager.STREAM_VOICE_CALL |
|
|
|
|
|
else -> AudioManager.STREAM_MUSIC |
|
|
|
|
|
} |
|
|
AudioTrack( |
|
|
AudioTrack( |
|
|
AudioManager.STREAM_MUSIC, |
|
|
streamType, |
|
|
SAMPLE_RATE, |
|
|
SAMPLE_RATE, |
|
|
CHANNEL_CONFIG, |
|
|
CHANNEL_CONFIG, |
|
|
AUDIO_FORMAT, |
|
|
AUDIO_FORMAT, |
|
|
@ -243,9 +295,43 @@ class BytedanceAudioPlayer : AudioDataListener { |
|
|
// 设置播放位置监听器 |
|
|
// 设置播放位置监听器 |
|
|
audioTrack?.setPlaybackPositionUpdateListener(playbackListener) |
|
|
audioTrack?.setPlaybackPositionUpdateListener(playbackListener) |
|
|
|
|
|
|
|
|
|
|
|
// 配置音频路由 |
|
|
|
|
|
configureAudioRouting() |
|
|
|
|
|
|
|
|
// 开始播放 |
|
|
// 开始播放 |
|
|
audioTrack?.play() |
|
|
audioTrack?.play() |
|
|
|
|
|
|
|
|
Log.d(TAG, "AudioTrack 初始化成功") |
|
|
Log.d(TAG, "AudioTrack 初始化成功") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 配置音频路由 |
|
|
|
|
|
*/ |
|
|
|
|
|
private fun configureAudioRouting() { |
|
|
|
|
|
audioManager?.let { manager -> |
|
|
|
|
|
when (audioOutputDevice) { |
|
|
|
|
|
AudioOutputDevice.DEFAULT -> { |
|
|
|
|
|
// 默认模式:系统自动选择 |
|
|
|
|
|
manager.mode = AudioManager.MODE_NORMAL |
|
|
|
|
|
manager.isSpeakerphoneOn = false |
|
|
|
|
|
} |
|
|
|
|
|
AudioOutputDevice.SPEAKER -> { |
|
|
|
|
|
// 强制使用扬声器 |
|
|
|
|
|
manager.mode = AudioManager.MODE_NORMAL |
|
|
|
|
|
manager.isSpeakerphoneOn = true |
|
|
|
|
|
} |
|
|
|
|
|
AudioOutputDevice.HEADPHONES -> { |
|
|
|
|
|
// 强制使用耳机(如果已连接) |
|
|
|
|
|
manager.mode = AudioManager.MODE_NORMAL |
|
|
|
|
|
manager.isSpeakerphoneOn = false |
|
|
|
|
|
// 注意:Android不能强制路由到耳机,只能在耳机已连接时使用 |
|
|
|
|
|
} |
|
|
|
|
|
AudioOutputDevice.EARPIECE -> { |
|
|
|
|
|
// 强制使用听筒 |
|
|
|
|
|
manager.mode = AudioManager.MODE_IN_COMMUNICATION |
|
|
|
|
|
manager.isSpeakerphoneOn = false |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |