sofar 0.3.1__py2.py3-none-any.whl → 1.1.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sofar/__init__.py +13 -7
- sofar/io.py +423 -0
- sofar/sofa.py +1795 -0
- sofar/sofa_conventions/VERSION +1 -0
- sofar/sofa_conventions/conventions/FreeFieldDirectivityTF_1.1.csv +59 -0
- sofar/sofa_conventions/conventions/FreeFieldDirectivityTF_1.1.json +444 -0
- sofar/{conventions/source → sofa_conventions/conventions}/FreeFieldHRIR_1.0.csv +3 -3
- sofar/{conventions → sofa_conventions/conventions}/FreeFieldHRIR_1.0.json +3 -3
- sofar/{conventions/source → sofa_conventions/conventions}/FreeFieldHRTF_1.0.csv +2 -2
- sofar/{conventions → sofa_conventions/conventions}/FreeFieldHRTF_1.0.json +3 -3
- sofar/{conventions/source → sofa_conventions/conventions}/GeneralFIR-E_2.0.csv +2 -2
- sofar/{conventions → sofa_conventions/conventions}/GeneralFIR-E_2.0.json +2 -2
- sofar/{conventions/source/GeneralFIR_2.0.csv → sofa_conventions/conventions/GeneralFIR_1.0.csv} +2 -2
- sofar/{conventions/GeneralFIR_2.0.json → sofa_conventions/conventions/GeneralFIR_1.0.json} +2 -2
- sofar/{conventions/source/GeneralFIR_1.0.csv → sofa_conventions/conventions/GeneralSOS_1.0.csv} +11 -11
- sofar/{conventions/GeneralFIR_1.0.json → sofa_conventions/conventions/GeneralSOS_1.0.json} +48 -37
- sofar/{conventions/source → sofa_conventions/conventions}/GeneralTF-E_1.0.csv +3 -3
- sofar/{conventions → sofa_conventions/conventions}/GeneralTF-E_1.0.json +4 -4
- sofar/{conventions/source → sofa_conventions/conventions}/GeneralTF_1.0.csv +1 -1
- sofar/{conventions → sofa_conventions/conventions}/GeneralTF_1.0.json +1 -1
- sofar/{conventions/source → sofa_conventions/conventions}/GeneralTF_2.0.csv +4 -4
- sofar/{conventions → sofa_conventions/conventions}/GeneralTF_2.0.json +4 -4
- sofar/sofa_conventions/conventions/SimpleFreeFieldHRIR_1.0.csv +47 -0
- sofar/{conventions → sofa_conventions/conventions}/SimpleFreeFieldHRIR_1.0.json +1 -1
- sofar/{conventions/source → sofa_conventions/conventions}/SimpleFreeFieldHRSOS_1.0.csv +1 -1
- sofar/{conventions → sofa_conventions/conventions}/SimpleFreeFieldHRSOS_1.0.json +1 -1
- sofar/{conventions/source/SimpleFreeFieldHRTF_2.0.csv → sofa_conventions/conventions/SimpleFreeFieldHRTF_1.0.csv} +3 -3
- sofar/{conventions/SimpleFreeFieldHRTF_2.0.json → sofa_conventions/conventions/SimpleFreeFieldHRTF_1.0.json} +4 -4
- sofar/{conventions/source → sofa_conventions/conventions}/SimpleHeadphoneIR_1.0.csv +9 -9
- sofar/sofa_conventions/conventions/SimpleHeadphoneIR_1.0.json +396 -0
- sofar/{conventions/source → sofa_conventions/conventions}/SingleRoomMIMOSRIR_1.0.csv +18 -8
- sofar/{conventions → sofa_conventions/conventions}/SingleRoomMIMOSRIR_1.0.json +124 -50
- sofar/{conventions/source → sofa_conventions/conventions}/SingleRoomSRIR_1.0.csv +18 -8
- sofar/{conventions → sofa_conventions/conventions}/SingleRoomSRIR_1.0.json +124 -50
- sofar/{conventions/source → sofa_conventions/conventions/deprecated}/FreeFieldDirectivityTF_1.0.csv +2 -2
- sofar/{conventions → sofa_conventions/conventions/deprecated}/FreeFieldDirectivityTF_1.0.json +2 -2
- sofar/sofa_conventions/conventions/deprecated/MultiSpeakerBRIR_0.3.csv +48 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldHRIR_0.4.csv +43 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldHRIR_0.4.json +333 -0
- sofar/{conventions/source/SimpleFreeFieldHRIR_1.0.csv → sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_0.4.csv} +15 -18
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_0.4.json +340 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_1.0.csv +44 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_1.0.json +340 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.1.csv +51 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.1.json +396 -0
- sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.2.csv +51 -0
- sofar/{conventions/SimpleHeadphoneIR_1.0.json → sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.2.json} +3 -3
- sofar/sofa_conventions/conventions/deprecated/SingleRoomDRIR_0.2.csv +47 -0
- sofar/sofa_conventions/conventions/deprecated/SingleRoomDRIR_0.2.json +360 -0
- sofar/sofa_conventions/rules/deprecations.json +12 -0
- sofar/sofa_conventions/rules/rules.json +800 -0
- sofar/sofa_conventions/rules/unit_aliases.json +11 -0
- sofar/sofa_conventions/rules/upgrade.json +190 -0
- sofar/update_conventions.py +427 -0
- sofar/utils.py +315 -0
- {sofar-0.3.1.dist-info → sofar-1.1.0.dist-info}/AUTHORS.rst +1 -0
- sofar-1.1.0.dist-info/METADATA +89 -0
- sofar-1.1.0.dist-info/RECORD +75 -0
- {sofar-0.3.1.dist-info → sofar-1.1.0.dist-info}/WHEEL +1 -1
- {sofar-0.3.1.dist-info → sofar-1.1.0.dist-info}/top_level.txt +1 -0
- tests/__init__.py +0 -0
- tests/test_deprecations.py +19 -0
- tests/test_io.py +344 -0
- tests/test_sofa.py +354 -0
- tests/test_sofa_upgrade_conventions.py +102 -0
- tests/test_sofa_verify.py +472 -0
- tests/test_utils.py +241 -0
- sofar/conventions/source/MultiSpeakerBRIR_0.3.csv +0 -48
- sofar/sofar.py +0 -2531
- sofar-0.3.1.dist-info/METADATA +0 -69
- sofar-0.3.1.dist-info/RECORD +0 -46
- /sofar/{conventions/source → sofa_conventions/conventions}/SimpleFreeFieldSOS_1.0.csv +0 -0
- /sofar/{conventions → sofa_conventions/conventions}/SimpleFreeFieldSOS_1.0.json +0 -0
- /sofar/{conventions/source → sofa_conventions/conventions/deprecated}/GeneralFIRE_1.0.csv +0 -0
- /sofar/{conventions → sofa_conventions/conventions/deprecated}/GeneralFIRE_1.0.json +0 -0
- /sofar/{conventions → sofa_conventions/conventions/deprecated}/MultiSpeakerBRIR_0.3.json +0 -0
- /sofar/{conventions/source → sofa_conventions/conventions/deprecated}/SingleRoomDRIR_0.3.csv +0 -0
- /sofar/{conventions → sofa_conventions/conventions/deprecated}/SingleRoomDRIR_0.3.json +0 -0
- {sofar-0.3.1.dist-info → sofar-1.1.0.dist-info}/LICENSE +0 -0
sofar/utils.py
ADDED
@@ -0,0 +1,315 @@
|
|
1
|
+
import os
|
2
|
+
import glob
|
3
|
+
import numpy as np
|
4
|
+
import numpy.testing as npt
|
5
|
+
import warnings
|
6
|
+
import sofar as sf
|
7
|
+
|
8
|
+
|
9
|
+
def version():
|
10
|
+
"""Return version of sofar and SOFA conventions"""
|
11
|
+
|
12
|
+
sofa_conventions = os.path.join(
|
13
|
+
os.path.dirname(__file__), "sofa_conventions", 'VERSION')
|
14
|
+
with open(sofa_conventions) as file:
|
15
|
+
sofa_conventions = file.readline().strip()
|
16
|
+
|
17
|
+
return (f"sofar v{sf.__version__} implementing "
|
18
|
+
f"SOFA standard {sofa_conventions}")
|
19
|
+
|
20
|
+
|
21
|
+
def _verify_convention_and_version(version, convention):
|
22
|
+
"""
|
23
|
+
Verify if convention and version exist. Raise a Value error if it does not.
|
24
|
+
|
25
|
+
Parameters
|
26
|
+
----------
|
27
|
+
version : str
|
28
|
+
The version to be checked
|
29
|
+
convention : str
|
30
|
+
The name of the convention to be checked
|
31
|
+
"""
|
32
|
+
|
33
|
+
# check if the convention exists
|
34
|
+
if convention not in _get_conventions("name"):
|
35
|
+
raise ValueError(
|
36
|
+
f"Convention '{convention}' does not exist")
|
37
|
+
|
38
|
+
name_version = _get_conventions("name_version")
|
39
|
+
|
40
|
+
# check which version is wanted
|
41
|
+
version_exists = False
|
42
|
+
for versions in name_version:
|
43
|
+
# check if convention and version match
|
44
|
+
if versions[0] == convention \
|
45
|
+
and str(float(versions[1])) == version:
|
46
|
+
version_exists = True
|
47
|
+
|
48
|
+
if not version_exists:
|
49
|
+
raise ValueError((
|
50
|
+
f"{convention} v{version} is not a valid SOFA Convention."
|
51
|
+
"If you are trying to read the data use "
|
52
|
+
"sofar.read_sofa_as_netcdf(). Call sofar.list_conventions() for a "
|
53
|
+
"list of valid Conventions"))
|
54
|
+
|
55
|
+
|
56
|
+
def list_conventions():
|
57
|
+
"""
|
58
|
+
List available SOFA conventions by printing to the console.
|
59
|
+
"""
|
60
|
+
print(_get_conventions("string"))
|
61
|
+
|
62
|
+
|
63
|
+
def _get_conventions(return_type, conventions_path=None):
|
64
|
+
"""
|
65
|
+
Get available SOFA conventions.
|
66
|
+
|
67
|
+
Parameters
|
68
|
+
----------
|
69
|
+
return_type : string, optional
|
70
|
+
``'path'``
|
71
|
+
Return a list with the full paths and filenames of the convention
|
72
|
+
files (json files)
|
73
|
+
``'path_source'``
|
74
|
+
Return a list with the full paths and filenames of the source
|
75
|
+
convention files from API_MO (csv files)
|
76
|
+
``'name'``
|
77
|
+
Return a list of the convention names without version
|
78
|
+
``'name_version'``
|
79
|
+
Return a list of tuples containing the convention name and version.
|
80
|
+
``'string'``
|
81
|
+
Returns a string that lists the names and versions of all
|
82
|
+
conventions.
|
83
|
+
conventions_path : str, optional
|
84
|
+
The path to the the `conventions` folder containing the csv and json
|
85
|
+
files.
|
86
|
+
|
87
|
+
Returns
|
88
|
+
-------
|
89
|
+
See parameter `return_type`.
|
90
|
+
"""
|
91
|
+
# directory containing the SOFA conventions
|
92
|
+
if conventions_path is None:
|
93
|
+
conventions_path = os.path.join(
|
94
|
+
os.path.dirname(__file__), "sofa_conventions", 'conventions')
|
95
|
+
|
96
|
+
reg_str = "*.csv" if return_type == "path_source" else "*.json"
|
97
|
+
|
98
|
+
# SOFA convention files
|
99
|
+
standardized = list(glob.glob(os.path.join(conventions_path, reg_str)))
|
100
|
+
deprecated = list(
|
101
|
+
glob.glob(os.path.join(conventions_path, "deprecated", reg_str)))
|
102
|
+
paths = standardized + deprecated
|
103
|
+
|
104
|
+
conventions_str = "Available SOFA conventions:\n"
|
105
|
+
|
106
|
+
conventions = []
|
107
|
+
versions = []
|
108
|
+
for path in paths:
|
109
|
+
fileparts = os.path.basename(path).split(sep="_")
|
110
|
+
conventions += [fileparts[0]]
|
111
|
+
versions += [fileparts[1][:-5]]
|
112
|
+
conventions_str += f"{conventions[-1]} (Version {versions[-1]})\n"
|
113
|
+
|
114
|
+
if return_type is None:
|
115
|
+
return
|
116
|
+
elif return_type.startswith("path"):
|
117
|
+
return paths
|
118
|
+
elif return_type == "name":
|
119
|
+
return conventions
|
120
|
+
elif return_type == "name_version":
|
121
|
+
return list(zip(conventions, versions))
|
122
|
+
elif return_type == "string":
|
123
|
+
return conventions_str
|
124
|
+
else:
|
125
|
+
raise ValueError(f"return_type {return_type} is invalid")
|
126
|
+
|
127
|
+
|
128
|
+
def equals(sofa_a, sofa_b, verbose=True, exclude=None):
|
129
|
+
"""
|
130
|
+
Compare two SOFA objects against each other.
|
131
|
+
|
132
|
+
Parameters
|
133
|
+
----------
|
134
|
+
sofa_a : Sofa
|
135
|
+
SOFA object
|
136
|
+
sofa_b : Sofa
|
137
|
+
SOFA object
|
138
|
+
verbose : bool, optional
|
139
|
+
Print differences to the console. The default is True.
|
140
|
+
exclude : str, optional
|
141
|
+
Specify what fields should be excluded from the comparison
|
142
|
+
|
143
|
+
``'GLOBAL'``
|
144
|
+
Exclude all global attributes, i.e., fields starting with 'GLOBAL:'
|
145
|
+
``'DATE'``
|
146
|
+
Exclude date attributs, i.e., fields that contain 'Date'
|
147
|
+
``'ATTR'``
|
148
|
+
Exclude all attributes, i.e., fields that contain ':'
|
149
|
+
|
150
|
+
The default is None, which does not exclude anything.
|
151
|
+
|
152
|
+
Returns
|
153
|
+
-------
|
154
|
+
is_identical : bool
|
155
|
+
``True`` if sofa_a and sofa_b are identical, ``False`` otherwise.
|
156
|
+
"""
|
157
|
+
|
158
|
+
is_identical = True
|
159
|
+
|
160
|
+
# get and filter keys
|
161
|
+
# ('_*' are SOFA object private variables, '__' are netCDF attributes)
|
162
|
+
keys_a = [k for k in sofa_a.__dict__.keys() if not k.startswith("_")]
|
163
|
+
keys_b = [k for k in sofa_b.__dict__.keys() if not k.startswith("_")]
|
164
|
+
|
165
|
+
if exclude is not None:
|
166
|
+
if exclude.upper() == "GLOBAL":
|
167
|
+
keys_a = [k for k in keys_a if not k.startswith("GLOBAL_")]
|
168
|
+
keys_b = [k for k in keys_b if not k.startswith("GLOBAL_")]
|
169
|
+
elif exclude.upper() == "ATTR":
|
170
|
+
keys_a = [k for k in keys_a if
|
171
|
+
sofa_a._convention[k]["type"] != "attribute"]
|
172
|
+
keys_b = [k for k in keys_b if
|
173
|
+
sofa_b._convention[k]["type"] != "attribute"]
|
174
|
+
elif exclude.upper() == "DATE":
|
175
|
+
keys_a = [k for k in keys_a if "Date" not in k]
|
176
|
+
keys_b = [k for k in keys_b if "Date" not in k]
|
177
|
+
else:
|
178
|
+
raise ValueError(
|
179
|
+
f"exclude is {exclude} but must be GLOBAL, DATE, or ATTR")
|
180
|
+
|
181
|
+
# check for equal length
|
182
|
+
if len(keys_a) != len(keys_b):
|
183
|
+
return _equals_raise_warning((
|
184
|
+
f"not identical: sofa_a has {len(keys_a)} attributes for "
|
185
|
+
f"comparison and sofa_b has {len(keys_b)}."), verbose)
|
186
|
+
|
187
|
+
# check if the keys match
|
188
|
+
if set(keys_a) != set(keys_b):
|
189
|
+
return _equals_raise_warning(
|
190
|
+
"not identical: sofa_a and sofa_b do not have the ame attributes",
|
191
|
+
verbose)
|
192
|
+
|
193
|
+
# compare the data inside the SOFA object
|
194
|
+
for key in keys_a:
|
195
|
+
|
196
|
+
# get data and types
|
197
|
+
a = getattr(sofa_a, key)
|
198
|
+
b = getattr(sofa_b, key)
|
199
|
+
type_a = sofa_a._convention[key]["type"]
|
200
|
+
type_b = sofa_b._convention[key]["type"]
|
201
|
+
|
202
|
+
# compare attributes
|
203
|
+
if type_a == "attribute" and type_b == "attribute":
|
204
|
+
|
205
|
+
# compare
|
206
|
+
if a != b:
|
207
|
+
is_identical = _equals_raise_warning(
|
208
|
+
f"not identical: different values for {key}", verbose)
|
209
|
+
|
210
|
+
# compare double variables
|
211
|
+
elif type_a == "double" and type_b == "double":
|
212
|
+
|
213
|
+
try:
|
214
|
+
npt.assert_allclose(np.squeeze(a), np.squeeze(b))
|
215
|
+
except AssertionError:
|
216
|
+
is_identical = _equals_raise_warning(
|
217
|
+
"not identical: different values for {key}", verbose)
|
218
|
+
|
219
|
+
# compare string variables
|
220
|
+
elif type_a == "string" and type_b == "string":
|
221
|
+
try:
|
222
|
+
assert np.all(
|
223
|
+
np.squeeze(a).astype("S") == np.squeeze(b).astype("S"))
|
224
|
+
except AssertionError:
|
225
|
+
is_identical = _equals_raise_warning(
|
226
|
+
"not identical: different values for {key}", verbose)
|
227
|
+
else:
|
228
|
+
is_identical = _equals_raise_warning(
|
229
|
+
(f"not identical: {key} has different data types "
|
230
|
+
f"({type_a}, {type_b})"), verbose)
|
231
|
+
|
232
|
+
return is_identical
|
233
|
+
|
234
|
+
|
235
|
+
def _equals_raise_warning(message, verbose):
|
236
|
+
if verbose:
|
237
|
+
warnings.warn(message)
|
238
|
+
return False
|
239
|
+
|
240
|
+
|
241
|
+
def _atleast_nd(array, ndim):
|
242
|
+
"""
|
243
|
+
Get numpy array with specified number of dimensions. Dimensions are
|
244
|
+
appended at the end if ndim > 3.
|
245
|
+
"""
|
246
|
+
try:
|
247
|
+
array = array.copy()
|
248
|
+
except AttributeError:
|
249
|
+
array = array
|
250
|
+
|
251
|
+
if ndim == 1:
|
252
|
+
array = np.atleast_1d(array)
|
253
|
+
if ndim == 2:
|
254
|
+
array = np.atleast_2d(array)
|
255
|
+
if ndim >= 3:
|
256
|
+
array = np.atleast_3d(array)
|
257
|
+
for _ in range(ndim - array.ndim):
|
258
|
+
array = array[..., np.newaxis]
|
259
|
+
return array
|
260
|
+
|
261
|
+
|
262
|
+
def _nd_newaxis(array, ndim):
|
263
|
+
"""Append dimensions to the end of an array until array.ndim == ndim"""
|
264
|
+
array = np.array(array)
|
265
|
+
|
266
|
+
for _ in range(ndim - array.ndim):
|
267
|
+
array = array[..., np.newaxis]
|
268
|
+
return array
|
269
|
+
|
270
|
+
|
271
|
+
def _complete_sofa(convention="GeneralTF"):
|
272
|
+
"""
|
273
|
+
Generate SOFA file with all required data for testing verification rules.
|
274
|
+
"""
|
275
|
+
|
276
|
+
sofa = sf.Sofa(convention)
|
277
|
+
# Listener meta data
|
278
|
+
sofa.add_variable("ListenerView", [1, 0, 0], "double", "IC")
|
279
|
+
sofa.add_attribute("ListenerView_Type", "cartesian")
|
280
|
+
sofa.add_attribute("ListenerView_Units", "metre")
|
281
|
+
sofa.add_variable("ListenerUp", [0, 0, 1], "double", "IC")
|
282
|
+
# Receiver meta data
|
283
|
+
sofa.add_variable("ReceiverView", [1, 0, 0], "double", "IC")
|
284
|
+
sofa.add_attribute("ReceiverView_Type", "cartesian")
|
285
|
+
sofa.add_attribute("ReceiverView_Units", "metre")
|
286
|
+
sofa.add_variable("ReceiverUp", [0, 0, 1], "double", "IC")
|
287
|
+
# Source meta data
|
288
|
+
sofa.add_variable("SourceView", [1, 0, 0], "double", "IC")
|
289
|
+
sofa.add_attribute("SourceView_Type", "cartesian")
|
290
|
+
sofa.add_attribute("SourceView_Units", "metre")
|
291
|
+
sofa.add_variable("SourceUp", [0, 0, 1], "double", "IC")
|
292
|
+
# Emitter meta data
|
293
|
+
sofa.add_variable("EmitterView", [1, 0, 0], "double", "IC")
|
294
|
+
sofa.add_attribute("EmitterView_Type", "cartesian")
|
295
|
+
sofa.add_attribute("EmitterView_Units", "metre")
|
296
|
+
sofa.add_variable("EmitterUp", [0, 0, 1], "double", "IC")
|
297
|
+
sofa.add_attribute("GLOBAL_EmitterDescription", "what an emitter")
|
298
|
+
sofa.add_variable("EmitterDescriptions", ["emitter array"], "string", "MS")
|
299
|
+
# Room meta data
|
300
|
+
sofa.add_attribute("GLOBAL_RoomShortName", "Hall")
|
301
|
+
sofa.add_attribute("GLOBAL_RoomDescription", "Wooden floor")
|
302
|
+
sofa.add_attribute("GLOBAL_RoomLocation", "some where nice")
|
303
|
+
sofa.add_variable("RoomTemperature", 0, "double", "I")
|
304
|
+
sofa.add_attribute("RoomTemperature_Units", "kelvin")
|
305
|
+
sofa.add_attribute("GLOBAL_RoomGeometry", "some/file")
|
306
|
+
sofa.add_variable("RoomVolume", 200, "double", "I")
|
307
|
+
sofa.add_attribute("RoomVolume_Units", "cubic metre")
|
308
|
+
sofa.add_variable("RoomCornerA", [0, 0, 0], "double", "IC")
|
309
|
+
sofa.add_variable("RoomCornerB", [1, 1, 1], "double", "IC")
|
310
|
+
sofa.add_variable("RoomCorners", 0, "double", "I")
|
311
|
+
sofa.add_attribute("RoomCorners_Type", "cartesian")
|
312
|
+
sofa.add_attribute("RoomCorners_Units", "metre")
|
313
|
+
|
314
|
+
sofa.verify()
|
315
|
+
return sofa
|
@@ -0,0 +1,89 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: sofar
|
3
|
+
Version: 1.1.0
|
4
|
+
Summary: Maybe the most complete python package for SOFA files so far
|
5
|
+
Home-page: https://pyfar.org/
|
6
|
+
Download-URL: https://pypi.org/project/sofar/
|
7
|
+
Author: The pyfar developers
|
8
|
+
Author-email: info@pyfar.org
|
9
|
+
License: MIT license
|
10
|
+
Project-URL: Bug Tracker, https://github.com/pyfar/sofar/issues
|
11
|
+
Project-URL: Documentation, https://sofar.readthedocs.io/
|
12
|
+
Project-URL: Source Code, https://github.com/pyfar/sofar
|
13
|
+
Keywords: sofar
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
17
|
+
Classifier: Natural Language :: English
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
23
|
+
Requires-Python: >=3.8
|
24
|
+
License-File: LICENSE
|
25
|
+
License-File: AUTHORS.rst
|
26
|
+
Requires-Dist: netCDF4
|
27
|
+
Requires-Dist: numpy (>=1.14.0)
|
28
|
+
Requires-Dist: beautifulsoup4
|
29
|
+
Requires-Dist: requests
|
30
|
+
|
31
|
+
======
|
32
|
+
Readme
|
33
|
+
======
|
34
|
+
|
35
|
+
Sofar is maybe the most complete Python package for the SOFA file format so
|
36
|
+
far. SOFA files store spatially distributed acoustic data such as impulse
|
37
|
+
responses or transfer functions. They are defined by the AES69-2022 standard
|
38
|
+
(see references). These are the key features of sofar
|
39
|
+
|
40
|
+
* Uses a complete definition of the AES69-2022 standard (see references) maintained at `sofa_conventions`_
|
41
|
+
* Read, edit, and write SOFA files
|
42
|
+
* Add custom attributes to SOFA files
|
43
|
+
* Full Verification of the content of a SOFA files against AES69-2022
|
44
|
+
* Upgrade data that uses outdated SOFA conventions
|
45
|
+
* Open license allows unrestricted use
|
46
|
+
* sofar is tested using continuous integration on
|
47
|
+
|
48
|
+
Installation
|
49
|
+
============
|
50
|
+
|
51
|
+
Use pip to install sofar
|
52
|
+
|
53
|
+
.. code-block:: console
|
54
|
+
|
55
|
+
$ pip install sofar
|
56
|
+
|
57
|
+
(Requires Python >= 3.8)
|
58
|
+
|
59
|
+
Getting Started
|
60
|
+
===============
|
61
|
+
|
62
|
+
Check out `read the docs`_ for example use cases a quick introduction to SOFA
|
63
|
+
and sofar, and the complete documentation. A more detailed introduction to SOFA
|
64
|
+
is given by Majdak et. al. 2022 (see references below) Packages related to
|
65
|
+
sofar are listed at `pyfar.org`_. For more information on the SOFA file format
|
66
|
+
visit `sofaconventions.org`_.
|
67
|
+
|
68
|
+
Contributing
|
69
|
+
============
|
70
|
+
|
71
|
+
Refer to the `contribution guidelines`_ for more information.
|
72
|
+
|
73
|
+
.. _sofa_conventions : https://github.com/pyfar/sofa_conventions
|
74
|
+
.. _contribution guidelines: https://github.com/pyfar/sofar/blob/develop/CONTRIBUTING.rst
|
75
|
+
.. _pyfar.org: https://pyfar.org
|
76
|
+
.. _read the docs: https://sofar.readthedocs.io/en/latest
|
77
|
+
.. _sofaconventions.org: https://sofaconventions.org
|
78
|
+
|
79
|
+
References
|
80
|
+
==========
|
81
|
+
|
82
|
+
AES69-2022: *AES standard for file exchange - Spatial acoustic data file
|
83
|
+
format*, Audio Engineering Society, Inc., New York, NY, USA.
|
84
|
+
(https://www.aes.org/publications/standards/search.cfm?docID=99)
|
85
|
+
|
86
|
+
P. Majdak, F. Zotter, F. Brinkmann, J. De Muynke, M. Mihocic, and M.
|
87
|
+
Noisternig, "Spatially Oriented Format for Acoustics 2.1: Introduction and
|
88
|
+
Recent Advances", *J. Audio Eng. Soc.*, vol. 70, no. 7/8, pp. 565-584,
|
89
|
+
Jul. 2022. DOI: https://doi.org/10.17743/jaes.2022.0026
|
@@ -0,0 +1,75 @@
|
|
1
|
+
sofar/__init__.py,sha256=aCj2m61sjgjlh0_kcGsZXm_cnKmlx5c-tQ5iEGvDjE4,595
|
2
|
+
sofar/io.py,sha256=XdkfG6jo4RI8JqUgXEfx2ka06_CfffPJXs0HqeMSri0,15761
|
3
|
+
sofar/sofa.py,sha256=QVr0UKr-aqo2K7_PEHtXk9VuvrjE36QgcYIqFJ6KBrc,68255
|
4
|
+
sofar/update_conventions.py,sha256=36ba0cAVtKppJCrsEhP2aiv1DGEtxNx06hsUAERDlwM,16753
|
5
|
+
sofar/utils.py,sha256=0JwYLLyVu1ZcAfUQKSucywcQPlHB2Ojez7fxIXhuaFw,10754
|
6
|
+
sofar/sofa_conventions/VERSION,sha256=IuQRql5P3qfPUQuJwuh-RX5TvI_Wd4bDdSm8VLNQqds,34
|
7
|
+
sofar/sofa_conventions/conventions/FreeFieldDirectivityTF_1.1.csv,sha256=Wait37FTh6ROKgpX7-JsVM-ohtKfsdHjxVL5vN7uTTs,5510
|
8
|
+
sofar/sofa_conventions/conventions/FreeFieldDirectivityTF_1.1.json,sha256=sm8Pj_Z1n1tW2jYlu00eikR3yCcANEm_Kw5Eq9jrbw4,13138
|
9
|
+
sofar/sofa_conventions/conventions/FreeFieldHRIR_1.0.csv,sha256=C_fc7wHjBLc2krPja33sQaDSZDvlr80GSsgOj904GBY,2479
|
10
|
+
sofar/sofa_conventions/conventions/FreeFieldHRIR_1.0.json,sha256=-777Wo-fgKq844AwxIlompmIF9rHLRXWIAJjgaqADdU,8127
|
11
|
+
sofar/sofa_conventions/conventions/FreeFieldHRTF_1.0.csv,sha256=BbE417cDQ6eQ8UFl3MVWv9SatQ2OHZ7UF7u_ZCfCoBk,2222
|
12
|
+
sofar/sofa_conventions/conventions/FreeFieldHRTF_1.0.json,sha256=aN7x7UU-HkT5a-cVmJ29IXP1h_8DFsyInsbWFm65oVk,7996
|
13
|
+
sofar/sofa_conventions/conventions/GeneralFIR-E_2.0.csv,sha256=daSPbImANSYn5rIHQkn6fdD3kE0Z-wq9iyCGfMEroGY,1998
|
14
|
+
sofar/sofa_conventions/conventions/GeneralFIR-E_2.0.json,sha256=ERs0oQrZL2JLQOcFE7umPIOIMqm2pQy_lOA8UPtLg18,6615
|
15
|
+
sofar/sofa_conventions/conventions/GeneralFIR_1.0.csv,sha256=HInvJ980U9wtu2QUT9JZ3rUlR7Qfi2yzgZb4j3q6n58,2011
|
16
|
+
sofar/sofa_conventions/conventions/GeneralFIR_1.0.json,sha256=dmVz1aTMv-9EKO9NwzTbWq-QU-uOa9d-zhS4ScKhDLU,7054
|
17
|
+
sofar/sofa_conventions/conventions/GeneralSOS_1.0.csv,sha256=3JzYYRULpd8WmCC0Mze6wTzr7PluwEjy6lxt74VDsow,2130
|
18
|
+
sofar/sofa_conventions/conventions/GeneralSOS_1.0.json,sha256=3FYXzhcb2jJFjlKFlF60Uv_82boZUNbfSVlS54It8xM,7357
|
19
|
+
sofar/sofa_conventions/conventions/GeneralTF-E_1.0.csv,sha256=lztME1Skiac-Bj1OctWuQ05_THozrPzil6a01F9PufI,2015
|
20
|
+
sofar/sofa_conventions/conventions/GeneralTF-E_1.0.json,sha256=bQZn8O67anSvZJgo7PkIP9TaPprBlhqPmzpp6dy8LQY,6757
|
21
|
+
sofar/sofa_conventions/conventions/GeneralTF_1.0.csv,sha256=RD1aXHuRIECKkHDZm0cLyRp-m32-hg_hRxNegdJnttc,1956
|
22
|
+
sofar/sofa_conventions/conventions/GeneralTF_1.0.json,sha256=mPbpQcfEeJt2lKJODGgEgusDOzLU3HbmD3Fr6LbXnt0,6696
|
23
|
+
sofar/sofa_conventions/conventions/GeneralTF_2.0.csv,sha256=8fpM1wnY4YL9DbIwstrU5UCokh9rheiookSJYSMqKR8,1957
|
24
|
+
sofar/sofa_conventions/conventions/GeneralTF_2.0.json,sha256=pn6V2AVJum8y0L0cpn3Kt5ostsmErjcF41_XeiPdk88,6699
|
25
|
+
sofar/sofa_conventions/conventions/SimpleFreeFieldHRIR_1.0.csv,sha256=gZ_jA2hAdetCxgV3P-I9T3QTG7hNwogon0r1pKpv8iM,2239
|
26
|
+
sofar/sofa_conventions/conventions/SimpleFreeFieldHRIR_1.0.json,sha256=ScZn_Jf9AI1o6gyFcdl5xFzdnvSfuMcdyG9GH5eOdDc,8446
|
27
|
+
sofar/sofa_conventions/conventions/SimpleFreeFieldHRSOS_1.0.csv,sha256=YXI6K_HiGH6wVTr3QIUhhtKQRKD3uUaj56D6xzCYUzE,2303
|
28
|
+
sofar/sofa_conventions/conventions/SimpleFreeFieldHRSOS_1.0.json,sha256=dG_O9QiWYrB8IkZD-z3W1GUJw8E_c7NT5CN4xOmhhwk,8254
|
29
|
+
sofar/sofa_conventions/conventions/SimpleFreeFieldHRTF_1.0.csv,sha256=x7fyeLrub2clGMszUtv6v9lg2cF8hI-FzeAqJEimXyc,2015
|
30
|
+
sofar/sofa_conventions/conventions/SimpleFreeFieldHRTF_1.0.json,sha256=HFNW4_1A3t1M6geUTUVLZrf7sAmhy59T47d__YXdylM,7791
|
31
|
+
sofar/sofa_conventions/conventions/SimpleFreeFieldSOS_1.0.csv,sha256=YphFfFEwTSosaYOTbUv2gz-Pb5yrQv7AFVZuXh-WUjM,2298
|
32
|
+
sofar/sofa_conventions/conventions/SimpleFreeFieldSOS_1.0.json,sha256=SILNWPWs3JFVtnDvKDpyZ3RBGboD8SWaqmaT83EUb7w,8249
|
33
|
+
sofar/sofa_conventions/conventions/SimpleHeadphoneIR_1.0.csv,sha256=7B_ijKN7AVftugYTvCCM8n76ofs8gCWkBvFc_9D-STk,3103
|
34
|
+
sofar/sofa_conventions/conventions/SimpleHeadphoneIR_1.0.json,sha256=pUUme-CuoipR06Yi26yfntWwMMWs7wrriqseg6NPK2M,9863
|
35
|
+
sofar/sofa_conventions/conventions/SingleRoomMIMOSRIR_1.0.csv,sha256=2OzG5t9rYzDrywP__99ylHmyAM9Mjx758jO5dudv0Jc,4053
|
36
|
+
sofar/sofa_conventions/conventions/SingleRoomMIMOSRIR_1.0.json,sha256=5tjPriXZk8WEIr15i41L7hQRwAxzw_gA0nxWkZfe_7g,14346
|
37
|
+
sofar/sofa_conventions/conventions/SingleRoomSRIR_1.0.csv,sha256=l7aeLNxbOyXgfQIjCo3QzpRpBonMp9ssfhps4fejHxc,4199
|
38
|
+
sofar/sofa_conventions/conventions/SingleRoomSRIR_1.0.json,sha256=gVwyzGni9e3w5wehnxe6Oe5knO2Np5doHYLzRsfrlfc,14491
|
39
|
+
sofar/sofa_conventions/conventions/deprecated/FreeFieldDirectivityTF_1.0.csv,sha256=Ha6178_LxRod-L2UcI7SX3o6Cf1YRFs1zgIpn0kd0rA,5125
|
40
|
+
sofar/sofa_conventions/conventions/deprecated/FreeFieldDirectivityTF_1.0.json,sha256=GcOb3cbv63C7S4cJ7IVvU7NAAd1h8mA7RHaAFQ_jqz8,12627
|
41
|
+
sofar/sofa_conventions/conventions/deprecated/GeneralFIRE_1.0.csv,sha256=2_ed7Y0CGJmCx9V3X7L4gipJ94nLzH7Jl9fXXtI7rLQ,1988
|
42
|
+
sofar/sofa_conventions/conventions/deprecated/GeneralFIRE_1.0.json,sha256=y3A1fhyS4XUAIbEGv92pwrYp01HutgIKajI4WmJYYKQ,6603
|
43
|
+
sofar/sofa_conventions/conventions/deprecated/MultiSpeakerBRIR_0.3.csv,sha256=E_jW15CNWUsp5W9Uw2EbNNyvDxDMFvs-CAELDmYtp0I,2560
|
44
|
+
sofar/sofa_conventions/conventions/deprecated/MultiSpeakerBRIR_0.3.json,sha256=PBC2SOliEORqJpuSweO77_tePFBVVoJ6zZ_COUNrZME,8884
|
45
|
+
sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldHRIR_0.4.csv,sha256=WQRiDw51jZK9yGyfCtUVGpKbi0FMl-BtG2yn-5s5AME,2093
|
46
|
+
sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldHRIR_0.4.json,sha256=sGWA33puXRpPmoMOZfBT0SgbLGIJIACNcxsJdACmKKY,7704
|
47
|
+
sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_0.4.csv,sha256=lHINbpcuvHBNsAOL6KGWPy3SMEHorFcovgafXivBt0w,1989
|
48
|
+
sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_0.4.json,sha256=RIAUjtb5QvLn3dny_b4uxdsHXD0nkxbK49w4RiJslJI,7770
|
49
|
+
sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_1.0.csv,sha256=nzm1DDWj3blCljLFQZ1K2OU_GKgkW9x0Ow638aL_cys,2028
|
50
|
+
sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_1.0.json,sha256=aXc5prNxJslNt6ESYACRHhzUxMWa80rDhvChRxGSoDM,7770
|
51
|
+
sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.1.csv,sha256=eADQZ2qv5odeUMQsgc8YZQQA5sRN-2M3Ym5LnGrbI_E,3153
|
52
|
+
sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.1.json,sha256=5AUrkT4s-FMFkqbVEiak7YKugtzwb0iU7o3XuYbNtYs,9870
|
53
|
+
sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.2.csv,sha256=sBlw9J3Clix0b7aPRo-uQntM43pqBlqSG00OHJ_i7OM,3153
|
54
|
+
sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.2.json,sha256=fHK0c3DUTvAUxOAKC1LuJZlxSyKoSjF0HpJkkpUYCyA,9870
|
55
|
+
sofar/sofa_conventions/conventions/deprecated/SingleRoomDRIR_0.2.csv,sha256=JsPEm-KgQ_b_2nGzVys4jnJTSedSQtcoOk1x31ycqLE,2045
|
56
|
+
sofar/sofa_conventions/conventions/deprecated/SingleRoomDRIR_0.2.json,sha256=oR-qubagKs28ICI7243Ep2zX0AFn_8lvYPNPseg16O8,8097
|
57
|
+
sofar/sofa_conventions/conventions/deprecated/SingleRoomDRIR_0.3.csv,sha256=l6PAKjsNuaFHVUlT4rVdZY9Pj7nh_jF8d82i5YSbkss,1999
|
58
|
+
sofar/sofa_conventions/conventions/deprecated/SingleRoomDRIR_0.3.json,sha256=ccCvFNWYwATIRCPxQke73ckogc2Y3hBP97_JOtPusns,8097
|
59
|
+
sofar/sofa_conventions/rules/deprecations.json,sha256=hyq112XNfojROZsAEbcV7EOLin-xaTiNi4o4WdplS74,453
|
60
|
+
sofar/sofa_conventions/rules/rules.json,sha256=iH8e5UUVRQcGZ_AoULk4A2IiAT1yK7R2CvRQyCMYhYA,22501
|
61
|
+
sofar/sofa_conventions/rules/unit_aliases.json,sha256=QXgOfpe8bnmUybVhSSj3nIUYw50CAVehh7sdFNPFl0k,212
|
62
|
+
sofar/sofa_conventions/rules/upgrade.json,sha256=viHPPqBaPblE-fY8BOuVUraQYrBhckrS3w5XKqHCTIE,5167
|
63
|
+
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
64
|
+
tests/test_deprecations.py,sha256=coHOEq5XUquYxRge8XKeg005M84wcgSHTEE--CoEar4,584
|
65
|
+
tests/test_io.py,sha256=xMBdWP3vdnk_6aYAhg30nEgGvPfYm30a1qouGHIwRNo,11808
|
66
|
+
tests/test_sofa.py,sha256=DpiI517T-gLoajy9nQCHT7ouxM40JwirsqkVMFT5cWo,11314
|
67
|
+
tests/test_sofa_upgrade_conventions.py,sha256=HqIuvFFub1_HCH6qHTLPcPyBZ2ayDKzmZR7RwnTxo_o,3377
|
68
|
+
tests/test_sofa_verify.py,sha256=cleTkFJurJfDEZrnSTfBYe2VqsCZ1VxWr9efNGjWr88,15631
|
69
|
+
tests/test_utils.py,sha256=yT290xCaeDW4l3DHUeFGiVmLe9xMguETN1NAGRTJ-X4,8307
|
70
|
+
sofar-1.1.0.dist-info/AUTHORS.rst,sha256=JGcf9PJwl0w14PgTRjI3HXpcwDbkGbUN4mkYoZ8smL8,164
|
71
|
+
sofar-1.1.0.dist-info/LICENSE,sha256=qdGH_RUPveBBfGYShm6OIBd6bRJdwp3fDCpjMsowmqA,1068
|
72
|
+
sofar-1.1.0.dist-info/METADATA,sha256=brotqjwBrvQqE3_i4ncZSatGgM0B5IOx49trmGmqH-c,3168
|
73
|
+
sofar-1.1.0.dist-info/WHEEL,sha256=a-zpFRIJzOq5QfuhBzbhiA1eHTzNCJn8OdRvhdNX0Rk,110
|
74
|
+
sofar-1.1.0.dist-info/top_level.txt,sha256=2JSMmeQ5zeMumFynTJU1oODh09wxSiN3gFpsY-bNsSo,12
|
75
|
+
sofar-1.1.0.dist-info/RECORD,,
|
tests/__init__.py
ADDED
File without changes
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import pytest
|
2
|
+
from packaging import version
|
3
|
+
import re
|
4
|
+
import sofar as sf
|
5
|
+
|
6
|
+
|
7
|
+
# deprecate in 1.3.0 ----------------------------------------------------------
|
8
|
+
def test_pad_zero_modi():
|
9
|
+
with pytest.warns(
|
10
|
+
UserWarning,
|
11
|
+
match=re.escape('Sofa.info() will be deprecated in sofar 1.3.0')):
|
12
|
+
sofa = sf.Sofa('GeneralTF')
|
13
|
+
sofa.info()
|
14
|
+
|
15
|
+
if version.parse(sf.__version__) >= version.parse('1.3.0'):
|
16
|
+
with pytest.raises(ValueError):
|
17
|
+
# remove Sofa.info() from pyfar 1.3.0!
|
18
|
+
sofa = sf.Sofa('GeneralTF')
|
19
|
+
sofa.info()
|