Skip to content
This documentation describes version 4.0.0, which is not released yet. The current version on PyPI is 3.3.0 and does not carry everything described here.

room.auditorium

Auditorium measures derived from impulse responses (ISO 3382-1:2009, Annex A).

Annex A is informative. Nothing about the sound strength is normative in ISO 3382-1:2009: not Equations (A.1) to (A.9), not the 1 dB just-noticeable difference of Table A.1, not the typical range. What the annex fixes is the definition everyone quotes, and that is what this module implements.

Reverberation time says what the room does to energy over time and nothing about how loud the room is. Sound strength, G, is the quantity that does: the energy of the measured impulse response against the energy the same source puts out at 10 m in a free field (A.2.1, Equation (A.1)). It is one of the two measures in Table A.1 that need a calibrated source, the other being the late lateral sound level of A.2.5, which is referred to the same place; and it is the reason the annex spends four equations on how to obtain that free-field reference when there is no anechoic room 10 m across to measure it in.

The three printed routes to the reference are all here, and they are routes to the same number:

  • measure at a distance d >= 3 m and correct by the inverse square law, Equations (A.4) and (A.8);
  • measure the source in a reverberation room of known absorption area, Equation (A.5);
  • take the source’s sound power level and subtract the free-field spread, Equation (A.9).

The last two carry printed integers, 37 dB and 31 dB, and both are the correctly rounded value of a closed form: is 37,0127 dB and is 30,9921 dB. Rounded to whole decibels they land 6 dB apart, where the exact offsets are dB apart, so the reverberation-room route and the sound-power route cannot agree to better than 0,0206 dB. That is 2 % of the 1 dB just-noticeable difference Table A.1 prints for G, and this module reproduces the standard’s integers rather than the closed forms: a library that quietly used 30,9921 dB would disagree with every hand calculation done from the printed page.

Both closed forms hold at a characteristic impedance of exactly , which is the value that makes the three reference quantities consistent: pW, the reference sound power. Neither equation prints that caveat. Air at 20 degrees and 101,325 kPa is nearer , worth 0,14 dB, an order of magnitude more than either rounding: the offsets are a convention of the decibel scales, not a property of the air in the hall, and this module does not make them follow the weather.

Auto-generated from the source docstrings by scripts/generate_api_docs.py (make api-docs). Do not edit by hand.

AuditoriumQuantity(
symbol: str,
aspect: str,
averaging_bands_hz: tuple[float, ...],
just_noticeable_difference: float | None,
*,
relative_jnd: bool,
typical_range: tuple[float, float],
energy_averaged: bool,
unit: str,
)

One row of ISO 3382-1:2009, Table A.1.

symbol is the quantity as the table prints it, aspect the subjective listener aspect it is grouped under, and averaging_bands_hz the octave bands its single number is the average over, which is not the same set for every row: five quantities average two bands and two average four.

just_noticeable_difference is None for the late lateral sound level, whose JND the table prints as “Not known”; it is a fraction rather than an absolute difference when relative_jnd is True, which is only the early decay time’s “Rel. 5 %”. energy_averaged is True only for the late lateral level, which footnote a sends to Equation (A.17) while every other row is averaged arithmetically.

typical_range is the pair the table prints, and footnote b conditions it: frequency-averaged values at single positions in unoccupied concert and multi-purpose halls up to 25 000 m³. It is not a range for one band, for an occupied hall, or for a spatial average.

A measurement outside the conditions ISO 3382-1:2009 prints for it.

Constant (float).

DIFFUSE_FIELD_REFERENCE_OFFSET_DB = 37.0

Constant (float).

DIRECTIVITY_ARC_DEG = 30.0
directivity_energy_average(
levels: ArrayLike,
axis: int = -1,
) -> NDArray[np.float64] | float

Average a free-field measurement over bearings around the source.

The note under ISO 3382-1:2009, Equation (A.4) asks for the free-field reference to be measured all the way round the source and combined as an energy mean, so that the source’s own directivity does not decide the reference level:

An arithmetic mean of the decibel values is a different number, and a lower one, for any source that is not omnidirectional.

The note asks for the measurement “at every 12,5 degrees”, and 12,5 degrees does not divide 360: 28 steps stop at 350 degrees and 29 overshoot to 362,5. There is no set of bearings that follows the printed instruction literally, so what this function checks is what the instruction can mean, a uniform sampling of the full turn no coarser than the printed step: N >= 29 bearings, 360/N degrees apart. The 5 degree survey of 4.2.1, which the same standard uses for the source directivity of Table 1, does divide 360 exactly, into 72.

