|
|
|
|
@ -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 |
|
|
|
|
|