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.
Files changed (79) hide show
  1. sofar/__init__.py +13 -7
  2. sofar/io.py +423 -0
  3. sofar/sofa.py +1795 -0
  4. sofar/sofa_conventions/VERSION +1 -0
  5. sofar/sofa_conventions/conventions/FreeFieldDirectivityTF_1.1.csv +59 -0
  6. sofar/sofa_conventions/conventions/FreeFieldDirectivityTF_1.1.json +444 -0
  7. sofar/{conventions/source → sofa_conventions/conventions}/FreeFieldHRIR_1.0.csv +3 -3
  8. sofar/{conventions → sofa_conventions/conventions}/FreeFieldHRIR_1.0.json +3 -3
  9. sofar/{conventions/source → sofa_conventions/conventions}/FreeFieldHRTF_1.0.csv +2 -2
  10. sofar/{conventions → sofa_conventions/conventions}/FreeFieldHRTF_1.0.json +3 -3
  11. sofar/{conventions/source → sofa_conventions/conventions}/GeneralFIR-E_2.0.csv +2 -2
  12. sofar/{conventions → sofa_conventions/conventions}/GeneralFIR-E_2.0.json +2 -2
  13. sofar/{conventions/source/GeneralFIR_2.0.csv → sofa_conventions/conventions/GeneralFIR_1.0.csv} +2 -2
  14. sofar/{conventions/GeneralFIR_2.0.json → sofa_conventions/conventions/GeneralFIR_1.0.json} +2 -2
  15. sofar/{conventions/source/GeneralFIR_1.0.csv → sofa_conventions/conventions/GeneralSOS_1.0.csv} +11 -11
  16. sofar/{conventions/GeneralFIR_1.0.json → sofa_conventions/conventions/GeneralSOS_1.0.json} +48 -37
  17. sofar/{conventions/source → sofa_conventions/conventions}/GeneralTF-E_1.0.csv +3 -3
  18. sofar/{conventions → sofa_conventions/conventions}/GeneralTF-E_1.0.json +4 -4
  19. sofar/{conventions/source → sofa_conventions/conventions}/GeneralTF_1.0.csv +1 -1
  20. sofar/{conventions → sofa_conventions/conventions}/GeneralTF_1.0.json +1 -1
  21. sofar/{conventions/source → sofa_conventions/conventions}/GeneralTF_2.0.csv +4 -4
  22. sofar/{conventions → sofa_conventions/conventions}/GeneralTF_2.0.json +4 -4
  23. sofar/sofa_conventions/conventions/SimpleFreeFieldHRIR_1.0.csv +47 -0
  24. sofar/{conventions → sofa_conventions/conventions}/SimpleFreeFieldHRIR_1.0.json +1 -1
  25. sofar/{conventions/source → sofa_conventions/conventions}/SimpleFreeFieldHRSOS_1.0.csv +1 -1
  26. sofar/{conventions → sofa_conventions/conventions}/SimpleFreeFieldHRSOS_1.0.json +1 -1
  27. sofar/{conventions/source/SimpleFreeFieldHRTF_2.0.csv → sofa_conventions/conventions/SimpleFreeFieldHRTF_1.0.csv} +3 -3
  28. sofar/{conventions/SimpleFreeFieldHRTF_2.0.json → sofa_conventions/conventions/SimpleFreeFieldHRTF_1.0.json} +4 -4
  29. sofar/{conventions/source → sofa_conventions/conventions}/SimpleHeadphoneIR_1.0.csv +9 -9
  30. sofar/sofa_conventions/conventions/SimpleHeadphoneIR_1.0.json +396 -0
  31. sofar/{conventions/source → sofa_conventions/conventions}/SingleRoomMIMOSRIR_1.0.csv +18 -8
  32. sofar/{conventions → sofa_conventions/conventions}/SingleRoomMIMOSRIR_1.0.json +124 -50
  33. sofar/{conventions/source → sofa_conventions/conventions}/SingleRoomSRIR_1.0.csv +18 -8
  34. sofar/{conventions → sofa_conventions/conventions}/SingleRoomSRIR_1.0.json +124 -50
  35. sofar/{conventions/source → sofa_conventions/conventions/deprecated}/FreeFieldDirectivityTF_1.0.csv +2 -2
  36. sofar/{conventions → sofa_conventions/conventions/deprecated}/FreeFieldDirectivityTF_1.0.json +2 -2
  37. sofar/sofa_conventions/conventions/deprecated/MultiSpeakerBRIR_0.3.csv +48 -0
  38. sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldHRIR_0.4.csv +43 -0
  39. sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldHRIR_0.4.json +333 -0
  40. sofar/{conventions/source/SimpleFreeFieldHRIR_1.0.csv → sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_0.4.csv} +15 -18
  41. sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_0.4.json +340 -0
  42. sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_1.0.csv +44 -0
  43. sofar/sofa_conventions/conventions/deprecated/SimpleFreeFieldTF_1.0.json +340 -0
  44. sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.1.csv +51 -0
  45. sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.1.json +396 -0
  46. sofar/sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.2.csv +51 -0
  47. sofar/{conventions/SimpleHeadphoneIR_1.0.json → sofa_conventions/conventions/deprecated/SimpleHeadphoneIR_0.2.json} +3 -3
  48. sofar/sofa_conventions/conventions/deprecated/SingleRoomDRIR_0.2.csv +47 -0
  49. sofar/sofa_conventions/conventions/deprecated/SingleRoomDRIR_0.2.json +360 -0
  50. sofar/sofa_conventions/rules/deprecations.json +12 -0
  51. sofar/sofa_conventions/rules/rules.json +800 -0
  52. sofar/sofa_conventions/rules/unit_aliases.json +11 -0
  53. sofar/sofa_conventions/rules/upgrade.json +190 -0
  54. sofar/update_conventions.py +427 -0
  55. sofar/utils.py +315 -0
  56. {sofar-0.3.1.dist-info → sofar-1.1.0.dist-info}/AUTHORS.rst +1 -0
  57. sofar-1.1.0.dist-info/METADATA +89 -0
  58. sofar-1.1.0.dist-info/RECORD +75 -0
  59. {sofar-0.3.1.dist-info → sofar-1.1.0.dist-info}/WHEEL +1 -1
  60. {sofar-0.3.1.dist-info → sofar-1.1.0.dist-info}/top_level.txt +1 -0
  61. tests/__init__.py +0 -0
  62. tests/test_deprecations.py +19 -0
  63. tests/test_io.py +344 -0
  64. tests/test_sofa.py +354 -0
  65. tests/test_sofa_upgrade_conventions.py +102 -0
  66. tests/test_sofa_verify.py +472 -0
  67. tests/test_utils.py +241 -0
  68. sofar/conventions/source/MultiSpeakerBRIR_0.3.csv +0 -48
  69. sofar/sofar.py +0 -2531
  70. sofar-0.3.1.dist-info/METADATA +0 -69
  71. sofar-0.3.1.dist-info/RECORD +0 -46
  72. /sofar/{conventions/source → sofa_conventions/conventions}/SimpleFreeFieldSOS_1.0.csv +0 -0
  73. /sofar/{conventions → sofa_conventions/conventions}/SimpleFreeFieldSOS_1.0.json +0 -0
  74. /sofar/{conventions/source → sofa_conventions/conventions/deprecated}/GeneralFIRE_1.0.csv +0 -0
  75. /sofar/{conventions → sofa_conventions/conventions/deprecated}/GeneralFIRE_1.0.json +0 -0
  76. /sofar/{conventions → sofa_conventions/conventions/deprecated}/MultiSpeakerBRIR_0.3.json +0 -0
  77. /sofar/{conventions/source → sofa_conventions/conventions/deprecated}/SingleRoomDRIR_0.3.csv +0 -0
  78. /sofar/{conventions → sofa_conventions/conventions/deprecated}/SingleRoomDRIR_0.3.json +0 -0
  79. {sofar-0.3.1.dist-info → sofar-1.1.0.dist-info}/LICENSE +0 -0
