HRTF¶
- class hrtfpykit.hrtf.HRTF(Sofa=None)¶
Bases:
objectRepresent an HRTF or HRIR object loaded from SOFA data.
HRTFis the main in-memory object used to inspect, subset, transform, synchronize, and save HRTF/HRIR data loaded from SOFA files. It supports the SimpleFreeFieldHRIR and SimpleFreeFieldHRTF conventions and keeps both acoustic representations available:The object stores acoustic arrays separately from the backing
SOFAobject. Transformations and selections operate on the in-memory arrays first; SOFA variables are updated only whenupdate_sofa()orsave()is called. This makes processing workflows explicit and prevents intermediate edits from being written to the file representation automatically.Spatial metadata and source-grid operations are exposed through
Sources. That object resolves SOFASourcePositiondata, named directions, coordinate-system conversion, and plane-based queries used by processing, metrics, dataset extraction, andhrtfpykit.plotsfunctions.The object stores the optional SOFA backing object and starts with empty metadata fields. Domain interface objects such as
IR,TF,Sources, andTransformare created lazily when their properties are first accessed. Raw SOFA variables are not parsed and missing domains are not derived here;load_hrtf()performs that loading and synchronization work.Notes
A typical workflow is to load an object with
load_hrtf(), inspect or subset data withSourcesandselect(), apply transforms throughtransform, visualize by passing the object tohrtfpykit.plotsfunctions, then synchronize and export withupdate_sofa()andsave().The instance keeps a reference to the backing
SOFAobject inSofa. Transformation state is tracked internally; selected source subsets are tracked separately bySources. Mesh2HRTF compatible loading state is also stored so reset and TF-domain workflows can reconstruct HRIR data with the same convention used at load time.- Parameters:
Sofa (
SOFA| None, default=None) –SOFAobject that backs the HRTF instance. When None, the object is created empty and should be populated later.
- Sofa¶
Backing
SOFAobject used for source metadata, persistence, and SOFA synchronization.- Type:
SOFAor None
- SOFAConventions¶
Active SOFA convention associated with the loaded or constructed HRTF object.
- Type:
str or None
- fft_length¶
FFT length used when synchronizing between IR and TF representations.
- Type:
int or None
- mesh2hrtf_compatible¶
Whether SimpleFreeFieldHRTF data should use Mesh2HRTF compatible TF-to-IR reconstruction.
- Type:
bool
- mesh2hrtf_n_shift¶
Circular shift in samples used when
mesh2hrtf_compatibleis True.- Type:
int or None
- _transformed¶
Internal flag indicating whether the in-memory acoustic data were produced by a transform workflow.
- Type:
bool
- property IR: IR¶
Access the time-domain HRIR representation object.
This object stores
IR.valuesandIR.sample_ratefor the parentHRTFobject and exposes time-domain inspection helpers such as sample length and duration.Examples
Load a SOFA file and access the HRIR samples, sample-rate metadata, signal length, and duration through
hrtf.IR, then compute ITD with the public metric:>>> from hrtfpykit.hrtf import load_hrtf, itd >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> ir_values = hrtf.IR.values >>> sample_rate = hrtf.IR.sample_rate >>> first_position_left_ir = hrtf.IR.values[0, 0, :] >>> itd_samples = itd(hrtf, output="samples") >>> ir_values.shape (793, 2, 256) >>> sample_rate 44100.0 >>> first_position_left_ir.shape (256,) >>> hrtf.IR.ir_length 256 >>> round(hrtf.IR.ir_duration, 3) 5.805 >>> itd_samples.shape (793,)
- property TF: TF¶
Access the frequency-domain HRTF representation object.
This object stores complex
TF.valuesandTF.frequency_binsfor the parent object and exposes derived magnitude, phase, real, and imaginary views used by transforms, metrics, and plots.Examples
Load a SOFA file and access the frequency-domain HRTF values, frequency axis, bin metadata, and derived spectral arrays through
hrtf.TF:>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.TF.values.shape (793, 2, 129) >>> hrtf.TF.values.dtype dtype('complex128') >>> hrtf.TF.frequency_bins[:5] array([ 0. , 172.265625, 344.53125 , 516.796875, 689.0625 ]) >>> hrtf.TF.tf_length 129 >>> hrtf.TF.frequency_bins_step 172.265625 >>> hrtf.TF.min_frequency_bin 0.0 >>> hrtf.TF.max_frequency_bin 22050.0 >>> hrtf.TF.magnitude.shape (793, 2, 129) >>> hrtf.TF.get_magnitude_db().shape (793, 2, 129) >>> hrtf.TF.phase.shape (793, 2, 129) >>> hrtf.TF.real.shape (793, 2, 129) >>> hrtf.TF.imag.shape (793, 2, 129)
- property Sources: Sources¶
Access the spatial source-grid object.
Sourcesreads SOFASourcePositiondata, converts between the supported coordinate systems, resolves named positions, and tracks selected source indices after spatial subsetting.Examples
Load a SOFA file and access source-grid positions, coordinate-system metadata, available angles, nearest-position matches, and selected source views through
hrtf.Sources:>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.Sources.source_coordinate_system 'spherical' >>> hrtf.Sources.get_positions().shape (793, 3) >>> hrtf.Sources.get_positions()[:3] array([[ 0. , -45. , 1.5], [ 0. , -30. , 1.5], [ 0. , -20. , 1.5]]) >>> hrtf.Sources.get_positions(angle_unit="radians")[0] array([ 0. , -0.78539816, 1.5 ]) >>> hrtf.Sources.get_azimuth_angles()[:5] array([ 0., 5., 10., 15., 20.]) >>> hrtf.Sources.get_elevation_angles() array([-45., -30., -20., -10., 0., 10., 20., 30., 45., 60., 75., 90.]) >>> elevations_at_front, real_azimuth = ( ... hrtf.Sources.get_elevation_angles_for_azimuth(0.0) ... ) >>> elevations_at_front[:5] array([-45., -30., -20., -10., 0.]) >>> real_azimuth 0.0 >>> azimuths_on_horizontal, real_elevation = ( ... hrtf.Sources.get_azimuth_angles_for_elevation(0.0) ... ) >>> azimuths_on_horizontal[:5] array([ 0., 5., 10., 15., 20.]) >>> real_elevation 0.0 >>> hrtf.Sources.get_position_index("front") (4, array([0. , 0. , 1.5])) >>> selected = hrtf.select(positions=["front", "left", "right"]) >>> selected.Sources.get_positions().shape (3, 3)
- property transform: Transform¶
Access the immutable transformation interface for this HRTF.
Methods on this object clone the current HRTF, apply one processing operation, synchronize the affected IR or TF representation, and return the derived HRTF without mutating the original instance.
- clone()¶
Create a deep clone of the current
HRTFobject.The clone receives copied IR and TF arrays, sample-rate and frequency-bin metadata, FFT length, Mesh2HRTF compatible reconstruction settings, transformation state, and source selection state. If the source object is backed by an open SOFA dataset, the clone receives an independent in-memory SOFA clone. If the source SOFA dataset is closed, the clone keeps a closed SOFA object and uses the copied in-memory IR, TF, and Sources data for runtime operations.
- Returns:
New object with copied acoustic arrays, domain metadata, source selection state, SOFA convention metadata, Mesh2HRTF compatible loading state, transformation flag, and matching SOFA open/closed state.
- Return type:
Examples
Clone a loaded HRTF before changing array values so the original object remains unchanged:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf_copy = hrtf.clone() >>> hrtf_copy.IR.values[0, 0, 0] += 1.0 >>> hrtf_copy.IR.values[0, 0, 0] == hrtf.IR.values[0, 0, 0] False
- reset()¶
Reset in-memory HRTF data to the backed SOFA content.
This method discards current in-memory acoustic edits and reloads the active domain data from
Sofa. The backed SOFA dataset must be open. HRIR files are restored fromData.IRandData.SamplingRateand then converted to TF. HRTF files are restored fromData.Real,Data.Imag, andNand then converted to IR. If the object was loaded with Mesh2HRTF compatible reconstruction, reset uses the same compatibility flag and sample shift. Source selections are cleared when theSourcesobject has already been initialized.- Returns:
Current instance after restoring IR/TF, source-state, and metadata from the backed
SOFAobject while keeping the stored Mesh2HRTF compatible reconstruction settings.- Return type:
- Raises:
ValueError – If no SOFA file is attached, the SOFA file is not loaded, the SOFA dataset is closed, the convention is unsupported, or required acoustic variables are missing or empty.
Examples
Restore a selected HRTF object from its backed SOFA content:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> selected = hrtf.select(positions=["front", "left", "right"]) >>> selected.IR.values.shape (3, 2, 256) >>> restored = selected.reset() >>> restored.IR.values.shape (793, 2, 256) >>> restored.is_transformed() False
- is_transformed()¶
Return the current HRTF transformation flag.
The flag is set by transform workflows that modify acoustic data. It is a workflow state indicator, not a byte-by-byte comparison between in-memory arrays and the backing SOFA object. Source selection is tracked separately on
Sourcesand is handled independently byupdate_sofa().- Returns:
True if a transform workflow has marked the object as transformed; False otherwise.
- Return type:
bool
Examples
Check whether a transform returned a derived HRTF while the original object stayed unchanged:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> windowed = hrtf.transform.apply_window("hann") >>> hrtf.is_transformed() False >>> windowed.is_transformed() True
- update_sofa(change_sofa_dimensions=False, sofa_convention='same')¶
Synchronize in-memory IR/TF data into the backed
SOFAobject.The method converts the current acoustic representation into the requested SOFA convention and writes the corresponding SOFA variables on a cloned or updated
SOFAobject. It updates HRIR output throughData.IRandData.SamplingRate; HRTF output throughData.Real,Data.Imag, andN. Obsolete variables from the opposite convention are removed when the output convention changes. If a SimpleFreeFieldHRTF file omitted the DC bin at load time, synchronization writes the normalized TF with the inserted DC bin.Dimension handling is conservative. If transformed data no longer fit existing SOFA dimensions, synchronization raises unless
change_sofa_dimensions=True. Source selections are written as realMdimension changes, including single-source selections withM=1. Ear-selected HRTFs stay valid in memory, but SOFA synchronization requires both left and right receiver channels. When resizing is allowed, supported dependent variables on the measurement axis are subset with the current source selection; unsupported dependent variables still raise explicit errors to avoid silently corrupting SOFA structure.The method updates the in-memory
Sofaobject only and requires its backing SOFA dataset to be open. Usesave()to persist the synchronized SOFA object to disk. When TF values must be converted back to IR during synchronization, the stored Mesh2HRTF compatible reconstruction settings are reused.- Parameters:
change_sofa_dimensions (bool, default=False) – If True, allows resizing fixed SOFA dimensions when transformed data shape differs from backed variables.
sofa_convention ({
same,SimpleFreeFieldHRIR,SimpleFreeFieldHRTF}, default=``same``) – Output SOFA convention to enforce during synchronization.samekeeps the original backed SOFA convention.
- Returns:
This method updates
Sofain-place and does not return data.- Return type:
None
- Raises:
ValueError – If no SOFA file is loaded, the SOFA dataset is closed, the convention is unsupported, required domain values are missing, transformed shapes cannot be represented by the SOFA dimensions, or requested dimension changes would affect variables that the method cannot update safely.
Examples
Synchronize a selected source subset into the backed SOFA object before saving or inspecting SOFA variables:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> selected = hrtf.select(positions=["front", "left", "right"]) >>> selected.update_sofa(change_sofa_dimensions=True) >>> source_positions = selected.Sofa.Variables.get("SourcePosition").value >>> source_positions.shape (3, 3)
- save(path=None, overwrite=False, change_sofa_dimensions=False, sofa_convention='same')¶
Update SOFA variables and save to disk.
This is the persistence endpoint for the HRTF workflow. It first calls
update_sofa()so the backedSOFAobject reflects the current in-memory IR/TF state and requested convention, then delegates the disk write tosave(). The backed SOFA dataset must be open.- Parameters:
path (str | Path | None, default=None) – Output file path. If None, the current backed SOFA path is used by the underlying
SOFAobject.overwrite (bool, default=False) – If True, allows overwriting an existing file.
change_sofa_dimensions (bool, default=False) – Forwarded to
update_sofa()to control SOFA dimension resizing.sofa_convention ({
same,SimpleFreeFieldHRIR,SimpleFreeFieldHRTF}, default=``same``) – Forwarded toupdate_sofa()to select output convention.
- Returns:
Path to the saved SOFA file.
- Return type:
Path
- Raises:
ValueError – If no SOFA file is attached, the SOFA dataset is closed, or synchronization fails.
FileExistsError – If the target path already exists and overwrite=False.
Examples
Save a processed HRTF to a new SOFA file using a relative output path:
>>> from pathlib import Path >>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> windowed = hrtf.transform.apply_window("hann") >>> output_dir = Path("processed") >>> output_dir.mkdir(exist_ok=True) >>> saved_path = windowed.save( ... output_dir / "P0001_windowed.sofa", ... overwrite=True, ... ) >>> saved_path.name 'P0001_windowed.sofa'
- select(positions=None, position_coordinate_system='spherical', plane=None, plane_angle=0.0, azimuth_angles=None, elevation_angles=None, ear='both', angle_unit='degrees', start_sample=None, end_sample=None)¶
Select a spatial subset, ear subset, and/or IR crop from the HRTF.
Selection returns a cloned
HRTFobject and leaves the original object unchanged. Spatial selection can be expressed with explicit positions, named positions, a geometric plane, or azimuth/elevation angle filters. These source-selection modes are mutually exclusive:positionscannot be combined withplaneor angle filters, andplanecannot be combined with angle filters.azimuth_anglesandelevation_anglesmay be used together; when both are provided, selected sources must satisfy both filters. Source selections are applied along the leading source axis of bothIR.valuesandTF.valueswhen those domains are available.Ear selection keeps both ears by default. Selecting
leftorrightremoves the ear axis entry from available IR and TF arrays. IR cropping is applied along the final sample axis and automatically recomputes the TF representation from the cropped IR using the cropped IR length as the FFT length.- Parameters:
positions (np.ndarray | list[list[float]] | list[float] | None, default=None) – Explicit positions or named aliases to select. Named positions use the source-grid aliases such as
front,back,left, andright. Numeric positions are interpreted in position_coordinate_system.position_coordinate_system ({
spherical,cartesian,lateral-polar}, default=``spherical``) – Coordinate system used by numeric positions queries.plane (str | None, default=None) – Plane name to filter positions. Supported values are horizontal, median, and frontal.
plane_angle (float, default=0.0) – Plane coordinate used to resolve the nearest available plane. For
plane="horizontal"this is spherical elevation. Forplane="median"this is lateral-polar lateral angle. Forplane="frontal"this is spherical azimuth.azimuth_angles (float, sequence of float, numpy.ndarray, or None, default=None) – Azimuth angle or angles used to keep matching source positions. Requested values resolve to the nearest available source-grid azimuth in spherical coordinates and may be combined only with elevation_angles.
elevation_angles (float, sequence of float, numpy.ndarray, or None, default=None) – Elevation angle or angles used to keep matching source positions. Requested values resolve to the nearest available source-grid elevation in spherical coordinates and may be combined only with azimuth_angles.
ear ({
both,left,right}, default=``both``) – Ear selection.angle_unit ({
degrees,radians}, default=``degrees``) – Angle unit used for spatial queries and plane angles.start_sample (int | None, default=None) – IR crop start sample index.
end_sample (int | None, default=None) – IR crop end sample index.
- Returns:
New
HRTFobject containing the selected subset.- Return type:
- Raises:
ValueError – If the requested selection is invalid, no positions remain after filtering, more than one source-selection mode is requested, the requested ear is unavailable, crop boundaries are invalid, or IR data are unavailable for cropping.
Examples
Select three named directions, keep only the left ear, and crop the HRIR samples used in the returned HRTF:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.IR.values.shape (793, 2, 256) >>> selected = hrtf.select( ... positions=["front", "left", "right"], ... ear="left", ... start_sample=0, ... end_sample=128, ... ) >>> selected.IR.values.shape (3, 128) >>> selected.TF.values.shape (3, 65) >>> selected.fft_length 128
Composed interfaces¶
HRTF keeps the loaded HRTF/HRIR state in one object
and exposes related data and operations through composed interface properties.
In normal use, users do not create those interface objects directly. They load
or receive an HRTF instance and access each working surface from that instance:
IR for the IR
object with time-domain HRIR values, sample-rate metadata,
ir_duration; use itd()
for interaural time difference metrics, ild() for
interaural level difference metrics, and rms() for RMS
levels. TF for
the TF object with frequency-domain HRTF values,
frequency bins, magnitude,
phase, real,
and imag; Sources
for the Sources object with
get_positions(), coordinate conversion,
named position queries, and selection state; and transform
for the Transform object with immutable
processing operations.
Plotting is intentionally separate from the core HRTF class. Use
hrtfpykit.plots functions such as plot_magnitude()
or plot_source_grid() and pass the loaded HRTF object as
the first argument. This keeps visualization in the plotting layer while still
using the current IR, TF, and source-grid state of the object.
- class hrtfpykit.hrtf.domain.IR(hrtf)¶
Represent the time-domain HRIR view owned by an
HRTFobject.IRstores the HRIR sample array and sample-rate metadata used by the parentHRTFabstraction. It is created lazily byIRand acts as the in-memory time-domain view of SOFAData.IRdata or data reconstructed from SimpleFreeFieldHRTF frequency-domain files.The object does not own independent source metadata. Its leading axes are expected to stay aligned with
Sourcesand with the siblingTFrepresentation. Time-domain transforms updateIR.valuesandIR.sample_ratefirst, then synchronizeTF.valuesandTF.frequency_binsthrough FFT helpers.- values¶
Time-domain impulse-response values. Standard HRTF data use layout (positions, ears, samples), but methods generally treat any leading axes before the final sample axis as preserved metadata axes.
- Type:
numpy.ndarray or None
- sample_rate¶
Sampling rate in hertz for the impulse-response data.
- Type:
float or None
Examples
Load an HRIR-backed SOFA file and inspect the time-domain array exposed by the HRTF object:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.IR.values.shape (793, 2, 256) >>> hrtf.IR.sample_rate 44100.0
- property ir_length: int¶
Return the number of HRIR samples along the final axis.
The value is derived from the final axis of
IR.valuesand therefore reflects the current in-memory time-domain representation, including any padding, resampling, or replacement performed throughtransform.- Returns:
Number of samples on the final axis of
IR.values.- Return type:
int
- Raises:
AttributeError – If
IR.valuesis None.
Examples
Load an HRTF and read the number of samples in each HRIR:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.IR.ir_length 256
- property ir_duration: float¶
Return the current HRIR duration in milliseconds.
Duration is computed from the sample count and
IR.sample_rateusing the shared DSP duration helper. Leading source and ear axes do not affect the result; only the final sample axis is used.- Returns:
Duration in milliseconds, computed from
IR.valuesandIR.sample_rate.- Return type:
float
- Raises:
ValueError – If
IR.valuesis missing, not a NumPy array, has no sample axis, orIR.sample_rateis missing or not finite and positive.
Examples
Load an HRTF and compute the duration represented by each HRIR:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> round(hrtf.IR.ir_duration, 3) 5.805
- class hrtfpykit.hrtf.domain.TF(hrtf)¶
Represent the frequency-domain HRTF view owned by an
HRTFobject.TFstores the complex HRTF frequency-response array and its frequency bins for the parentHRTFabstraction. It is created lazily byTFand acts as the in-memory frequency-domain view of SOFAData.RealandData.Imagdata or data computed from HRIR files.The object is expected to stay aligned with the sibling
IRandSourcesrepresentations. Frequency-domain transforms updateTF.valuesandTF.frequency_binsfirst, then synchronizeIR.valuesandIR.sample_ratethrough inverse FFT helpers.- values¶
Complex frequency-domain transfer-function values. Standard HRTF data use layout (positions, ears, frequency_bins), but derived properties preserve any leading axes before the final frequency axis.
- Type:
numpy.ndarray or None
- frequency_bins¶
One-dimensional frequency-bin values in hertz corresponding to the final axis of
TF.values.- Type:
numpy.ndarray or None
Examples
Load an HRIR SOFA file and inspect the derived frequency-domain view:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.TF.values.shape (793, 2, 129) >>> hrtf.TF.frequency_bins.shape (129,)
- property tf_length: int¶
Return the number of HRTF frequency bins along the final axis.
The value is derived from the final axis of
TF.valuesand corresponds to the current one-sided frequency-domain representation used by theHRTFobject.- Returns:
Number of frequency bins on the final axis of
TF.values.- Return type:
int
- Raises:
AttributeError – If
TF.valuesis None.
Examples
Read the number of one-sided frequency bins in the current HRTF:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.TF.tf_length 129
- property frequency_bins_step: float | None¶
Return the frequency-bin spacing in hertz when it is uniform.
The method compares consecutive entries in
TF.frequency_bins. It returns the common spacing for uniformly sampled spectra and None when the bins are not uniformly spaced.- Returns:
Uniform spacing in hertz, or None when consecutive frequency differences are not equal within the local tolerance.
- Return type:
float | None
- Raises:
ValueError – If
TF.frequency_binsis None.IndexError – If fewer than two frequency bins are available.
Examples
Inspect the spacing of a uniformly sampled one-sided frequency grid:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> round(hrtf.TF.frequency_bins_step, 3) 172.266
- property min_frequency_bin: float¶
Return the minimum available frequency bin in hertz.
This is normally 0 Hz for one-sided spectra loaded from HRIR data, but it reflects the current
TF.frequency_binsarray exactly.- Returns:
Minimum value in
TF.frequency_bins.- Return type:
float
- Raises:
TypeError – If
TF.frequency_binsis None.ValueError – If
TF.frequency_binsis empty.
Examples
Read the lowest available frequency bin:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.TF.min_frequency_bin 0.0
- property max_frequency_bin: float¶
Return the maximum available frequency bin in hertz.
For uniformly sampled one-sided spectra, this usually corresponds to the Nyquist frequency implied by the IR sample rate and FFT length.
- Returns:
Maximum value in
TF.frequency_bins.- Return type:
float
- Raises:
TypeError – If
TF.frequency_binsis None.ValueError – If
TF.frequency_binsis empty.
Examples
Read the highest available frequency bin:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.TF.max_frequency_bin 22050.0
- property magnitude: ndarray¶
Return the linear magnitude of the complex HRTF values.
The result has the same source, ear, and frequency-bin layout as
TF.valuesand is computed through the shared DSP magnitude helper.- Returns:
Linear magnitude values with the same shape as
TF.values.- Return type:
numpy.ndarray
- Raises:
ValueError – If
TF.valuesis missing or is not a NumPy array.
Examples
Compute linear magnitude without modifying the complex transfer function:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> magnitude_values = hrtf.TF.magnitude >>> magnitude_values.shape (793, 2, 129)
- get_magnitude_db(reference=1.0)¶
Return TF magnitude in decibels.
This method computes the linear magnitude of
TF.valuesand converts it with the shared dB conversion helper. It is the domain-object convenience API used by plotting and spectral inspection workflows.- Parameters:
reference (float | {
max}, default=1.0) – Positive reference magnitude used for 20 * log10(magnitude / reference). The special valuemaxnormalizes to the maximum magnitude present inTF.values.- Returns:
Magnitude values in decibels with the same shape as
TF.values.- Return type:
numpy.ndarray
- Raises:
ValueError – If
TF.valuesis missing or invalid, if a magnitude is invalid, or if reference is not accepted by the dB conversion helper.
Examples
Convert the current transfer-function magnitude to decibels:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> magnitude_db = hrtf.TF.get_magnitude_db() >>> magnitude_db.shape (793, 2, 129)
- property phase: ndarray¶
Return the phase of the complex HRTF values in degrees.
The result has the same shape as
TF.valuesand uses the library phase helper so phase handling is consistent with transform methods.- Returns:
Phase values in degrees with the same shape as
TF.values.- Return type:
numpy.ndarray
- Raises:
ValueError – If
TF.valuesis missing or is not a NumPy array.
Examples
Inspect phase values for every source, ear, and frequency bin:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> phase_values = hrtf.TF.phase >>> phase_values.shape (793, 2, 129)
- property real: ndarray¶
Return the real component of the complex HRTF values.
This property exposes
Data.Real-style values for the current frequency-domain representation without modifying the parent HRTF.- Returns:
Real component of
TF.valueswith the same shape.- Return type:
numpy.ndarray
- Raises:
ValueError – If
TF.valuesis missing or is not a NumPy array.
Examples
Access the SOFA-style real component of the complex HRTF:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> real_values = hrtf.TF.real >>> real_values.shape (793, 2, 129)
- property imag: ndarray¶
Return the imaginary component of the complex HRTF values.
This property exposes
Data.Imag-style values for the current frequency-domain representation without modifying the parent HRTF.- Returns:
Imaginary component of
TF.valueswith the same shape.- Return type:
numpy.ndarray
- Raises:
ValueError – If
TF.valuesis missing or is not a NumPy array.
Examples
Access the SOFA-style imaginary component of the complex HRTF:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> imag_values = hrtf.TF.imag >>> imag_values.shape (793, 2, 129)
- class hrtfpykit.hrtf.sources.Sources(hrtf=None)¶
Manage source positions, coordinate systems, and spatial selections.
Sourcesis the source-position manager used byHRTF. It stores an in-memory copy of SOFASourcePositionvalues and theirSourcePosition:TypeandSourcePosition:Unitsattributes, converts positions on demand, and resolves source-grid queries used by selection, metrics, spherical harmonics, and plotting utilities.The manager stores the target coordinate system in
source_coordinate_system. Changing that value changes howget_positions()and query methods expose positions; it does not rewrite the stored SOFASourcePositionarray by itself. Spatial subsets created throughselect()are also respected, so returned arrays and matched indices refer to the current HRTF view rather than necessarily to every source in the original SOFA file.Notes
Conventions implemented by this class:
Spherical (SOFA-style): (azimuth, elevation, radius) with azimuth in [0, 360) degrees (anticlockwise in horizontal plane), elevation in [-90, 90] degrees (positive up), and non-negative radius.
Lateral-polar: (lateral, polar, radius) with lateral in [-90, 90] degrees (positive left), polar normalized to [-90, 270) degrees, and non-negative radius.
Cartesian: (x, y, z) with +y as left and +z as up.
At lateral poles (abs(lateral) == 90) and at zero radius, polar is singular. This implementation uses a deterministic placeholder polar = 0.
- Parameters:
hrtf (
HRTF| None, default=None) – Owning HRTF instance. Most user code obtains this object fromSources. When provided, the HRTF must have a loadedSOFAobject containingSourcePositionmetadata.
- source_coordinate_system¶
Target coordinate system used by source-grid query methods.
- Type:
str
- _selected_indices¶
Source-position indices retained by the current HRTF view after spatial selection.
- Type:
numpy.ndarray or None
Examples
Load an HRTF and inspect the source manager exposed by the HRTF object:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.Sources.source_coordinate_system 'spherical' >>> hrtf.Sources.get_positions().shape (793, 3)
- get_positions(angle_unit='degrees', coordinate_system='spherical', plane=None, plane_angle=None)¶
Return the current source grid in the requested coordinate system.
Positions are read from the in-memory
SourcePositionsnapshot stored on this object. The source coordinate system is taken fromSourcePosition:Typeand converted on read. By default, positions are returned in spherical coordinates. If the owning HRTF has been spatially selected, only the selected source rows are returned. Ifplaneis provided, hrtfpykit first resolves the nearest measured plane, keeps only those source rows, and then returns them incoordinate_system.- Parameters:
angle_unit ({
degrees,radians}, default=``degrees``) – Angular unit used for returned spherical or lateral-polar angles. Cartesian coordinates are returned in their stored distance unit.coordinate_system ({
spherical,cartesian,lateral-polar}, default=``spherical``) – Coordinate system used for returned positions.plane ({
horizontal,median,frontal} or None, default=None) –Optional source plane used to filter returned positions. Plane matching is independent of the requested output
coordinate_system."horizontal"selects a constant spherical elevation."median"selects a constant lateral-polar lateral angle."frontal"selects the nearest requested spherical azimuth and the nearest opposite azimuth.
plane_angle (float or None, default=None) – Requested plane coordinate in
angle_unit. Forplane="horizontal"this is spherical elevation. Forplane="median"this is lateral-polar lateral angle. Forplane="frontal"this is spherical azimuth; the opposite azimuth is added automatically.Noneuses 0 for horizontal and median planes, and 90 degrees or pi / 2 radians for the frontal plane.
- Returns:
Source-position array with shape (N, 3). The columns are (azimuth, elevation, radius) for spherical, (lateral, polar, radius) for lateral-polar, or (x, y, z) for cartesian coordinates.
- Return type:
np.ndarray
- Raises:
ValueError – If angle_unit is unsupported, plane is unsupported, the source or target coordinate system is unsupported, SOFA angular units cannot be interpreted, or the requested conversion is not implemented.
Notes
SOFA angular units are detected from the
SourcePosition:Unitsattribute. Angular source data stored in radians are converted through cartesian coordinates when degree/radian conversion is required.Examples
Load an HRTF, inspect the full spherical source grid, then request plane-filtered positions in spherical and Cartesian coordinates:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.Sources.get_positions().shape (793, 3) >>> horizontal = hrtf.Sources.get_positions(plane="horizontal") >>> horizontal.shape (72, 3) >>> frontal_xyz = hrtf.Sources.get_positions( ... coordinate_system="cartesian", ... plane="frontal", ... ) >>> frontal_xyz.shape (22, 3)
- get_azimuth_angles(angle_unit='degrees')¶
Return unique azimuth values available in the current source grid.
The source grid is first normalized to spherical coordinates. This makes the result independent of the active source_coordinate_system while still respecting any spatial subset selected on the owning HRTF.
- Parameters:
angle_unit ({
degrees,radians}, default=``degrees``) – Angular unit used for returned azimuth values.- Returns:
One-dimensional array of sorted unique azimuth angles rounded to two decimals.
- Return type:
np.ndarray
- Raises:
ValueError – If positions cannot be read or converted to spherical coordinates.
Examples
Load an HRTF and inspect the available azimuth grid in degrees:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.Sources.get_azimuth_angles()[:5] array([ 0., 5., 10., 15., 20.])
- get_elevation_angles(angle_unit='degrees')¶
Return unique elevation values available in the current source grid.
The source grid is first normalized to spherical coordinates. This makes the result independent of the active source_coordinate_system while still respecting any spatial subset selected on the owning HRTF.
- Parameters:
angle_unit ({
degrees,radians}, default=``degrees``) – Angular unit used for returned elevation values.- Returns:
One-dimensional array of sorted unique elevation angles rounded to two decimals.
- Return type:
np.ndarray
- Raises:
ValueError – If positions cannot be read or converted to spherical coordinates.
Examples
Load an HRTF and inspect the available elevation grid in degrees:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.Sources.get_elevation_angles() array([-45., -30., -20., -10., 0., 10., 20., 30., 45., 60., 75., 90.])
- get_elevation_angles_for_azimuth(azimuth, angle_unit='degrees')¶
Return elevations available at the nearest source-grid azimuth.
This method is useful for plane-based plotting and selection UIs where a requested azimuth may not exist exactly in the measured source grid. The azimuth match is circular, so values near 0 and 360 degrees, or 0 and 2*pi radians, are treated as neighbors.
- Parameters:
azimuth (float) – Requested azimuth angle used to query the source grid.
angle_unit ({
degrees,radians}, default=``degrees``) – Angular unit for azimuth, returned elevations, and real_azimuth.
- Returns:
(elevation_angles, real_azimuth) where elevation_angles is a one-dimensional array of unique elevations available at the matched azimuth, and real_azimuth is the actual azimuth selected from the grid. Both outputs are rounded to two decimals.
- Return type:
tuple[np.ndarray, float]
- Raises:
ValueError – If azimuth is boolean or non-finite, angle_unit is unsupported, or positions cannot be read or converted to spherical coordinates.
Examples
Ask for the elevations available at the source-grid azimuth nearest to 0 degrees:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> elevations, real_azimuth = hrtf.Sources.get_elevation_angles_for_azimuth(0.0) >>> elevations[:5] array([-45., -30., -20., -10., 0.]) >>> real_azimuth 0.0
- get_azimuth_angles_for_elevation(elevation, angle_unit='degrees')¶
Return azimuths available at the nearest source-grid elevation.
This method is useful for horizontal-plane workflows where the requested elevation may not exist exactly in the measured source grid. Elevation matching uses the nearest available numerical elevation in spherical coordinates.
- Parameters:
elevation (float) – Requested elevation angle used to query the source grid.
angle_unit ({
degrees,radians}, default=``degrees``) – Angular unit for elevation, returned azimuths, and real_elevation.
- Returns:
(azimuth_angles, real_elevation) where azimuth_angles is a one-dimensional array of unique azimuths available at the matched elevation, and real_elevation is the actual elevation selected from the grid. Both outputs are rounded to two decimals.
- Return type:
tuple[np.ndarray, float]
- Raises:
ValueError – If elevation is boolean or non-finite, angle_unit is unsupported, or positions cannot be read or converted to spherical coordinates.
Examples
Ask for the azimuths available at the source-grid elevation nearest to the horizontal plane:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> azimuths, real_elevation = hrtf.Sources.get_azimuth_angles_for_elevation(0.0) >>> azimuths[:5] array([ 0., 5., 10., 15., 20.]) >>> real_elevation 0.0
- get_position_index(position, coordinate_system='spherical', angle_unit='degrees')¶
Return the nearest source index and its resolved grid position.
The query is matched against the current source grid, including any source subset already selected on the owning
HRTFobject. Numeric positions are interpreted in coordinate_system. Named positions use the canonical horizontal spherical aliasesfront,back,left, andrightand are then returned in the requested coordinate system.- Parameters:
position (np.ndarray | list[float] | tuple[float, float, float] | str) – Query position. Numeric spherical and lateral-polar queries may be angle-only (2,) or full (3,) coordinates. Cartesian queries must be (3,). String queries must be one of the supported named positions.
coordinate_system ({
spherical,cartesian,lateral-polar}, default=``spherical``) – Coordinate system of numeric position queries and returned real_position.angle_unit ({
degrees,radians}, default=``degrees``) – Angular unit for spherical/lateral-polar inputs and outputs.
- Returns:
(idx, real_position) where idx is the nearest source index in the current source view and real_position is the matched grid coordinate rounded to two decimals in coordinate_system.
- Return type:
tuple[int, np.ndarray]
- Raises:
ValueError – If the coordinate system or angle unit is unsupported, source positions do not form an (N, 3) grid, a named position is unknown, a query has an invalid shape, or the needed coordinate conversion is unsupported.
Examples
Resolve a named source direction to the nearest measured source index and its real spherical grid position:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.Sources.get_position_index("front") (4, array([0. , 0. , 1.5]))
- class hrtfpykit.hrtf.transforms.Transform(hrtf)¶
Provide immutable HRTF-processing operations for one parent object.
Transformis accessed throughtransformand provides non-mutating HRTF-processing operations. Each method clones the parentHRTFobject, applies one operation to the clone, resynchronizes the affected domain representation, marks the returned object as transformed, and leaves the original HRTF unchanged.Time-domain operations modify the impulse-response array stored in
IR.values, then refresh the frequency-response array stored inTF.values. Frequency-domain operations do the reverse: they modify the transfer-function array and refresh the impulse-response array. This keeps both acoustic representations available for later plotting, metric calculation, SOFA synchronization, and export.Examples
Access the transform namespace from a loaded HRTF and apply a preprocessing step without changing the original object:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> windowed = hrtf.transform.apply_window("hann") >>> hrtf.is_transformed() False >>> windowed.is_transformed() True
- apply_window(window_name, start_sample=None, end_sample=None, ear='both')¶
Apply a time-domain window to IR values and rebuild TF.
The window is applied along the final IR sample axis. By default, it spans the complete HRIR and applies to both ears.
start_sampleandend_samplerestrict the window to one interval while samples outside that interval remain unchanged.earselects whether the window is applied to both ears, only the left ear, or only the right ear.- Parameters:
window_name (str) – Window identifier passed to the DSP layer, for example
hann,hamming,blackman, orrectangular.start_sample (int or None, default=None) – First sample included in the windowed interval. None starts at sample 0.
end_sample (int or None, default=None) – First sample after the windowed interval. None uses the full IR length.
ear ({
both,left,right}, default=``both``) – Ear channel that receives the window.leftuses channel 0 andrightuses channel 1.
- Returns:
A new HRTF instance with windowed IR values and refreshed TF data.
- Return type:
- Raises:
ValueError – If IR data are unavailable, ear is invalid, the requested window is unsupported, or the requested sample interval is invalid.
Examples
Apply a Hann window to the left-ear HRIR samples and keep the source layout unchanged:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> windowed = hrtf.transform.apply_window( ... "hann", ... start_sample=0, ... end_sample=128, ... ear="left", ... ) >>> windowed.IR.values.shape (793, 2, 256) >>> windowed.TF.values.shape (793, 2, 129)
- apply_crop(start_sample=None, end_sample=None, window=None, ear='both')¶
Remove an IR sample interval, append zero padding, and rebuild TF.
The crop operates on the final IR sample axis.
start_sampleis included andend_sampleis excluded, matching Python slicing andapply_window(). Samples after the removed interval are shifted left, then zeros are appended at the end so the IR sample length stays unchanged. When window is not None, the final retained samples adjacent to the zero-padded tail are tapered with the named window before TF is recomputed.earselects whether the crop is applied to both ears, only the left ear, or only the right ear.- Parameters:
start_sample (int or None, default=None) – First sample removed from each impulse response. None starts at sample 0.
end_sample (int or None, default=None) – First sample after the removed interval. This value is required. For example,
start_sample=4andend_sample=8removes samples 4, 5, 6, and 7.start_sample=Noneandend_sample=4removes samples 0, 1, 2, and 3.window (str or None, default=None) – Optional window name used to taper the final retained samples before the trailing zero padding. Supported names match
apply_window(). None skips tapering.ear ({
both,left,right}, default=``both``) – Ear channel that receives the crop.leftuses channel 0 andrightuses channel 1.
- Returns:
A new HRTF instance with cropped and zero-padded IR values and refreshed TF data.
- Return type:
- Raises:
ValueError – If IR data are unavailable, ear is invalid, the crop interval is invalid, or window is not None and the requested window name is unsupported.
Examples
Remove samples 64 through 127 from the left-ear HRIRs and keep the original IR length by appending zeros:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> cropped = hrtf.transform.apply_crop(64, 128, window="hann", ear="left") >>> cropped.IR.values.shape (793, 2, 256) >>> cropped.TF.values.shape (793, 2, 129) >>> cropped.is_transformed() True
- apply_padding(padding_length, location='end', value=0, preserve_length=False, ear='both')¶
Pad IR values along the sample axis and rebuild TF.
Padding is applied to the final IR axis while preserving source and ear layout. By default, padding applies to both ears and the sample axis grows by padding_length.
earcan target one ear. For start padding without length preservation, the selected ear is shifted right and unselected ears are extended with trailing zeros so their timing stays unchanged.preserve_length=Trueapplies start padding and removes the same number of samples from the end.- Parameters:
padding_length (int) – Number of samples added to the IR.
location ({
start,end}, default=``end``) – Side where the padding is applied.value (float, default=0) – Constant value used in the selected padded region.
preserve_length (bool, default=False) – If True, only start padding is accepted and the returned IR keeps the original sample length.
ear ({
both,left,right}, default=``both``) – Ear channel that receives the requested padding.leftuses channel 0 andrightuses channel 1.
- Returns:
A new HRTF instance with padded IR values and refreshed TF data.
- Return type:
- Raises:
ValueError – If IR data are unavailable, padding length is invalid, location or ear is not supported, or preserve_length=True is used with end padding.
Examples
Delay the left-ear HRIR by prepending zeros while keeping the original IR sample count:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.IR.values.shape (793, 2, 256) >>> padded = hrtf.transform.apply_padding( ... 4, ... location="start", ... preserve_length=True, ... ear="left", ... ) >>> padded.IR.values.shape (793, 2, 256) >>> padded.TF.values.shape (793, 2, 129)
- upsampling(new_sample_rate)¶
Upsample impulse-response values and resynchronize TF data.
The transform changes
IR.valuesandIR.sample_ratein the returned object, then recomputesTF.valuesand frequency bins from the resampled IR.- Parameters:
new_sample_rate (float) – Target sample rate in Hz. It must be strictly greater than the current IR sample rate.
- Returns:
A new HRTF instance with upsampled IR values, updated IR sample rate, and refreshed TF data.
- Return type:
- Raises:
ValueError – If IR data or sample-rate metadata are unavailable, or if the target sample rate is not finite and greater than the current rate.
Examples
Resample a SOFA-loaded HRTF to a higher sampling rate before later time-domain processing:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.IR.sample_rate 44100.0 >>> hrtf.IR.values.shape (793, 2, 256) >>> upsampled = hrtf.transform.upsampling(88200.0) >>> upsampled.IR.sample_rate 88200.0 >>> upsampled.IR.values.shape (793, 2, 512)
- downsampling(new_sample_rate)¶
Downsample impulse-response values and resynchronize TF data.
The transform changes
IR.valuesandIR.sample_ratein the returned object, then recomputesTF.valuesand frequency bins from the resampled IR.- Parameters:
new_sample_rate (float) – Target sample rate in Hz. It must be strictly lower than the current IR sample rate.
- Returns:
A new HRTF instance with downsampled IR values, updated IR sample rate, and refreshed TF data.
- Return type:
- Raises:
ValueError – If IR data or sample-rate metadata are unavailable, or if the target sample rate is not finite and lower than the current rate.
Examples
Resample a SOFA-loaded HRTF to a lower sampling rate and keep TF data synchronized with the new HRIR samples:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.IR.sample_rate 44100.0 >>> hrtf.IR.values.shape (793, 2, 256) >>> downsampled = hrtf.transform.downsampling(22050.0) >>> downsampled.IR.sample_rate 22050.0 >>> downsampled.IR.values.shape (793, 2, 128)
- apply_fir_filter(filter, cutoff=None, num_taps=101, window=None, ear='both')¶
Apply FIR filtering to IR values and rebuild TF.
Filtering is performed in the time domain along the final IR sample axis. By default, both ears are filtered.
earcan restrict the filtering to the left or right channel while the other channel remains unchanged.- Parameters:
filter (str) – Filter type. Low-pass, high-pass, and band-pass aliases are accepted by the DSP layer.
cutoff (float | tuple[float, float] | None, default=None) – Cutoff frequency or frequency pair in Hz.
num_taps (int, default=101) – FIR filter length.
window (str | None, default=None) – Optional FIR design window.
ear ({
both,left,right}, default=``both``) – Ear channel that receives the filter.leftuses channel 0 andrightuses channel 1.
- Returns:
A new HRTF instance with filtered IR values and refreshed TF data.
- Return type:
- Raises:
ValueError – If IR data or sample-rate metadata are unavailable, filter arguments are invalid, ear is invalid, or cutoff values are incompatible with the sample rate.
Examples
Low-pass filter the left-ear HRIRs with an FIR design:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> filtered = hrtf.transform.apply_fir_filter( ... filter="lowpass", ... cutoff=3000.0, ... num_taps=31, ... ear="left", ... ) >>> filtered.IR.values.shape (793, 2, 256) >>> filtered.is_transformed() True
- apply_iir_filter(filter, cutoff=None, order=10, ear='both')¶
Apply IIR filtering to IR values and rebuild TF.
Filtering is performed in the time domain along the final IR sample axis. By default, both ears are filtered.
earcan restrict the filtering to the left or right channel while the other channel remains unchanged.- Parameters:
filter (str) – Filter type. Low-pass, high-pass, and band-pass aliases are accepted by the DSP layer.
cutoff (float | tuple[float, float] | None, default=None) – Cutoff frequency or frequency pair in Hz.
order (int, default=10) – Butterworth filter order.
ear ({
both,left,right}, default=``both``) – Ear channel that receives the filter.leftuses channel 0 andrightuses channel 1.
- Returns:
A new HRTF instance with filtered IR values and refreshed TF data.
- Return type:
- Raises:
ValueError – If IR data or sample-rate metadata are unavailable, filter arguments are invalid, ear is invalid, or cutoff values are incompatible with the sample rate.
Examples
Apply a Butterworth low-pass filter to the right-ear HRIRs:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> filtered = hrtf.transform.apply_iir_filter( ... filter="lowpass", ... cutoff=3000.0, ... order=4, ... ear="right", ... ) >>> filtered.IR.values.shape (793, 2, 256) >>> filtered.TF.values.shape (793, 2, 129)
- minimum_phase(method='homomorphic', fft_length=None, epsilon=1e-12)¶
Convert IR values to minimum phase and rebuild TF.
The transform replaces each current HRIR with a minimum-phase version derived from its magnitude response. The returned object then refreshes the TF representation from the minimum-phase IR.
- Parameters:
method (str, default=``homomorphic``) – Minimum-phase method key passed to the DSP layer.
fft_length (int | None, default=None) – Optional FFT length used during cepstral reconstruction.
epsilon (float, default=1e-12) – Small positive floor used for numerical stability.
- Returns:
A new HRTF instance with minimum-phase IR values and refreshed TF data.
- Return type:
- Raises:
ValueError – If IR data are unavailable or minimum-phase parameters are invalid.
Examples
Convert the HRIRs to a minimum-phase representation while preserving the current HRTF layout:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> minimum = hrtf.transform.minimum_phase() >>> minimum.IR.values.shape (793, 2, 256) >>> minimum.is_transformed() True
- to_ctf(weights=False, magnitude_average='log', attenuation=None)¶
Convert the current HRTF into its common transfer function (CTF).
The CTF collapses the source axis into a single common response per ear. The returned HRTF keeps a singleton source axis for compatibility with source-based plotting and SOFA update workflows.
- Parameters:
weights (bool, optional) – If False, all source positions contribute equally. If True, diffuse-field weights are derived internally from the HRTF source positions using spherical Voronoi areas.
magnitude_average ({
log,linear}, optional) – Rule used to average source magnitudes before the minimum-phase CTF reconstruction.logcomputes a log-magnitude average (geometric mean in linear magnitude).linearcomputes a direct linear-magnitude average (arithmetic mean).attenuation (float | None, optional) – Optional attenuation in dB applied to the CTF magnitude before the minimum-phase reconstruction. If None, no attenuation is applied.
- Returns:
A new HRTF instance containing the CTF. The output keeps a singleton compatibility source axis.
- Return type:
- Raises:
ValueError – If TF data, IR reference length, source geometry, weighting inputs, or averaging parameters are invalid.
Examples
Estimate the common transfer function of a SOFA-loaded HRTF and inspect the singleton source axis kept in the result:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.TF.values.shape (793, 2, 129) >>> ctf = hrtf.transform.to_ctf(weights=False) >>> ctf.TF.values.shape (1, 2, 129)
- to_dtf(weights=False, magnitude_average='log', attenuation=None)¶
Convert the current HRTF into its directional transfer function (DTF).
The DTF removes a common transfer component estimated from the current HRTF. The returned HRTF preserves the source layout of the input object and rebuilds IR data from the DTF-domain TF values.
- Parameters:
weights (bool, optional) – If False, all source positions contribute equally to the internal CTF estimate. If True, diffuse-field weights are derived internally from the HRTF source positions using spherical Voronoi areas.
magnitude_average ({
log,linear}, optional) – Rule used to estimate the internal CTF magnitude before the DTF division.logcomputes a log-magnitude average (geometric mean in linear magnitude).linearcomputes a direct linear-magnitude average (arithmetic mean).attenuation (float | None, optional) – Optional attenuation in dB applied to the DTF after the CTF division. If None, no attenuation is applied.
- Returns:
A new HRTF instance containing the DTF while preserving the source layout of the current HRTF.
- Return type:
- Raises:
ValueError – If TF data, IR reference length, source geometry, weighting inputs, or averaging parameters are invalid.
Examples
Remove the common transfer component while keeping the original source grid layout:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.TF.values.shape (793, 2, 129) >>> dtf = hrtf.transform.to_dtf(weights=False) >>> dtf.TF.values.shape (793, 2, 129) >>> dtf.IR.values.shape (793, 2, 256)
- modify_ir(new_ir)¶
Replace time-domain IR values and rebuild TF data.
new_ir replaces the full current IR array. The leading dimensions before the final sample axis must match the current spatial and ear layout. When
new_iris anIRorHRTFobject and provides a sample rate, that sample rate is copied into the returned HRTF before TF recomputation.- Parameters:
new_ir (np.ndarray | IR | HRTF) – Time-domain data used to replace the current IR values. NumPy arrays must keep the same spatial and ear layout as the current HRTF.
IRandHRTFinputs contribute their IR values, and when available their sample rate.- Returns:
A new HRTF instance with modified IR values and rebuilt TF data.
- Return type:
- Raises:
ValueError – If replacement data are missing, empty, not array-like in the expected way, or do not match the current leading IR/TF layout.
Examples
Replace HRIR values with an edited copy and let the transform rebuild the transfer functions:
>>> import numpy as np >>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> ir = np.array(hrtf.IR.values, copy=True) >>> ir[..., :8] = 0.0 >>> modified = hrtf.transform.modify_ir(ir) >>> modified.IR.values[0, 0, :8] array([0., 0., 0., 0., 0., 0., 0., 0.]) >>> modified.IR.values.shape (793, 2, 256) >>> modified.TF.values.shape (793, 2, 129)
- modify_phase(new_phase, unit='degrees')¶
Replace TF phase values and rebuild IR.
The transform preserves the current TF magnitude and replaces only the phase component. The replacement phase must be compatible with the current TF layout and is interpreted according to unit.
- Parameters:
new_phase (np.ndarray) – Phase array with the same TF layout as the current HRTF.
unit ({
degrees,radians}, default=``degrees``) – Unit used by new_phase.
- Returns:
A new HRTF instance with modified TF phase and rebuilt IR data.
- Return type:
- Raises:
ValueError – If TF data or frequency bins are unavailable, unit is invalid, or the replacement phase cannot be broadcast to the TF layout.
Examples
Replace phase values with an edited phase array while preserving the current TF magnitude:
>>> import numpy as np >>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> phase = np.array(hrtf.TF.phase, copy=True) >>> phase[..., 1:] *= 0.95 >>> modified = hrtf.transform.modify_phase(phase, unit="radians") >>> modified.TF.values.shape (793, 2, 129) >>> modified.IR.values.shape (793, 2, 256)
- modify_tf(new_tf)¶
Replace frequency-domain TF values and rebuild IR data.
new_tf replaces the full complex TF array. The leading dimensions before the frequency axis must match the current TF layout, or the current IR leading layout when no TF data are present. Frequency bins are copied from
TForHRTFinputs when available; otherwise they are reused or inferred from the current sample rate when the TF length changes.- Parameters:
new_tf (np.ndarray | TF | HRTF) – Frequency-domain data used to replace the current TF values. NumPy arrays must keep the same spatial and ear layout as the current HRTF.
TFandHRTFinputs contribute their TF values and, when available, their frequency bins.- Returns:
A new HRTF instance with modified TF values and rebuilt IR data.
- Return type:
- Raises:
ValueError – If replacement data are missing, empty, have incompatible leading shape, contain too few frequency bins, or require frequency-bin inference without valid sample-rate metadata.
Examples
Replace TF values with a scaled complex copy and rebuild the time-domain representation:
>>> import numpy as np >>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> round(float(hrtf.TF.magnitude[0, 0, 1]), 6) 0.209696 >>> tf = np.array(hrtf.TF.values, copy=True) * 0.98 >>> modified = hrtf.transform.modify_tf(tf) >>> round(float(modified.TF.magnitude[0, 0, 1]), 6) 0.205502 >>> modified.TF.values.shape (793, 2, 129) >>> modified.IR.values.shape (793, 2, 256)
- modify_magnitude(new_magnitude, scale='linear')¶
Replace TF magnitude values and rebuild IR.
The transform preserves the current TF phase and replaces only the magnitude component. new_magnitude must be compatible with the current TF layout and is interpreted according to scale.
- Parameters:
new_magnitude (np.ndarray) – Magnitude array with the same TF layout as the current HRTF.
scale ({
linear,db}, default=``linear``) – Magnitude scale used by new_magnitude.
- Returns:
A new HRTF instance with modified TF magnitude and rebuilt IR data.
- Return type:
- Raises:
ValueError – If TF data or frequency bins are unavailable, scale is invalid, or the replacement magnitude cannot be broadcast to the TF layout.
Examples
Replace the TF magnitude with a slightly attenuated copy and keep the original phase:
>>> import numpy as np >>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> round(float(hrtf.TF.magnitude[0, 0, 1]), 6) 0.209696 >>> magnitude = np.array(hrtf.TF.magnitude, copy=True) * 0.95 >>> modified = hrtf.transform.modify_magnitude(magnitude, scale="linear") >>> round(float(modified.TF.magnitude[0, 0, 1]), 6) 0.199212 >>> modified.TF.values.shape (793, 2, 129) >>> modified.IR.values.shape (793, 2, 256)
- apply_gain(gain, scale='db', ear='both')¶
Apply a TF-domain gain and rebuild IR.
Gain modifies TF magnitude while preserving phase. Scalar gains apply globally by default.
earcan restrict the gain to the left or right channel while the other channel remains unchanged. Array gains must be broadcast-compatible with the selected TF layout.- Parameters:
gain (float | np.ndarray) – Gain applied to the current TF magnitude while preserving phase. In scale=``db``, negative values attenuate and positive values amplify.
scale ({
linear,db}, default=``db``) – Scale used by gain.ear ({
both,left,right}, default=``both``) – Ear channel that receives the gain.leftuses channel 0 andrightuses channel 1.
- Returns:
A new HRTF instance with gain-adjusted TF values and rebuilt IR data.
- Return type:
- Raises:
ValueError – If TF data are unavailable, scale or ear is invalid, or gain cannot be broadcast to the selected TF layout.
Examples
Apply a broadband gain in dB to the left ear:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> round(float(hrtf.TF.magnitude[0, 0, 1]), 6) 0.209696 >>> louder_left = hrtf.transform.apply_gain(3.0, scale="db", ear="left") >>> round(float(louder_left.TF.magnitude[0, 0, 1]), 6) 0.296294 >>> louder_left.TF.values.shape (793, 2, 129) >>> louder_left.is_transformed() True
- modify_fft_length(new_fft_length)¶
Set the HRTF FFT length and recompute TF from the current IR.
The returned object keeps the current IR unchanged and rebuilds TF using new_fft_length. This changes frequency-bin spacing and TF length but does not add time-domain information to the HRIR.
- Parameters:
new_fft_length (int) – FFT size used for IR-to-TF conversion.
- Returns:
A new HRTF instance with updated FFT length and recomputed TF data.
- Return type:
- Raises:
ValueError – If IR data are unavailable or the FFT length is invalid for real-FFT conversion.
Examples
Increase the FFT length used for IR-to-TF conversion and inspect the resulting frequency-bin count:
>>> from hrtfpykit.hrtf import load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> hrtf.fft_length 256 >>> hrtf.TF.values.shape (793, 2, 129) >>> modified = hrtf.transform.modify_fft_length(512) >>> modified.fft_length 512 >>> modified.TF.values.shape (793, 2, 257)
- add_itd(itd, unit='samples')¶
Add an interaural time delay to IR values and rebuild TF.
Positive ITD values delay the left ear; negative values delay the right ear. A scalar delay is applied to every source position. Array delays must match the leading IR shape before the ear and sample axes, which allows position-dependent ITD perturbations.
- Parameters:
itd (float) – ITD value to apply. Positive values delay the left ear and negative values delay the right ear.
unit ({
time,samples}, default=``samples``) – Unit used by itd.timeis interpreted in microseconds.
- Returns:
A new HRTF instance with ITD-modified IR values and refreshed TF data.
- Return type:
- Raises:
ValueError – If IR data do not contain two ear channels, delay values are non-finite, delay-array shape is incompatible with the IR layout, time values are requested without sample-rate metadata, or the absolute delay is not smaller than the IR length.
Examples
Add a two-sample delay to the left ear for every source position:
>>> from hrtfpykit.hrtf import load_hrtf, itd >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> itd(hrtf, output="samples")[:5] array([-3, -4, -3, -4, -4]) >>> delayed = hrtf.transform.add_itd(2, unit="samples") >>> itd(delayed, output="samples")[:5] array([-1, -2, -1, -2, -2]) >>> delayed.IR.values.shape (793, 2, 256) >>> delayed.is_transformed() True
- delete_itd(method='threshold', thresh_level=-10.0, upper_cut_freq=3000.0, filter_order=10)¶
Estimate and remove ITD from the current IR values, then resync TF.
The ITD sign convention follows itd: positive ITD means left-ear delay relative to right-ear and negative ITD means right-ear delay relative to left-ear. Compensation is applied per source by advancing the delayed channel and zero-filling the tail introduced by the shift.
- Parameters:
method ({
threshold,maxiacce}, default=``threshold``) – ITD estimator used to compute the delay per position.thresh_level (float, default=-10.0) – Threshold offset in dB used when method=``threshold``.
upper_cut_freq (float, default=3000.0) – Low-pass cutoff in Hz applied before ITD estimation.
filter_order (int, default=10) – Butterworth low-pass filter order used before ITD estimation.
- Returns:
A new HRTF instance with ITD-compensated IR values and refreshed TF data. Compensation is applied per source position.
- Return type:
- Raises:
ValueError – If IR data do not contain two ear channels, ITD estimation parameters are invalid, or an estimated delay is not smaller than the IR length.
Examples
Estimate and remove ITD from each source position before comparing magnitude-focused features:
>>> from hrtfpykit.hrtf import load_hrtf, itd >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> itd(hrtf, output="samples")[:5] array([-3, -4, -3, -4, -4]) >>> no_itd = hrtf.transform.delete_itd() >>> itd(no_itd, output="samples")[:5] array([0, 0, 0, 0, 0]) >>> no_itd.IR.values.shape (793, 2, 256) >>> no_itd.TF.values.shape (793, 2, 129)
- add_ild(ild)¶
Add an interaural level difference in dB and rebuild IR.
Positive values increase the left ear level relative to the right ear. Negative values increase the right ear level relative to the left ear. The transform applies a symmetric TF-domain correction: half of the ILD is added to the left channel and half is subtracted from the right channel. Existing phase values are preserved and the time-domain IR is rebuilt from the modified TF.
- Parameters:
ild (float | numpy.ndarray) – ILD values in dB. A scalar applies the same ILD to every source and frequency bin. An array matching the TF leading shape before the ear and frequency axes applies one ILD per source entry and broadcasts across frequency. An array matching the TF leading shape plus the frequency axis applies frequency-dependent ILD values.
- Returns:
A new HRTF instance with ILD-modified TF values and rebuilt IR data.
- Return type:
- Raises:
ValueError – If TF data are unavailable or do not contain at least two ear channels, if ILD values are empty or non-finite, or if an ILD array shape does not match the accepted source or source and frequency layouts.
Examples
Add 6 dB of ILD to every source position and frequency bin:
>>> from hrtfpykit.hrtf import ild, load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> modified = hrtf.transform.add_ild(6.0) >>> ild(modified, mode="frequency-dependent").shape (793, 129) >>> modified.IR.values.shape (793, 2, 256) >>> modified.is_transformed() True
- delete_ild(epsilon=1e-12)¶
Remove frequency-dependent interaural level difference and rebuild IR.
The transform computes signed frequency-dependent ILD from the current TF values, then applies the inverse ILD with
add_ild(). This equalizes the first two ear channels at each source position and frequency bin while preserving TF phase.- Parameters:
epsilon (float, default=1e-12) – Positive floor passed to
hrtfpykit.hrtf.ild()while measuring the current frequency-dependent ILD.- Returns:
A new HRTF instance with frequency-dependent ILD removed from TF values and rebuilt IR data.
- Return type:
- Raises:
ValueError – If TF data are unavailable, if TF data do not contain at least two ear channels, or if epsilon is not finite and positive.
Examples
Remove frequency-dependent ILD before inspecting phase-focused data:
>>> from hrtfpykit.hrtf import ild, load_hrtf >>> hrtf = load_hrtf("P0001_FreeFieldComp_44kHz.sofa") >>> no_ild = hrtf.transform.delete_ild() >>> ild(no_ild, mode="frequency-dependent").shape (793, 129) >>> no_ild.TF.values.shape (793, 2, 129) >>> no_ild.is_transformed() True