User-visible symptom
When pocketsphinx_batch decodes multiple independent recordings with spectral-subtraction noise removal enabled, an utterance's acoustic score can change according to which recordings precede it in the control file. This makes batch output dependent on input order even though each entry names a separate audio file.
Using five audio files shipped in PocketSphinx's Librivox test data, reversing the control file changed the reported score for all five utterances in a v5.1.1 build. The word hypotheses happened to remain identical for those fixtures. Hypothesis changes have been observed through a separate API-based multi-utterance caller, but have not been reproduced in pocketsphinx_batch with the public fixtures used here.
Mechanism in v5.1.1
These references are to commit 511126b492dcb267cf30d49d631946d7b61a9530 (v5.1.1).
programs/pocketsphinx_batch.c:425-492 processes each control-file entry with one shared decoder. For raw audio it calls ps_decode_raw() at line 472; there is no ps_start_stream() call.
src/pocketsphinx.c:1030-1069 shows that ps_decode_raw() calls ps_start_utt() and ps_end_utt() around each file.
src/pocketsphinx.c:1091-1122 shows what ps_start_utt() resets before calling acmod_start_utt(); it does not reset front-end noise statistics.
include/pocketsphinx.h:769-780 states that noise-removal statistics are retained across utterances and that ps_start_stream() resets them.
programs/pocketsphinx_batch.c:756-887 iterates the control file using the same decoder instance, so retained statistics from earlier entries become input state for later entries.
Retaining a stream-level estimate is reasonable for segmented continuous audio. The problem here is that batch entries can also be independent recordings, and the tool provides no per-entry stream reset or selectable policy.
Minimal reproduction
Build v5.1.1. Convert the five shipped Librivox WAV fixtures to 16 kHz, mono, signed 16-bit little-endian raw audio; for example with FFmpeg:
mkdir -p /tmp/ps-libri-raw
for f in test/data/librivox/*.wav; do
ffmpeg -loglevel error -y -i "$f" -ar 16000 -ac 1 -f s16le \
"/tmp/ps-libri-raw/$(basename "${f%.wav}").raw"
done
Create /tmp/forward.ctl:
sense_and_sensibility_01_austen_64kb-0870
sense_and_sensibility_01_austen_64kb-0880
sense_and_sensibility_01_austen_64kb-0890
sense_and_sensibility_01_austen_64kb-0920
sense_and_sensibility_01_austen_64kb-0930
Create /tmp/reverse.ctl with those five lines reversed. Run both orders:
pocketsphinx_batch \
-hmm model/en-us/en-us \
-dict model/en-us/cmudict-en-us.dict \
-lm model/en-us/en-us.lm.bin \
-ctl /tmp/forward.ctl -cepdir /tmp/ps-libri-raw -cepext .raw \
-adcin yes -hyp /tmp/forward.hyp -loglevel WARN
pocketsphinx_batch \
-hmm model/en-us/en-us \
-dict model/en-us/cmudict-en-us.dict \
-lm model/en-us/en-us.lm.bin \
-ctl /tmp/reverse.ctl -cepdir /tmp/ps-libri-raw -cepext .raw \
-adcin yes -hyp /tmp/reverse.hyp -loglevel WARN
Match rows by utterance ID rather than line number. In one build of the pinned commit, the score pairs for forward versus reverse order were:
0870 -30531 -30435
0880 -11225 -11374
0890 -22486 -22245
0920 -23555 -23566
0930 -13812 -13809
All five hypothesis strings were identical between these two runs. Exact scores may vary with build configuration, but the comparison is made between two orderings of the same executable and fixtures.
What this does and does not establish
The shared decoder, per-file ps_start_utt() path, absence of ps_start_stream(), and documented cross-utterance retention are established directly in pinned source. The score changes above were measured in pocketsphinx_batch built from that source using shipped PocketSphinx audio.
The broader finding originated in application usage, not in PocketSphinx's test suite. In that separate usage path, reversing 55 independent utterances changed 6 hypothesis strings, and resetting the stream before each utterance eliminated order differences. Those measurements establish that retained front-end state can reach hypotheses in that caller, but they do not establish the same magnitude—or any hypothesis change—in pocketsphinx_batch.
This report does not establish a WER change on the five public fixtures, does not quantify corpus-level impact, and does not claim that partition-count behavior was directly reproduced. Partitioning a control file can change predecessor state at partition boundaries, but its practical effect remains an inference until measured through the relevant partitioning workflow.
It is also not claimed that retaining noise state is wrong for a continuous recording split into segments. The ambiguity is that pocketsphinx_batch has no way to distinguish that case from independent files.
Smallest addressing change
For batches of independent recordings, call ps_start_stream() before decoding each raw-audio control entry when noise removal is enabled. A regression test can decode at least two independent fixtures in both orders and compare per-utterance outputs.
Whether this should be the unconditional batch default or an option is a design choice: preserving current behavior is useful for segmented continuous audio, while resetting is appropriate for independent recordings. The smallest compatibility-preserving change would add an explicit option and document both modes.
User-visible symptom
When
pocketsphinx_batchdecodes multiple independent recordings with spectral-subtraction noise removal enabled, an utterance's acoustic score can change according to which recordings precede it in the control file. This makes batch output dependent on input order even though each entry names a separate audio file.Using five audio files shipped in PocketSphinx's Librivox test data, reversing the control file changed the reported score for all five utterances in a v5.1.1 build. The word hypotheses happened to remain identical for those fixtures. Hypothesis changes have been observed through a separate API-based multi-utterance caller, but have not been reproduced in
pocketsphinx_batchwith the public fixtures used here.Mechanism in v5.1.1
These references are to commit
511126b492dcb267cf30d49d631946d7b61a9530(v5.1.1).programs/pocketsphinx_batch.c:425-492processes each control-file entry with one shared decoder. For raw audio it callsps_decode_raw()at line 472; there is nops_start_stream()call.src/pocketsphinx.c:1030-1069shows thatps_decode_raw()callsps_start_utt()andps_end_utt()around each file.src/pocketsphinx.c:1091-1122shows whatps_start_utt()resets before callingacmod_start_utt(); it does not reset front-end noise statistics.include/pocketsphinx.h:769-780states that noise-removal statistics are retained across utterances and thatps_start_stream()resets them.programs/pocketsphinx_batch.c:756-887iterates the control file using the same decoder instance, so retained statistics from earlier entries become input state for later entries.Retaining a stream-level estimate is reasonable for segmented continuous audio. The problem here is that batch entries can also be independent recordings, and the tool provides no per-entry stream reset or selectable policy.
Minimal reproduction
Build v5.1.1. Convert the five shipped Librivox WAV fixtures to 16 kHz, mono, signed 16-bit little-endian raw audio; for example with FFmpeg:
Create
/tmp/forward.ctl:Create
/tmp/reverse.ctlwith those five lines reversed. Run both orders:Match rows by utterance ID rather than line number. In one build of the pinned commit, the score pairs for forward versus reverse order were:
All five hypothesis strings were identical between these two runs. Exact scores may vary with build configuration, but the comparison is made between two orderings of the same executable and fixtures.
What this does and does not establish
The shared decoder, per-file
ps_start_utt()path, absence ofps_start_stream(), and documented cross-utterance retention are established directly in pinned source. The score changes above were measured inpocketsphinx_batchbuilt from that source using shipped PocketSphinx audio.The broader finding originated in application usage, not in PocketSphinx's test suite. In that separate usage path, reversing 55 independent utterances changed 6 hypothesis strings, and resetting the stream before each utterance eliminated order differences. Those measurements establish that retained front-end state can reach hypotheses in that caller, but they do not establish the same magnitude—or any hypothesis change—in
pocketsphinx_batch.This report does not establish a WER change on the five public fixtures, does not quantify corpus-level impact, and does not claim that partition-count behavior was directly reproduced. Partitioning a control file can change predecessor state at partition boundaries, but its practical effect remains an inference until measured through the relevant partitioning workflow.
It is also not claimed that retaining noise state is wrong for a continuous recording split into segments. The ambiguity is that
pocketsphinx_batchhas no way to distinguish that case from independent files.Smallest addressing change
For batches of independent recordings, call
ps_start_stream()before decoding each raw-audio control entry when noise removal is enabled. A regression test can decode at least two independent fixtures in both orders and compare per-utterance outputs.Whether this should be the unconditional batch default or an option is a design choice: preserving current behavior is useful for segmented continuous audio, while resetting is appropriate for independent recordings. The smallest compatibility-preserving change would add an explicit option and document both modes.