Browse Source

android: keep radio VAD blocked until AudioTrack plays the receive tail

master
evgeny 4 days ago
parent
commit
a1e8bfe80a
  1. 4
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/NativeLib.kt
  2. 28
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/RadioAudioEngine.kt
  3. 3
      tools/chatgui-android/jni_bridge/android_jni_bridge.c

4
tools/chatgui-android/app/src/main/java/com/utun/chat/data/NativeLib.kt

@ -210,7 +210,7 @@ object NativeLib {
fun radioSubscribers(groupId: Long): Int = nativeRadioSubscribers(groupId)
fun radioAudioStart(groupId: Long, channels: Int): Boolean = nativeRadioAudioStart(groupId, channels)
fun radioAudioFeed(groupId: Long, samples: ShortArray): Boolean = nativeRadioAudioFeed(groupId, samples)
fun radioAudioPull(groupId: Long, out: ShortArray): Int = nativeRadioAudioPull(groupId, out)
fun radioAudioPull(groupId: Long, out: ShortArray, playedFrames: Long): Int = nativeRadioAudioPull(groupId, out, playedFrames)
fun radioAudioStop() { nativeRadioAudioStop() }
fun radioCaptureStart(groupId: Long, channels: Int, vad: Boolean): Boolean = nativeRadioCaptureStart(groupId, channels, vad)
fun radioCaptureStop() { nativeRadioCaptureStop() }
@ -334,7 +334,7 @@ object NativeLib {
private external fun nativeRadioSubscribers(groupId: Long): Int
private external fun nativeRadioAudioStart(groupId: Long, channels: Int): Boolean
private external fun nativeRadioAudioFeed(groupId: Long, samples: ShortArray): Boolean
private external fun nativeRadioAudioPull(groupId: Long, out: ShortArray): Int
private external fun nativeRadioAudioPull(groupId: Long, out: ShortArray, playedFrames: Long): Int
private external fun nativeRadioAudioStop()
private external fun nativeRadioCaptureStart(groupId: Long, channels: Int, vad: Boolean): Boolean
private external fun nativeRadioCaptureStop()

28
tools/chatgui-android/app/src/main/java/com/utun/chat/data/RadioAudioEngine.kt

@ -692,14 +692,32 @@ class RadioAudioEngine {
private fun playLoop() {
val frame = FRAME_SAMPLES * PLAY_CHANNELS
val buf = ShortArray(frame)
val silence = ShortArray(frame)
val frameNs = FRAME_MS * 1_000_000L /* 20мс в наносекундах */
var nextNs = System.nanoTime()
var previousHead = 0L
var playedFrames = 0L
val group = audioGroup
val track = audioTrack ?: return
LogManager.addLog("DEBUG", "radio", "playback progress tracking group=$group guard=200ms")
while (playbackRunning) {
val track = audioTrack ?: return
val n = NativeLib.radioAudioPull(audioGroup, buf)
if (n > 0) track.write(buf, 0, n)
if (n < frame) track.write(silence, 0, frame - n)
/* AudioTrack возвращает unsigned 32-bit позицию; разворачиваем wrap (~25 часов @48k). */
val head = track.playbackHeadPosition.toLong() and 0xffffffffL
playedFrames += (head - previousHead) and 0xffffffffL
previousHead = head
val n = NativeLib.radioAudioPull(group, buf, playedFrames)
if (n < frame) buf.fill(0, n.coerceAtLeast(0), frame)
var written = 0
while (written < frame && playbackRunning) {
val count = track.write(buf, written, frame - written)
if (count <= 0) {
if (playbackRunning) {
LogManager.addLog("ERROR", "radio", "playback write failed group=$group rc=$count written=$written/$frame")
mainHandler.post { if (audioTrack === track) stop() }
}
return
}
written += count
}
/* абсолютный deadline: не даём Thread.sleep накопить систематический дрейф */
nextNs += frameNs

3
tools/chatgui-android/jni_bridge/android_jni_bridge.c

@ -2735,12 +2735,13 @@ JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeRadioVadMode(
}
JNIEXPORT jint JNICALL Java_com_utun_chat_data_NativeLib_nativeRadioAudioPull(
JNIEnv* env, jobject thiz, jlong groupId, jshortArray out) {
JNIEnv* env, jobject thiz, jlong groupId, jshortArray out, jlong playedFrames) {
(void)thiz;
if (!out) return 0;
jsize len = (*env)->GetArrayLength(env, out);
jshort* data = (*env)->GetShortArrayElements(env, out, NULL);
if (!data) return 0;
radio_audio_playback_progress((uint64_t)groupId, (uint64_t)playedFrames, 0);
int n = utun_bridge_radio_audio_pull((uint64_t)groupId, (int16_t*)data, (int)len);
(*env)->ReleaseShortArrayElements(env, out, data, 0);
return (jint)n;

Loading…
Cancel
Save