The mean itself is the one phonometry.building.energy_average_level computes; this function adds the bearing count the note constrains, and averages one band per row when it is handed a two-dimensional survey.

Parameters

NameDescription
levelsThe levels measured at the bearings, in dB, one per bearing, evenly spaced around the source. A two-dimensional array averages one band per row (or per column, see axis), which is the shape the rest of this module works in.
axisAxis the bearings run along; the last by default.

Returns: Their energy mean, in dB: a float for a single turn, an array with axis removed for several.

Raises

ExceptionWhen
ValueErrorIf fewer than ceil(360 / 12,5) = 29 bearings lie along axis, or the levels are empty or non-finite.

Constant (float).

DIRECTIVITY_STEP_DEG = 5.0

Constant (float).

DIRECTIVITY_SURVEY_DISTANCE_M = 1.5

Constant (float).

EARLY_ENERGY_LIMIT_S = 0.08
early_lateral_energy_fraction(
ir: Signal | list[float] | NDArray[np.float64],
lateral_ir: Signal | list[float] | NDArray[np.float64],
fs: int | None = None,
*,
weighting: str = 'squared',
limits: tuple[float, float] | None = (125.0, 4000.0),
fraction: int = 1,
) -> LateralEnergyResult

Early lateral energy fraction of a pair of responses, per band.

ISO 3382-1:2009, Equation (A.14), the share of the first 80 ms that arrives from the side:

and Equation (A.15), the cosine-weighted variant that A.2.4 calls subjectively more accurate:

The modulus in (A.15) is printed and it matters: the figure-of-eight response changes sign with the side a reflection comes from, so without it two mirror-image reflections cancel to zero instead of adding.

The two lower limits differ. The numerator starts at 5 ms because the null of the figure-of-eight microphone is pointed at the source and the 5 ms keeps whatever leaks through it out of the lateral share; the denominator starts at 0 because the direct sound belongs to the total.

Time zero comes from the omnidirectional response. A figure-of-eight microphone aimed as A.2.4 asks has no direct sound to trigger on, so its own onset would land on the first strong reflection and shift both limits by that much. Both integrals here are timed from the direct sound of ir, band by band.

Parameters

NameDescription
irImpulse response measured with the omnidirectional microphone at the measurement point (1D).
lateral_irImpulse response measured at the same point with a figure-of-eight microphone whose null points at the source (1D).
fsSample rate in Hz. Required for bare arrays; a Signal brings its own.
weighting"squared" for , Equation (A.14), whose contributions vary as the square of the cosine of the angle of incidence; "cosine" for , Equation (A.15), whose contributions vary as the cosine itself.
limits(f_min, f_max) band-centre limits in Hz; default the octave bands 125 Hz to 4 kHz. Table A.1 averages this quantity over 125 Hz to 1 kHz. None measures the broadband responses.
fractionBandwidth fraction (1 = octave, 3 = one-third octave).

Returns: A LateralEnergyResult with one entry per band.

Raises

ExceptionWhen
ValueErrorIf weighting is not one of LATERAL_WEIGHTINGS, if a response is not one-dimensional or is silent, if the two responses do not split into the same number of bands or are of different lengths, or if they are too short for the 80 ms window.

Constant (tuple).

EARLY_LATERAL_WINDOW_S = (0.005, 0.08)

Constant (tuple).

EARLY_SUPPORT_WINDOW_S = (0.02, 0.1)
free_field_reference_level(
level: ArrayLike,
distance: float,
) -> NDArray[np.float64] | float

Free-field level at 10 m from one measured at another distance.

ISO 3382-1:2009, Equations (A.4) and (A.8), which are the same inverse-square correction written once for the exposure level and once for the stationary-source pressure level:

Both are printed for a point “d (>= 3 m) from the source”, far enough for the free field to have taken over; a shorter distance raises AuditoriumWarning.

The note under (A.4) adds that the measurement is to be repeated around the source and energy-averaged, “at every 12,5 degrees”, to average out the source’s own directivity. Feed this function the averaged level, not one bearing; directivity_energy_average is that mean, and its docstring says what the printed step does and does not determine.

Parameters

NameDescription
levelThe level measured at distance, in dB. Any shape.
distanceSource-to-microphone distance of that measurement, in metres.

Returns: The level referred to 10 m, in dB, in the shape of level.

Raises

ExceptionWhen
ValueErrorIf distance is not a positive, finite length.
gliding_directivity_deviation(levels: ArrayLike) -> NDArray[np.float64]

Deviation of each gliding arc from the whole-turn reference (4.2.1).

