Class AnalysisPipeline
DtmfDecoder (batch)
and DtmfDetector (push).
One AnalysisPipeline owns:
- a small
double[]block buffer sized toDtmfConfig.analysisBlockSize(), - a
GoertzelBanktuned to the eight DTMF frequencies (FrequencyBins.ALL_EIGHT), and - the tone-confirmation state machine from
design.md(Idle → Confirming → Active → Ending → Idle).
Samples enter via accept(double) or acceptAll(double[], int, int)
one at a time. Every N = analysisBlockSize samples, the block
buffer is windowed (unless the configured WindowFunction is
WindowFunction.RECTANGULAR) and fed through the Goertzel bank in
a single GoertzelBank.computeMagnitudesSquaredInto(double[], double[])
call. The peaks of the low (indices 0–3 in
FrequencyBins.ALL_EIGHT) and high (indices 4–7) groups are
picked by argmax; the candidate is validated by
ConfidenceScorer.compute(double, double, double) against
DtmfConfig.detectionThreshold() and by
TwistEvaluator.withinTolerance(double, DtmfConfig). The valid
candidate (or "no candidate") drives the state machine.
Emissions are handed to a Consumer<DtmfTone> sink supplied at
construction time. A tone is emitted on the first non-confirming block
after the state machine has entered Active, provided its duration
(in samples) is at least
round(config.minimumToneDuration() * config.sampleRate()). Tones
shorter than the minimum are discarded.
Sample indices reported on each emitted DtmfTone are cumulative
from the first sample ever passed to this pipeline instance; this gives
the push detector chunk invariance (Requirement 6.7) by construction.
Instances are mutable and not thread-safe. Each pipeline is tagged with
a channel value at construction so the stereo-independent
detector can run two pipelines in parallel with the correct channel tag
on each emission.
Package-private by convention: com.tino1b2be.dtmf.internal.*
is not part of the published API. The type is public so tests in
the same package and the DtmfDetector in
com.tino1b2be.dtmf can reach it via the existing internal-friend
pattern other helpers follow.
- Since:
- 2.0.0
-
Constructor Summary
ConstructorsConstructorDescriptionAnalysisPipeline(DtmfConfig config, int channel, Consumer<DtmfTone> sink) Construct a pipeline bound to the given config, channel tag, and tone sink. -
Method Summary
Modifier and TypeMethodDescriptionvoidaccept(double sample) Feed a single sample through the pipeline.voidacceptAll(double[] samples, int offset, int length) Feed a range of samples through the pipeline.voidflush()Finalise any in-progress tone.longReturns the cumulative number of samples that have been fed toaccept(double)since this pipeline was constructed.
-
Constructor Details
-
AnalysisPipeline
Construct a pipeline bound to the given config, channel tag, and tone sink.- Parameters:
config- configuration whose sample rate, analysis block size, detection threshold, confirmation frames, minimum tone duration, window function, and twist tolerances all feed the state machine; non-nullchannel- channel tag written onto every emittedDtmfTone; must be>= 0. Detectors use0for mono or left,1for rightsink- consumer invoked with each confirmed tone, at the first non-confirming block (Tone_End_Event); non-null- Throws:
NullPointerException- ifconfigorsinkis nullIllegalArgumentException- ifchannel < 0
-
-
Method Details
-
accept
public void accept(double sample) Feed a single sample through the pipeline. Samples accumulate into an internal block buffer; everyanalysisBlockSizesamples a block is evaluated and the state machine advanced, possibly emitting aDtmfToneto the sink.- Parameters:
sample- next sample in the signal (any finite double)
-
acceptAll
public void acceptAll(double[] samples, int offset, int length) Feed a range of samples through the pipeline. Equivalent to callingaccept(double)in order for each element insamples[offset .. offset + length).- Parameters:
samples- source array; non-nulloffset- starting index; must satisfy0 <= offset && offset + length <= samples.lengthlength- number of samples to consume; must be>= 0- Throws:
NullPointerException- ifsamplesis nullIndexOutOfBoundsException- ifoffset/lengthdescribe a range outsidesamples
-
flush
public void flush()Finalise any in-progress tone. If the state machine is inAnalysisPipeline.State.ACTIVEorAnalysisPipeline.State.ENDINGand the tone duration so far meets the configured minimum, the tone is emitted withendSample = currentSample. The internal block buffer contents (a possibly-partial block) are not processed; flush is a state-machine termination, not a block boundary.After
flush()the pipeline is returned toAnalysisPipeline.State.IDLEand can be fed further samples. -
samplesProcessed
public long samplesProcessed()Returns the cumulative number of samples that have been fed toaccept(double)since this pipeline was constructed..- Returns:
- the cumulative number of samples that have been fed to
accept(double)since this pipeline was constructed
-