Source code for mesh2hrtf.Output2HRTF.merge_sofa_files
import os
import glob
import numpy as np
import sofar as sf
[docs]def merge_sofa_files(paths, pattern=None, savedir=None):
"""
Merge HRTFs and HRIRs from SOFA-files containing left and right ear data.
The names of the merged SOFA files and with the "merged.sofa".
Parameters
----------
paths : tuple
A tuple containing paths to folders. SOFA files are searched in
`paths/Output2HRTF` if it exist and directly in `paths` otherwise.
The names may contain an asterisk to process data in multiple folders.
E.g., if ``paths[0]`` is ``"some/path/left/*"`` and ``paths[1]`` is
``"some/path/right/*"`` all SOFA files in the matching folders will be
merged. Note the SOFA files contained in the folders must have the same
names to be merged. Currently, `paths` must contain exactly two
paths.
pattern : str
Merge only files that contain `pattern` in their filename. The default
``None`` merges all SOFA files.
savedir : str
Directory for saving the merged SOFA files. The default ``None`` saves
the files to the directory given by `left`.
"""
if savedir is not None and not os.path.isdir(savedir):
raise ValueError(f"savedir {savedir} is not a directory")
if not isinstance(paths, (tuple, list)) or len(paths) != 2:
raise ValueError("paths, must be a tuple or list of length two")
# check which data to merge
if pattern is None:
pattern = "*.sofa"
elif not pattern.endswith("sofa"):
pattern = f"{pattern}*.sofa"
left = paths[0]
right = paths[1]
# get all search directories
left_dirs = glob.glob(left)
right_dirs = glob.glob(right)
if len(left_dirs) != len(right_dirs):
raise ValueError(("The number of directories found with glob.glob()"
f" does not match for {left} and {right}"))
# loop directories
for left_dir, right_dir in zip(left_dirs, right_dirs):
# check if Output2HRTF folder exists
if os.path.isdir(os.path.join(left_dir, "Output2HRTF")) \
and os.path.isdir(os.path.join(right_dir, "Output2HRTF")):
left_dir = os.path.join(left_dir, "Output2HRTF")
right_dir = os.path.join(right_dir, "Output2HRTF")
# get and check all SOFA files in Output2HRTF folder
left_files = glob.glob(os.path.join(left_dir, pattern))
right_files = glob.glob(os.path.join(right_dir, pattern))
if len(left_files) != len(right_files):
raise ValueError((
"The umber of sofa files found with glob.glob()"
f" does not match for {left_dir} and {right_dir}"))
# loop all SOFA files
for left_file, right_file in zip(left_files, right_files):
# check file names
if (os.path.basename(left_file)
!= os.path.basename(right_file)):
raise ValueError((
"Found mismatching. Each Output2HRTF folder must "
"contain SOFA files with the same names. Error for"
f" {left_file} and {right_file}"))
# filename of merged data
head, tail = os.path.split(left_file)
tail = tail[:-len(".sofa")] + "_merged.sofa"
if savedir is not None:
head = savedir
# merge data
_merge_sofa_files(
(left_file, right_file), os.path.join(head, tail))
def _merge_sofa_files(files, savename):
"""read two sofa files, join the data, and save the result"""
left = sf.read_sofa(files[0])
right = sf.read_sofa(files[1])
# join Data
if left.GLOBAL_DataType.startswith("TF"):
# check if data can be joined
if left.N.size != right.N.size or \
np.any(np.abs(left.N - right.N)) > 1e-6:
raise ValueError(("Number of frequencies or frequencies do not "
f"agree for {left} and {right}"))
# join data
left.Data_Real = np.concatenate(
(left.Data_Real, right.Data_Real), axis=1)
left.Data_Imag = np.concatenate(
(left.Data_Imag, right.Data_Imag), axis=1)
elif left.GLOBAL_DataType.startswith("FIR"):
# check if data can be joined
if left.get_dimension("N") != right.get_dimension("N") or \
left.Data_SamplingRate != right.Data_SamplingRate:
raise ValueError(("Number of samples or sampling rates do not"
f"agree for {left} and {right}"))
# join data
left.Data_IR = np.concatenate(
(left.Data_IR, right.Data_IR), axis=1)
left.Data_Delay = np.zeros((1, left.Data_IR.shape[1]))
else:
raise ValueError("Joining only works for DataTypes 'TF' and 'FIR'")
left.ReceiverPosition = np.concatenate(
(np.atleast_2d(left.ReceiverPosition),
np.atleast_2d(right.ReceiverPosition)), axis=0)
# write joined data to disk
sf.write_sofa(savename, left)