ISO 3382-1:2009, 4.2.1 qualifies a source by averaging its free-field output over “gliding” 30 degree arcs and comparing each of them with the reference, which is “a 360 degree energetic average in the measurement plane”. Where no turntable is available it asks for measurements every 5 degrees, and for gliding averages “each covering six neighbouring points”, which is 30 degrees of a 72-point survey.

Both averages here are energetic, which is what the printed word makes of the reference and what the arcs must therefore be for the comparison to mean anything. The standard does not say so of the arcs, and it does not say whether the six points of a window lead, trail or straddle their arc either; the window here starts at each measured bearing and runs forwards, and it wraps, so a survey of N bearings gives N deviations.

Parameters

NameDescription
levelsSound pressure levels around the source, in dB, one per bearing, evenly spaced over a full turn.

Returns: One deviation per bearing, in dB, of the arc that starts there against the whole-turn reference.

Raises

ExceptionWhen
ValueErrorIf the bearings do not divide the arc a whole number of times, or there are too few of them to make one.

Constant (tuple).

IACC_EARLY_WINDOW_S = (0.0, 0.08)

Constant (float).

IACC_JND = 0.075

Constant (float).

IACC_LATE_START_S = 0.08

Constant (float).

IACC_SEARCH_S = 0.001
interaural_cross_correlation(
left: Signal | list[float] | NDArray[np.float64],
right: Signal | list[float] | NDArray[np.float64],
fs: int | None = None,
*,
window: tuple[float, float | None] = (0.0, None),
limits: tuple[float, float] | None = (125.0, 4000.0),
fraction: int = 1,
) -> InterauralCorrelationResult

Interaural cross correlation of a binaural response, per band.

ISO 3382-1:2009, Equation (B.1) defines the normalised function

and Equation (B.2) takes the coefficient from it:

Both the square root and the modulus are printed, and both matter. The root is what bounds the function by one; a text layer that drops it leaves a quantity that is not a correlation at all. The modulus is what makes two anti-phase ears give 1 rather than 0, which is the answer that matches what they describe, two signals as dissimilar as they can be only in sign.

B.4 puts three windows to it. The general form runs from the direct sound to a time of the order of the reverberation time, which is the default here; IACC_EARLY_WINDOW_S is the early coefficient and IACC_LATE_START_S starts the reverberant one. Time zero is the direct sound, located on the two channels together so that a listener turned away from the source does not move it.

Parameters

NameDescription
leftImpulse response at the entrance to the left ear canal (1D).
rightImpulse response at the entrance to the right ear canal (1D), of the same length.
fsSample rate in Hz. Required for bare arrays; a Signal brings its own.
window(t_1, t_2) in seconds from the direct sound, with t_2 None for the end of the response.
limits(f_min, f_max) band-centre limits in Hz; default the octave bands 125 Hz to 4 kHz, which is the range B.4 names. None correlates the broadband responses.
fractionBandwidth fraction (1 = octave, 3 = one-third octave).

Returns: An InterauralCorrelationResult with one entry per band.

Raises

ExceptionWhen
ValueErrorIf a response is not one-dimensional or is silent, if the two channels are of different lengths, if the window starts before the direct sound or is empty or reversed, or if a band has no energy inside it.
InterauralCorrelationResult(
frequencies: NDArray[np.float64] | None,
coefficient: NDArray[np.float64],
delay: NDArray[np.float64],
lag: NDArray[np.float64],
correlation: NDArray[np.float64],
)

Per-band interaural cross correlation (ISO 3382-1:2009, Annex B).

frequencies holds the exact band centre frequencies in Hz, or is None for a broadband measurement. coefficient is the IACC of Equation (B.2), the largest magnitude the normalised correlation function reaches inside the +/-1 ms search window, and delay the lag in seconds at which it reaches it: positive when the right ear is the later of the two, because (B.1) evaluates the right channel at .

lag and correlation carry the function itself over the search window, one row per band, so it can be drawn rather than summarised. B.4 assumes a just-noticeable difference of 0,075.

InterauralCorrelationResult.plot(
ax: Axes | None = None,
*,
language: str = 'en',
**kwargs: Any,
) -> Axes

Plot the correlation function over the search window, per band.

Requires matplotlib (pip install phonometry[plot]); returns the Axes.

late_lateral_average(levels: ArrayLike) -> float

Frequency-average the late lateral level over its four octave bands.

ISO 3382-1:2009, Equation (A.17):

