Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53f286e407 |
@@ -55,6 +55,7 @@ class VoiceService(
|
||||
private var playbackGeneration = 0L
|
||||
private var activePlaybackTrack: AudioTrack? = null
|
||||
@Volatile private var receiveVolume = 1.0f
|
||||
@Volatile private var receiveGain = 2.5f
|
||||
|
||||
private val audioManager = context.getSystemService(AudioManager::class.java)
|
||||
private var audioFocusRequest: AudioFocusRequest? = null
|
||||
@@ -129,6 +130,11 @@ class VoiceService(
|
||||
}
|
||||
}
|
||||
|
||||
/** 设置 PCM 预增益(1.0 = 原始音量,2.5 = 默认) */
|
||||
fun setReceiveGain(gain: Float) {
|
||||
receiveGain = gain.coerceIn(0.5f, 5.0f)
|
||||
}
|
||||
|
||||
fun enableReceivePlayback() {
|
||||
if (speakerEnabled) forceLoudspeaker()
|
||||
}
|
||||
@@ -176,10 +182,12 @@ class VoiceService(
|
||||
synchronized(playbackLock) { activePlaybackTrack = track }
|
||||
}
|
||||
val activeTrack = track ?: continue
|
||||
// 应用 PCM 预增益
|
||||
val gainedFrame = applyPcmGain(frame, receiveGain)
|
||||
if (acceptedFrames <= 5 || acceptedFrames % 250L == 0L) {
|
||||
Log.i(TAG, "Writing mixed PCM bytes=${frame.size} trackState=${activeTrack.state} playState=${activeTrack.playState}")
|
||||
Log.i(TAG, "Writing mixed PCM bytes=${gainedFrame.size} gain=$receiveGain trackState=${activeTrack.state}")
|
||||
}
|
||||
val written = activeTrack.write(frame, 0, frame.size, AudioTrack.WRITE_BLOCKING)
|
||||
val written = activeTrack.write(gainedFrame, 0, gainedFrame.size, AudioTrack.WRITE_BLOCKING)
|
||||
if (written != frame.size) {
|
||||
trackWriteFailures++
|
||||
Log.w(TAG, "AudioTrack.write incomplete: $written/${frame.size}")
|
||||
@@ -341,4 +349,21 @@ class VoiceService(
|
||||
selfSpeakingJob?.cancel(); selfSpeakingJob = null
|
||||
Log.d(TAG, "VoiceService destroyed")
|
||||
}
|
||||
|
||||
/**
|
||||
* 对 PCM16 字节数组应用增益,clamp 到 int16 范围防止溢出。
|
||||
* PCM 格式:小端序 interleaved stereo。
|
||||
*/
|
||||
private fun applyPcmGain(pcm: ByteArray, gain: Float): ByteArray {
|
||||
if (gain == 1.0f) return pcm
|
||||
val result = pcm.copyOf()
|
||||
for (i in result.indices step 2) {
|
||||
// 小端序:低字节在前
|
||||
val sample = ((result[i + 1].toInt() shl 8) or (result[i].toInt() and 0xFF)).toShort()
|
||||
val gained = (sample.toFloat() * gain).toInt().coerceIn(Short.MIN_VALUE.toInt(), Short.MAX_VALUE.toInt())
|
||||
result[i] = (gained and 0xFF).toByte()
|
||||
result[i + 1] = (gained shr 8).toByte()
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user