fix(cw): the record deleted its own text while decoding
The record pane concatenated the live decode onto the archived text. The live decode
is the 20 s window, replaced wholesale every 1.5 s because DeepCW is a whole-segment
CTC model that rewrites earlier characters as more context arrives. So the tail of
the record kept changing and could get shorter - text vanishing from under the
operator while the decoder was still running.
A previous attempt (828fd0fb) added decodePending() to cover the gap while audio
waits to be archived, but the call site was never wired up. The function had no
callers and pendingText was only ever cleared, never assigned, so that fix has never
once run and the gap it targeted stayed open. Both are deleted here.
The record now binds to archived text only, which is append-only, so it cannot
shrink. That moves the whole problem to latency, which was 20 s window + 15 s batch:
nothing at all in the record for the first 35 s of a session, and thereafter a stall
of up to 15 s each cycle. The batch is now 4 s, holding the stall under the ~4.7 s a
seven-character call sign takes at 18 WPM, while the archive path still fires less
than half as often as the live redecode.
Neither holding place drains on its own: the live window only reaches the archive by
being pushed out by newer audio, and the pending batch only by filling up. So pausing
or leaving the screen discarded whatever was in flight - the end of every
transmission, the part with the call sign in it. flush() archives both, pending batch
first so the text is not transposed, and is called on pause and before close(). On
the way out it runs on appScope, because the screen's own scope is cancelled as it
leaves and would abort the decode.
Also: the record was an unlabelled grey box showing a bare ellipsis, which reads as a
disabled text field. It now has a label, an empty state that says what it is for, and
a copy button - until now there was no way to get the decoded text off the screen at
all, so an operator who had just copied a call sign by ear had to transcribe it a
second time by hand.
CwArchiveTimingTest covers the timing against the real constants rather than copies;
it caught a 5 s batch exceeding the call-sign bound during this change. The archive
path had no test coverage before.
This commit is contained in:
1 parent
78a6f270bf
commit
5ed76dba31
6 files changed
+293
-46
No files matched your search
@@ -37,9 +37,11 @@ interface ICwDecoder {
|
||||
val decodedText: StateFlow<String>
|
||||
|
||||
/**
|
||||
* Permanent transcript of everything that has scrolled out of the live
|
||||
* window. Unlike [decodedText] this only ever grows (until [reset]); it is
|
||||
* what the user reads back after a signal has passed.
|
||||
* Transcript of audio that has been archived, and will not be revised.
|
||||
*
|
||||
* Only ever grows until [reset]. [decodedText] is rewritten from scratch on every
|
||||
* redecode, so a pane that concatenates it loses text the operator has already read -
|
||||
* which a paper log does not do. This is the flow such a pane must bind to.
|
||||
*/
|
||||
val historyText: StateFlow<String>
|
||||
|
||||
@@ -74,6 +76,17 @@ interface ICwDecoder {
|
||||
/** Non-null when the decoder cannot run, for example the model failed to load. */
|
||||
val errorMessage: StateFlow<String?>
|
||||
|
||||
/**
|
||||
* Decode whatever audio is still held in the pipeline into [historyText].
|
||||
*
|
||||
* Nothing reaches [historyText] until audio has been pushed out of the live window and
|
||||
* then accumulated into a full archive batch, so the last stretch of a session is always
|
||||
* still in flight when capture stops - and neither holding place drains on its own. That
|
||||
* stretch is the end of the transmission, the part with the call sign in it. Call on
|
||||
* pause and before [close].
|
||||
*/
|
||||
suspend fun flush()
|
||||
|
||||
/** Feed captured mono PCM in -1..1. Safe to call from a capture thread. */
|
||||
suspend fun processBuffer(samples: FloatArray, sampleRate: Int)
|
||||
|
||||
|
||||
Reference in new issue
Block a user