The 0,25 is one quarter, so this is an energy mean and four equal band values return that value unchanged. It is the one exception footnote a of Table A.1 makes: every other quantity in that table is averaged arithmetically over its bands, and only is averaged over energy. The four bands are the 125 Hz, 250 Hz, 500 Hz and 1 kHz octaves, in that order.

Parameters

NameDescription
levelsThe four octave-band values of , in dB, in the order of LATE_LATERAL_AVERAGE_BANDS_HZ.

Returns: in dB.

Raises

ExceptionWhen
ValueErrorIf four values are not given, or they are not finite.

Constant (tuple).

LATE_LATERAL_AVERAGE_BANDS_HZ = (125.0, 250.0, 500.0, 1000.0)
late_lateral_sound_level(
ir: Signal | list[float] | NDArray[np.float64],
lateral_ir: Signal | list[float] | NDArray[np.float64],
reference_ir: Signal | list[float] | NDArray[np.float64] | None = None,
fs: int | None = None,
*,
reference_level: ArrayLike | None = None,
limits: tuple[float, float] | None = (125.0, 4000.0),
fraction: int = 1,
) -> LateLateralResult

Late lateral sound level of a measured response, per band.

ISO 3382-1:2009, Equation (A.16), the level of the lateral energy that arrives after the early window, against the same free-field reference the sound strength uses:

Where is a fraction and cancels its own calibration, is a level and does not: it needs the calibrated omnidirectional source of A.2.1 and a free-field reference, which reaches this function the same two ways it reaches sound_strength. It also needs the relative sensitivity of the two microphones to have been calibrated in a free field (A.3.2).

Time zero comes from the omnidirectional response, for the reason given under early_lateral_energy_fraction: a figure-of-eight microphone aimed as A.2.5 asks has no direct sound to trigger on, and an 80 ms boundary placed on its first reflection is not the printed one. ir supplies that time zero and nothing else.

Parameters

NameDescription
irImpulse response measured with the omnidirectional microphone at the measurement point (1D), for its direct sound.
lateral_irImpulse response measured at the same point with a figure-of-eight microphone whose null points at the source (1D).
reference_irImpulse response of the same source at 10 m in a free field (1D). Mutually exclusive with reference_level.
fsSample rate in Hz. Required for bare arrays; a Signal brings its own.
reference_levelThe free-field reference exposure level in dB, as a scalar or one value per band. Mutually exclusive with reference_ir.
limits(f_min, f_max) band-centre limits in Hz; default the octave bands 125 Hz to 4 kHz. Equation (A.17) averages this quantity over 125 Hz to 1 kHz. None measures the broadband responses.
fractionBandwidth fraction (1 = octave, 3 = one-third octave).

Returns: A LateLateralResult with one entry per band.

Raises

ExceptionWhen
ValueErrorIf neither or both reference forms are given, if a response is not one-dimensional or is silent, if the two hall responses are of different lengths, or if they are too short to reach the 80 ms boundary.

Constant (float).

LATE_LATERAL_START_S = 0.08

Constant (tuple).

LATE_SUPPORT_WINDOW_S = (0.1, 1.0)
LateLateralResult(
frequencies: NDArray[np.float64] | None,
levels: NDArray[np.float64],
reference_level: NDArray[np.float64],
)

Per-band late lateral sound level (ISO 3382-1:2009, A.2.5).

frequencies holds the exact band centre frequencies in Hz, or is None for a broadband measurement. levels is in dB (Equation (A.16)) and reference_level the sound pressure exposure level of the free-field response at 10 m it is referred to, however that was obtained. Table A.1 gives a typical range of -14 dB to +1 dB over the 125 Hz to 1 kHz octave bands and no just-noticeable difference at all: “Not known”.

LateLateralResult.plot(
ax: Axes | None = None,
*,
language: str = 'en',
**kwargs: Any,
) -> Axes

Plot the per-band late lateral level against the Table A.1 range.

Requires matplotlib (pip install phonometry[plot]); returns the Axes.

Constant (tuple).

LATERAL_WEIGHTINGS = ('squared', 'cosine')
LateralEnergyResult(
frequencies: NDArray[np.float64] | None,
energy_fraction: NDArray[np.float64],
weighting: str,
)

Per-band early lateral energy fraction (ISO 3382-1:2009, A.2.4).

frequencies holds the exact band centre frequencies in Hz, or is None for a broadband measurement. energy_fraction is or depending on weighting, which is "squared" for Equation (A.14) and "cosine" for Equation (A.15). Both are dimensionless and Table A.1 gives them a just-noticeable difference of 0,05 and a typical range of 0,05 to 0,35 over the 125 Hz to 1 kHz octave bands.