tests/test_utils.py ADDED
@@ -0,0 +1,241 @@
1
+ import shutil
2
+ import sofar as sf
3
+ from sofar.utils import _get_conventions
4
+ from sofar.update_conventions import _compile_conventions, _check_congruency
5
+ import os
6
+ import json
7
+ from tempfile import TemporaryDirectory
8
+ import pytest
9
+ from pytest import raises
10
+ import numpy as np
11
+ from copy import deepcopy
12
+ import warnings
13
+
14
+
15
+ def test_list_conventions(capfd):
16
+
17
+ # check output to console using pytest default fixture capfd
18
+ sf.list_conventions()
19
+ out, _ = capfd.readouterr()
20
+ assert "Available SOFA conventions:" in out
21
+
22
+
23
+ def test__get_conventions(capfd):
24
+
25
+ # check the return type
26
+ paths = _get_conventions(return_type="path")
27
+ assert isinstance(paths, list)
28
+ assert os.path.isfile(paths[0])
29
+ assert paths[0].endswith(".json")
30
+ out, _ = capfd.readouterr()
31
+ assert out == ""
32
+
33
+ paths = _get_conventions(return_type="path_source")
34
+ assert isinstance(paths, list)
35
+ assert os.path.isfile(paths[0])
36
+ assert paths[0].endswith(".csv")
37
+ out, _ = capfd.readouterr()
38
+ assert out == ""
39
+
40
+ names = _get_conventions(return_type="name")
41
+ assert isinstance(names, list)
42
+ assert not os.path.isfile(names[0])
43
+
44
+ names_versions = _get_conventions(return_type="name_version")
45
+ assert isinstance(names_versions, list)
46
+ assert isinstance(names_versions[0], tuple)
47
+
48
+ with raises(ValueError, match="return_type None is invalid"):
49
+ _get_conventions(return_type="None")
50
+
51
+
52
+ @pytest.mark.parametrize('branch', ['master', 'development'])
53
+ def test__congruency(capfd, branch):
54
+ """
55
+ Check if conventions from SOFAToolbox and sofaconventions.org are
56
+ identical.
57
+ """
58
+ out, _ = capfd.readouterr()
59
+ _check_congruency(branch=branch)
60
+ out, _ = capfd.readouterr()
61
+ if out != "":
62
+ warnings.warn(out, Warning)
63
+
64
+
65
+ def test_update_conventions(capfd):
66
+
67
+ # create temporary directory and copy existing conventions
68
+ temp_dir = TemporaryDirectory()
69
+ work_dir = os.path.join(temp_dir.name, "sofa_conventions", "conventions")
70
+ shutil.copytree(
71
+ os.path.join(
72
+ os.path.dirname(__file__), "..", "sofar", "sofa_conventions"),
73
+ os.path.join(temp_dir.name, "sofa_conventions"))
74
+
75
+ # delete standardized GeneralTF_2.0 to test adding
76
+ os.remove(os.path.join(work_dir, "GeneralTF_2.0.csv"))
77
+ os.remove(os.path.join(work_dir, "GeneralTF_2.0.json"))
78
+ # modify standardized GeneralFIR_1.0 to test updating
79
+ with open(os.path.join(work_dir, "GeneralFIR_1.0.csv"), "w") as fid:
80
+ fid.write("modified")
81
+ # move MultiSpeakerBRIR_0.3 to standardized to test deprecation
82
+ os.rename(
83
+ os.path.join(work_dir, "deprecated", "MultiSpeakerBRIR_0.3.csv"),
84
+ os.path.join(work_dir, "MultiSpeakerBRIR_0.3.csv"))
85
+ os.rename(
86
+ os.path.join(work_dir, "deprecated", "MultiSpeakerBRIR_0.3.json"),
87
+ os.path.join(work_dir, "MultiSpeakerBRIR_0.3.json"))
88
+ # modify deprecated convention to test updating
89
+ with open(os.path.join(work_dir, "deprecated",
90
+ "SimpleFreeFieldHRIR_0.4.csv"), "w") as fid:
91
+ fid.write("modified")
92
+ # delete deprecated GeneralTF_2.0 to test adding
93
+ os.remove(os.path.join(
94
+ work_dir, "deprecated", "SimpleFreeFieldTF_0.4.csv"))
95
+ os.remove(os.path.join(
96
+ work_dir, "deprecated", "SimpleFreeFieldTF_0.4.json"))
97
+
98
+ # first run to test if conventions were updated
99
+ sf.update_conventions(conventions_path=work_dir, assume_yes=True)
100
+ out, _ = capfd.readouterr()
101
+ assert "added convention: GeneralTF_2.0" in out
102
+ assert "updated convention: GeneralFIR_1.0" in out
103
+ assert "deprecated convention: MultiSpeakerBRIR_0.3" in out
104
+ assert not os.path.isfile(
105
+ os.path.join(work_dir, "MultiSpeakerBRIR_0.3.csv"))
106
+ assert not os.path.isfile(
107
+ os.path.join(work_dir, "MultiSpeakerBRIR_0.3.json"))
108
+ assert "updated deprecated convention: SimpleFreeFieldHRIR_0.4" in out
109
+ assert "added deprecated convention: SimpleFreeFieldTF_0.4" in out
110
+
111
+ # second run to make sure that up to date conventions are not overwritten
112
+ sf.update_conventions(conventions_path=work_dir, assume_yes=True)
113
+ out, _ = capfd.readouterr()
114
+ assert "added" not in out
115
+ assert "updated" not in out
116
+ assert "deprecated" not in out
117
+
118
+
119
+ def test__compile_conventions():
120
+ """Test compiling the json conventions from the csv files."""
121
+
122
+ # create temporary directory and copy existing source conventions
123
+ temp_dir = TemporaryDirectory()
124
+ shutil.copytree(
125
+ os.path.join(os.path.dirname(__file__), "..", "sofar",
126
+ "sofa_conventions", "conventions"),
127
+ os.path.join(temp_dir.name, "conventions"))
128
+
129
+ # compile conventions
130
+ _compile_conventions(os.path.join(temp_dir.name, "conventions"))
131
+
132
+ # get list of reference json files
133
+ paths_ref = _get_conventions("path")
134
+ paths_test = _get_conventions(
135
+ "path", os.path.join(temp_dir.name, "conventions"))
136
+
137
+ for path_ref, path_test in zip(paths_ref, paths_test):
138
+
139
+ # load reference conventions
140
+ with open(path_ref, "r") as file:
141
+ ref_data = json.load(file)
142
+
143
+ with open(path_test, "r") as file:
144
+ test_data = json.load(file)
145
+
146
+ # compare conventions
147
+ assert ref_data == test_data
148
+
149
+
150
+ def test_equals_global_parameters():
151
+
152
+ sofa_a = sf.Sofa("SimpleFreeFieldHRIR")
153
+
154
+ # check invalid
155
+ with raises(ValueError, match="exclude is"):
156
+ sf.equals(sofa_a, sofa_a, exclude="wrong")
157
+
158
+ # check identical objects
159
+ assert sf.equals(sofa_a, sofa_a)
160
+
161
+ # check different number of keys
162
+ sofa_b = deepcopy(sofa_a)
163
+ sofa_b.protected = False
164
+ delattr(sofa_b, "ReceiverPosition")
165
+ sofa_b.protected = True
166
+ with pytest.warns(UserWarning, match="not identical: sofa_a has"):
167
+ is_identical = sf.equals(sofa_a, sofa_b)
168
+ assert not is_identical
169
+
170
+ # check different keys
171
+ sofa_b = deepcopy(sofa_a)
172
+ sofa_b.protected = False
173
+ sofa_b.PositionReceiver = sofa_b.ReceiverPosition
174
+ delattr(sofa_b, "ReceiverPosition")
175
+ sofa_b.protected = True
176
+ with pytest.warns(UserWarning, match="not identical: sofa_a and sofa_b"):
177
+ is_identical = sf.equals(sofa_a, sofa_b)
178
+ assert not is_identical
179
+
180
+ # check mismatching data types
181
+ sofa_b = deepcopy(sofa_a)
182
+ sofa_b.protected = False
183
+ sofa_b._convention["ReceiverPosition"]["type"] = "int"
184
+ sofa_b.protected = True
185
+ with pytest.warns(UserWarning, match="not identical: ReceiverPosition"):
186
+ is_identical = sf.equals(sofa_a, sofa_b)
187
+ assert not is_identical
188
+
189
+ # check exclude GLOBAL attributes
190
+ sofa_b = deepcopy(sofa_a)
191
+ sofa_b.protected = False
192
+ delattr(sofa_b, "GLOBAL_Version")
193
+ sofa_b.protected = True
194
+ is_identical = sf.equals(sofa_a, sofa_b, exclude="GLOBAL")
195
+ assert is_identical
196
+
197
+ # check exclude Date attributes
198
+ sofa_b = deepcopy(sofa_a)
199
+ sofa_b.protected = False
200
+ delattr(sofa_b, "GLOBAL_DateModified")
201
+ sofa_b.protected = True
202
+ is_identical = sf.equals(sofa_a, sofa_b, exclude="DATE")
203
+ assert is_identical
204
+
205
+ # check exclude Date attributes
206
+ sofa_b = deepcopy(sofa_a)
207
+ sofa_b.protected = False
208
+ delattr(sofa_b, "GLOBAL_DateModified")
209
+ sofa_b.protected = True
210
+ is_identical = sf.equals(sofa_a, sofa_b, exclude="ATTR")
211
+ assert is_identical
212
+
213
+
214
+ @pytest.mark.parametrize("value_a, value_b, attribute, fails", [
215
+ ("1", "2", "GLOBAL_SOFAConventionsVersion", True),
216
+ ([[1, 2]], [1, 2], "Data_IR", False),
217
+ ([[1, 2]], [1, 3], "Data_IR", True),
218
+ ("HD 650", ["HD 650"], "SourceModel", False),
219
+ ("HD 650", np.array(["HD 650"], dtype="U"), "SourceModel", False),
220
+ ("HD 650", np.array(["HD 650"], dtype="S"), "SourceModel", False),
221
+ ("HD 650", "HD-650", "SourceModel", True)
222
+ ])
223
+ def test_equals_attribute_values(value_a, value_b, attribute, fails):
224
+
225
+ # generate SOFA objects (SimpleHeadphoneIR has string variables)
226
+ sofa_a = sf.Sofa("SimpleHeadphoneIR")
227
+ sofa_a.protected = False
228
+ sofa_b = deepcopy(sofa_a)
229
+
230
+ # set parameters
231
+ setattr(sofa_a, attribute, value_a)
232
+ sofa_a.protected = True
233
+ setattr(sofa_b, attribute, value_b)
234
+ sofa_b.protected = True
235
+
236
+ # compare
237
+ if fails:
238
+ with pytest.warns(UserWarning):
239
+ assert not sf.equals(sofa_a, sofa_b)
240
+ else:
241
+ assert sf.equals(sofa_a, sofa_b)
@@ -1,48 +0,0 @@
1
- Name Default Flags Dimensions Type Comment
2
- GLOBAL:Conventions SOFA rm attribute
3
- GLOBAL:Version 1.0 rm attribute
4
- GLOBAL:SOFAConventions MultiSpeakerBRIR rm attribute This convention is for BRIRs recorded in reverberant conditions from multiple loudspeaker sources at a number of listener orientations.
5
- GLOBAL:SOFAConventionsVersion 0.3 rm attribute
6
- GLOBAL:APIName rm attribute
7
- GLOBAL:APIVersion rm attribute
8
- GLOBAL:ApplicationName attribute
9
- GLOBAL:ApplicationVersion attribute
10
- GLOBAL:AuthorContact m attribute
11
- GLOBAL:Comment m attribute
12
- GLOBAL:DataType FIRE rm attribute We use FIR datatype which in addition depends on Emitters (E)
13
- GLOBAL:History attribute
14
- GLOBAL:License No license provided, ask the author for permission m attribute
15
- GLOBAL:Organization m attribute
16
- GLOBAL:References attribute
17
- GLOBAL:RoomType reverberant m attribute
18
- GLOBAL:Origin attribute
19
- GLOBAL:DateCreated m attribute
20
- GLOBAL:DateModified m attribute
21
- GLOBAL:Title m attribute
22
- ListenerPosition [0 0 0] m IC, MC double
23
- ListenerPosition:Type cartesian m attribute
24
- ListenerPosition:Units metre m attribute
25
- ReceiverPosition [0 0.09 0; 0 -0.09 0] m rCI, rCM double
26
- ReceiverPosition:Type cartesian m attribute
27
- ReceiverPosition:Units metre m attribute
28
- SourcePosition [0 0 1] m IC, MC double
29
- SourcePosition:Type spherical m attribute
30
- SourcePosition:Units degree, degree, metre m attribute
31
- EmitterPosition [0 0 0] m eCI, eCM double Each speaker is represented as an emitter. Use EmitterPosition to represent the position of a particular speaker. Size of EmitterPosition determines E
32
- EmitterPosition:Type cartesian m attribute
33
- EmitterPosition:Units metre m attribute
34
- GLOBAL:DatabaseName m attribute name of the database to which these data belong
35
- GLOBAL:ListenerShortName m attribute ID of the subject from the database
36
- GLOBAL:RoomDescription attribute narrative description of the room
37
- ListenerUp [0 0 1] m IC, MC double
38
- ListenerView [1 0 0] m IC, MC double
39
- ListenerView:Type cartesian m attribute
40
- ListenerView:Units metre m attribute
41
- EmitterUp [0 0 1] ECI, ECM double When EmitterUp provided, EmitterView must be provided as well
42
- EmitterView [1 0 0] ECI, ECM double When EmitterView provided, EmitterUp must be provided as well
43
- EmitterView:Type cartesian attribute
44
- EmitterView:Units metre attribute
45
- Data.IR [1 1] m mREn double
46
- Data.SamplingRate 48000 m I double
47
- Data.SamplingRate:Units hertz m attribute
48
- Data.Delay [0 0] m IRE, MRE double