Block Processing
Standards: IEC 61260IEC 61672Key references: Oppenheim & Schafer 2010
Some measurements never fit in memory: an hour-long environmental recording, a live monitor that must report levels while the microphone is still capturing, or an embedded logger that only ever sees one buffer at a time. In all of these the signal has to be processed block by block, and the filters must behave exactly as if they had seen the whole signal at once.
The OctaveFilterBank, WeightingFilter (for A, C, or Z-weighting) and
TimeWeighting classes support block (streaming) processing: the internal
filter state is carried between calls, so concatenated block outputs match a
single full-signal pass.
With stateful=True the concatenated block outputs match the continuous
result exactly; without state, every block boundary restarts the filter
transient.
Show the code for this figure
import matplotlib.pyplot as pltimport numpy as npfrom phonometry import metrology
# 1 kHz octave band: four stateful blocks vs one continuous passfs, block = 8000, 1000rng = np.random.default_rng(42)x = rng.standard_normal(4 * block)t = np.arange(x.size) / fs
bank = metrology.OctaveFilterBank(fs, fraction=1, limits=[900, 1100], stateful=True, resample=False)streamed = np.concatenate([ bank.filter(x[i * block:(i + 1) * block], sigbands=True, detrend=False, calculate_level=False)[2][0] for i in range(4)])offline = metrology.OctaveFilterBank(fs, fraction=1, limits=[900, 1100], resample=False).filter( x, sigbands=True, detrend=False, calculate_level=False)[2][0]print(np.max(np.abs(streamed - offline))) # 0.0 (bit-exact)
fig, ax = plt.subplots(figsize=(9, 4.5))ax.plot(t, offline, linewidth=2.5, alpha=0.35, label="Continuous (whole signal)")ax.plot(t, streamed, label="Stateful blocks (state carried)")ax.axvline(block / fs, color="gray", linestyle=":", label="Block boundary")ax.set(xlim=(0.11, 0.17), xlabel="Time [s]", ylabel="Amplitude")ax.legend()plt.show()Create a stateful filter bank with stateful=True. The internal state is
zero-initialized by default but may be initialized for step-response
steady-state (like scipy.signal.sosfilt_zi) with steady_ic=True. The
options that must be disabled in stateful mode are summarized in the
constraints table at the end of this guide.
Example
Section titled “Example”This example streams a WAV file with soundfile,
an optional dependency (pip install soundfile). Any block source works just as
well: scipy.io.wavfile plus manual slicing, or a live capture callback.
import soundfile as sffrom phonometry import metrology
fs = 48000octave_filter = metrology.OctaveFilterBank(fs, 1, stateful=True, resample=False)afilter = metrology.WeightingFilter(fs, "A", stateful=True)
for block in sf.blocks("measurement.wav", blocksize=256, overlap=0):
# Apply A-filter weighted = afilter.filter(block)
# Split into octave bands block_spl, _, block_output = octave_filter.filter(weighted, sigbands=True, detrend=False)
# further signal processing ...Time weighting across blocks
Section titled “Time weighting across blocks”Use the TimeWeighting class (state carried automatically):
from phonometry import metrology
tw = metrology.TimeWeighting(fs, mode="fast")# audio_blocks: successive frames of your microphone recording (Pa),# e.g. from sf.blocks("measurement.wav", ...) as in the block above.for block in audio_blocks: envelope = tw.process(block)Or manage the state yourself with the functional API; see Time Weighting.
Multichannel state
Section titled “Multichannel state”All stateful classes handle multichannel input (channels, samples): the state
is allocated lazily on the first call to match the channel count, and reallocated
if the channel count changes (e.g. switching from stereo to mono resets the state).
What the carried state is
Section titled “What the carried state is”Every filter in the library runs as a cascade of second-order sections in transposed direct form II. For one section with coefficients :
The pair per section is the filter’s entire memory of the
past. stateful=True stores these values when a block ends and restores
them when the next begins, so the recursion cannot tell where one buffer
stopped and the next started: the concatenated output equals the
single-pass output exactly, not approximately. The TimeWeighting detector
carries even less, just its last envelope value , which is the same
value the functional API hands back as initial_state (see
Time Weighting).
Streaming pitfalls
Section titled “Streaming pitfalls”- Per-block preprocessing creates seams. Anything computed from a single
block that should be global, like detrending (removing the block’s own
mean) or normalization, gives each block a slightly different operation:
the outputs no longer concatenate into the continuous result. This is why
detrendmust beFalsein stateful mode. - Not every metric streams. Energy metrics accumulate cleanly (a running Leq is a running energy sum), but rank statistics do not: the L90 of a recording is not any combination of per-block L90 values. Stream the envelope and compute percentiles once, on the pooled result.
- The first block still carries the onset transient. State starts at
rest, so the filter settles during the first instants exactly as in a
single pass. Use
steady_ic=Trueto start in step-response steady state, or discard the settling time once (not once per block). - One stream per object. A stateful instance holds the memory of one signal; feeding two interleaved streams through it corrupts both. Create one filter object per stream: the design cost is paid once at construction, not per block.
Real-time level meter pattern
Section titled “Real-time level meter pattern”The canonical streaming loop weights, envelopes and reports block by block, carrying all state across calls:
import numpy as npfrom phonometry import metrology
fs, block = 48000, 4800 # 100 ms blocksaw = metrology.WeightingFilter(fs, "A", stateful=True)env = metrology.TimeWeighting(fs, mode="fast") # the class is inherently stateful
for x in audio_stream(block): # your capture callback y = env.process(aw.filter(x)) spl = 10 * np.log10(y[..., -1] / (2e-5) ** 2) # instantaneous LAF display(spl)Stateful-mode constraints
Section titled “Stateful-mode constraints”| Option | Stateful behavior | Why |
|---|---|---|
detrend | must be False | Per-block detrending creates boundary discontinuities |
resample | must be False | The resampler is not stateful |
zero_phase | unsupported | Forward-backward filtering needs the whole signal |
high_accuracy (weighting) | resolves to False by default (the legacy bilinear design, see Frequency Weighting); explicitly passing True raises ValueError | The polyphase resampling inside is block-incompatible |
steady_ic | optional | Starts the filters in step-response steady state |
What this guide covers
Section titled “What this guide covers”Covered. IEC 61260-1:2014 and IEC 61672-1:2013 exactly as far as
Filter Banks, Frequency
Weighting and Time
Weighting already implement them:
stateful=True on OctaveFilterBank, WeightingFilter and TimeWeighting
carries the direct-form-II-transposed section state (z1, z2), or the
envelope’s last value for TimeWeighting, across block boundaries, so the
concatenated streamed output is bit-exact with a single full-signal pass.
This page adds no normative content of its own; it only proves and
documents that equivalence.
Not covered. zero_phase forward-backward filtering needs the whole
signal, so it is unsupported in stateful mode; use the offline path of
Filter Banks instead. high_accuracy
weighting design resolves to the legacy bilinear filter in stateful mode
(passing True explicitly raises ValueError), because its polyphase
resampling stage is not block-compatible; see Frequency
Weighting for the offline high-accuracy
design. Rank statistics such as L90 do not stream either: stream the
envelope and compute percentiles once on the pooled result, as
Integrated and Statistical Levels describes.
See also
Section titled “See also”- API reference:
metrology.parametric_filtersandmetrology.core.
References
Section titled “References”- International Electrotechnical Commission. (2013). Electroacoustics — Sound level meters — Part 1: Specifications (IEC 61672-1:2013). The streamed frequency and time weightings are the same designs this standard governs; the carried state keeps the class and tolerance claims of the Frequency Weighting and Time Weighting pages valid in streaming use.
- International Electrotechnical Commission. (2014). Electroacoustics — Octave-band and fractional-octave-band filters — Part 1: Specifications (IEC 61260-1:2014). Block processing adds no normative content of its own: the streamed octave and fractional-octave filters are the same designs this standard governs, and carrying the internal filter state across blocks is exactly what makes the concatenated output identical to a single full-signal pass, so every class and tolerance claim of the Filter Banks page holds unchanged in streaming use.
- Oppenheim, A. V., & Schafer, R. W. (2010). Discrete-time signal processing (3rd ed.). Pearson. The direct-form filter structures and state recursion behind the carried state equation (ISBN 978-0-13-198842-2).