LateralEnergyResult.plot(
ax: Axes | None = None,
*,
language: str = 'en',
**kwargs: Any,
) -> Axes

Plot the per-band lateral fraction against the Table A.1 range.

Requires matplotlib (pip install phonometry[plot]); returns the Axes.

Constant (mapping).

MAX_SOURCE_DIRECTIVITY_DEVIATION_DB = {125.0: 1.0, 250.0: 1.0, 500.0: 1.0, 1000.0: 3.0, 2000.0: 5.0, 4000.0: 6.0}

Constant (float).

MAXIMUM_DIRECTIVITY_STEP_DEG = 12.5

Constant (tuple).

MID_FREQUENCY_OCTAVES_HZ = (500.0, 1000.0)

Constant (tuple).

MID_FREQUENCY_THIRD_OCTAVES_HZ = (400.0, 500.0, 630.0, 800.0, 1000.0, 1250.0)
minimum_receiver_positions(seats: ArrayLike) -> NDArray[np.float64] | float

Fewest microphone positions a hall of a given size wants (Table A.2).

ISO 3382-1:2009, Table A.2 prints three pairs, 500 seats to 6 positions, 1 000 to 8 and 2 000 to 10. They lie exactly on a straight line in the logarithm of the seat count, two positions per doubling:

which reproduces all three printed integers to the last bit. A.4 caps what that line may be used for: it asks for “a minimum of between 6 and 10 representative microphone positions”, so the result is clamped to the range the table covers, and this function will not extrapolate a 5 000-seat arena to thirteen positions on the strength of three rows.

A.4 also says the positions are to be evenly distributed over all audience seating areas, and that a hall broken into separate areas such as balconies and under-balcony areas will need more than this.

Parameters

NameDescription
seatsThe number of seats in the hall.

Returns: The minimum number of positions, not rounded, clamped to the 6 to 10 the table and A.4 between them authorise.

Raises

ExceptionWhen
ValueErrorIf a seat count is not a positive, finite number.

Constant (float).

MINIMUM_REFERENCE_DISTANCE_M = 3.0
octave_pair_averages(
values: ArrayLike,
frequency: ArrayLike,
) -> dict[str, float]

The low, mid and high pair averages of ISO 3382-1:2009, A.5.

A more concise presentation than a full band table: the 125 Hz and 250 Hz results averaged into a low-frequency one, 500 Hz and 1 kHz into a mid-frequency one, and 2 kHz and 4 kHz into a high-frequency one, all arithmetically.

This is a different product from the single number of single_number_average, and the two must not be confused. They coincide for the five quantities Table A.1 averages over 500 Hz and 1 kHz, and they do not for the lateral ones, whose single number spans four bands. A.5 also warns that lateral energy fractions in the 4 kHz octave are not usually thought to be subjectively important, so the high pair means little for them.

Parameters

NameDescription
valuesPer-band values, one per entry of frequency.
frequencyThe band centre frequencies in Hz.

Returns: {"low": ..., "mid": ..., "high": ...} in the values’ unit.

Raises

ExceptionWhen
ValueErrorIf the two arrays disagree in length, or if the band axis does not carry all six octaves from 125 Hz to 4 kHz.

Constant (mapping).

OCTAVE_PAIRS_HZ = {'low': (125.0, 250.0), 'mid': (500.0, 1000.0), 'high': (2000.0, 4000.0)}
perceptibly_different(
symbol: str,
first: ArrayLike,
second: ArrayLike,
) -> bool

Whether two values of a quantity differ by its Table A.1 JND.

The comparison is absolute for every quantity but the early decay time, whose JND the table prints as “Rel. 5 %”, a fraction of the value rather than a difference in seconds. The late lateral sound level has no JND at all, printed as “Not known”, and this function refuses to invent one.

Parameters

NameDescription
symbolThe quantity, as TABLE_A1 keys it. "J_LFC" is taken too: Table A.1 prints the early lateral energy fraction as one row, “J_LF or J_LFC”, so both weightings of early_lateral_energy_fraction share its four bands, its just-noticeable difference and its typical range.
firstOne value, in the quantity’s unit.
secondThe other, of the same shape.

Returns: True when the two differ by at least the just-noticeable difference, so a listener could be expected to hear it.

Raises

ExceptionWhen
ValueErrorIf the symbol is not one Table A.1 prints, or if the table prints no just-noticeable difference for it.

Constant (float).

REFERENCE_DISTANCE_M = 10.0
reverberation_room_reference_level(
reverberation_room_level: ArrayLike,
absorption_area: ArrayLike,
) -> NDArray[np.float64] | float

