fix: Keep the paused point as audio finishes
Some checks are pending
Build / build (push) Waiting to run

A pause just as a sentence finished could lose the listener's place.
The audio had finished, but playback had not yet reported completion,
so resuming could repeat the whole sentence instead of its final second.

Kept the heard position available through that brief handoff, until
playback stopped or the next sentence began. Added a regression test
that reproduced the lost position before the fix and checked it for
both a full sentence and audio resumed partway through a sentence.
This commit is contained in:
Chmouel Boudjnah 2026-10-09 10:51:58 +02:00
parent 947391f852
commit 8e88960b3e
2 changed files with 33 additions and 1 deletions

View file

@ -51,6 +51,7 @@ class AudioTrackPcmOutput(
private var applied = 1f
/** The clip of the current turn: where its frames start on the track's head, and its bounds. Under [lock]. */
// Retain it through completion: a pause can precede the handoff back to the engine.
private var clip: Clip? = null
/** The speed the track plays at, set to [speed] first. Under [lock]. */
@ -146,7 +147,6 @@ class AudioTrackPcmOutput(
val (head, rate) = synchronized(lock) {
if (turn != myTurn) return@withContext
val head = track.playbackHeadPosition.toLong() and 0xffffffffL
if (head >= target && clip === playing) clip = null
head to speedLocked(track)
}
if (head >= target) return@withContext

View file

@ -0,0 +1,32 @@
package com.chmouel.liseur.tts
import android.app.Application
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@Config(sdk = [35], application = Application::class)
@RunWith(RobolectricTestRunner::class)
class PcmOutputTest {
@Test
fun completedAudioKeepsItsHeardPositionUntilHalted() = runTest {
val output = AudioTrackPcmOutput(writer = StandardTestDispatcher(testScheduler))
val frames = 3 * SpeechAudio.SAMPLE_RATE
val pcm = ByteArray(frames * 2)
try {
output.play(pcm)
assertEquals(frames, output.halt())
assertNull(output.halt())
output.play(pcm, fromFrame = SpeechAudio.SAMPLE_RATE)
assertEquals(frames, output.halt())
} finally {
output.release()
}
}
}