Document Android radio automatic capture and background window contracts
This commit is contained in:
1 parent
d6dc6234d5
commit
f71aff3e11
2 files changed
+27
-4
No files matched your search
@@ -1,6 +1,14 @@
|
||||
package com.utun.chat.data
|
||||
|
||||
/** Один снимок режима и связанных порогов; значения режимов совпадают с radio_audio.h. */
|
||||
/** Сохранённые параметры автопередачи; enabled отделён от выбранного детектора (VAD/LEVEL).
|
||||
* OFF используется только для захвата без автоматики и не является сохраняемым mode.
|
||||
* valid проверяет весь снимок, включая параметры невыбранного детектора; nativeValues сам проверку не выполняет.
|
||||
* Диапазоны совпадают с реестром chat_setting.c, номера режимов — с radio_audio.h.
|
||||
* @param levelOn порог включения по уровню, -90..0 dBFS.
|
||||
* @param levelOff порог выключения по уровню, -90..0 dBFS, строго ниже levelOn.
|
||||
* @param hangoverMs тишина до автоматического отбоя, 50..2000 мс.
|
||||
* @param vadThreshold порог вероятности речи Silero, 10..95 процентов.
|
||||
* @param vadGain предел усиления отдельного компрессора копии PCM для VAD, 1..60 dB; не усиление передаваемого PCM. */
|
||||
data class RadioAutoSettings(
|
||||
val enabled: Boolean = false,
|
||||
val mode: Int = VAD,
|
||||
@@ -14,11 +22,14 @@ data class RadioAutoSettings(
|
||||
fun valid(): Boolean = mode in VAD..LEVEL && levelOn in -90..0 && levelOff in -90..0 && levelOff < levelOn &&
|
||||
hangoverMs in 50..2000 && vadThreshold in 10..95 && vadGain in 1..60
|
||||
|
||||
// Порядок фиксирован контрактом nativeRadioConfigure; вся группа применяется одним заданием uasync.
|
||||
/** Новая копия [enabled, mode, levelOn, levelOff, hangoverMs, vadThreshold, vadGain] для nativeRadioConfigure.
|
||||
* Native проверяет и применяет снимок целиком в uasync, результат приходит отдельно по request ID. */
|
||||
fun nativeValues(): IntArray = intArrayOf(if (enabled) 1 else 0, mode, levelOn, levelOff, hangoverMs, vadThreshold, vadGain)
|
||||
}
|
||||
|
||||
/** Решение о захвате одинаково для VAD/уровня; измеритель не включает автопередачу. */
|
||||
/** Для уже проверенных settings: null — захват не нужен, OFF — только PTT/измеритель, VAD/LEVEL — с автоматикой.
|
||||
* Неактивная рация исключает любой захват. Автоматика требует включённый экран и разрешение фонового окна;
|
||||
* ручной PTT обходит эти два ограничения, измеритель работает только при включённом экране и не включает автоматику. */
|
||||
internal fun radioCaptureMode(active: Boolean, screenOn: Boolean, autoAllowed: Boolean,
|
||||
settings: RadioAutoSettings, manual: Boolean, monitor: Boolean): Int? {
|
||||
if (!active) return null
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package com.utun.chat.data
|
||||
|
||||
/** Окно VAD расходуется только в фоне; все изменения выполняются в radio-audio-io. */
|
||||
/** Фоновое окно обоих детекторов автопередачи (VAD и уровня), длительность задаёт durationMs.
|
||||
* В foreground открытое окно не ограничено временем; возвращение в приложение восстанавливает полный бюджет,
|
||||
* следующий уход в фон даёт новое окно целиком. Повторная установка того же foreground/enabled его не продлевает.
|
||||
* Вызывающий передаёт nowMs в монотонной шкале SystemClock.elapsedRealtime, отдельно учитывает экран и готовность аудио.
|
||||
* Изменения и timeLeftMs — только в radio-audio-io; capture-поток может читать volatile deadlineMs и isAllowed. */
|
||||
class RadioVadWindow(private val durationMs: Long) {
|
||||
/** 0 — закрыто, Long.MAX_VALUE — открыто без ограничения foreground, иначе исключающая граница в elapsedRealtime. */
|
||||
@Volatile var deadlineMs = 0L
|
||||
private set
|
||||
var foreground = false
|
||||
@@ -9,6 +14,7 @@ class RadioVadWindow(private val durationMs: Long) {
|
||||
private var enabled = false
|
||||
private var remainingMs = durationMs
|
||||
|
||||
/** Смена видимости приложения: вход в foreground восстанавливает бюджет, выход запускает его отсчёт. */
|
||||
fun setForeground(value: Boolean, nowMs: Long) {
|
||||
if (foreground == value) return
|
||||
if (value) remainingMs = durationMs
|
||||
@@ -16,24 +22,30 @@ class RadioVadWindow(private val durationMs: Long) {
|
||||
updateDeadline(nowMs)
|
||||
}
|
||||
|
||||
/** Только переход false→true открывает новое окно; true→false закрывает его. */
|
||||
fun setEnabled(value: Boolean, nowMs: Long) {
|
||||
if (enabled == value) return
|
||||
enabled = value
|
||||
if (value) restart(nowMs) else close()
|
||||
}
|
||||
|
||||
/** Восстановить полный бюджет (например, экран включился); выключенную настройку enabled не включает. */
|
||||
fun restart(nowMs: Long) {
|
||||
remainingMs = durationMs
|
||||
updateDeadline(nowMs)
|
||||
}
|
||||
|
||||
/** Немедленно закрыть окно (например, экран погас), сохранив enabled и foreground.
|
||||
* Повторный setEnabled(true) не откроет его; нужны restart либо новый переход в foreground. */
|
||||
fun close() {
|
||||
remainingMs = 0L
|
||||
deadlineMs = 0L
|
||||
}
|
||||
|
||||
/** На самой границе deadline окно уже истекло; разрешение экрана/PTT проверяется отдельно. */
|
||||
fun isAllowed(nowMs: Long): Boolean = nowMs < deadlineMs
|
||||
|
||||
/** Остаток в фоне или полный бюджет открытого foreground; для закрытого/истёкшего окна — 0. Только io-поток. */
|
||||
fun timeLeftMs(nowMs: Long): Long =
|
||||
if (deadlineMs == Long.MAX_VALUE) remainingMs else (deadlineMs - nowMs).coerceAtLeast(0L)
|
||||
|
||||
|
||||
Reference in new issue
Block a user