Free-field reference level at 10 m from a reverberation-room measurement.

ISO 3382-1:2009, Equation (A.5):

with m². The route exists because a room 10 m across and anechoic is rarer than a reverberation room: the source is measured in the diffuse field instead, and the absorption area converts that reading into the free-field one. A follows from the room’s reverberation time through Sabine’s formula, Equation (A.6), which the library publishes as phonometry.room.sabine_absorption_area. Watch the constant when reproducing a hand calculation: (A.6) prints , which is at m/s, while that function defaults to 343 m/s and so to 0,1611. The difference moves by 0,030 dB; pass speed_of_sound=345.39 to get the printed constant back.

The printed 37 dB is dB rounded; see the module docstring for what that rounding costs.

(A.5) also carries no Waterhouse correction, unlike the reverberation-room sound power method of ISO 3741 that it otherwise mirrors. The omitted is worth over a decibel in the 125 Hz band of a small room, above the 1 dB just-noticeable difference Table A.1 gives G. That is a property of the printed method, and this function reproduces the method rather than quietly improving it.

Parameters

NameDescription
reverberation_room_levelSpatial-average sound pressure exposure level measured in the reverberation room, in dB. The standard calls this too, which is the same symbol it gave the level measured in the hall under test a page earlier; substituting (A.5) into (A.1) as printed would cancel the hall out of G altogether. The two roles get different names here, and docs/ERRATA.md carries the rest.
absorption_areaEquivalent sound absorption area of that room, in m², broadcast against reverberation_room_level.

Returns: The reference exposure level at 10 m, in dB.

Raises

ExceptionWhen
ValueErrorIf the absorption area is not a positive, finite area.
single_number_average(
symbol: str,
values: ArrayLike,
frequency: ArrayLike,
) -> float

The Table A.1 single number of one auditorium quantity.

Footnote a of ISO 3382-1:2009, Table A.1 makes this the arithmetical average over the octave bands the table names for that quantity, with one exception: the late lateral sound level, which late_lateral_average energy-averages through Equation (A.17). A.5 asks for the index “m” to be applied to the symbol of the result.

The band set is per quantity and is not always the mid pair. The sound strength, early decay time, clarity, definition and centre time average the 500 Hz and 1 kHz octaves; the lateral quantities average 125 Hz to 1 kHz, four bands. A.5 prints both cases as examples for exactly that reason, over two bands and over four, and an accessor that hard-codes the mid pair is wrong for half the table.

Parameters

NameDescription
symbolThe quantity, as TABLE_A1 keys it. "J_LFC" is taken too: Table A.1 prints the early lateral energy fraction as one row, “J_LF or J_LFC”, so both weightings of early_lateral_energy_fraction share its four bands, its just-noticeable difference and its typical range.
valuesIts per-band values, one per entry of frequency.
frequencyThe band centre frequencies in Hz, as a result carries them.

Returns: The single number, in the quantity’s own unit.

Raises

ExceptionWhen
ValueErrorIf the symbol is not one Table A.1 prints, if the two arrays disagree in length, or if the band axis does not carry every band the quantity averages.
sound_pressure_exposure_level(
ir: Signal | list[float] | NDArray[np.float64],
fs: int | None = None,
*,
limits: tuple[float, float] | None = (125.0, 4000.0),
fraction: int = 1,
) -> NDArray[np.float64] | float

Sound pressure exposure level of an impulse response, per band.

ISO 3382-1:2009, Equation (A.2):

with s and uPa. Time zero is the start of the direct sound (A.2.1), found per band with the A.3.4 trigger, and the integral runs to the end of the response supplied.

A.2.1 asks that end to lie at or beyond the point where the decay curve has fallen 30 dB. Whether a room response reaches it is visible in the dynamic_range of phonometry.room.room_parameters, and sound_strength raises AuditoriumWarning when it does not; this function evaluates whatever it is given, because the same equation is (A.3) applied to a free-field response, which has no reverberant decay to reach 30 dB of.

This is the quantity Equations (A.3) to (A.5) also operate on: the same function applied to the free-field response at 10 m gives .

Parameters

NameDescription
irMeasured impulse response (1D). A phonometry.io.Signal brings its calibration, which is applied; a bare array is read as pascals, so its exposure level is only referenced to 20 uPa if the samples already are.
fsSample rate in Hz. Required for a bare array; a Signal brings its own, and an explicit value that disagrees with it raises instead of silently winning.
limits(f_min, f_max) band-centre limits in Hz; default the octave bands 125 Hz to 4 kHz (ISO 3382-1:2009, 5.1). None integrates the broadband response as a single band.
fractionBandwidth fraction (1 = octave, 3 = one-third octave).

