Separate playback controls from blocked capture cleanup and gate stopping cores

This commit is contained in:
evgeny committed 2026-10-29 13:35:19 +01:00
1 parent b25766ae5a
commit a9099a5ea7
2 files changed
+15 -10

No files matched your search

@@ -195,8 +195,9 @@ class AudioRecorderManager {
if (captureMode == null) releaseCaptureDevice()
return 0
}
@Synchronized fun cancelRecording() {
if (captureMode == CaptureMode.RECORD) finishCapture(false) else if (captureMode == null) releaseCaptureDevice()
fun cancelRecording() {
if (closing.get()?.isDone == false) return
synchronized(this) { if (captureMode == CaptureMode.RECORD) finishCapture(false) else if (captureMode == null) releaseCaptureDevice() }
}
fun isActive(): Boolean = captureMode == CaptureMode.RECORD && captureRunning
@@ -209,11 +210,13 @@ class AudioRecorderManager {
}
return synchronized(this) { if (isTesting) true else startCapture(CaptureMode.TEST, "__test__") }
}
@Synchronized fun stopMicTest() {
if (captureMode == CaptureMode.TEST) finishCapture(false) else if (captureMode == null) releaseCaptureDevice()
fun stopMicTest() {
if (closing.get()?.isDone == false) return
synchronized(this) { if (captureMode == CaptureMode.TEST) finishCapture(false) else if (captureMode == null) releaseCaptureDevice() }
}
@Volatile private var playing = false
private val playbackLock = Any()
private var playThread: Thread? = null
@Volatile private var playGeneration = 0
@@ -268,8 +271,9 @@ class AudioRecorderManager {
/** Запустить со startMs, округлённого вниз до 20 мс; seek декодирует и отбрасывает предшествующий PCM.
* Отрицательный startMs ограничивается нулём; прогресс учитывает только принятые AudioTrack сэмплы. */
@Synchronized fun playFromPosition(filePath: String, startMs: Int, onProgress: ((Float, Int) -> Unit)? = null, onComplete: () -> Unit = {}) {
fun playFromPosition(filePath: String, startMs: Int, onProgress: ((Float, Int) -> Unit)? = null, onComplete: () -> Unit = {}) {
if (closing.get()?.isDone == false) { LogManager.addLog("WARN", "AudioPlayer", "play rejected: cleanup pending"); return }
synchronized(playbackLock) {
stopPlayback()
playing = true
val myGen = playGeneration
@@ -332,7 +336,7 @@ class AudioRecorderManager {
}
val total = (skippedSamples + readTotal).toFloat() / totalSamples.coerceAtLeast(1).toFloat()
val eMs = ((skippedSamples + readTotal) * 1000 / (sampleRate * channels)).toInt()
synchronized(this@AudioRecorderManager) {
synchronized(playbackLock) {
if (myGen == playGeneration) onProgress?.invoke(total, eMs)
}
}
@@ -355,7 +359,7 @@ class AudioRecorderManager {
try { if (handle != 0L) { NativeLib.voiceDecodeClose(handle); handle = 0 } }
catch (e: Exception) { LogManager.addLog("ERROR", "AudioPlayer", "decode close failed generation=$myGen; retained: $e") }
if (!notified) {
synchronized(this@AudioRecorderManager) {
synchronized(playbackLock) {
if (playGeneration == myGen) {
playing = false; playThread = null
try { onComplete() }
@@ -380,16 +384,17 @@ class AudioRecorderManager {
LogManager.addLog("ERROR", "AudioPlayer", "Playback worker start failed: $e")
onComplete()
}
}
}
/** Асинхронно остановить текущий worker; позицию для следующего playFromPosition хранит вызывающий код. */
@Synchronized fun pausePlayback() {
fun pausePlayback() = synchronized(playbackLock) {
playGeneration++; playing = false
playThread?.interrupt(); playThread = null
}
/** Инвалидировать callbacks и прервать worker; собственные AudioTrack/decoder он освобождает в finally. */
@Synchronized fun stopPlayback() {
fun stopPlayback() = synchronized(playbackLock) {
playGeneration++; playing = false
playThread?.interrupt(); playThread = null
}
@@ -95,7 +95,7 @@ object NativeLib {
return nativeUpdateSockets(configText)
}
fun isReady(): Boolean = nativeIsRunning()
fun isReady(): Boolean = !coreStopping && nativeIsRunning()
fun getMyNodeId(): Long = nativeGetMyNodeId()