Returns: The exposure level in dB, one entry per band, or a float for a broadband response.

Raises

ExceptionWhen
ValueErrorIf the response is not one-dimensional or is silent, if limits is malformed, or if a band has no energy.
sound_strength(
ir: Signal | list[float] | NDArray[np.float64],
reference_ir: Signal | list[float] | NDArray[np.float64] | None = None,
fs: int | None = None,
*,
reference_level: ArrayLike | None = None,
limits: tuple[float, float] | None = (125.0, 4000.0),
fraction: int = 1,
) -> SoundStrengthResult

Sound strength G of a measured impulse response, per band.

ISO 3382-1:2009, Equation (A.1):

Equation (A.7) is the same difference between stationary-source levels rather than exposure levels, which is why this function accepts the reference either as a second impulse response or as a level already in hand: exactly one of reference_ir and reference_level is required. A level obtained from free_field_reference_level, reverberation_room_reference_level or, through sound_strength_from_power, from the source’s power level, goes in the second slot.

Both responses are split into the same bands and each is integrated from its own direct sound, so a common gain on the pair cancels exactly. A gain applied to only one of them does not: the two recordings must share a calibration, which is the whole reason G needs a calibrated source where every other measure in Table A.1 does not.

Parameters

NameDescription
irImpulse response measured in the room under test (1D).
reference_irImpulse response of the same source at 10 m in a free field (1D). Mutually exclusive with reference_level.
fsSample rate in Hz. Required for bare arrays; a Signal brings its own.
reference_levelThe free-field reference exposure level in dB, as a scalar or one value per band. Mutually exclusive with reference_ir.
limits(f_min, f_max) band-centre limits in Hz; default the octave bands 125 Hz to 4 kHz (ISO 3382-1:2009, 5.1). None measures the broadband response as a single band.
fractionBandwidth fraction (1 = octave, 3 = one-third octave).

Returns: A SoundStrengthResult with one entry per band.

Raises

ExceptionWhen
ValueErrorIf neither or both reference forms are given, if a response is not one-dimensional or is silent, or if reference_level does not broadcast onto the band axis.
sound_strength_from_power(
pressure_level: ArrayLike,
power_level: ArrayLike,
) -> NDArray[np.float64] | float

Sound strength from the source’s sound power level.

ISO 3382-1:2009, Equation (A.9):

The third route to G, and the only one that needs no free-field measurement at all: the 31 dB is the spread of a point source over the sphere of radius 10 m, dB, rounded. A.2.1 asks for the source’s power level to be measured to ISO 3741, which the library implements in phonometry.emission.sound_power.

Parameters

NameDescription
pressure_levelSound pressure level at the measurement point in the room under test, in dB.
power_levelSound power level of the source, in dB, broadcast against pressure_level.

Returns: The sound strength G, in dB.

Constant (float).

SOUND_STRENGTH_POWER_OFFSET_DB = 31.0
SoundStrengthResult(
frequencies: NDArray[np.float64] | None,
strength: NDArray[np.float64],
exposure_level: NDArray[np.float64],
reference_level: NDArray[np.float64],
)

Per-band sound strength G and the two levels it is the difference of.

frequencies holds the exact band centre frequencies in Hz, or is None for a broadband measurement, in which case every array has length 1. strength is G in dB (ISO 3382-1:2009, Equation (A.1)), exposure_level the sound pressure exposure level of the response measured in the room (Equation (A.2)) and reference_level that of the free-field response at 10 m (Equation (A.3)), however it was obtained. All three are in decibels and strength is exactly exposure_level - reference_level.

SoundStrengthResult.plot(
ax: Axes | None = None,
*,
language: str = 'en',
**kwargs: Any,
) -> Axes | np.ndarray

Plot the per-band sound strength against the Table A.1 range.

With ax given, only the strength panel is drawn on it; otherwise a second panel shows the two levels G is the difference of. Requires matplotlib (pip install phonometry[plot]); returns the Axes (or an array of two).

source_directivity_limit(centre: float) -> float

The Table 1 limit for one octave band, in dB.

Parameters

NameDescription
centreNominal octave band centre frequency in Hz.

Returns: The maximum deviation from omnidirectionality the table prints.

Raises

ExceptionWhen
ValueErrorIf Table 1 has no row for that band. It prints six, 125 Hz to 4 kHz, and a survey in the 63 Hz or 8 kHz octave has no printed limit to be held to rather than the nearest one.

Constant (tuple).

STAGE_DIRECT_WINDOW_S = (0.0, 0.01)
stage_support(
ir: Signal | list[float] | NDArray[np.float64],
fs: int | None = None,
*,
limits: tuple[float, float] | None = (250.0, 2000.0),
fraction: int = 1,
) -> StageSupportResult

Early and late stage support of a platform response, per band.

ISO 3382-1:2009, Equations (C.1) and (C.2), the reflected energy a musician hears from the platform against the direct sound of their own instrument:

Both are measured with the source and the microphone 1,0 m apart on the platform, at the same height, with nothing reflecting within 2 m (C.2.1 and C.2.3). Because they share a denominator, their difference is a property of the hall alone and does not depend on how loud the direct sound was.

Two limits are in the equations and not in the prose. C.2.1 describes the early support as “the reflected energy within the first 0,1 s”, but (C.1) starts at 20 ms, discarding the 10 ms to 20 ms interval; C.2.2 describes the late support as “the reflected energy after the first 0,1 s” with no upper bound, but (C.2) stops at one second, which matters in any hall whose reverberation time is longer than that. The equations govern, and docs/ERRATA.md records the rest.

Parameters

NameDescription
irImpulse response measured on the platform (1D), 1,0 m from the acoustic centre of an omnidirectional source.
fsSample rate in Hz. Required for a bare array; a Signal brings its own.
limits(f_min, f_max) band-centre limits in Hz; default the 250 Hz to 2 kHz octave bands C.2.4 averages over. None measures the broadband response.
fractionBandwidth fraction (1 = octave, 3 = one-third octave). C.2.4 asks for octave bands.

Returns: A StageSupportResult with one entry per band.

Raises

ExceptionWhen
ValueErrorIf the response is not one-dimensional or is silent, if a band has no energy, or if the response is shorter than the one second Equation (C.2) integrates to.

Constant (tuple).

STAGE_SUPPORT_BANDS_HZ = (250.0, 500.0, 1000.0, 2000.0)

Constant (float).

STAGE_SUPPORT_DISTANCE_M = 1.0

Constant (tuple).

STAGE_SUPPORT_HEIGHTS_M = (1.0, 1.5)

Constant (int).

STAGE_SUPPORT_POSITIONS = 3

STAGE_SUPPORT_SINGLE_NUMBER_STANDARD_DEVIATION_DB

Section titled “STAGE_SUPPORT_SINGLE_NUMBER_STANDARD_DEVIATION_DB”

Constant (float).

STAGE_SUPPORT_SINGLE_NUMBER_STANDARD_DEVIATION_DB = 0.3

Constant (float).

STAGE_SUPPORT_STANDARD_DEVIATION_DB = 1.0
StageSupportResult(
frequencies: NDArray[np.float64] | None,
early: NDArray[np.float64],
late: NDArray[np.float64],
)

Per-band stage support (ISO 3382-1:2009, Annex C).

frequencies holds the exact band centre frequencies in Hz, or is None for a broadband measurement. early is in dB (Equation (C.1)) and late in dB (Equation (C.2)), both referred to the same direct sound. Table C.1 gives their typical ranges as -24 dB to -8 dB and -24 dB to -10 dB and prints “Not known” for both just-noticeable differences, so this module has none.

StageSupportResult.plot(
ax: Axes | None = None,
*,
language: str = 'en',
**kwargs: Any,
) -> Axes

Plot both supports per band against the Table C.1 ranges.

Requires matplotlib (pip install phonometry[plot]); returns the Axes.

Constant (mapping).

Constant (tuple).

TEST_REPORT_ITEMS = ('a statement that the measurements were made in conformity with ISO 3382-1', 'name and place of the room tested', 'sketch plan of the room, with an indication of the scale', 'volume of the room, with an explanation of how it is defined if the room is not completely enclosed', 'for rooms for speech and music, the number and type of seats, their upholstery and covering, and which parts of the seat are covered', 'a description of the shape and material of the walls and the ceiling', 'state or states of occupancy during measurements, and the number of occupants', 'condition of any variable equipment, such as curtains, public-address or electronic reverberation enhancement systems', 'for theatres, whether the safety curtain or decorative curtains were up or down', 'description, where appropriate, of the stage furnishing, including any concert enclosure', 'temperature and relative humidity in the room during the measurement', 'description of measuring apparatus, source and microphones, and whether tape recorders were employed', 'description of the sound signal used', 'coverage chosen, including the source and microphone positions and their heights, preferably shown on a plan', 'date of measurement and name of the measuring organization')